2026-04-28 f84e425bb9debb5f2af8e417bf07d2a2b0077609
Merge remote-tracking branch 'origin/dev_NEW_pro' into dev_NEW_pro
已添加2个文件
已修改6个文件
433 ■■■■ 文件已修改
src/api/basicData/storageAttachment.js 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionOrder.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Dialog/FileList.vue 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Modal/RepairModal.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/index.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/PlanModal.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/index.vue 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrderManagement/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/basicData/storageAttachment.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
// é™„件页面接口
import request from '@/utils/request'
// é™„件查询
export function attachmentList(query) {
    return request({
        url: '/basic/storage_attachment/list',
        method: 'get',
        params: query
    })
}
// é™„件新增
export function createAttachment(data) {
    return request({
        url: '/basic/storage_attachment/add',
        method: 'post',
        data
    })
}
// é™„件删除
export function deleteAttachment(data) {
    return request({
        url: '/basic/storage_attachment/delete',
        method: 'delete',
        data
    })
}
src/api/productionManagement/productionOrder.js
@@ -116,7 +116,7 @@
// ç”Ÿäº§è®¢å•-补料记录列表
export function listMaterialSupplementRecord(query) {
  return request({
    url: "/productOrderMaterial/supplementRecord",
    url: "/productionOrderPickRecord/feeding",
    method: "get",
    params: query,
  });
src/components/Dialog/FileList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,253 @@
<template>
  <el-dialog
      v-model="isShow"
      :title="title"
      :width="width"
      @close="handleClose"
      class="attachment-dialog"
  >
    <!-- å·¥å…·æ  -->
    <div class="toolbar">
      <el-button
          type="primary"
          size="small"
          @click="handleUpload"
      >
        ä¸Šä¼ é™„ä»¶
      </el-button>
    </div>
    <!-- ä¸Šä¼ ç»„件弹窗 -->
    <el-dialog
        v-model="uploadDialogVisible"
        title="上传附件"
        width="50%"
        @close="handleUploadClose"
    >
      <AttachmentUpload
          v-model:file-list="newFileList"
      />
      <template #footer>
        <el-button @click="handleUploadClose">关闭</el-button>
      </template>
    </el-dialog>
    <!-- æ–‡ä»¶åˆ—表表格 -->
    <div class="table-container">
      <el-table
          :data="tableData"
          border
          class="attachment-table"
          :height="tableData.length > 0 ? 'auto' : '120px'"
      >
        <el-table-column
            label="附件名称"
            prop="originalFilename"
            show-overflow-tooltip
        />
        <el-table-column
            v-if="showActions"
            fixed="right"
            label="操作"
            :width="120"
            align="center"
        >
          <template #default="scope">
            <el-button
                link
                type="primary"
                size="small"
                :href="scope.row.downloadURL"
                class="download-link"
            >
              ä¸‹è½½
            </el-button>
            <el-button
                link
                type="danger"
                size="small"
                @click="handleDelete(scope.row)"
            >
              åˆ é™¤
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </el-dialog>
</template>
<script setup>
import { ref, computed, getCurrentInstance, onMounted, watch } from 'vue'
import AttachmentUpload from '@/components/AttachmentUpload/file/index.vue'
import {attachmentList, deleteAttachment, createAttachment} from "@/api/basicData/storageAttachment.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  recordType: {
    type: String,
    default: '',
    required: true
  },
  recordId: {
    type: Number,
    default: 0,
    required: true
  },
  title: {
    type: String,
    default: '附件'
  },
  width: {
    type: String,
    default: '50%'
  },
  showActions: {
    type: Boolean,
    default: true
  }
})
const emit = defineEmits([
  'close',
  'download',
  'upload',
  'delete'
])
const { proxy } = getCurrentInstance()
const tableData = ref([])
const uploadDialogVisible = ref(false)
const newFileList = ref([])
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit("update:visible", val);
  },
});
const handleClose = () => {
  isShow.value = false
}
const handleUpload = () => {
  uploadDialogVisible.value = true
}
const handleUploadClose = async () => {
  // æ£€æŸ¥æ˜¯å¦æœ‰æ–°ä¸Šä¼ çš„æ–‡ä»¶
  if (newFileList.value.length > 0) {
    try {
      await createAttachment({
        application: 'file',
        recordType: props.recordType,
        recordId: props.recordId,
        storageBlobDTOs: [...newFileList.value, ...tableData.value]
      })
      newFileList.value = []
      // åˆ·æ–°åˆ—表
      setList()
    } catch (error) {
      proxy?.$modal?.msgError('上传失败')
    }
  }
  uploadDialogVisible.value = false
}
const handleDelete = async (row, index) => {
  try {
    await deleteAttachment([row.storageAttachmentId])
    proxy?.$modal?.msgSuccess('删除成功')
    setList()
  } catch (error) {
    proxy?.$modal?.msgError('删除失败')
  }
}
const setList = () => {
  attachmentList({
    recordType: props.recordType,
    recordId: props.recordId,
  }).then(res => {
    if (res && res.data) {
      tableData.value = res.data || []
    }
  })
}
onMounted(() => {
  setList()
})
</script>
<style scoped>
.attachment-dialog {
  border-radius: 12px;
}
.toolbar {
  margin-bottom: 16px;
  text-align: right;
}
.table-container {
  max-height: 40vh;
  overflow-y: auto;
  min-height: 120px;
  padding-bottom: 16px;
  box-sizing: border-box;
  will-change: scroll-position;
  transform: translateZ(0);
  -webkit-overflow-scrolling: touch;
}
:deep(.el-table) {
  margin-bottom: 0;
}
:deep(.el-table__body-wrapper) {
  overflow-y: auto;
  will-change: transform;
  transform: translateZ(0);
}
:deep(.el-table__body tr) {
  transition: none;
}
:deep(.el-dialog__footer) {
  padding-top: 12px;
  border-top: 1px solid #e9ecef;
}
.attachment-table {
  border-radius: 8px;
}
:deep(.el-dialog__header) {
  background-color: #f8f9fa;
  border-bottom: 1px solid #e9ecef;
  padding: 16px 20px;
}
:deep(.el-dialog__title) {
  font-size: 16px;
  font-weight: 600;
}
:deep(.el-dialog__body) {
  padding: 16px 20px;
}
:deep(.el-table__empty-text) {
  color: #999;
}
</style>
src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -49,8 +49,8 @@
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="类目">
            <el-input v-model="form.machineryCategory" placeholder="请输入类目" />
          <el-form-item label="项目">
            <el-input v-model="form.machineryCategory" placeholder="请输入项目" />
          </el-form-item>
        </el-col>
      </el-row>
@@ -77,12 +77,20 @@
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="24">
          <el-form-item label="附件" prop="attachmentIds">
            <FileUpload v-model:file-list="form.storageBlobDTOs" />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </FormDialog>
</template>
<script setup>
import FormDialog from "@/components/Dialog/FormDialog.vue";
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
import {
  addRepair,
  editRepair,
@@ -106,6 +114,7 @@
const userStore = useUserStore();
const deviceOptions = ref([]);
const fileList = ref([]);
const loadDeviceName = async () => {
  const { data } = await getDeviceLedger();
@@ -121,6 +130,7 @@
  remark: undefined, // æ•…障现象
  status: 0, // æŠ¥ä¿®çŠ¶æ€
  machineryCategory: undefined,
  storageBlobDTOs: [],
});
const setDeviceModel = (deviceId) => {
@@ -137,6 +147,7 @@
  form.remark = data.remark;
  form.status = data.status;
  form.machineryCategory = data.machineryCategory;
  form.storageBlobDTOs = data.storageBlobVOs || [];
};
const sendForm = async () => {
@@ -168,6 +179,7 @@
const openAdd = async () => {
  id.value = undefined;
  visible.value = true;
  fileList.value = [];
  await nextTick();
  await loadDeviceName();
};
src/views/equipmentManagement/repair/index.vue
@@ -127,22 +127,31 @@
          >
            åˆ é™¤
          </el-button>
          <el-button
              type="primary"
              link
              @click="openFileDialog(row)"
          >
            é™„ä»¶
          </el-button>
        </template>
      </PIMTable>
    </div>
    <RepairModal ref="repairModalRef" @ok="getTableData"/>
    <MaintainModal ref="maintainModalRef" @ok="getTableData"/>
    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" :record-type="'device_repair'" :record-id="recordId"  />
  </div>
</template>
<script setup>
import { onMounted, getCurrentInstance, computed } from "vue";
import {onMounted, getCurrentInstance, computed, ref, defineAsyncComponent} from "vue";
import {usePaginationApi} from "@/hooks/usePaginationApi";
import {getRepairPage, delRepair} from "@/api/equipmentManagement/repair";
import RepairModal from "./Modal/RepairModal.vue";
import {ElMessageBox, ElMessage} from "element-plus";
import dayjs from "dayjs";
import MaintainModal from "./Modal/MaintainModal.vue";
const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
defineOptions({
  name: "设备报修",
@@ -188,7 +197,7 @@
        prop: "deviceModel",
      },
      {
        label: "类目",
        label: "项目",
        align: "center",
        prop: "machineryCategory",
      },
@@ -258,6 +267,15 @@
  getTableData();
};
// æ‰“开附件弹窗
const recordId =ref(0)
const fileDialogVisible = ref(false)
const openFileDialog = async (row) => {
  recordId.value = row.id
  fileDialogVisible.value = true
}
// å¤šé€‰åŽåšä»€ä¹ˆ
const handleSelectionChange = (selectionList) => {
  multipleList.value = selectionList;
src/views/equipmentManagement/upkeep/Form/PlanModal.vue
@@ -32,10 +32,10 @@
          disabled
        />
      </el-form-item>
      <el-form-item label="类目">
      <el-form-item label="项目">
        <el-input
            v-model="form.machineryCategory"
            placeholder="请输入类目"
            placeholder="请输入项目"
        />
      </el-form-item>
      <el-form-item label="录入人">
@@ -73,6 +73,13 @@
          clearable
        />
      </el-form-item>
      <el-row :gutter="30">
        <el-col :span="24">
          <el-form-item label="附件" prop="attachmentIds">
            <FileUpload v-model:file-list="form.storageBlobDTOs" />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </FormDialog>
</template>
@@ -90,6 +97,7 @@
import { onMounted } from "vue";
import dayjs from "dayjs";
import { userListNoPage } from "@/api/system/user.js";
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
defineOptions({
  name: "设备保养新增计划",
@@ -115,6 +123,7 @@
  createUser: undefined, // å½•入人
  status: 0, //保修状态
  machineryCategory: undefined,
  storageBlobDTOs: [],
});
const setDeviceModel = (deviceId) => {
@@ -133,9 +142,12 @@
  form.createUser = Number(data.createUser);
  form.status = data.status;
  form.machineryCategory = data.machineryCategory;
  form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
    "YYYY-MM-DD HH:mm:ss"
  );
  if (data.maintenancePlanTime) {
    form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
      "YYYY-MM-DD HH:mm:ss"
    );
  }
  form.storageBlobDTOs = data.storageBlobVOs || [];
};
// ç”¨æˆ·åˆ—表
src/views/equipmentManagement/upkeep/index.vue
@@ -218,40 +218,27 @@
    <PlanModal ref="planModalRef" @ok="getTableData" />
        <MaintenanceModal ref="maintainModalRef" @ok="getTableData" />
        <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" />
    <FileListDialog
      ref="fileListDialogRef"
      v-model="fileDialogVisible"
      :show-upload-button="true"
      :show-delete-button="true"
      :delete-method="handleAttachmentDelete"
      :name-column-label="'附件名称'"
      :rulesRegulationsManagementId="currentMaintenanceTaskId"
      @upload="handleAttachmentUpload" />
    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" :record-type="'device_maintenance'" :record-id="currentMaintenanceTaskId"  />
  </div>
</template>
<script setup>
import { ref, onMounted, reactive, getCurrentInstance, nextTick, computed } from 'vue'
import {ref, onMounted, reactive, getCurrentInstance, nextTick, computed, defineAsyncComponent} from 'vue'
import { Search } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import PlanModal from './Form/PlanModal.vue'
import MaintenanceModal from './Form/MaintenanceModal.vue'
import FormDia from './Form/formDia.vue'
import FileListDialog from '@/components/Dialog/FileListDialog.vue'
import {
  getUpkeepPage,
  delUpkeep,
  deviceMaintenanceTaskList,
  deviceMaintenanceTaskDel,
} from '@/api/equipmentManagement/upkeep'
import {
  listMaintenanceTaskFiles,
  addMaintenanceTaskFile,
  delMaintenanceTaskFile,
} from '@/api/equipmentManagement/maintenanceTaskFile'
import dayjs from 'dayjs'
const { proxy } = getCurrentInstance()
const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
// Tab相关
const activeTab = ref('scheduled')
@@ -373,7 +360,7 @@
        prop: "createUserName",
    },
  {
    label: "类目",
    label: "项目",
    align: "center",
    prop: "machineryCategory",
  },
@@ -602,77 +589,10 @@
  getTableData()
}
// é™„件相关方法
// æŸ¥è¯¢é™„件列表
const fetchMaintenanceTaskFiles = async (deviceMaintenanceId) => {
  try {
    const params = {
      current: 1,
      size: 100,
      deviceMaintenanceId,
      rulesRegulationsManagementId:deviceMaintenanceId
    }
    const res = await listMaintenanceTaskFiles(params)
    const records = res?.data?.records || []
    const mapped = records.map(item => ({
      id: item.id,
      name: item.fileName || item.name,
      url: item.fileUrl || item.url,
      raw: item,
    }))
    fileListDialogRef.value?.setList(mapped)
  } catch (error) {
    ElMessage.error('获取附件列表失败')
  }
}
// æ‰“开附件弹窗
const openFileDialog = async (row) => {
  currentMaintenanceTaskId.value = row.id
  fileDialogVisible.value = true
  await fetchMaintenanceTaskFiles(row.id)
}
// åˆ·æ–°é™„件列表
const refreshFileList = async () => {
  if (!currentMaintenanceTaskId.value) return
  await fetchMaintenanceTaskFiles(currentMaintenanceTaskId.value)
}
// ä¸Šä¼ é™„ä»¶
const handleAttachmentUpload = async (filePayload) => {
  if (!currentMaintenanceTaskId.value) return
  try {
    const payload = {
      name: filePayload?.fileName || filePayload?.name,
      url: filePayload?.fileUrl || filePayload?.url,
      deviceMaintenanceId: currentMaintenanceTaskId.value,
    }
    await addMaintenanceTaskFile(payload)
    ElMessage.success('文件上传成功')
    await refreshFileList()
  } catch (error) {
    ElMessage.error('文件上传失败')
  }
}
// åˆ é™¤é™„ä»¶
const handleAttachmentDelete = async (row) => {
  if (!row?.id) return false
  try {
    await ElMessageBox.confirm('确认删除该附件?', '提示', { type: 'warning' })
  } catch {
    return false
  }
  try {
    await delMaintenanceTaskFile(row.id)
    ElMessage.success('删除成功')
    await refreshFileList()
    return true
  } catch (error) {
    ElMessage.error('删除失败')
    return false
  }
}
onMounted(() => {
src/views/productionManagement/workOrderManagement/index.vue
@@ -225,6 +225,7 @@
    {
      label: "工序名称",
      prop: "operationName",
      width: "100",
    },
    {
      label: "需求数量",