|
@@ -223,6 +223,7 @@
|
|
|
:underline="false"
|
|
:underline="false"
|
|
|
icon="el-icon-view"
|
|
icon="el-icon-view"
|
|
|
@click="openConfigure(row, true)"
|
|
@click="openConfigure(row, true)"
|
|
|
|
|
+ v-if="!row._isRouteOnly"
|
|
|
>
|
|
>
|
|
|
详情
|
|
详情
|
|
|
</el-link>
|
|
</el-link>
|
|
@@ -231,7 +232,7 @@
|
|
|
:underline="false"
|
|
:underline="false"
|
|
|
icon="el-icon-setting"
|
|
icon="el-icon-setting"
|
|
|
@click="openStepSetting(row)"
|
|
@click="openStepSetting(row)"
|
|
|
- v-if="attributeData.status != 1"
|
|
|
|
|
|
|
+ v-if="attributeData.status != 1 && !row._isRouteOnly"
|
|
|
>
|
|
>
|
|
|
工步
|
|
工步
|
|
|
</el-link>
|
|
</el-link>
|
|
@@ -240,7 +241,7 @@
|
|
|
:underline="false"
|
|
:underline="false"
|
|
|
icon="el-icon-plus"
|
|
icon="el-icon-plus"
|
|
|
@click="openCreateStep(row)"
|
|
@click="openCreateStep(row)"
|
|
|
- v-if="attributeData.status != 1"
|
|
|
|
|
|
|
+ v-if="attributeData.status != 1 && !row._isRouteOnly"
|
|
|
>
|
|
>
|
|
|
创建工步
|
|
创建工步
|
|
|
</el-link>
|
|
</el-link>
|
|
@@ -249,9 +250,13 @@
|
|
|
:underline="false"
|
|
:underline="false"
|
|
|
icon="el-icon-setting"
|
|
icon="el-icon-setting"
|
|
|
@click="openConfigure(row)"
|
|
@click="openConfigure(row)"
|
|
|
|
|
+ v-if="!row._isRouteOnly"
|
|
|
>
|
|
>
|
|
|
配置
|
|
配置
|
|
|
</el-link>
|
|
</el-link>
|
|
|
|
|
+ <span v-if="row._isRouteOnly" class="workmanship-route-only-tip">
|
|
|
|
|
+ 未配置
|
|
|
|
|
+ </span>
|
|
|
<!-- <el-popconfirm
|
|
<!-- <el-popconfirm
|
|
|
class="ele-action"
|
|
class="ele-action"
|
|
|
title="确定要删除当前工序吗?"
|
|
title="确定要删除当前工序吗?"
|
|
@@ -511,7 +516,9 @@
|
|
|
// 工艺路线分组展开状态:{ [routeId]: true/false },默认全部展开
|
|
// 工艺路线分组展开状态:{ [routeId]: true/false },默认全部展开
|
|
|
expandedRouteMap: {},
|
|
expandedRouteMap: {},
|
|
|
// 完整分组数据(含分组标题行),用于按展开状态过滤显示
|
|
// 完整分组数据(含分组标题行),用于按展开状态过滤显示
|
|
|
- rawGroupedList: []
|
|
|
|
|
|
|
+ rawGroupedList: [],
|
|
|
|
|
+ // 表格数据源请求序号(只用于比较,不参与渲染)
|
|
|
|
|
+ loadSeq: 0
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
@@ -661,7 +668,9 @@
|
|
|
this.refreshTableView();
|
|
this.refreshTableView();
|
|
|
});
|
|
});
|
|
|
},
|
|
},
|
|
|
- // 根据工艺路线对工序数据进行分组
|
|
|
|
|
|
|
+ // 根据工艺路线对工序数据进行分组:
|
|
|
|
|
+ // 分组依据是"工艺路线接口返回的工序列表",即每条工艺路线展示它自己的完整工序;
|
|
|
|
|
+ // 多条工艺路线共用同一道工序(sourceTaskId 相同)时,各分组下各展示一份,不做过滤。
|
|
|
async groupTaskParamByRoute(dataList, routes) {
|
|
async groupTaskParamByRoute(dataList, routes) {
|
|
|
if (!Array.isArray(dataList) || dataList.length === 0) {
|
|
if (!Array.isArray(dataList) || dataList.length === 0) {
|
|
|
return [];
|
|
return [];
|
|
@@ -683,75 +692,162 @@
|
|
|
});
|
|
});
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
- // 通过路由查询工序,建立 sourceTaskId -> routingId 的映射
|
|
|
|
|
- const taskRouteMap = {};
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 1) 按工艺路线逐条查询工序:N 次请求的结果各自保留、互不覆盖,
|
|
|
|
|
+ // 任何一条路线的返回都不能把其它路线的结果顶掉。
|
|
|
|
|
+ const routeEntryList = routes.map((r) => ({
|
|
|
|
|
+ route: r,
|
|
|
|
|
+ routeKey: String(r.id),
|
|
|
|
|
+ items: []
|
|
|
|
|
+ }));
|
|
|
await Promise.all(
|
|
await Promise.all(
|
|
|
- routes.map(async (r) => {
|
|
|
|
|
|
|
+ routeEntryList.map(async (entry) => {
|
|
|
try {
|
|
try {
|
|
|
const res = await route.taskinstanceList({
|
|
const res = await route.taskinstanceList({
|
|
|
- routingId: r.id,
|
|
|
|
|
|
|
+ routingId: entry.route.id,
|
|
|
isDetail: true,
|
|
isDetail: true,
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
|
size: -1
|
|
size: -1
|
|
|
});
|
|
});
|
|
|
const list = (res && res.list) || [];
|
|
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) {
|
|
} catch (e) {
|
|
|
console.log(e, 'get route task list err');
|
|
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) => {
|
|
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 = [];
|
|
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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
result.push({
|
|
result.push({
|
|
|
- _rowKey: `group_header_${group.route.id}_${gIdx}`,
|
|
|
|
|
|
|
+ _rowKey: `group_header_${entry.routeKey}_${gIdx}`,
|
|
|
_isGroupHeader: true,
|
|
_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) {
|
|
if (ungrouped.length > 0) {
|
|
|
result.push({
|
|
result.push({
|
|
|
- _rowKey: `group_header_ungrouped_${groups.length}`,
|
|
|
|
|
|
|
+ _rowKey: `group_header_ungrouped_${routeEntryList.length}`,
|
|
|
_isGroupHeader: true,
|
|
_isGroupHeader: true,
|
|
|
_routeId: UNGROUPED_ROUTE_ID,
|
|
_routeId: UNGROUPED_ROUTE_ID,
|
|
|
_routeInfo: { name: '未关联工艺路线' }
|
|
_routeInfo: { name: '未关联工艺路线' }
|
|
|
});
|
|
});
|
|
|
- ungrouped.forEach((item) => result.push(item));
|
|
|
|
|
|
|
+ ungrouped.forEach((item) => {
|
|
|
|
|
+ item._routeId = UNGROUPED_ROUTE_ID;
|
|
|
|
|
+ result.push(item);
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
return result;
|
|
return result;
|
|
|
},
|
|
},
|
|
@@ -978,6 +1074,10 @@
|
|
|
},
|
|
},
|
|
|
/* 表格数据源 */
|
|
/* 表格数据源 */
|
|
|
async datasource({ page, limit, where }) {
|
|
async datasource({ page, limit, where }) {
|
|
|
|
|
+ // 请求序号:datasource 可能被并发触发多次(attributeData 深度监听 + 切换 tab 都会 reload),
|
|
|
|
|
+ // 只允许最新一次查询写回表格数据,避免先发后到的旧数据覆盖新数据
|
|
|
|
|
+ const loadSeq = (this.loadSeq || 0) + 1;
|
|
|
|
|
+ this.loadSeq = loadSeq;
|
|
|
let data = await getMbomPage({
|
|
let data = await getMbomPage({
|
|
|
...where,
|
|
...where,
|
|
|
bomCategoryId: this.resourceBomId,
|
|
bomCategoryId: this.resourceBomId,
|
|
@@ -985,6 +1085,9 @@
|
|
|
pageNum: page,
|
|
pageNum: page,
|
|
|
size: limit
|
|
size: limit
|
|
|
});
|
|
});
|
|
|
|
|
+ if (loadSeq !== this.loadSeq) {
|
|
|
|
|
+ return this.buildVisibleRows();
|
|
|
|
|
+ }
|
|
|
if (data?.length > 0) {
|
|
if (data?.length > 0) {
|
|
|
this.currentTableData = data[0];
|
|
this.currentTableData = data[0];
|
|
|
let dataList = [];
|
|
let dataList = [];
|
|
@@ -1014,6 +1117,10 @@
|
|
|
dataList,
|
|
dataList,
|
|
|
routes
|
|
routes
|
|
|
);
|
|
);
|
|
|
|
|
+ // 分组过程包含异步请求,这里再校验一次请求序号,确保只有最新一次查询写回
|
|
|
|
|
+ if (loadSeq !== this.loadSeq) {
|
|
|
|
|
+ return this.buildVisibleRows();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// 保存完整分组数据(含标题行),便于按展开状态过滤
|
|
// 保存完整分组数据(含标题行),便于按展开状态过滤
|
|
|
this.rawGroupedList = groupedList;
|
|
this.rawGroupedList = groupedList;
|
|
@@ -1027,7 +1134,32 @@
|
|
|
const processList = groupedList.filter(
|
|
const processList = groupedList.filter(
|
|
|
(item) => !this.isGroupHeader(item)
|
|
(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();
|
|
return this.buildVisibleRows();
|
|
|
|
|
|
|
@@ -1224,6 +1356,10 @@
|
|
|
color: #409eff;
|
|
color: #409eff;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ .workmanship-route-only-tip {
|
|
|
|
|
+ color: #e6a23c;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|