Procházet zdrojové kódy

fix: 修改BOM工艺路线的BUG

tanzx před 3 dny
rodič
revize
2c51f65dc6

+ 5 - 0
src/views/factoryModel/station/components/stationBeatDialog.vue

@@ -86,6 +86,11 @@
             prop: 'leaderName',
             slot: 'factory'
           },
+          {
+            // 工位操作人(人员)姓名,后端 FactoryWorkstationVO.userNames,多个人以逗号分隔
+            label: '操作人姓名',
+            prop: 'userNames'
+          },
           {
             label: '所属区域',
             prop: 'areaName'

+ 5 - 0
src/views/factoryModel/station/index.vue

@@ -137,6 +137,11 @@
             prop: 'leaderName',
             slot: 'factory'
           },
+          {
+            // 工位操作人(人员)姓名,后端 FactoryWorkstationVO.userNames,多个人以逗号分隔
+            label: '操作人姓名',
+            prop: 'userNames'
+          },
           {
             label: '所属区域',
             prop: 'areaName'

+ 181 - 45
src/views/material/BOMmanage/components/workmanship.vue

@@ -223,6 +223,7 @@
             :underline="false"
             icon="el-icon-view"
             @click="openConfigure(row, true)"
+            v-if="!row._isRouteOnly"
           >
             详情
           </el-link>
@@ -231,7 +232,7 @@
             :underline="false"
             icon="el-icon-setting"
             @click="openStepSetting(row)"
-            v-if="attributeData.status != 1"
+            v-if="attributeData.status != 1 && !row._isRouteOnly"
           >
             工步
           </el-link>
@@ -240,7 +241,7 @@
             :underline="false"
             icon="el-icon-plus"
             @click="openCreateStep(row)"
-            v-if="attributeData.status != 1"
+            v-if="attributeData.status != 1 && !row._isRouteOnly"
           >
             创建工步
           </el-link>
@@ -249,9 +250,13 @@
             :underline="false"
             icon="el-icon-setting"
             @click="openConfigure(row)"
+            v-if="!row._isRouteOnly"
           >
             配置
           </el-link>
+          <span v-if="row._isRouteOnly" class="workmanship-route-only-tip">
+            未配置
+          </span>
           <!-- <el-popconfirm
             class="ele-action"
             title="确定要删除当前工序吗?"
@@ -511,7 +516,9 @@
         // 工艺路线分组展开状态:{ [routeId]: true/false },默认全部展开
         expandedRouteMap: {},
         // 完整分组数据(含分组标题行),用于按展开状态过滤显示
-        rawGroupedList: []
+        rawGroupedList: [],
+        // 表格数据源请求序号(只用于比较,不参与渲染)
+        loadSeq: 0
       };
     },
     methods: {
@@ -661,7 +668,9 @@
           this.refreshTableView();
         });
       },
-      // 根据工艺路线对工序数据进行分组
+      // 根据工艺路线对工序数据进行分组:
+      // 分组依据是"工艺路线接口返回的工序列表",即每条工艺路线展示它自己的完整工序;
+      // 多条工艺路线共用同一道工序(sourceTaskId 相同)时,各分组下各展示一份,不做过滤。
       async groupTaskParamByRoute(dataList, routes) {
         if (!Array.isArray(dataList) || dataList.length === 0) {
           return [];
@@ -683,75 +692,162 @@
           });
           return result;
         }
-        // 通过路由查询工序,建立 sourceTaskId -> routingId 的映射
-        const taskRouteMap = {};
+
+        // 1) 按工艺路线逐条查询工序:N 次请求的结果各自保留、互不覆盖,
+        //    任何一条路线的返回都不能把其它路线的结果顶掉。
+        const routeEntryList = routes.map((r) => ({
+          route: r,
+          routeKey: String(r.id),
+          items: []
+        }));
         await Promise.all(
-          routes.map(async (r) => {
+          routeEntryList.map(async (entry) => {
             try {
               const res = await route.taskinstanceList({
-                routingId: r.id,
+                routingId: entry.route.id,
                 isDetail: true,
                 pageNum: 1,
                 size: -1
               });
               const list = (res && res.list) || [];
-              list.forEach((it) => {
-                const detail = it.detail || it;
-                const stid =
-                  detail.sourceTaskId != null
-                    ? detail.sourceTaskId
-                    : it.sourceTaskId;
-                if (stid != null) {
-                  taskRouteMap[stid] = r.id;
-                }
-              });
+              entry.items = list
+                .map((it, idx) => {
+                  const detail = it.detail || it;
+                  const stid =
+                    detail.sourceTaskId != null
+                      ? detail.sourceTaskId
+                      : it.sourceTaskId;
+                  if (stid == null) {
+                    return null;
+                  }
+                  return {
+                    sourceTaskId: stid,
+                    detail,
+                    orderNum: it.orderNum != null ? it.orderNum : idx + 1
+                  };
+                })
+                .filter((it) => it != null);
             } catch (e) {
               console.log(e, 'get route task list err');
             }
           })
         );
-        // 按工艺路线顺序分组,剩余未匹配到路线的归入"未分组"
-        const groups = routes.map((r) => ({ route: r, processes: [] }));
-        const ungrouped = [];
+
+        // 2) MBOM 工序记录索引:sourceTaskId -> 记录列表(保持 mBomPage 返回的顺序)
+        const recordBySourceTaskId = new Map();
         dataList.forEach((item) => {
-          const routeId =
-            taskRouteMap[item.sourceTaskId] != null
-              ? taskRouteMap[item.sourceTaskId]
-              : item.routingId != null
-              ? item.routingId
-              : null;
-          const matchedGroup = routeId
-            ? groups.find((g) => g.route.id === routeId)
-            : null;
-          if (matchedGroup) {
-            item._routeId = matchedGroup.route.id;
-            matchedGroup.processes.push(item);
-          } else {
-            item._routeId = UNGROUPED_ROUTE_ID;
-            ungrouped.push(item);
+          if (item.sourceTaskId == null) {
+            return;
+          }
+          const key = String(item.sourceTaskId);
+          if (!recordBySourceTaskId.has(key)) {
+            recordBySourceTaskId.set(key, []);
           }
+          recordBySourceTaskId.get(key).push(item);
         });
+        // 已被前面分组优先占用的记录(只影响分配优先级,不阻止后续路线复用同一条记录)
+        const occupiedRecords = new Set();
+        // 取某条路线上某道工序对应的 MBOM 记录:
+        // 1) 记录自身 routingId 就是本路线的最精确,优先;
+        // 2) 其次取还没被别组占用的记录;
+        // 3) 都被占用时复用其中一条 —— 共用工序要在每条路线下各显示一份,不能过滤掉。
+        const pickRecord = (routeId, sourceTaskId) => {
+          const list = recordBySourceTaskId.get(String(sourceTaskId));
+          if (!list || !list.length) {
+            return null;
+          }
+          const own = list.find(
+            (rec) =>
+              rec.routingId != null && String(rec.routingId) === String(routeId)
+          );
+          if (own) {
+            return own;
+          }
+          return list.find((rec) => !occupiedRecords.has(rec)) || list[0];
+        };
+
+        // 3) 按工艺路线顺序组装分组数据
+        const renderedRecords = new Set();
         const result = [];
-        groups.forEach((group, gIdx) => {
-          if (group.processes.length === 0) {
+        routeEntryList.forEach((entry, gIdx) => {
+          const rows = [];
+          const pushRecordRow = (record, seq) => {
+            // 同一条 MBOM 工序可能同时出现在多条工艺路线分组下,
+            // 必须复制出行对象,否则 _routeId/_rowKey 会互相覆盖,分组展开状态会错乱
+            const row = Object.assign({}, record);
+            row._routeId = entry.route.id;
+            row._rowKey = `route_${entry.routeKey}_${
+              record.id != null ? 'id_' + record.id : 'idx_' + seq
+            }_${seq}`;
+            rows.push(row);
+            renderedRecords.add(record);
+          };
+          entry.items.forEach((item, idx) => {
+            const record = pickRecord(entry.route.id, item.sourceTaskId);
+            if (record) {
+              occupiedRecords.add(record);
+              pushRecordRow(record, idx);
+              return;
+            }
+            // 该路线有这道工序、但 MBOM 里还没有对应记录:用接口明细补一行展示,
+            // 保证"路线里有就要显示",不因为没配置就被过滤掉(该行不可配置)
+            rows.push({
+              _rowKey: `routeonly_${entry.routeKey}_${String(
+                item.sourceTaskId
+              )}_${idx}`,
+              _routeId: entry.route.id,
+              _isRouteOnly: true,
+              code: item.detail.code,
+              name: item.detail.name,
+              controlName: item.detail.controlName,
+              workCenterName: item.detail.workCenterName,
+              routingId: entry.route.id,
+              sourceTaskId: item.sourceTaskId,
+              sort: null,
+              categoryParamStepList: []
+            });
+          });
+          // 接口没返回数据(或该路线确实没工序)时的兜底:
+          // 把 routingId 属于本路线、且还没出现在任何分组里的记录补进来,避免丢数据
+          if (rows.length === 0) {
+            dataList.forEach((record) => {
+              if (
+                record.routingId != null &&
+                String(record.routingId) === entry.routeKey &&
+                !renderedRecords.has(record)
+              ) {
+                occupiedRecords.add(record);
+                pushRecordRow(record, rows.length);
+              }
+            });
+          }
+          if (rows.length === 0) {
             return;
           }
           result.push({
-            _rowKey: `group_header_${group.route.id}_${gIdx}`,
+            _rowKey: `group_header_${entry.routeKey}_${gIdx}`,
             _isGroupHeader: true,
-            _routeId: group.route.id,
-            _routeInfo: group.route
+            _routeId: entry.route.id,
+            _routeInfo: entry.route
           });
-          group.processes.forEach((item) => result.push(item));
+          rows.forEach((row) => result.push(row));
         });
+
+        // 4) MBOM 里有、但所有工艺路线接口都没返回的工序 → 归入"未关联工艺路线"
+        const ungrouped = dataList.filter(
+          (record) => !renderedRecords.has(record)
+        );
         if (ungrouped.length > 0) {
           result.push({
-            _rowKey: `group_header_ungrouped_${groups.length}`,
+            _rowKey: `group_header_ungrouped_${routeEntryList.length}`,
             _isGroupHeader: true,
             _routeId: UNGROUPED_ROUTE_ID,
             _routeInfo: { name: '未关联工艺路线' }
           });
-          ungrouped.forEach((item) => result.push(item));
+          ungrouped.forEach((item) => {
+            item._routeId = UNGROUPED_ROUTE_ID;
+            result.push(item);
+          });
         }
         return result;
       },
@@ -978,6 +1074,10 @@
       },
       /* 表格数据源 */
       async datasource({ page, limit, where }) {
+        // 请求序号:datasource 可能被并发触发多次(attributeData 深度监听 + 切换 tab 都会 reload),
+        // 只允许最新一次查询写回表格数据,避免先发后到的旧数据覆盖新数据
+        const loadSeq = (this.loadSeq || 0) + 1;
+        this.loadSeq = loadSeq;
         let data = await getMbomPage({
           ...where,
           bomCategoryId: this.resourceBomId,
@@ -985,6 +1085,9 @@
           pageNum: page,
           size: limit
         });
+        if (loadSeq !== this.loadSeq) {
+          return this.buildVisibleRows();
+        }
         if (data?.length > 0) {
           this.currentTableData = data[0];
           let dataList = [];
@@ -1014,6 +1117,10 @@
             dataList,
             routes
           );
+          // 分组过程包含异步请求,这里再校验一次请求序号,确保只有最新一次查询写回
+          if (loadSeq !== this.loadSeq) {
+            return this.buildVisibleRows();
+          }
 
           // 保存完整分组数据(含标题行),便于按展开状态过滤
           this.rawGroupedList = groupedList;
@@ -1027,7 +1134,32 @@
           const processList = groupedList.filter(
             (item) => !this.isGroupHeader(item)
           );
-          await this.fillStepLists(processList);
+          // 同一道 MBOM 工序可能同时出现在多条工艺路线分组下,
+          // 工步只请求一次,再把结果共享给各分组里的同名行
+          const stepRowMap = new Map();
+          const stepRowList = [];
+          processList.forEach((row) => {
+            if (row._isRouteOnly) {
+              return;
+            }
+            const key = row.id != null ? String(row.id) : row._rowKey;
+            if (stepRowMap.has(key)) {
+              return;
+            }
+            stepRowMap.set(key, row);
+            stepRowList.push(row);
+          });
+          await this.fillStepLists(stepRowList);
+          processList.forEach((row) => {
+            if (row._isRouteOnly) {
+              return;
+            }
+            const key = row.id != null ? String(row.id) : row._rowKey;
+            const source = stepRowMap.get(key);
+            if (source && source !== row) {
+              row.categoryParamStepList = source.categoryParamStepList;
+            }
+          });
 
           return this.buildVisibleRows();
 
@@ -1224,6 +1356,10 @@
             color: #409eff;
           }
         }
+        .workmanship-route-only-tip {
+          color: #e6a23c;
+          font-size: 12px;
+        }
       }
     }
   }