From 0d9cdd50397b2a2d5874d6be1204528d9ed74dd5 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Wed, 14 Jan 2026 18:51:33 +0800 Subject: [PATCH] fix(web): update add node --- .../views/Workflow/components/Chat/Chat.tsx | 1 + .../Workflow/components/Nodes/AddNode.tsx | 9 +- .../Workflow/components/Nodes/LoopNode.tsx | 25 +++--- .../Workflow/components/PortClickHandler.tsx | 89 +++++++++++++++++-- .../views/Workflow/hooks/useWorkflowGraph.ts | 15 ++-- 5 files changed, 111 insertions(+), 28 deletions(-) diff --git a/web/src/views/Workflow/components/Chat/Chat.tsx b/web/src/views/Workflow/components/Chat/Chat.tsx index 9b1d3254..c1047251 100644 --- a/web/src/views/Workflow/components/Chat/Chat.tsx +++ b/web/src/views/Workflow/components/Chat/Chat.tsx @@ -55,6 +55,7 @@ const Chat = forwardRef(({ appId setOpen(false) setChatList([]) setVariables([]) + setConversationId(null) } const handleEditVariables = () => { variableConfigModalRef.current?.handleOpen(variables) diff --git a/web/src/views/Workflow/components/Nodes/AddNode.tsx b/web/src/views/Workflow/components/Nodes/AddNode.tsx index b9e602d4..a37da651 100644 --- a/web/src/views/Workflow/components/Nodes/AddNode.tsx +++ b/web/src/views/Workflow/components/Nodes/AddNode.tsx @@ -13,11 +13,12 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => { const handleNodeSelect = (selectedNodeType: any) => { const parentBBox = node.getBBox(); const cycleId = data.cycle; + const horizontalSpacing = 20; const id = `${selectedNodeType.type.replace(/-/g, '_') }_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` const newNode = graph.addNode({ ...(graphNodeLibrary[selectedNodeType.type] || graphNodeLibrary.default), - x: parentBBox.x, + x: parentBBox.x + horizontalSpacing, y: parentBBox.y, id, data: { @@ -35,7 +36,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => { if (cycleId) { const parentNode = graph.getNodes().find((n: any) => n.getData()?.id === cycleId); if (parentNode) { - parentNode.addChild(newNode); + parentNode.insertChild(newNode); } } @@ -47,7 +48,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => { source: { cell: edge.getSourceCellId(), port: edge.getSourcePortId() }, target: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'left')?.id || 'left' }, attrs: edge.getAttrs(), - zIndex: 3 + zIndex: 1, }); }); @@ -58,7 +59,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => { source: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'right')?.id || 'right' }, target: { cell: edge.getTargetCellId(), port: targetPortId }, attrs: edge.getAttrs(), - zIndex: 3 + zIndex: 1, }); }); diff --git a/web/src/views/Workflow/components/Nodes/LoopNode.tsx b/web/src/views/Workflow/components/Nodes/LoopNode.tsx index 40b4b8ec..71f4dc44 100644 --- a/web/src/views/Workflow/components/Nodes/LoopNode.tsx +++ b/web/src/views/Workflow/components/Nodes/LoopNode.tsx @@ -11,9 +11,13 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => { const { t } = useTranslation() useEffect(() => { - initNodes() - // 检查是否需要添加add-node - checkAndAddAddNode() + // 使用setTimeout确保在所有节点都添加完成后再创建连线 + const timer = setTimeout(() => { + initNodes() + checkAndAddAddNode() + }, 50) + + return () => clearTimeout(timer) }, []) const checkAndAddAddNode = () => { @@ -29,7 +33,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => { const addNode = graph.addNode({ ...graphNodeLibrary.addStart, - x: cycleStartBBox.x + 64, + x: cycleStartBBox.x + 84, y: cycleStartBBox.y, data: { type: 'add-node', @@ -40,7 +44,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => { }, }); - node.addChild(addNode); + node.insertChild(addNode); // 连接cycle-start和add-node const sourcePorts = cycleStartNode.getPorts(); @@ -48,6 +52,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => { const sourcePort = sourcePorts.find((port: any) => port.group === 'right')?.id || 'right'; const targetPort = targetPorts.find((port: any) => port.group === 'left')?.id || 'left'; + // 直接创建连线,不使用异步 graph.addEdge({ source: { cell: cycleStartNode.id, port: sourcePort }, target: { cell: addNode.id, port: targetPort }, @@ -61,7 +66,6 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => { }, }, }, - zIndex: 10 }); } } @@ -93,7 +97,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => { }); const addNode = graph.addNode({ ...graphNodeLibrary.addStart, - x: centerX + 64, + x: centerX + 84, y: centerY, data: { type: 'add-node', @@ -103,8 +107,8 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => { cycle: data.id, }, }); - node.addChild(cycleStartNode) - node.addChild(addNode) + node.insertChild(cycleStartNode) + node.insertChild(addNode) const sourcePorts = cycleStartNode.getPorts() const targetPorts = addNode.getPorts() let sourcePort = sourcePorts.find((port: any) => port.group === 'right')?.id || 'right'; @@ -124,11 +128,10 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => { strokeWidth: 1, targetMarker: { name: 'block', - size: 8, + size: 2, }, }, }, - zIndex: 10 } graph.addEdge(edgeConfig) diff --git a/web/src/views/Workflow/components/PortClickHandler.tsx b/web/src/views/Workflow/components/PortClickHandler.tsx index 9b83215a..8342ae6a 100644 --- a/web/src/views/Workflow/components/PortClickHandler.tsx +++ b/web/src/views/Workflow/components/PortClickHandler.tsx @@ -36,11 +36,77 @@ const PortClickHandler: React.FC = ({ graph }) => { if (!sourceNode || !graph) return; const sourceNodeData = sourceNode.getData(); + const sourceNodeType = sourceNodeData?.type; - // 计算新节点位置(在源节点右侧) + // 如果是cycle-start节点,需要处理add-node节点 + let addNodePosition = null; + if (sourceNodeType === 'cycle-start' && sourceNodeData.cycle) { + const cycleId = sourceNodeData.cycle; + const addNodes = graph.getNodes().filter((n: any) => + n.getData()?.type === 'add-node' && n.getData()?.cycle === cycleId + ); + + if (addNodes.length > 0) { + const addNode = addNodes[0]; + addNodePosition = addNode.getBBox(); + addNode.remove(); + } + } + + // 计算新节点位置,避免重叠 const sourceBBox = sourceNode.getBBox(); - const newX = sourceBBox.x + sourceBBox.width + 50; - const newY = sourceBBox.y; + const nodeWidth = graphNodeLibrary[selectedNodeType.type]?.width || 120; + const nodeHeight = graphNodeLibrary[selectedNodeType.type]?.height || 88; + const horizontalSpacing = sourceNodeType === 'cycle-start' ? 40 : 80; + const verticalSpacing = 10; + + // 获取源连接桩的group信息 + const sourcePortInfo = sourceNode.getPorts().find((p: any) => p.id === sourcePort); + const sourcePortGroup = sourcePortInfo?.group || sourcePort; + console.log('sourcePortGroup', sourcePortGroup, sourcePortInfo) + + // 如果有add-node位置,使用该位置,否则计算新位置 + let newX, newY; + if (addNodePosition) { + newX = addNodePosition.x; + newY = addNodePosition.y; + } else { + // 根据连接桩位置决定节点放置方向 + if (sourcePortGroup === 'left') { + // 左侧连接桩,在左侧添加节点 + newX = sourceBBox.x - nodeWidth*2 - horizontalSpacing; + newY = sourceBBox.y; + } else { + // 右侧连接桩,在右侧添加节点 + newX = sourceBBox.x + sourceBBox.width + horizontalSpacing; + newY = sourceBBox.y; + } + + // 检查位置是否与现有节点重叠(只考虑与当前节点相连的节点) + const checkOverlap = (x: number, y: number) => { + // 获取与源节点相连的节点 + const connectedNodes = new Set(); + graph.getConnectedEdges(sourceNode).forEach((edge: any) => { + const sourceId = edge.getSourceCellId(); + const targetId = edge.getTargetCellId(); + if (sourceId !== sourceNode.id) connectedNodes.add(sourceId); + if (targetId !== sourceNode.id) connectedNodes.add(targetId); + }); + + return graph.getNodes().some((node: any) => { + if (node.id === sourceNode.id) return false; + if (!connectedNodes.has(node.id)) return false; // 只考虑相连的节点 + const bbox = node.getBBox(); + return !(x + nodeWidth < bbox.x || x > bbox.x + bbox.width || + y + nodeHeight < bbox.y || y > bbox.y + bbox.height); + }); + }; + + // 如果位置被占用,向下寻找空位 + while (checkOverlap(newX, newY)) { + newY += nodeHeight + verticalSpacing; + } + } // 创建新节点 const id = `${selectedNodeType.type.replace(/-/g, '_')}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` @@ -63,14 +129,22 @@ const PortClickHandler: React.FC = ({ graph }) => { if (sourceNodeData.cycle) { const parentNode = graph.getNodes().find((n: any) => n.getData()?.id === sourceNodeData.cycle); if (parentNode) { - parentNode.addChild(newNode); + parentNode.insertChild(newNode); } } // 创建连线 setTimeout(() => { const targetPorts = newNode.getPorts(); - const targetPort = targetPorts.find((port: any) => port.group === 'left')?.id || 'left'; + let targetPort; + + if (sourcePortGroup === 'left') { + // 从左侧连接桩连出,连接到新节点的右侧 + targetPort = targetPorts.find((port: any) => port.group === 'right')?.id || 'right'; + } else { + // 从右侧连接桩连出,连接到新节点的左侧 + targetPort = targetPorts.find((port: any) => port.group === 'left')?.id || 'left'; + } graph.addEdge({ source: { cell: sourceNode.id, port: sourcePort }, @@ -85,7 +159,7 @@ const PortClickHandler: React.FC = ({ graph }) => { }, }, }, - zIndex: 0 + zIndex: sourceNodeData.cycle && sourceNodeType == 'cycle-start' ? 1 : sourceNodeData.cycle ? 2 : 0 }); // 循环节点内子节点通过连接桩添加时,调整循环节点大小 @@ -108,8 +182,9 @@ const PortClickHandler: React.FC = ({ graph }) => { }, { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }); const padding = 20; + const bottomPadding = 50; const newWidth = Math.max(240, bounds.maxX - bounds.minX + padding * 2); - const newHeight = Math.max(120, bounds.maxY - bounds.minY + padding * 2); + const newHeight = Math.max(120, bounds.maxY - bounds.minY + padding + bottomPadding); parentNode.prop('size', { width: newWidth, height: newHeight }); } diff --git a/web/src/views/Workflow/hooks/useWorkflowGraph.ts b/web/src/views/Workflow/hooks/useWorkflowGraph.ts index c281a76e..f3439571 100644 --- a/web/src/views/Workflow/hooks/useWorkflowGraph.ts +++ b/web/src/views/Workflow/hooks/useWorkflowGraph.ts @@ -235,7 +235,7 @@ export const useWorkflowGraph = ({ if (parentNode) { const addedChild = graphRef.current?.addNode(childNode) if (addedChild) { - parentNode.addChild(addedChild) + parentNode.insertChild(addedChild) } } } @@ -275,6 +275,11 @@ export const useWorkflowGraph = ({ }, 100) } if (edges.length) { + // 计算loop和iteration类型节点的数量 + const loopIterationCount = nodes.filter(node => + node.type === 'loop' || node.type === 'iteration' + ).length; + // 去重处理:对于if-else和question-classifier节点,不同连接桩允许连接到相同节点 const uniqueEdges = edges.filter((edge, index, arr) => { return arr.findIndex(e => { @@ -349,7 +354,7 @@ export const useWorkflowGraph = ({ }, }, }, - zIndex: targetCell.getData()?.cycle ? 3 : 0 + zIndex: loopIterationCount } return edgeConfig @@ -725,7 +730,6 @@ export const useWorkflowGraph = ({ }, }, }, - zIndex: 0, }); }, validateConnection({ sourceCell, targetCell, targetMagnet }) { @@ -762,9 +766,8 @@ export const useWorkflowGraph = ({ }, embedding: { enabled: true, - validate (this, { parent }) { - const parentData = parent.getData() - return parentData.type === 'iteration' || parentData.type === 'loop' + validate (this) { + return false } }, translating: {