Bladeren bron

feat(productionPlan): 支持物料需求清单及动态BOM类型切换

BOM对比弹窗新增PBOM/MBOM/物料需求清单类型选择,按类型独立加载版本选项,并适配对比接口的sourceType与planCode参数。
yusheng 1 maand geleden
bovenliggende
commit
b56c37cda0

+ 42 - 5
src/views/productionPlan/components/BomCompareDialog.vue

@@ -24,7 +24,19 @@
           <div class="source-panel__title">
             <span class="source-badge">A</span>
             <div>
-              <strong>PBOM</strong>
+              <el-select
+                v-model="source.bomType"
+                size="mini"
+                class="bom-type-select"
+                @change="handleBomTypeChange('source', $event)"
+              >
+                <el-option
+                  v-for="item in bomTypeOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
               <span>作为变更前版本</span>
             </div>
           </div>
@@ -71,7 +83,19 @@
           <div class="source-panel__title">
             <span class="source-badge">B</span>
             <div>
-              <strong>MBOM</strong>
+              <el-select
+                v-model="target.bomType"
+                size="mini"
+                class="bom-type-select"
+                @change="handleBomTypeChange('target', $event)"
+              >
+                <el-option
+                  v-for="item in bomTypeOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
               <span>作为变更后版本</span>
             </div>
           </div>
@@ -582,10 +606,15 @@
         pollResolve: null,
         filterTimer: null,
         plan: {},
-        source: {},
-        target: {},
+        source: { bomType: 1 },
+        target: { bomType: 2 },
         sourceOptions: [],
         targetOptions: [],
+        bomTypeOptions: [
+          { value: 1, label: 'PBOM' },
+          { value: 2, label: 'MBOM' },
+          { value: 3, label: '物料需求清单' }
+        ],
         sourceTree: [],
         targetTree: [],
         differences: [],
@@ -1296,7 +1325,7 @@
 
   .source-panel__title > div {
     display: flex;
-    align-items: baseline;
+    align-items: center;
     gap: 8px;
   }
 
@@ -1308,6 +1337,14 @@
     font-size: 12px;
   }
 
+  .source-panel__title .bom-type-select {
+    width: 96px;
+  }
+  .source-panel__title .bom-type-select .el-input__inner {
+    font-weight: 700;
+    color: #303133;
+  }
+
   .source-badge {
     display: inline-flex;
     align-items: center;

+ 159 - 47
src/views/productionPlan/components/bomCompareDialog.logic.js

@@ -9,6 +9,20 @@ import {
   exportBomCompareTree
 } from '@/api/productionPlan/bomCompare';
 
+const BOM_TYPE_LABELS = {
+  1: 'PBOM',
+  2: 'MBOM',
+  3: '物料需求清单'
+};
+
+const bomTypeLabel = (bomType) => BOM_TYPE_LABELS[bomType] || 'PBOM';
+
+const BOM_TYPE_TO_SOURCE_TYPE = {
+  1: 'PBOM',
+  2: 'MBOM',
+  3: 'MATERIAL_REQUIREMENTS'
+};
+
 const API_TO_UI_TYPE = {
   ADDED: 'added',
   DELETED: 'removed',
@@ -64,13 +78,15 @@ export default {
   },
 
   createSource(item = {}, bomType) {
+    const resolvedBomType = Number(item.bomType || bomType || 1);
     return {
       bomId: item.bomId || item.id || '',
+      bomType: resolvedBomType,
       categoryId: item.categoryId || this.plan.categoryId || '',
       code: item.code || item.categoryCode || this.plan.productCode || '-',
       name: item.name || item.categoryName || this.plan.productName || '-',
       version: item.version || item.versions || '-',
-      view: bomType === 1 ? 'PBOM' : 'MBOM',
+      view: bomTypeLabel(resolvedBomType),
       factory: item.factories || item.factory || '-',
       updatedAt: item.updateTime || item.createTime || '-'
     };
@@ -89,7 +105,7 @@ export default {
     return {
       ...item,
       bomId,
-      bomType,
+      bomType: Number(bomType),
       categoryId: item.categoryId || this.plan.categoryId || '',
       code,
       name,
@@ -104,41 +120,70 @@ export default {
       this.$message.warning('当前计划缺少物品 ID,无法查询 PBOM/MBOM 版本');
       return;
     }
+    await Promise.all([
+      this.loadSideBomOptions('source'),
+      this.loadSideBomOptions('target')
+    ]);
+    await Promise.resolve();
+    if (
+      sequence === this.requestSequence &&
+      this.visible &&
+      this.source.bomId &&
+      this.target.bomId
+    ) {
+      this.runCompare();
+    }
+  },
+
+  async loadSideBomOptions(side) {
+    const categoryId = this.plan.categoryId;
+    const bomType = Number(this[side].bomType);
+    const optionsKey = side === 'source' ? 'sourceOptions' : 'targetOptions';
+
+    // 物料需求清单:下拉固定为计划编号,不走接口查询
+    if (bomType === 3) {
+      const planCode = this.plan.code || '';
+      const productName = this.plan.productName || '';
+      const fixedOption = {
+        bomId: planCode,
+        bomType: 3,
+        code: planCode,
+        name: productName,
+        version: '-',
+        label: `${planCode}${productName}`
+      };
+      this[optionsKey] = [fixedOption];
+      this[side] = this.createSource(fixedOption, bomType);
+      return;
+    }
+
+    if (!categoryId) {
+      this.$message.warning(
+        `当前计划缺少物品 ID,无法查询 ${bomTypeLabel(bomType)} 版本`
+      );
+      return;
+    }
     this.optionsLoading = true;
+    const sequence = this.requestSequence;
     try {
-      const [sourceList, targetList] = await Promise.all([
-        bomVersionList({ categoryId, bomType: 1, isTemp: 0 }),
-        bomVersionList({ categoryId, bomType: 2, isTemp: 0 })
-      ]);
+      const list = await bomVersionList({ categoryId, bomType, isTemp: 0 });
       if (sequence !== this.requestSequence || !this.visible) return;
-      this.sourceOptions = (sourceList || [])
-        .filter(
-          (item) => item.status === undefined || Number(item.status) === 1
-        )
-        .map((item) => this.normalizeBomOption(item, 1));
-      this.targetOptions = (targetList || [])
+      const options = (list || [])
         .filter(
           (item) => item.status === undefined || Number(item.status) === 1
         )
-        .map((item) => this.normalizeBomOption(item, 2));
+        .map((item) => this.normalizeBomOption(item, bomType));
+      this[optionsKey] = options;
 
-      const preferredSource =
-        this.sourceOptions.find(
-          (item) => String(item.bomId) === String(this.plan.bomCategoryId || '')
-        ) || this.sourceOptions[0];
-      const preferredTarget =
-        this.targetOptions.find(
+      const preferred =
+        options.find(
           (item) => String(item.bomId) === String(this.plan.bomCategoryId || '')
-        ) || this.targetOptions[0];
-      if (preferredSource) this.source = this.createSource(preferredSource, 1);
-      if (preferredTarget) this.target = this.createSource(preferredTarget, 2);
-
-      if (!this.sourceOptions.length || !this.targetOptions.length) {
-        const missing = [];
-        if (!this.sourceOptions.length) missing.push('PBOM');
-        if (!this.targetOptions.length) missing.push('MBOM');
+        ) || options[0];
+      this[side] = this.createSource(preferred || {}, bomType);
+
+      if (!options.length) {
         this.$message.warning(
-          `当前产品未维护可用的 ${missing.join('、')} 版本`
+          `当前产品未维护可用的 ${bomTypeLabel(bomType)} 版本`
         );
       }
     } catch (error) {
@@ -147,13 +192,17 @@ export default {
     } finally {
       if (sequence === this.requestSequence) this.optionsLoading = false;
     }
-    await Promise.resolve();
-    if (
-      sequence === this.requestSequence &&
-      this.visible &&
-      this.sourceOptions.length &&
-      this.targetOptions.length
-    ) {
+  },
+
+  async handleBomTypeChange(side, bomType) {
+    const normalizedBomType = Number(bomType);
+    this[side].bomType = normalizedBomType;
+    this[side].view = bomTypeLabel(normalizedBomType);
+    this[side].bomId = '';
+    this.cancelPendingRequests();
+    this.resetResultState();
+    await this.loadSideBomOptions(side);
+    if (this.visible && this.source.bomId && this.target.bomId) {
       this.runCompare();
     }
   },
@@ -163,7 +212,7 @@ export default {
     const item = options.find(
       (option) => String(option.bomId) === String(this[side].bomId)
     );
-    if (item) this[side] = this.createSource(item, side === 'source' ? 1 : 2);
+    if (item) this[side] = this.createSource(item, item.bomType);
     this.cancelPendingRequests();
     this.resetResultState();
     this.markPending();
@@ -197,13 +246,32 @@ export default {
   },
 
   buildStartPayload() {
+    const baseSourceType =
+      BOM_TYPE_TO_SOURCE_TYPE[Number(this.source.bomType)] || 'PBOM';
+    const targetSourceType =
+      BOM_TYPE_TO_SOURCE_TYPE[Number(this.target.bomType)] || 'MBOM';
+    const baseIsMaterial = baseSourceType === 'MATERIAL_REQUIREMENTS';
+    const targetIsMaterial = targetSourceType === 'MATERIAL_REQUIREMENTS';
+
     return {
-      baseBomId: this.source.bomId || undefined,
-      baseCategoryId: this.source.bomId ? undefined : this.source.categoryId,
-      baseVersion: this.source.bomId ? undefined : this.source.version,
-      targetBomId: this.target.bomId || undefined,
-      targetCategoryId: this.target.bomId ? undefined : this.target.categoryId,
-      targetVersion: this.target.bomId ? undefined : this.target.version,
+      baseSourceType,
+      targetSourceType,
+      basePlanCode: baseIsMaterial ? this.source.bomId || undefined : undefined,
+      targetPlanCode: targetIsMaterial
+        ? this.target.bomId || undefined
+        : undefined,
+      baseBomId: baseIsMaterial ? undefined : this.source.bomId || undefined,
+      baseCategoryId:
+        !baseIsMaterial && !this.source.bomId ? this.source.categoryId : undefined,
+      baseVersion:
+        !baseIsMaterial && !this.source.bomId ? this.source.version : undefined,
+      targetBomId: targetIsMaterial ? undefined : this.target.bomId || undefined,
+      targetCategoryId:
+        !targetIsMaterial && !this.target.bomId
+          ? this.target.categoryId
+          : undefined,
+      targetVersion:
+        !targetIsMaterial && !this.target.bomId ? this.target.version : undefined,
       compareLevel: this.compareLevel === 'single' ? 1 : 2,
       compareContent: this.compareMode === 'structure' ? 1 : 2
     };
@@ -211,7 +279,11 @@ export default {
 
   async runCompare() {
     if (!this.source.bomId || !this.target.bomId) {
-      this.$message.warning('请选择基准 PBOM 和目标 MBOM');
+      this.$message.warning(
+        `请选择需要比较的${bomTypeLabel(
+          Number(this.source.bomType)
+        )} 和 ${bomTypeLabel(Number(this.target.bomType))}`
+      );
       return;
     }
     this.cancelPendingRequests();
@@ -243,8 +315,28 @@ export default {
       ? String(task.status).toUpperCase()
       : this.taskStatus;
     this.taskMessage = task.message || '';
-    if (task.baseBom) this.source = this.createSource(task.baseBom, 1);
-    if (task.targetBom) this.target = this.createSource(task.targetBom, 2);
+    if (task.baseBom)
+      this.source = this.createSource(
+        {
+          ...task.baseBom,
+          bomType: this.source.bomType,
+          ...(Number(this.source.bomType) === 3
+            ? { bomId: this.source.bomId }
+            : {})
+        },
+        this.source.bomType
+      );
+    if (task.targetBom)
+      this.target = this.createSource(
+        {
+          ...task.targetBom,
+          bomType: this.target.bomType,
+          ...(Number(this.target.bomType) === 3
+            ? { bomId: this.target.bomId }
+            : {})
+        },
+        this.target.bomType
+      );
     if (task.summary) this.summary = this.normalizeSummary(task.summary);
   },
 
@@ -408,8 +500,28 @@ export default {
   },
 
   applyTreeResult(tree) {
-    if (tree.baseBom) this.source = this.createSource(tree.baseBom, 1);
-    if (tree.targetBom) this.target = this.createSource(tree.targetBom, 2);
+    if (tree.baseBom)
+      this.source = this.createSource(
+        {
+          ...tree.baseBom,
+          bomType: this.source.bomType,
+          ...(Number(this.source.bomType) === 3
+            ? { bomId: this.source.bomId }
+            : {})
+        },
+        this.source.bomType
+      );
+    if (tree.targetBom)
+      this.target = this.createSource(
+        {
+          ...tree.targetBom,
+          bomType: this.target.bomType,
+          ...(Number(this.target.bomType) === 3
+            ? { bomId: this.target.bomId }
+            : {})
+        },
+        this.target.bomType
+      );
     this.sourceTree = this.normalizeTree(tree.baseTree, 'source');
     this.targetTree = this.normalizeTree(tree.targetTree, 'target');
     if (tree.summary) this.summary = this.normalizeSummary(tree.summary);