diff --git a/frontend/src/components/canvas/ProcessCanvas/index.vue b/frontend/src/components/canvas/ProcessCanvas/index.vue index e0df1583f..e89a125b1 100644 --- a/frontend/src/components/canvas/ProcessCanvas/index.vue +++ b/frontend/src/components/canvas/ProcessCanvas/index.vue @@ -1589,6 +1589,26 @@ const parent = node.getParent(); const parentId = parent ? parent.id : undefined; const isGroupInner = !!parentId; + // 先获取并处理连线,避免 removeNode 后 getConnectedEdges 取不到(与循环容器分支同理) + const connectedEdgesBeforeRemove = this.graph.getConnectedEdges(node); + connectedEdgesBeforeRemove.forEach((edge) => { + const edgeId = edge.id; + const source = edge.getSource(); + const target = edge.getTarget(); + const isInnerLine = parentId && this.activities[parentId]?.type === 'SubCanvas'; + if (isInnerLine) { + // 分组子节点的连线:从内嵌pipeline中删除 + this.deleteInnerLine(edgeId, parentId); + } else { + // 外层节点的连线:从外层store中删除 + this.onLineChange('delete', { + id: edgeId, + source: { cell: source.cell }, + target: { cell: target.cell }, + }); + } + this.graph.removeEdge(edgeId); + }); if (parentId && isGroupInner) { this.removeFromPipelineTree(node.id, parentId); } @@ -1602,27 +1622,6 @@ const nodeInstance = this.getNodeInstance(node.id); this.graph.select(nodeInstance); } - // 删除节点两端旧的连线-获取节点在画布上的实际连线(包括分组子节点的内嵌连线) - const connectedEdges = this.graph.getConnectedEdges(node.id); - connectedEdges.forEach((edge) => { - const edgeId = edge.id; - const source = edge.getSource(); - const target = edge.getTarget(); - const parentNode = node.getParent(); - const isInnerLine = parentNode && this.activities[parentNode.id]?.type === 'SubCanvas'; - if (isInnerLine) { - // 分组子节点的连线:从内嵌pipeline中删除 - this.deleteInnerLine(edgeId, parentNode.id); - } else { - // 外层节点的连线:从外层store中删除 - this.onLineChange('delete', { - id: edgeId, - source: { cell: source.cell }, - target: { cell: target.cell }, - }); - } - this.graph.removeEdge(edgeId); - }); // 被删除的节点只存在一条输入连线和输出连线时才允许自动连线 const { incoming = [], outgoing } = nodeConfig; if ( diff --git a/frontend/src/utils/validatePipeline.js b/frontend/src/utils/validatePipeline.js index e438e0637..f64d225bc 100644 --- a/frontend/src/utils/validatePipeline.js +++ b/frontend/src/utils/validatePipeline.js @@ -469,6 +469,7 @@ const validatePipeline = { let message; let tasknode = 0; let subflow = 0; + let subcanvas = 0; const errorId = []; const isLineNumValid = data.locations.every((loc) => { const rule = NODE_RULE[loc.type]; @@ -479,6 +480,8 @@ const validatePipeline = { tasknode += 1; } else if (loc.type === 'subflow') { subflow += 1; + } else if (loc.type === 'SubCanvas') { + subcanvas += 1; } data.lines.forEach((line) => { if (line.source.id === loc.id) { @@ -508,7 +511,7 @@ const validatePipeline = { return this.getMessage(false, message, errorId); } - if ((tasknode + subflow) === 0) { + if ((tasknode + subflow + subcanvas) === 0) { message = i18n.t('请添加任务节点'); return this.getMessage(false, message); } diff --git a/frontend/src/views/template/TemplateEdit/index.vue b/frontend/src/views/template/TemplateEdit/index.vue index 75d558741..6da6021cd 100644 --- a/frontend/src/views/template/TemplateEdit/index.vue +++ b/frontend/src/views/template/TemplateEdit/index.vue @@ -693,7 +693,7 @@ ? this.subprocess_info : this.constructedSubprocessInfo; if (!subprocessInfo) return; - subprocessInfo.forEach(item => { + subprocessInfo.forEach((item) => { processCanvas.onUpdateNodeInfo(item.subprocess_node_id, { hasUpdated: item.expired }); }); }, @@ -2165,7 +2165,7 @@ }); }, // 点击保存模板按钮回调 - onSaveTemplate(saveAndCreate, pid) { + async onSaveTemplate(saveAndCreate, pid) { if (this.templateSaving || this.createTaskSaving) { return; } @@ -2179,7 +2179,11 @@ this.isExecuteSchemeDialog = true; } } else { - this.checkNodeAndSaveTemplate(); + try { + await this.checkNodeAndSaveTemplate(); + } catch (error) { + console.warn(error); + } } }, // 校验节点配置 @@ -2204,8 +2208,10 @@ ellipsisCopy: true, delay: 10000, }); - return; + throw new Error(overlapResult.message); } + // 清空因重叠失败记录的节点,避免status 残留 + this.validateConnectFailList = []; const validateMessage = validatePipeline.isNodeLineNumValid(canvasData); if (!validateMessage.result) { // 获取检验不合格节点 @@ -2228,7 +2234,7 @@ ellipsisCopy: true, delay: 10000, }); - return; + throw new Error(validateMessage.message); } // 节点配置是否错误 const nodeWithErrors = document.querySelectorAll('.canvas-node-item .failed'); @@ -2246,22 +2252,23 @@ ellipsisCopy: true, delay: 10000, }); - return; + throw new Error(message); } const isAllNodeValid = this.validateAtomNode(); - if (isAllNodeValid) { - if (this.common && this.saveAndCreate && this.pid === undefined) { // 公共流程保存并创建任务,没有选择项目 - this.$refs.templateHeader.setProjectSelectDialogShow(); - } else { - if (this.isExecuteScheme) { - if (this.type === 'clone' || this.isTemplateDataChanged) { - this.isExecuteSchemeDialog = true; - } else { - this.isEditProcessPage = false; - } + if (!isAllNodeValid) { + throw new Error('validateAtomNode failed'); + } + if (this.common && this.saveAndCreate && this.pid === undefined) { // 公共流程保存并创建任务,没有选择项目 + this.$refs.templateHeader.setProjectSelectDialogShow(); + } else { + if (this.isExecuteScheme) { + if (this.type === 'clone' || this.isTemplateDataChanged) { + this.isExecuteSchemeDialog = true; } else { - await this.saveTemplate(); + this.isEditProcessPage = false; } + } else { + await this.saveTemplate(); } } }, @@ -2585,9 +2592,13 @@ } }, // 多 tab 打开同一流程模板 - onMultipleTabConfirm() { - this.checkNodeAndSaveTemplate(); - this.multipleTabDialogShow = false; + async onMultipleTabConfirm() { + try { + await this.checkNodeAndSaveTemplate(); + this.multipleTabDialogShow = false; + } catch (error) { + console.warn(error); + } }, getTplTabData() { return {