Bladeren bron

Merge remote-tracking branch 'origin/master'

lipenghui 1 week geleden
bovenliggende
commit
a1c5e90a48
1 gewijzigde bestanden met toevoegingen van 230 en 72 verwijderingen
  1. 230 72
      src/views/pms/iotmainworkorder/IotMainWorkOrderOptimize.vue

+ 230 - 72
src/views/pms/iotmainworkorder/IotMainWorkOrderOptimize.vue

@@ -152,6 +152,24 @@
           </template>
         </el-table-column>
 
+        <el-table-column :label="t('mainPlan.mainStatus')" align="center" width="140">
+          <template #default="scope">
+            <div class="status-container">
+              <el-switch
+                v-model="scope.row.status"
+                :active-value="1"
+                :inactive-value="0"
+                :disabled="scope.row.initialStatus === 1"
+                @change="handleStatusChange(scope.row)"
+                class="status-switch"
+              />
+              <span class="status-text">
+                {{ getStatusText(scope.row) }}
+              </span>
+            </div>
+          </template>
+        </el-table-column>
+
         <el-table-column :label="t('main.mileage')" key="mileageRule" align="center"
                          :width="columnWidths.mileageRule" v-if="false">
           <template #default="scope">
@@ -294,23 +312,6 @@
           </el-table-column>
         </el-table-column>
 
-        <el-table-column :label="t('mainPlan.mainStatus')" align="center" width="140">
-          <template #default="scope">
-            <div class="status-container">
-              <el-switch
-                v-model="scope.row.status"
-                :active-value="1"
-                :inactive-value="0"
-                :disabled="scope.row.initialStatus === 1"
-                @change="handleStatusChange(scope.row)"
-                class="status-switch"
-              />
-              <span class="status-text">
-                {{ getStatusText(scope.row) }}
-              </span>
-            </div>
-          </template>
-        </el-table-column>
         <!--
         <el-table-column :label="t('iotMaintain.numberOfMaterials')" align="center" width="90">
           <template #default="scope">
@@ -1263,6 +1264,31 @@ const handleSizeChange = (newSize: number) => {
 
 // 处理状态变化
 const handleStatusChange = (row: IotMainWorkOrderBomVO) => {
+  // 如果是从未完成(0)切换到完成(1)
+  if (row.status === 1 && row.initialStatus === 0) {
+    // 检查消耗物料规则:如果设置为不消耗物料(rule=1),则跳过物料校验
+    if (row.rule === 1) {
+      console.log(`保养项 ${row.name} 设置为不消耗物料,跳过物料校验`);
+      message.success(`${row.deviceCode}-${formatMaintItemName(row.name)} 已标记为完成`);
+      return;
+    }
+
+    // 校验物料数据
+    const isValid = validateBomItemMaterials(row.bomNodeId);
+
+    if (!isValid) {
+      // 获取无效物料信息用于提示
+      const invalidMaterials = getInvalidMaterialsInfo(row.bomNodeId);
+      const errorMessage = `请填写物料必填项\n无效物料:\n${invalidMaterials.join('\n')}`;
+
+      message.error(errorMessage);
+
+      // 重置状态为未完成
+      row.status = 0;
+      return;
+    }
+  }
+
   console.log(`保养项 ${row.name} 状态改为: ${row.status === 1 ? '完成' : '未完成'}`);
 
   // 这里可以添加状态变化后的业务逻辑
@@ -1322,7 +1348,7 @@ const handleAddMaterial = () => {
 
     // 临时标识(用于区分新记录)
     isNew: true,
-    tempId: Date.now() // 临时ID用于唯一标识
+    tempId: Date.now() + Math.random() // 临时ID用于唯一标识
   };
 
   // 生成唯一键
@@ -1386,7 +1412,7 @@ const refreshCurrentBomItemMaterials = () => {
     currentBomItem: currentBomItem.value.name,
     materialCount: materialList.value.length,
     uniqueKey: uniqueKey,
-    materials: materialList.value
+    showAllMaterials: showAllMaterials.value
   });
 }
 
@@ -1593,40 +1619,115 @@ const queryParams = reactive({
 
 // 获取指定bomNodeId的物料数量
 const getMaterialCount = (bomNodeId: number) => {
-  console.log('当前BOM节点:' + bomNodeId)
-  return materialList.value.filter(item => item.bomNodeId === bomNodeId).length
-}
+  const uniqueKey = `${bomNodeId}`;
+  const materials = bomMaterialsMap.value[uniqueKey] || [];
+
+  // 只计算有效的物料数量
+  return materials.filter(material =>
+    !isMaterialDisabled(material) &&
+    material.materialName &&
+    material.unit &&
+    (material.unitPrice || 0) > 0 &&
+    (material.quantity || 0) > 0
+  ).length;
+}
+
+// 校验保养项下物料数据的方法
+const validateBomItemMaterials = (bomNodeId: number): boolean => {
+  const uniqueKey = `${bomNodeId}`;
+  const materials = bomMaterialsMap.value[uniqueKey] || [];
+
+  // 如果没有物料数据,直接返回true(允许完成)
+  if (materials.length === 0) {
+    return true;
+  }
+
+  // 检查所有物料的单价和消耗数量是否都大于0
+  const hasInvalidMaterial = materials.some(material => {
+    const unitPrice = Number(material.unitPrice) || 0;
+    const quantity = Number(material.quantity) || 0;
+
+    return unitPrice <= 0 || quantity <= 0;
+  });
+
+  return !hasInvalidMaterial;
+};
+
+// 获取校验失败的物料信息(用于错误提示)
+const getInvalidMaterialsInfo = (bomNodeId: number): string[] => {
+  const uniqueKey = `${bomNodeId}`;
+  const materials = bomMaterialsMap.value[uniqueKey] || [];
+  const invalidMaterials = [];
+
+  materials.forEach(material => {
+    const unitPrice = Number(material.unitPrice) || 0;
+    const quantity = Number(material.quantity) || 0;
+
+    if (unitPrice <= 0 || quantity <= 0) {
+      invalidMaterials.push(`${material.materialName || material.materialCode} (单价: ${unitPrice}, 数量: ${quantity})`);
+    }
+  });
+
+  return invalidMaterials;
+};
 
-// 删除物料(在表格中)
 const handleDeleteMaterialInTable = (material) => {
   // 确认删除对话框
   message.confirm('确认删除该物料吗?', { title: '提示' }).then(() => {
     const targetBomNodeId = material.bomNodeId;
-    const targetDeviceId = material.deviceId;
-    const uniqueKey = `${targetDeviceId}${targetBomNodeId}`;
+
+    // 修复:统一使用 bomNodeId 作为键(与新增物料保持一致)
+    const uniqueKey = `${targetBomNodeId}`;
+
+    console.log('删除物料调试信息:', {
+      material,
+      uniqueKey,
+      bomMaterialsMapKeys: Object.keys(bomMaterialsMap.value),
+      currentMaterials: bomMaterialsMap.value[uniqueKey] || []
+    });
 
     // 从 bomMaterialsMap 中删除
     if (bomMaterialsMap.value[uniqueKey]) {
-      const mapIndex = bomMaterialsMap.value[uniqueKey].findIndex(item =>
-        (item.isNew && item.tempId === material.tempId) ||
-        (!item.isNew &&
+      let mapIndex = -1;
+
+      // 简化查找逻辑
+      if (material.isNew && material.tempId) {
+        // 新增物料:通过 tempId 查找
+        mapIndex = bomMaterialsMap.value[uniqueKey].findIndex(item =>
+          item.isNew && item.tempId === material.tempId
+        );
+      } else {
+        // 已有物料:通过关键字段查找
+        mapIndex = bomMaterialsMap.value[uniqueKey].findIndex(item =>
+          !item.isNew &&
           item.factoryId === material.factoryId &&
           item.costCenterId === material.costCenterId &&
           item.storageLocationId === material.storageLocationId &&
-          item.materialCode === material.materialCode)
-      );
+          item.materialCode === material.materialCode
+        );
+      }
 
       if (mapIndex !== -1) {
+        // 执行删除
         bomMaterialsMap.value[uniqueKey].splice(mapIndex, 1);
+        message.success('物料删除成功');
+
+        console.log('删除成功后的物料列表:', bomMaterialsMap.value[uniqueKey]);
+
+        // 关键修复:立即更新当前显示的物料列表
+        refreshCurrentBomItemMaterials();
+
+        // 删除物料后重新计算费用
+        nextTick(() => {
+          calculateTotalCost();
+        });
+      } else {
+        message.warning('未找到要删除的物料记录');
+        console.error('未找到匹配的物料记录:', material);
       }
+    } else {
+      message.warning('未找到对应的物料列表');
     }
-
-    // 使用新的刷新方法更新显示
-    refreshCurrentBomItemMaterials();
-    // 删除物料后重新计算费用
-    nextTick(() => {
-      calculateTotalCost();
-    });
   }).catch(() => {
     // 用户取消删除
   });
@@ -1672,6 +1773,38 @@ const submitForm = async () => {
     return; // 校验失败则终止提交
   }
 
+  // 校验所有设置为完成状态的保养项的物料数据
+  const invalidBomItems = [];
+
+  list.value.forEach(row => {
+    // 只校验状态为完成(1)且消耗物料规则为消耗(0)的保养项
+    if (row.status === 1 && row.rule === '0') {
+      const isValid = validateBomItemMaterials(row.bomNodeId);
+      if (!isValid) {
+        invalidBomItems.push({
+          name: `${row.deviceCode}-${formatMaintItemName(row.name)}`,
+          invalidMaterials: getInvalidMaterialsInfo(row.bomNodeId)
+        });
+      }
+    }
+  });
+
+  // 如果有校验失败的保养项,显示错误信息并终止提交
+  if (invalidBomItems.length > 0) {
+    let errorMessage = '请填写物料必填项\n\n';
+
+    invalidBomItems.forEach((item, index) => {
+      errorMessage += `${index + 1}. ${item.name}:\n`;
+      item.invalidMaterials.forEach(material => {
+        errorMessage += `   - ${material}\n`;
+      });
+      errorMessage += '\n';
+    });
+
+    message.error(errorMessage);
+    return;
+  }
+
   // 校验表格数据
   const isValid = validateTableData()
   if (!isValid) return
@@ -1684,10 +1817,17 @@ const submitForm = async () => {
         ? item.lastNaturalDate
         : (item.lastNaturalDate ? dayjs(item.lastNaturalDate).valueOf() : null)
     }));
+
+    // 从 bomMaterialsMap 获取所有物料数据,而不是 materialList
+    const allMaterials = [];
+    for (const key in bomMaterialsMap.value) {
+      allMaterials.push(...bomMaterialsMap.value[key]);
+    }
+
     const data = {
       mainWorkOrder: formData.value,
       mainWorkOrderBom: convertedList,
-      mainWorkOrderMaterials: materialList.value
+      mainWorkOrderMaterials: allMaterials
     }
     await IotMainWorkOrderApi.fillWorkOrder(data)
     message.success(t('common.createSuccess'))
@@ -2134,7 +2274,7 @@ const validateTableData = (): boolean => {
   if (formData.value.outsourcingFlag !== 1) {
     list.value.forEach((row, index) => {
       const rowNumber = index + 1;
-      const deviceIdentifier = `${row.deviceCode}-${row.name}`;
+      const deviceIdentifier = `${row.deviceCode}-${formatMaintItemName(row.name)}`;
 
       // 检查消耗物料规则,如果为1(不消耗物料)则跳过校验
       if (row.rule === 1) {
@@ -2147,10 +2287,24 @@ const validateTableData = (): boolean => {
         (row.delayNaturalDate || 0) > 0 ||
         (row.delayDuration || 0) > 0;
 
-      // 未设置推迟保养且未选择物料
-      if (!hasDelay && getMaterialCount(row.bomNodeId) === 0) {
-        materialRequiredErrors.push(`第${rowNumber}行【${deviceIdentifier}】未添加物料`);
-        isValid = false;
+      // 未设置推迟保养时,需要校验物料
+      if (!hasDelay) {
+        // 使用 bomMaterialsMap 获取物料,更可靠
+        const uniqueKey = `${row.bomNodeId}`;
+        const materials = bomMaterialsMap.value[uniqueKey] || [];
+
+        // 过滤有效的物料(非禁用、非无效状态)
+        const validMaterials = materials.filter(material =>
+          material.materialName && // 物料名称不为空
+          material.unit && // 单位不为空
+          (material.unitPrice || 0) > 0 && // 单价大于0
+          (material.quantity || 0) > 0 // 数量大于0
+        );
+
+        if (validMaterials.length === 0) {
+          materialRequiredErrors.push(`第${rowNumber}行【${deviceIdentifier}】未添加有效物料`);
+          isValid = false;
+        }
       }
     });
   }
@@ -2251,7 +2405,7 @@ onMounted(async () => {
             : '';
 
           // 处理物料数据映射
-          if (item.deviceBomMaterials && item.deviceBomMaterials.length > 0) {
+          /* if (item.deviceBomMaterials && item.deviceBomMaterials.length > 0) {
             // 生成唯一键
             const uniqueKey = `${item.bomNodeId}`;
 
@@ -2266,7 +2420,7 @@ onMounted(async () => {
             }));
             // 存储到映射表中
             bomMaterialsMap.value[uniqueKey] = mappedMaterials;
-          }
+          } */
 
           if (item.mileageRule === 0) {
             item.nextMaintenanceKm = calculateNextMaintenanceKm(item);
@@ -2302,43 +2456,25 @@ onMounted(async () => {
     }
     // 查询当前保养工单已经关联的所有物料
     const materials = await IotMainWorkOrderBomMaterialApi.getWorkOrderBomMaterials(queryParams);
-    // 修复:重新初始化物料映射,确保接口数据完全覆盖
+    // 重新初始化物料映射,确保接口数据完全覆盖
     const tempBomMaterialsMap = {};
-    // 首先,用 getWorkOrderBOMs 返回的 deviceBomMaterials 初始化映射
-    list.value.forEach(item => {
-      const uniqueKey = `${item.bomNodeId}`;
-
-      if (item.deviceBomMaterials && item.deviceBomMaterials.length > 0) {
-        tempBomMaterialsMap[uniqueKey] = item.deviceBomMaterials.map(material => ({
-          ...material,
-          materialName: material.name,
-          materialCode: material.code,
-          projectDepartment: material.storageLocation,
-          totalInventoryQuantity: material.stockQuantity,
-          deviceId: item.deviceId // 添加 deviceId
-        }));
-      } else {
-        tempBomMaterialsMap[uniqueKey] = [];
-      }
-    });
-    // materialList.value = []
-    // 修改:如果有已关联的物料,使用接口返回的数据覆盖默认数据
+    // 首先,用 getWorkOrderBomMaterials 返回的数据初始化映射(主数据源)
     if (Array.isArray(materials) && materials.length > 0) {
-      // 同时更新 bomMaterialsMap
       materials.forEach(material => {
-        // 通过 bomNodeId 在 list 中查找对应的保养项,获取 deviceId
-        const correspondingItem = list.value.find(item => item.bomNodeId === material.bomNodeId);
-        const deviceId = correspondingItem ? correspondingItem.deviceId : null;
-
         const uniqueKey = `${material.bomNodeId}`;
         if (!tempBomMaterialsMap[uniqueKey]) {
           tempBomMaterialsMap[uniqueKey] = [];
         }
-        // 添加 deviceId 到物料数据
+
+        // 通过 bomNodeId 在 list 中查找对应的保养项,获取 deviceId
+        const correspondingItem = list.value.find(item => item.bomNodeId === material.bomNodeId);
+        const deviceId = correspondingItem ? correspondingItem.deviceId : null;
+
         const materialWithDeviceId = {
           ...material,
           deviceId: deviceId
         };
+
         // 避免重复添加
         const existingIndex = tempBomMaterialsMap[uniqueKey].findIndex(item =>
           item.materialCode === material.materialCode &&
@@ -2346,14 +2482,36 @@ onMounted(async () => {
           item.costCenterId === material.costCenterId &&
           item.storageLocationId === material.storageLocationId
         );
+
         if (existingIndex === -1) {
           tempBomMaterialsMap[uniqueKey].push(materialWithDeviceId);
         } else {
-          // 覆盖更新,优先使用 getWorkOrderBomMaterials 的数据
           tempBomMaterialsMap[uniqueKey][existingIndex] = materialWithDeviceId;
         }
       });
     }
+    // 其次,对于 getWorkOrderBOMs 接口返回的 deviceBomMaterials,
+    // 只有当 getWorkOrderBomMaterials 中没有对应保养项的物料时,才使用 deviceBomMaterials 作为备用
+    list.value.forEach(item => {
+      const uniqueKey = `${item.bomNodeId}`;
+
+      // 如果 getWorkOrderBomMaterials 中没有这个保养项的物料,且 deviceBomMaterials 有值,则使用备用数据
+      if ((!tempBomMaterialsMap[uniqueKey] || tempBomMaterialsMap[uniqueKey].length === 0) &&
+        item.deviceBomMaterials && item.deviceBomMaterials.length > 0) {
+
+        tempBomMaterialsMap[uniqueKey] = item.deviceBomMaterials.map(material => ({
+          ...material,
+          materialName: material.name,
+          materialCode: material.code,
+          projectDepartment: material.storageLocation,
+          totalInventoryQuantity: material.stockQuantity,
+          deviceId: item.deviceId
+        }));
+      } else if (!tempBomMaterialsMap[uniqueKey]) {
+        // 确保每个保养项在映射中都有对应的数组(即使是空的)
+        tempBomMaterialsMap[uniqueKey] = [];
+      }
+    });
 
     // 更新响应式映射
     bomMaterialsMap.value = tempBomMaterialsMap;