diff --git a/src/views/settings/creation/component/SaveWorkflowDialog.vue b/src/views/settings/creation/component/SaveWorkflowDialog.vue new file mode 100644 index 0000000..6ce746b --- /dev/null +++ b/src/views/settings/creation/component/SaveWorkflowDialog.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/views/settings/creation/index.vue b/src/views/settings/creation/index.vue index ac4aef2..afcf5e2 100644 --- a/src/views/settings/creation/index.vue +++ b/src/views/settings/creation/index.vue @@ -449,6 +449,18 @@
清空画布 + + 删除选中 + 保存工作流
@@ -564,25 +576,13 @@ - - - - - - - - - - - + :save-form="saveForm" + :current-editing-workflow-id="currentEditingWorkflowId" + :saving="saving" + @confirm="confirmSaveWorkflow" + /> @@ -770,7 +770,12 @@
- + @@ -789,6 +794,7 @@ import '@logicflow/core/dist/index.css'; import '@logicflow/extension/lib/style/index.css'; import SkillSelector from '/@/components/skill/NodeSkillSelector.vue'; import ModelSelector from '/@/components/model/ModelSelector.vue'; +import SaveWorkflowDialog from './component/SaveWorkflowDialog.vue'; import type { SkillItem } from '/@/api/settings/skill'; import { downloadToFile, @@ -3236,14 +3242,14 @@ watch( currentHttpBodyField.value = ''; showHttpBodyDialog.value = false; - // 重置 dynamicFormValues(不删除固定字段键,动态 expand 键按节点切换清理) - for (const key in dynamicFormValues) { - if (key.includes('_responseType_expand_')) { - delete dynamicFormValues[key]; - continue; - } - dynamicFormValues[key] = ''; + // 重置 dynamicFormValues(不删除固定字段键,动态 expand 键按节点切换清理) + for (const key in dynamicFormValues) { + if (key.includes('_responseType_expand_')) { + delete dynamicFormValues[key]; + continue; } + dynamicFormValues[key] = ''; + } const currentNodeCode = formState.nodeCode; const baseFormFields = nodeSchemaMap.value[currentNodeCode] || []; const baseFieldNames = new Set(baseFormFields.map((f) => f.field)); @@ -3779,7 +3785,100 @@ const resetFlow = () => { selectedElement.value = null; syncDsl(); }; -// 从后端 DSL 恢复工作流 +const cleanupReferencesToNode = (deletedNodeId: string) => { + const lf = logicFlowInstance.value; + if (!lf) return 0; + + const graphData = lf.getGraphData() as { nodes?: Item[] }; + const nodes = graphData.nodes || []; + let affectedCount = 0; + + nodes.forEach((node: any) => { + if (node.id === deletedNodeId) return; + const props = node.properties || {}; + const inputSource = Array.isArray(props.inputSource) ? props.inputSource : []; + const nextInputSource = inputSource.filter((item: any) => item?.nodeId !== deletedNodeId); + + if (nextInputSource.length === inputSource.length) return; + + affectedCount += 1; + const normalizedInputSource = nextInputSource.length > 0 ? nextInputSource : null; + lf.setProperties(node.id, { + ...props, + inputSource: normalizedInputSource, + }); + + if (selectedElement.value?.id === node.id) { + selectedElement.value.properties = { + ...props, + inputSource: normalizedInputSource, + }; + } + }); + + return affectedCount; +}; +const getAffectedDownstreamNodeNames = (deletedNodeId: string) => { + const lf = logicFlowInstance.value; + if (!lf) return [] as string[]; + + const graphData = lf.getGraphData() as { nodes?: Item[] }; + const nodes = graphData.nodes || []; + const names: string[] = []; + + nodes.forEach((node: any) => { + if (node.id === deletedNodeId) return; + const props = node.properties || {}; + const inputSource = Array.isArray(props.inputSource) ? props.inputSource : []; + const referenced = inputSource.some((item: any) => item?.nodeId === deletedNodeId); + if (!referenced) return; + + const nodeName = typeof node.text === 'string' ? node.text : node.text?.value || node.id; + names.push(String(nodeName)); + }); + + return names; +}; +const deleteSelectedElement = async () => { + const lf = logicFlowInstance.value; + const cur = selectedElement.value; + if (!lf || !cur) return; + + if (cur.kind === 'node' && (cur.properties?.nodeCode === START_NODE_CODE || cur.text === START_NODE_TEXT)) { + ElMessage.warning('开始节点不能删除'); + return; + } + + try { + let affectedCount = 0; + if (cur.kind === 'node') { + const affectedNodeNames = getAffectedDownstreamNodeNames(cur.id); + if (affectedNodeNames.length > 0) { + const previewNames = affectedNodeNames.slice(0, 8); + const overflowText = affectedNodeNames.length > 8 ? `\n...等 ${affectedNodeNames.length} 个节点` : ''; + await ElMessageBox.confirm( + `删除该节点将清理以下下级节点中的引用:\n${previewNames.join('、')}${overflowText}`, + '删除确认', + { + confirmButtonText: '继续删除', + cancelButtonText: '取消', + type: 'warning', + } + ); + } + + affectedCount = cleanupReferencesToNode(cur.id); + lf.deleteNode(cur.id); + } else { + lf.deleteEdge(cur.id); + } + selectedElement.value = null; + ElMessage.success(affectedCount > 0 ? `删除成功,已清理 ${affectedCount} 个下级节点引用` : '删除成功'); + } catch (error) { + if (error === 'cancel') return; + ElMessage.error('删除失败'); + } +};// 从后端 DSL 恢复工作流 const loadWorkflowFromDsl = (dsl: any) => { const lf = logicFlowInstance.value; if (!lf || !dsl) return; @@ -5527,3 +5626,7 @@ onBeforeUnmount(() => { justify-content: center; } + + + +