Browse Source

Merge branch 'dev' into test

xieyong 5 days ago
parent
commit
aa8dbb715d

+ 17 - 0
src/components/common/seekPage.vue

@@ -100,6 +100,20 @@
           if (!Array.isArray(val)) return;
           this.seekListRow = val.slice(0, this.maxLength);
           this.deboListRow = val.slice(this.maxLength);
+          // 初始化默认值
+          const defaults = {};
+          val.forEach((item) => {
+            if (
+              item.defaultValue !== undefined &&
+              item.defaultValue !== null &&
+              item.defaultValue !== ''
+            ) {
+              defaults[item.value] = item.defaultValue;
+            }
+          });
+          if (Object.keys(defaults).length) {
+            this.defaultWhere = { ...defaults, ...this.defaultWhere };
+          }
         }
       },
       defaultWhere: {
@@ -120,6 +134,9 @@
       if (cache) {
         this.defaultWhere = JSON.parse(cache);
         this.search();
+      } else if (Object.keys(this.defaultWhere).length) {
+        // 无缓存但有默认值(如 approvalStatus 默认 2),主动触发一次查询
+        this.search();
       }
     },
 

+ 3 - 1
src/components/selectionDialog/processRoute.vue

@@ -12,7 +12,7 @@
     :maxable="true"
   >
     <el-card shadow="never">
-      <route-search @search="reload"></route-search>
+      <route-search :key="routeSearchKey" @search="reload"></route-search>
 
       <ele-pro-table
         ref="table"
@@ -49,6 +49,7 @@
     data() {
       return {
         visible: false,
+        routeSearchKey: 0,
         columns: [
           {
             action: 'action',
@@ -147,6 +148,7 @@
         if (item) {
           this.radio = item.id;
         }
+        this.routeSearchKey++;
         this.visible = true;
       },
 

+ 2 - 1
src/components/selectionDialog/routeSearch.vue

@@ -54,7 +54,8 @@
             value: 'approvalStatus',
             type: 'select',
             placeholder: '请选择',
-            planList: this.approvalStatus
+            planList: this.approvalStatus,
+            defaultValue: 2
           },
           {
             label: '所属工厂:',

+ 99 - 0
src/utils/batchNoSort.js

@@ -0,0 +1,99 @@
+// batchNo 排序工具:处理类似 "20260914" 与 "20260914-1" 这类批次号排序,
+// 要求基础号相同时,把带 "-N" 后缀的按数字升序排在基础号之后。
+
+// 把 batchNo 拆成基础号与后缀,例如 "20260914-1" -> { base: "20260914", suffix: 1 }
+export function splitBatchNo(batchNo) {
+  const str = String(batchNo || '').trim();
+  if (!str) return { base: '', suffix: -1 };
+  const idx = str.lastIndexOf('-');
+  if (idx === -1) return { base: str, suffix: -1 };
+  const base = str.substring(0, idx);
+  const suffixStr = str.substring(idx + 1);
+  const suffixNum = parseInt(suffixStr, 10);
+  const suffix = Number.isNaN(suffixNum) ? -1 : suffixNum;
+  return { base, suffix };
+}
+
+// 比较两个 batchNo:基础号不同的按基础号升序;基础号相同的,
+// 无后缀的排在前面,带 "-N" 后缀的按 N 升序排在后面;没有 batchNo 的项放到最后。
+export function compareBatchNo(a, b) {
+  const aInfo = splitBatchNo(a);
+  const bInfo = splitBatchNo(b);
+  if (!aInfo.base && !bInfo.base) return 0;
+  if (!aInfo.base) return 1;
+  if (!bInfo.base) return -1;
+  if (aInfo.base !== bInfo.base) {
+    return aInfo.base.localeCompare(bInfo.base);
+  }
+  return aInfo.suffix - bInfo.suffix;
+}
+
+// 把扁平数据按 batchNo / code 折叠成树形:子项(如 batchNo="20260914-1"
+// 或 code="P001-1")会挂到基础号父项的 children 下;父节点本身不存在时,
+// 子项保持独立,不创建虚拟父节点。
+// 根节点和子节点都按 startTime 降序排(开始时间越大越靠前)。
+export function buildBatchNoTree(rows = []) {
+  const list = Array.isArray(rows) ? rows : [];
+  // 按开始时间降序比较的辅助函数
+  const byStartTimeDesc = (a, b) => {
+    const ta = new Date(a?.startTime || 0).getTime();
+    const tb = new Date(b?.startTime || 0).getTime();
+    return tb - ta;
+  };
+
+  // 索引:batchNo/code -> 行(用于 O(1) 查找父节点)
+  const batchNoIndex = new Map();
+  const codeIndex = new Map();
+  list.forEach((row) => {
+    if (row?.batchNo) batchNoIndex.set(String(row.batchNo), row);
+    if (row?.code) codeIndex.set(String(row.code), row);
+  });
+
+  // 找到行的父节点:需要 batchNo 和 code 两个字段同时能匹配到
+  // 同一个父节点(AND 关系),任一字段缺失或匹配到不同父都返回 null
+  const findParent = (row) => {
+    const batchInfo = splitBatchNo(row?.batchNo);
+    const codeInfo = splitBatchNo(row?.code);
+    // 任一字段没有 -N 后缀,就不满足 AND 条件
+    if (batchInfo.suffix < 0 || codeInfo.suffix < 0) return null;
+    if (!batchInfo.base || !codeInfo.base) return null;
+    const batchParent = batchNoIndex.get(batchInfo.base);
+    const codeParent = codeIndex.get(codeInfo.base);
+    if (!batchParent || !codeParent) return null;
+    if (batchParent === row || codeParent === row) return null;
+    // 两个字段必须匹配到同一个父节点
+    return batchParent === codeParent ? batchParent : null;
+  };
+
+  // 先建临时节点(带 children),再挂父子关系
+  const nodeMap = new Map();
+  list.forEach((row) => {
+    nodeMap.set(row, { ...row, children: undefined });
+  });
+
+  const rootRows = [];
+  nodeMap.forEach((node, row) => {
+    const parent = findParent(row);
+    if (parent) {
+      const parentNode = nodeMap.get(parent);
+      if (!Array.isArray(parentNode.children)) {
+        parentNode.children = [];
+      }
+      parentNode.children.push(node);
+    } else {
+      rootRows.push(node);
+    }
+  });
+
+  // 父子层内各自按 startTime 降序
+  const sortTree = (nodes) => {
+    nodes.forEach((n) => {
+      if (Array.isArray(n.children) && n.children.length) {
+        sortTree(n.children);
+      }
+    });
+    nodes.sort(byStartTimeDesc);
+  };
+  sortTree(rootRows);
+  return rootRows;
+}

+ 1 - 1
src/views/productionPlan/components/factoryAdd/index.vue

@@ -956,7 +956,7 @@
         let data = this.form.productInfoList[this.selectIndex];
         this.$set(data, 'bomVersionList', []);
         this.$set(data, 'bomCategoryId', '');
-        this.$set(data, 'model', '');
+        // this.$set(data, 'model', '');
         this.$set(data, 'routingList', []);
         this.$set(data, 'productType', '');
         this.$set(data, 'produceRoutingName', item.name);

+ 9 - 1
src/views/productionPlan/components/homogeneityInspectDialog.vue

@@ -1140,7 +1140,15 @@
           this.planId = data.id;
           let params = { categoryId: data.categoryId };
           const res = await listBomType(params);
-          if (!res || res.length === 0) return;
+          if (!res || res.length === 0) {
+            this.bomListType = [];
+            this.formInline.bomType = '';
+            this.bomListV = [];
+            this.formInline.bomId = '';
+            this.datasourceAllList = [];
+            this.datasourceList = [];
+            return;
+          };
           let bomMap = {
             1: 'PBOM',
             2: 'MBOM',

+ 56 - 4
src/views/productionPlan/components/newFactoryProductionScheduling.vue

@@ -72,15 +72,25 @@
               :label="item.label"
             />
           </el-tabs>
+          <el-button
+            size="mini"
+            plain
+            class="plan-table-expand-toggle"
+            @click="togglePlanTableExpandAll"
+          >
+            {{ planTableExpandAll ? '收起全部' : '展开全部' }}
+          </el-button>
         </div>
         <ele-pro-table
-          :key="planTableCacheKey"
+          :key="planTableCacheKey + '-' + (planTableExpandAll ? 'expanded' : 'collapsed')"
           width="100%"
           ref="planTable"
           :columns="columns"
           :cache-key="planTableCacheKey"
-          :datasource="planDataList"
+          :datasource="sortedPlanDataList"
           row-key="id"
+          :default-expand-all="planTableExpandAll"
+          :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
           :selection.sync="selection"
           :height="planTableHeight"
           :page-size="20"
@@ -263,6 +273,7 @@
     savePlanDotLine
   } from '@/api/productionPlan/planDotLine';
   import { deepClone } from '@/utils';
+  import { compareBatchNo, buildBatchNoTree } from '@/utils/batchNoSort';
   import {
     EXEC_TYPE,
     EXEC_TYPE_OPTIONS,
@@ -316,6 +327,8 @@
         planDataList: [],
         allPlanDataList: [],
         selection: [],
+        // 计划表是否展开所有父子节点(按 batchNo 折叠的树形结构)
+        planTableExpandAll: false,
         activeTab: 'factory',
         dateRange: [startDate, endDate],
         planPickerVisible: false,
@@ -441,6 +454,15 @@
           ? 'aps-team-scheduling-work-order-table'
           : 'aps-factory-scheduling-plan-table';
       },
+      // 不论 planDataList 由哪条路径赋值,最终给表格用的数据都按 batchNo 排序:
+      // 子 batchNo(20260914-1)会挂到基础号(20260914)的 children 下,
+      // 仅当父节点本身存在于数据中时才会折叠;父节点不存在时子项独立显示
+      sortedPlanDataList() {
+        if (!Array.isArray(this.planDataList)) {
+          return this.planDataList;
+        }
+        return buildBatchNoTree(this.planDataList);
+      },
       cacheKeyUrl() {
         return this.planTableCacheKey;
       },
@@ -1162,6 +1184,13 @@
         );
         const list = Array.isArray(data?.list) ? data.list : [];
         return [...list].sort((a, b) => {
+          // 主排序:按 batchNo 排序,基础号相同的把 "20260914-1" 这类
+          // 带 "-N" 后缀的按数字升序排在 "20260914" 基础号之后
+          const batchCompare = compareBatchNo(a?.batchNo, b?.batchNo);
+          if (batchCompare !== 0) {
+            return batchCompare;
+          }
+          // batchNo 相同时,兜底使用原始 ID 入参顺序
           const aIndex = orderMap.get(String(a?.id ?? ''));
           const bIndex = orderMap.get(String(b?.id ?? ''));
           return (
@@ -1180,7 +1209,12 @@
             .map((item) => this.getPlanRowKey(item))
             .filter((item) => item !== '')
         );
-        return sourceList
+        // 按 batchNo 排序:基础号相同的,"20260914-1" 这类带 "-N" 后缀的
+        // 按数字升序排在 "20260914" 基础号之后
+        const sortedSourceList = [...sourceList].sort((a, b) =>
+          compareBatchNo(a?.batchNo, b?.batchNo)
+        );
+        return sortedSourceList
           .filter((item) => selectedKeySet.has(this.getPlanRowKey(item)))
           .map((item, index) => ({
             ...deepClone(item),
@@ -1188,7 +1222,12 @@
           }));
       },
       buildWorkOrderTableRows(rows = []) {
-        return (rows || []).map((item, index) => ({
+        // 按 batchNo 排序:基础号相同的,"20260914-1" 这类带 "-N" 后缀的
+        // 按数字升序排在 "20260914" 基础号之后
+        const sortedRows = [...(rows || [])].sort((a, b) =>
+          compareBatchNo(a?.batchNo, b?.batchNo)
+        );
+        return sortedRows.map((item, index) => ({
           ...deepClone(item),
           id: item.id ?? item.code ?? `work-order-${index}`,
           serialNumber: index + 1
@@ -1228,6 +1267,9 @@
           }, 0);
         });
       },
+      togglePlanTableExpandAll() {
+        this.planTableExpandAll = !this.planTableExpandAll;
+      },
 
       // Plan picker flow
       async planPickerDatasource({ page, limit, where }) {
@@ -3579,6 +3621,16 @@
     box-sizing: border-box;
     border-bottom: 1px solid #e5edf6;
     background: linear-gradient(180deg, #f9fcff 0%, #f3f7fb 100%);
+    display: flex;
+    align-items: flex-end;
+    justify-content: space-between;
+  }
+
+  .plan-table-expand-toggle {
+    margin-bottom: 4px;
+    height: 28px;
+    padding: 0 12px;
+    font-size: 12px;
   }
 
   .plan-table-status-tabs ::v-deep {

+ 1 - 1
src/views/productionPlan/components/newFactoryProductionScheduling/TaskConfigPanel.vue

@@ -547,7 +547,7 @@
           <el-table-column label="工序并行" min-width="92" align="center">
             <template slot-scope="{ row }">
               <el-select
-                v-model="row.isParallelism"
+                v-model="row.isParallel"
                 placeholder="工序并行"
                 :disabled="readonlyMode"
                 class="config-table-control"

+ 1 - 1
src/views/saleOrder/components/create-order.vue

@@ -1568,7 +1568,7 @@
         let data = this.form.productInfoList[this.selectIndex];
         this.$set(data, 'bomVersionList', []);
         this.$set(data, 'bomCategoryId', '');
-        this.$set(data, 'model', '');
+        // this.$set(data, 'model', '');
         this.$set(data, 'routingList', []);
         this.$set(data, 'productType', '');
         this.$set(data, 'produceRoutingName', item.name);