6 Komitmen eb4371dd7f ... 27092e165e

Pembuat SHA1 Pesan Tanggal
  tanzx 27092e165e Merge branch 'dev' of http://110.41.163.243:9980/kd-aiot/kd-aiot-frontend into dev 1 bulan lalu
  tanzx 2c51f65dc6 fix: 修改BOM工艺路线的BUG 1 bulan lalu
  xieyong ab53bf554f refactor: 重构工位类型和工作中心的表单项,移除冗余代码 1 bulan lalu
  xieyong 92ab108378 feat: 添加排序号校验和获取下一个可用排序号的逻辑 1 bulan lalu
  xieyong fc5ec8fa7f feat: 添加收款日和付款日计算日期枚举,并更新相关逻辑 1 bulan lalu
  xieyong 6848c86540 合并test分支到dev 1 bulan lalu

+ 39 - 21
src/enum/dict.js

@@ -250,51 +250,69 @@ export const produceTypeList = [
   }
 ];
 
-// 付款日计算日期
-export const calcPayDateTypeOp = [
+// 收款日计算日期枚举
+export const ReceiveDateCalcTypeEnum = {
+  ORDER_DATE: 'ORDER_DATE',
+  RECEIVABLE_DATE: 'RECEIVABLE_DATE',
+  INVOICE_DATE: 'INVOICE_DATE',
+  INVOICE_NATURAL_MONTH_END: 'INVOICE_NATURAL_MONTH_END',
+  STATEMENT_DATE: 'STATEMENT_DATE'
+};
+
+// 付款日计算日期枚举
+export const PaymentDateCalcTypeEnum = {
+  ORDER_DATE: 'ORDER_DATE',
+  PAYMENT_DATE: 'PAYMENT_DATE',
+  INVOICE_DATE: 'INVOICE_DATE',
+  INVOICE_NATURAL_MONTH_END: 'INVOICE_NATURAL_MONTH_END',
+  STATEMENT_DATE: 'STATEMENT_DATE'
+};
+
+// 收款日计算日期
+export const calcReceiveDateTypeOp = [
   {
     label: '订单日期',
-    value: 1
+    value: ReceiveDateCalcTypeEnum.ORDER_DATE
   },
   {
-    label: '入库日期',
-    value: 2
+    label: '应收单日期',
+    value: ReceiveDateCalcTypeEnum.RECEIVABLE_DATE
   },
   {
-    label: '收票日期',
-    value: 3
+    label: '开票日期',
+    value: ReceiveDateCalcTypeEnum.INVOICE_DATE
   },
   {
-    label: '对账单日期',
-    value: 4
+    label: '开票自然月截止日',
+    value: ReceiveDateCalcTypeEnum.INVOICE_NATURAL_MONTH_END
   },
   {
-    label: '应付单日期',
-    value: 5
+    label: '对账单日期',
+    value: ReceiveDateCalcTypeEnum.STATEMENT_DATE
   }
 ];
 
-// 收款日计算日期
-export const calcReceiveDateTypeOp = [
+// 付款日计算日期
+export const calcPayDateTypeOp = [
   {
     label: '订单日期',
-    value: '1'
+    value: PaymentDateCalcTypeEnum.ORDER_DATE
   },
   {
-    label: '出库日期',
-    value: '2'
+    label: '应付单日期',
+    value: PaymentDateCalcTypeEnum.PAYMENT_DATE
   },
   {
-    label: '应收单日期',
-    value: '3'
+    label: '开票日期',
+    value: PaymentDateCalcTypeEnum.INVOICE_DATE
   },
   {
-    label: '开票日期',
-    value: '4'
+    label: '开票自然月截止日',
+    value: PaymentDateCalcTypeEnum.INVOICE_NATURAL_MONTH_END
   },
   {
     label: '对账单日期',
-    value: '5'
+    value: PaymentDateCalcTypeEnum.STATEMENT_DATE
   }
 ];
 

+ 62 - 56
src/views/factoryModel/station/components/edit.vue

@@ -39,43 +39,6 @@
             </el-form-item>
           </el-col>
 
-          <el-col :span="8">
-            <el-form-item label="工位类型:" prop="extInfo.type">
-              <el-select class="ele-fluid" v-model="form.extInfo.type">
-                <el-option
-                  label="设备"
-                  :value="1"
-                  @click.native="form.extInfo.teamId = null"
-                />
-                <el-option
-                  label="工位"
-                  :value="3"
-                  @click.native="
-                    form.extInfo.assetName = null;
-                    form.extInfo.assetCode = null;
-                    form.extInfo.criticalEquipment = '';
-                    form.extInfo.keyEquipment = '';
-                  "
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-
-          <el-col v-if="form.extInfo.type == 1" :span="8">
-            <el-form-item label="设备编码:" prop="extInfo.assetCode">
-              <el-input
-                v-model="form.extInfo.assetCode"
-                readonly
-                @click.native="chooseAsset"
-              />
-            </el-form-item>
-          </el-col>
-
-          <el-col v-if="form.extInfo.type == 1" :span="8">
-            <el-form-item label="设备名称:" prop="extInfo.assetName">
-              <el-input disabled v-model="form.extInfo.assetName" />
-            </el-form-item>
-          </el-col>
 
           <el-col :span="8">
             <el-form-item label="所属区域:" prop="location_city">
@@ -172,6 +135,46 @@
               </el-select>
             </el-form-item>
           </el-col>
+
+
+          <el-col :span="8">
+            <el-form-item label="工位类型:" prop="extInfo.type">
+              <el-select class="ele-fluid" v-model="form.extInfo.type">
+                <el-option
+                  label="设备"
+                  :value="1"
+                  @click.native="form.extInfo.teamId = null"
+                />
+                <el-option
+                  label="工位"
+                  :value="3"
+                  @click.native="
+                    form.extInfo.assetName = null;
+                    form.extInfo.assetCode = null;
+                    form.extInfo.criticalEquipment = '';
+                    form.extInfo.keyEquipment = '';
+                  "
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col v-if="form.extInfo.type == 1" :span="8">
+            <el-form-item label="设备编码:" prop="extInfo.assetCode">
+              <el-input
+                v-model="form.extInfo.assetCode"
+                readonly
+                @click.native="chooseAsset"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col v-if="form.extInfo.type == 1" :span="8">
+            <el-form-item label="设备名称:" prop="extInfo.assetName">
+              <el-input disabled v-model="form.extInfo.assetName" />
+            </el-form-item>
+          </el-col>
+
           <el-col :span="8">
             <el-form-item label="班组:" prop="extInfo.teamId">
               <el-select
@@ -191,6 +194,7 @@
               </el-select>
             </el-form-item>
           </el-col>
+
           <el-col :span="8">
             <el-form-item label="人员:" prop="extInfo.userIds">
               <el-select
@@ -209,6 +213,27 @@
             </el-form-item>
           </el-col>
 
+
+          <el-col :span="8">
+            <el-form-item label="所属工作中心:" prop="extInfo.workCenterId">
+              <el-select
+                v-model="form.extInfo.workCenterId"
+                filterable
+                disabled
+                placeholder="请选择"
+                style="width: 100%"
+              >
+                <el-option
+                  v-for="item in options.workCenterList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+
           <el-col :span="8">
             <el-form-item label="负责人部门:" prop="extInfo.principalDep">
               <ele-tree-select
@@ -253,25 +278,6 @@
             </el-form-item>
           </el-col> -->
 
-          <el-col :span="8">
-            <el-form-item label="所属工作中心:" prop="extInfo.workCenterId">
-              <el-select
-                v-model="form.extInfo.workCenterId"
-                filterable
-                disabled
-                placeholder="请选择"
-                style="width: 100%"
-              >
-                <el-option
-                  v-for="item in options.workCenterList"
-                  :key="item.id"
-                  :label="item.name"
-                  :value="item.id"
-                >
-                </el-option>
-              </el-select>
-            </el-form-item>
-          </el-col>
 
           <el-col :span="8">
             <el-form-item label="状态:" prop="enabled">

+ 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;
+        }
       }
     }
   }

+ 2 - 2
src/views/rulesManagement/paymentTerms/index.vue

@@ -83,7 +83,7 @@
   import searchForm from './components/searchForm.vue';
   import addDialog from '@/views/rulesManagement/receiveTerms/components/addDialog.vue';
   import tabMixins from '@/mixins/tableColumnsMixin';
-  import { calcReceiveDateTypeOp, payConfirmTypeOp, dateUnitOptions } from '@/enum/dict';
+  import { calcPayDateTypeOp, payConfirmTypeOp, dateUnitOptions } from '@/enum/dict';
   import {
     getTableList,
     deleteInformation,
@@ -139,7 +139,7 @@
             showOverflowTooltip: true,
             minWidth: 160,
             formatter: (row) => {
-              return calcReceiveDateTypeOp.find(item => item.value === row.dateType)?.label || '';
+              return calcPayDateTypeOp.find(item => item.value === row.dateType)?.label || '';
             }
           },
           {

+ 20 - 4
src/views/rulesManagement/receiveTerms/components/addDialog.vue

@@ -42,7 +42,7 @@
           style="width: 100%"
           :disabled="isDetail"
         >
-          <el-option v-for="item in calcReceiveDateTypeOp" :key="item.value" :label="item.label" :value="item.value" />
+          <el-option v-for="item in dateTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
         </el-select>
       </el-form-item>
 
@@ -173,7 +173,14 @@
 
 <script>
   import { addReceiveTerms, updateReceiveTerms, getInfo } from '@/api/rulesManagement/receiveTerms';
-  import { calcReceiveDateTypeOp, payConfirmTypeOp, dateUnitOptions } from '@/enum/dict';
+  import {
+    calcReceiveDateTypeOp,
+    calcPayDateTypeOp,
+    payConfirmTypeOp,
+    dateUnitOptions,
+    PaymentDateCalcTypeEnum,
+    ReceiveDateCalcTypeEnum
+  } from '@/enum/dict';
 
   let paymentId = 0;
 
@@ -191,7 +198,6 @@
         loading: false,
         isUpdate: false,
         isDetail: false,
-        calcReceiveDateTypeOp,
         payConfirmTypeOp,
         // 支付周期单位(天/月/年)
         dateUnitOptions,
@@ -216,6 +222,12 @@
         }
       };
     },
+    computed: {
+      // 收/付款日计算日期:根据 conditionType 切换对应枚举选项
+      dateTypeOptions() {
+        return this.conditionType === 'rules' ? calcReceiveDateTypeOp : calcPayDateTypeOp;
+      }
+    },
     methods: {
       // 生成编码 SKTJ+年月日+3位数字
       generateCode() {
@@ -253,11 +265,15 @@
       },
 
       resetForm() {
+        // 收/付款日计算日期默认值:按当前条件类型取对应枚举的"订单日期"
+        const defaultDateType = this.conditionType === 'rules'
+          ? ReceiveDateCalcTypeEnum.ORDER_DATE
+          : PaymentDateCalcTypeEnum.ORDER_DATE;
         this.form = {
           // id: null,
           code: '',
           name: '',
-          dateType: '1',        // 默认:订单日期
+          dateType: defaultDateType,        // 默认:订单日期
           determinationMethod: '1',         // 默认:交易日(隐藏月数、天数)
           determinationMethodMonth: 0,
           determinationMethodDay: 0,

+ 37 - 3
src/views/technology/route/components/user-taskinstance.vue

@@ -33,7 +33,11 @@
         </template>
 
         <template v-slot:orderNum="{ row }">
-          <el-input v-model="row.orderNum" placeholder="请输入排序"></el-input>
+          <el-input
+            v-model="row.orderNum"
+            placeholder="请输入排序"
+            @blur="checkOrderNum(row)"
+          ></el-input>
         </template>
 
         <template v-slot:processLabel="{ row }">
@@ -268,10 +272,40 @@
     },
 
     methods: {
+      // 获取下一个可用的 orderNum,避免重复
+      getNextOrderNum() {
+        const data = this.$refs.table.getData();
+        if (!data || data.length === 0) {
+          return 1;
+        }
+        const orderNums = data
+          .map((item) => Number(item.orderNum))
+          .filter((n) => !isNaN(n));
+        if (orderNums.length === 0) {
+          return 1;
+        }
+        return Math.max(...orderNums) + 1;
+      },
+      // 校验排序号是否重复
+      checkOrderNum(row) {
+        if (row.orderNum === '' || row.orderNum === null || row.orderNum === undefined) {
+          return;
+        }
+        const data = this.$refs.table.getData();
+        const dup = data.find(
+          (item) => item !== row && String(item.orderNum) === String(row.orderNum)
+        );
+        if (dup) {
+          this.$message.warning(`排序号 ${row.orderNum} 已存在,请重新输入`);
+          this.$nextTick(() => {
+            row.orderNum = '';
+          });
+        }
+      },
       addParamrter() {
         // console.log(this.$refs.ProductionCom?.selection);
         let arr = this.$refs.ProductionCom.selection;
-        let len = this.$refs.table.getData().length + 1;
+        let len = this.getNextOrderNum();
         let arrM = arr.map((item, index) => {
           return {
             ...item.detail,
@@ -370,7 +404,7 @@
         }
 
         if (data.length != 0) {
-          let len = this.$refs.table.getData().length + 1;
+          let len = this.getNextOrderNum();
           let arrM = data.map((item, index) => {
             return {
               ...item.detail,