فهرست منبع

feat: 合并保养工单填报页核心交互

- 完善 maintenance-work-order 页面头部表单、设备列表、延迟保养弹窗和吸底保存操作栏
- 接入保养物料列表,支持按保养项展示、选择/新增/删除物料、查看绑定物料和费用联动
- 补充保存逻辑,按创建、填报、退回修改场景分别调用对应工单接口
- 兼容绑定物料弹窗的物料编码/名称字段显示
- 优化 ZmTableColumn 自适应列宽对 formatter 和数据长度变化的处理
Zimo 3 روز پیش
والد
کامیت
5457df27ab

+ 24 - 2
src/components/ZmTable/ZmTableColumn.vue

@@ -215,10 +215,28 @@ const getTextWidth = (text: string) => {
   return width
 }
 
+const getCellRawValue = (row: any) => {
+  if (!props.prop) return ''
+  return props.realValue?.(row) ?? row[props.prop]
+}
+
+const getCellMeasureValue = (row: any, index: number) => {
+  const rawValue = getCellRawValue(row)
+  if (props.coverFormatter) return rawValue
+  if (!props.formatter) return rawValue
+
+  const column = {
+    ...bindProps.value,
+    property: props.prop
+  } as TableColumnCtx<T extends DefaultRow ? T : DefaultRow>
+
+  return props.formatter(row, column, rawValue, index)
+}
+
 const calculativeWidth = () => {
   if (!props.prop) return
   const values = tableContext.data.value
-    .map((item) => props.realValue?.(item) ?? item[props.prop as keyof typeof item])
+    .map((item, index) => getCellMeasureValue(item, index))
     .filter(hasFilterValue)
   let labelWidth = getTextWidth(bindProps.value.label || '') + 32
   if (hasHeaderAction.value) labelWidth += 8
@@ -233,7 +251,11 @@ const calculativeWidth = () => {
 }
 
 watch(
-  [() => tableContext.loading.value, () => tableContext.columnMaxWidth.value],
+  [
+    () => tableContext.loading.value,
+    () => tableContext.columnMaxWidth.value,
+    () => tableContext.data.value.length
+  ],
   () => {
     nextTick(() => {
       calculativeWidth()

+ 40 - 44
src/views/pms/iotmainworkorder/DeviceBomMaterials.vue

@@ -1,19 +1,18 @@
 <template>
   <Dialog v-model="dialogVisible" title="保养项物料" style="width: 1200px; min-height: 400px">
-
     <!-- 新增设备分类和BOM节点信息显示 -->
-    <div style="margin: 0 0px 15px; background: #f5f7fa; padding: 12px 20px; border-radius: 4px;">
+    <div style=" padding: 12px 20px;margin: 0 0 15px; background: #f5f7fa; border-radius: 4px">
       <el-row>
         <el-col :span="8">
-          <span style="font-weight: bold; margin-right: 10px;">设备编码:</span>
+          <span style=" margin-right: 10px;font-weight: bold">设备编码:</span>
           <span>{{ deviceCode }}</span>
         </el-col>
         <el-col :span="8">
-          <span style="font-weight: bold; margin-right: 10px;">设备名称:</span>
+          <span style=" margin-right: 10px;font-weight: bold">设备名称:</span>
           <span>{{ deviceName }}</span>
         </el-col>
         <el-col :span="8">
-          <span style="font-weight: bold; margin-right: 10px;">{{bomNodeLabel}}:</span>
+          <span style=" margin-right: 10px;font-weight: bold">{{ bomNodeLabel }}:</span>
           <span>{{ bomNodeName }}</span>
         </el-col>
       </el-row>
@@ -25,29 +24,28 @@
         :model="queryParams"
         ref="queryFormRef"
         :inline="true"
-        label-width="68px"
-      >
+        label-width="68px">
         <el-form-item :label="t('workOrderMaterial.materialCode')" prop="code">
           <el-input
             v-model="queryParams.code"
             :placeholder="t('workOrderMaterial.codeHolder')"
             clearable
-            @keyup.enter="handleQuery"
-          />
+            @keyup.enter="handleQuery" />
         </el-form-item>
         <el-form-item :label="t('workOrderMaterial.materialName')" prop="name">
           <el-input
             v-model="queryParams.name"
             :placeholder="t('workOrderMaterial.nameHolder')"
             clearable
-            @keyup.enter="handleQuery"
-          />
+            @keyup.enter="handleQuery" />
         </el-form-item>
         <el-form-item>
-          <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" />
-            {{ t('workOrderMaterial.search') }}</el-button>
-          <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />
-            {{ t('workOrderMaterial.reset') }}</el-button>
+          <el-button @click="handleQuery"
+            ><Icon icon="ep:search" class="mr-5px" /> {{ t('workOrderMaterial.search') }}</el-button
+          >
+          <el-button @click="resetQuery"
+            ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('workOrderMaterial.reset') }}</el-button
+          >
         </el-form-item>
       </el-form>
     </ContentWrap>
@@ -59,21 +57,18 @@
         ref="tableRef"
         :show-overflow-tooltip="true"
         @row-click="handleRowClick"
-        :row-class-name="rowClassName"
-      >
+        :row-class-name="rowClassName">
         <el-table-column
           :label="t('workOrderMaterial.materialCode')"
           align="center"
           prop="code"
           :show-overflow-tooltip="true"
-          class="!w-100px"
-        />
+          class="!w-100px" />
         <el-table-column
           :label="t('workOrderMaterial.materialName')"
           align="center"
           prop="name"
-          :show-overflow-tooltip="true"
-        />
+          :show-overflow-tooltip="true" />
         <el-table-column :label="t('workOrderMaterial.unit')" align="center" prop="unit" />
         <el-table-column :label="t('route.quantity')" align="center" prop="quantity" />
       </el-table>
@@ -82,8 +77,7 @@
         :total="total"
         v-model:page="queryParams.pageNo"
         v-model:limit="queryParams.pageSize"
-        @pagination="handleLocalPagination"
-      />
+        @pagination="handleLocalPagination" />
     </ContentWrap>
   </Dialog>
 </template>
@@ -115,9 +109,9 @@ const tableRef = ref()
 const selectedRows = ref<WorkOrderBomMaterialApi.IotMainWorkOrderBomMaterialVO[]>([]) // 多选数据(存储所有选中行的数组)
 
 const deviceName = ref('') // 设备名称
-const deviceCode = ref('')    // 设备编码
-const bomNodeName = ref('')   // 保养项 or 维修项 名称
-const bomNodeLabel = ref('')   // 保养项 or 维修项 名称
+const deviceCode = ref('') // 设备编码
+const bomNodeName = ref('') // 保养项 or 维修项 名称
+const bomNodeLabel = ref('') // 保养项 or 维修项 名称
 
 const queryParams = reactive({
   pageNo: 1,
@@ -134,7 +128,7 @@ const defaultQueryParams = {
   name: '',
   code: '',
   pageNo: 1,
-  pageSize: 10,
+  pageSize: 10
 }
 
 const drawerVisible = ref<boolean>(false)
@@ -147,19 +141,19 @@ const addMaterial = ref(null)
 
 // 计算当前页的数据切片
 const paginatedList = computed(() => {
-  const start = (queryParams.pageNo - 1) * queryParams.pageSize;
+  const start = (queryParams.pageNo - 1) * queryParams.pageSize
   return filteredList.value.slice(start, start + queryParams.pageSize)
-});
+})
 
 // 计算总条数(直接使用 list.length)
-const total = computed(() => filteredList.value.length);
+const total = computed(() => filteredList.value.length)
 
 // 本地分页切换逻辑
 const handleLocalPagination = (e: { page: number; limit: number }) => {
-  queryParams.pageNo = e.page;
-  queryParams.pageSize = e.limit;
+  queryParams.pageNo = e.page
+  queryParams.pageSize = e.limit
   // 无需调用 getList,paginatedList 会自动更新
-};
+}
 
 // 点击整行选中
 const handleRowClick = (row) => {
@@ -174,7 +168,7 @@ const open = async (deptId: number, bomNodeId: number, row: any, type: string) =
   queryParams.deptId = deptId
   queryParams.bomNodeId = bomNodeId
   queryParams.deviceId = row.deviceId
-  if(type === 'repair'){
+  if (type === 'repair') {
     queryParams.deviceId = row.deviceId
   }
   deviceName.value = row.deviceName
@@ -189,17 +183,18 @@ const open = async (deptId: number, bomNodeId: number, row: any, type: string) =
   // 修改后的设备BOM物料处理逻辑
   let sourceData = []
   if (row.deviceBomMaterials) {
-    sourceData = typeof row.deviceBomMaterials === 'string'
-      ? JSON.parse(row.deviceBomMaterials)
-      : row.deviceBomMaterials
+    sourceData =
+      typeof row.deviceBomMaterials === 'string'
+        ? JSON.parse(row.deviceBomMaterials)
+        : row.deviceBomMaterials
   }
   // 统一字段并保存
   originalList.value = sourceData.map(normalizeItem)
   // 初始化过滤列表
   resetQuery()
   // 重置分页参数
-  queryParams.pageNo = 1;
-  queryParams.pageSize = 10;
+  queryParams.pageNo = 1
+  queryParams.pageSize = 10
   loading.value = false
 }
 
@@ -215,8 +210,8 @@ const handleClose = () => {
 // 字段统一处理
 const normalizeItem = (item: any): WorkOrderBomMaterialApi.IotMainWorkOrderBomMaterialVO => ({
   ...item,
-  code: item.code,
-  name: item.name
+  code: item.code || item.materialCode,
+  name: item.name || item.materialName
 })
 
 /** 搜索按钮操作 */
@@ -225,7 +220,7 @@ const handleQuery = () => {
   const { code, name } = queryParams
 
   // 本地过滤逻辑
-  filteredList.value = originalList.value.filter(item => {
+  filteredList.value = originalList.value.filter((item) => {
     const codeMatch = (item.code || '').toLowerCase().includes(code.toLowerCase())
     const nameMatch = (item.name || '').toLowerCase().includes(name.toLowerCase())
     return codeMatch && nameMatch
@@ -248,22 +243,23 @@ const resetQuery = () => {
 .no-label-radio .el-radio__label {
   display: none;
 }
+
 .no-label-radio .el-radio__inner {
   margin-right: 0;
 }
 
 /* 自定义淡绿色按钮 */
 :deep(.custom-green-button) {
+  color: #67c23a;
   background-color: #e1f3d8;
   border-color: #e1f3d8;
-  color: #67c23a;
 }
 
 /* 悬停效果 */
 :deep(.custom-green-button:hover) {
+  color: #5daf34;
   background-color: #d1e8c0;
   border-color: #d1e8c0;
-  color: #5daf34;
 }
 
 /* 点击效果 */

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1637 - 3
src/views/pms/iotmainworkorder/maintenance-work-order.vue


+ 69 - 0
src/views/pms/iotmainworkorder/types.ts

@@ -0,0 +1,69 @@
+export interface WorkOrder {
+  name?: string
+  outsourcingFlag?: number | string
+  cost?: number | string
+  actualStartTime?: number | string
+  actualEndTime?: number | string
+  otherCost?: number | string
+  remark?: string
+  deptId?: number
+  id?: number
+}
+
+export interface BomMaterial {
+  name?: string
+  code?: string
+  unit?: string
+  unitPrice?: number | null
+  price?: number | null
+  quantity?: number | null
+  depleteCount?: number | null
+  materialId?: number
+  materialSource?: string
+  factory?: string
+  factoryId?: number
+  costCenter?: string
+  costCenterId?: number
+  projectDepartment?: string
+  storageLocationId?: number
+  totalInventoryQuantity?: number
+}
+
+export interface OriginalBomList {
+  name: string
+  rule: number
+  status: number
+  bomNodeId: number
+  deviceId: number
+  deviceCode: string
+  deviceName: string
+  runningTimeRule: 0 | 1
+  mileageRule: 0 | 1
+  naturalDateRule: 0 | 1
+  mainRuntime: number | null
+  totalRunTime: number | null
+  tempTotalRunTime: number | null
+  mainMileage: number | null
+  totalMileage: number | null
+  tempTotalMileage: number | null
+  lastMaintenanceDate: number | null
+  lastRunningTime: number | null
+  nextRunningTime: number | null
+  lastRunningKilometers: number | null
+  nextRunningKilometers: number | null
+  lastNaturalDate: number | null
+  nextNaturalDate: number | null
+  kiloCycleLead: number | null
+  timePeriodLead: number | null
+  naturalDatePeriodLead: number | null
+  deviceBomMaterials?: BomMaterial[]
+  delayDuration: number | null
+  delayKilometers: number | null
+  delayNaturalDate: number | null
+  delayReason?: string | null
+}
+
+export interface BomList extends OriginalBomList {
+  group: string
+  initialStatus: number
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است