|
|
@@ -26,4 +26,74 @@ export function compareBatchNo(a, b) {
|
|
|
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;
|
|
|
+}
|