diff --git a/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts b/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts index 19c63a2..7b90b19 100644 --- a/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts +++ b/src/components/FlowEditor/nodeEditors/validators/nodeValidators.ts @@ -1,4 +1,5 @@ import { Message } from '@arco-design/web-react'; +import { Edge } from '@xyflow/react'; export interface ValidationResult { isValid: boolean; @@ -365,6 +366,130 @@ export const validateAllNodes = (nodes: any[]): ValidationResult => { }; }; +/** + * 校验流程中的所有连接线 + * @param edges 流程中的所有连接线 + * @param nodes 流程中的所有节点(用于验证节点存在性) + * @returns 校验结果 + */ +export const validateAllEdges = (edges: Edge[], nodes: any[]): ValidationResult => { + const allErrors: string[] = []; + + // 创建节点ID到节点标题的映射,方便错误信息展示 + const nodeTitleMap = new Map(); + const nodeMap = new Map(); + + nodes.forEach(node => { + const nodeTitle = node.data?.title || node.id; + nodeTitleMap.set(node.id, nodeTitle); + nodeMap.set(node.id, node); + }); + + // 检查连接线的基本信息 + edges.forEach((edge, index) => { + // 检查连接线是否有源节点和目标节点 + if (!edge.source) { + allErrors.push(`第${index + 1}条连接线缺少源节点`); + return; + } + + if (!edge.target) { + allErrors.push(`第${index + 1}条连接线缺少目标节点`); + return; + } + + // 检查源节点和目标节点是否存在 + if (edge.source && !nodeTitleMap.has(edge.source)) { + allErrors.push(`第${index + 1}条连接线的源节点"${edge.source}"不存在`); + } + + if (edge.target && !nodeTitleMap.has(edge.target)) { + allErrors.push(`第${index + 1}条连接线的目标节点"${edge.target}"不存在`); + } + }); + + // 检查是否有未连接的节点(开始节点和结束节点除外) + const connectedNodeIds = new Set(); + edges.forEach(edge => { + if (edge.source) connectedNodeIds.add(edge.source); + if (edge.target) connectedNodeIds.add(edge.target); + }); + + nodes.forEach(node => { + // 如果节点没有被任何连接线连接 + if (!connectedNodeIds.has(node.id)) { + const nodeType = node.data?.type || node.type; + // 开始节点和结束节点可以不连接 + if (nodeType !== 'start' && nodeType !== 'end') { + const nodeName = node.data?.title || node.id; + allErrors.push(`节点"${nodeName}"未连接任何连线`); + } + } + }); + + // 检查节点的连接是否符合规则 + const sourceCount = new Map(); // 每个节点作为源节点的次数 + const targetCount = new Map(); // 每个节点作为目标节点的次数 + + edges.forEach(edge => { + if (edge.source) { + sourceCount.set(edge.source, (sourceCount.get(edge.source) || 0) + 1); + } + if (edge.target) { + targetCount.set(edge.target, (targetCount.get(edge.target) || 0) + 1); + } + }); + + // 检查每个节点的连接情况 + nodes.forEach(node => { + const nodeId = node.id; + const nodeType = node.data?.type || node.type; + const nodeName = node.data?.title || node.id; + const sourceEdges = sourceCount.get(nodeId) || 0; + const targetEdges = targetCount.get(nodeId) || 0; + + + // 特定节点类型可能有特殊的连接规则 + switch (nodeType) { + case 'SWITCH': + // 条件节点应该有至少一个输出连接 + if (sourceEdges === 0) { + allErrors.push(`条件节点"${nodeName}"缺少输出连接`); + } + break; + + case 'start': + // 开始节点应该有输出连接,但不需要输入连接 + if (sourceEdges === 0) { + allErrors.push(`开始节点"${nodeName}"缺少输出连接`); + } + break; + + case 'end': + // 结束节点应该有输入连接,但不需要输出连接 + if (targetEdges === 0) { + allErrors.push(`结束节点"${nodeName}"缺少输入连接`); + } + break; + + default: + // 其他节点应该至少有一个输入和一个输出连接 + if (targetEdges === 0) { + allErrors.push(`节点"${nodeName}"缺少输入连接`); + } + if (sourceEdges === 0) { + allErrors.push(`节点"${nodeName}"缺少输出连接`); + } + break; + } + }); + + return { + isValid: allErrors.length === 0, + errors: allErrors + }; +}; + /** * 显示校验错误信息 * @param errors 错误信息数组 diff --git a/src/hooks/useFlowCallbacks.ts b/src/hooks/useFlowCallbacks.ts index 45816e5..20a199b 100644 --- a/src/hooks/useFlowCallbacks.ts +++ b/src/hooks/useFlowCallbacks.ts @@ -24,7 +24,11 @@ import ImageNode from '@/components/FlowEditor/node/imageNode/ImageNode'; import CodeNode from '@/components/FlowEditor/node/codeNode/CodeNode'; import RestNode from '@/components/FlowEditor/node/restNode/RestNode'; import { updateCanvasDataMap } from '@/store/ideContainer'; -import { validateAllNodes, showValidationErrors } from '@/components/FlowEditor/nodeEditors/validators/nodeValidators'; +import { + validateAllNodes, + showValidationErrors, + validateAllEdges +} from '@/components/FlowEditor/nodeEditors/validators/nodeValidators'; import { Dispatch } from 'redux'; @@ -782,9 +786,16 @@ export const useFlowCallbacks = ( const saveFlowDataToServer = useCallback(async () => { try { // 首先校验所有节点数据是否完整 - const validation = validateAllNodes(nodes); - if (!validation.isValid) { - showValidationErrors(validation.errors); + const nodeValidation = validateAllNodes(nodes); + if (!nodeValidation.isValid) { + showValidationErrors(nodeValidation.errors); + return; + } + + // 然后校验所有连接线是否有效 + const edgeValidation = validateAllEdges(edges, nodes); + if (!edgeValidation.isValid) { + showValidationErrors(edgeValidation.errors); return; }