From 9012dced6ab881954cb7a03cde69c9d120dce0fa Mon Sep 17 00:00:00 2001 From: zhsama Date: Mon, 5 Jan 2026 17:42:31 +0800 Subject: [PATCH] feat(workflow): improve group node interaction handling - Enhanced `useNodesInteractions` to better manage group node handlers and connections, ensuring accurate identification of leaf nodes and their branches. - Updated logic to create handlers based on node connections, differentiating between internal and external connections. - Refined initial node setup to include target branches for group nodes, improving the overall interaction model for grouped elements. --- .../workflow/hooks/use-nodes-interactions.ts | 95 +++++++++++-------- .../components/workflow/nodes/group/node.tsx | 5 +- .../workflow/utils/workflow-init.ts | 10 ++ 3 files changed, 68 insertions(+), 42 deletions(-) diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts index 6d80024796..b52fabae1e 100644 --- a/web/app/components/workflow/hooks/use-nodes-interactions.ts +++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts @@ -103,7 +103,23 @@ function createGroupEdgePair(params: { }): { realEdge: Edge, uiEdge: Edge } | null { const { groupNodeId, handlerId, targetNodeId, targetHandle, nodes, baseEdgeData = {}, zIndex = 0 } = params - const { originalNodeId, originalSourceHandle } = parseGroupHandlerId(handlerId) + const groupNode = nodes.find(node => node.id === groupNodeId) + const groupData = groupNode?.data as GroupNodeData | undefined + const handler = groupData?.handlers?.find(h => h.id === handlerId) + + let originalNodeId: string + let originalSourceHandle: string + + if (handler?.nodeId && handler?.sourceHandle) { + originalNodeId = handler.nodeId + originalSourceHandle = handler.sourceHandle + } + else { + const parsed = parseGroupHandlerId(handlerId) + originalNodeId = parsed.originalNodeId + originalSourceHandle = parsed.originalSourceHandle + } + const originalNode = nodes.find(node => node.id === originalNodeId) const targetNode = nodes.find(node => node.id === targetNodeId) @@ -2580,9 +2596,40 @@ export const useNodesInteractions = () => { const outboundEdges = edges.filter(edge => bundledNodeIdSet.has(edge.source) && !bundledNodeIdSet.has(edge.target)) // leaf node: no outbound edges to other nodes in the selection - const leafNodeIds = bundledNodes - .filter(node => !edges.some(edge => edge.source === node.id && bundledNodeIdSet.has(edge.target))) - .map(node => node.id) + const handlers: GroupHandler[] = [] + const leafNodeIdSet = new Set() + + bundledNodes.forEach((node: Node) => { + const targetBranches = node.data._targetBranches || [{ id: 'source', name: node.data.title }] + targetBranches.forEach((branch) => { + // A branch should be a handler if it's either: + // 1. Connected to a node OUTSIDE the group + // 2. NOT connected to any node INSIDE the group + const isConnectedInside = edges.some(edge => + edge.source === node.id + && (edge.sourceHandle === branch.id || (!edge.sourceHandle && branch.id === 'source')) + && bundledNodeIdSet.has(edge.target), + ) + const isConnectedOutside = edges.some(edge => + edge.source === node.id + && (edge.sourceHandle === branch.id || (!edge.sourceHandle && branch.id === 'source')) + && !bundledNodeIdSet.has(edge.target), + ) + + if (isConnectedOutside || !isConnectedInside) { + const handlerId = `${node.id}-${branch.id}` + handlers.push({ + id: handlerId, + label: branch.name || node.data.title || node.id, + nodeId: node.id, + sourceHandle: branch.id, + }) + leafNodeIdSet.add(node.id) + } + }) + }) + + const leafNodeIds = Array.from(leafNodeIdSet) leafNodeIds.forEach(id => bundledNodeIdIsLeaf.add(id)) const members: GroupMember[] = bundledNodes.map((node) => { @@ -2592,42 +2639,6 @@ export const useNodesInteractions = () => { label: node.data.title, } }) - // Build handlers from all leaf nodes - // For multi-branch nodes (if-else, classifier), create one handler per branch - // For regular nodes, create one handler with 'source' handle - const handlerMap = new Map() - - leafNodeIds.forEach((nodeId) => { - const node = bundledNodes.find(n => n.id === nodeId) - if (!node) - return - - const targetBranches = node.data._targetBranches - if (targetBranches && targetBranches.length > 0) { - // Multi-branch node: create handler for each branch - targetBranches.forEach((branch: { id: string, name?: string }) => { - const handlerId = `${nodeId}-${branch.id}` - handlerMap.set(handlerId, { - id: handlerId, - label: branch.name || node.data.title || nodeId, - nodeId, - sourceHandle: branch.id, - }) - }) - } - else { - // Regular node: single 'source' handler - const handlerId = `${nodeId}-source` - handlerMap.set(handlerId, { - id: handlerId, - label: node.data.title || nodeId, - nodeId, - sourceHandle: 'source', - }) - } - }) - - const handlers: GroupHandler[] = Array.from(handlerMap.values()) // head nodes: nodes that receive input from outside the group const headNodeIds = [...new Set(inboundEdges.map(edge => edge.target))] @@ -2644,6 +2655,10 @@ export const useNodesInteractions = () => { headNodeIds, leafNodeIds, selected: true, + _targetBranches: handlers.map(handler => ({ + id: handler.id, + name: handler.label || handler.id, + })), } const { newNode: groupNode } = generateNewNode({ diff --git a/web/app/components/workflow/nodes/group/node.tsx b/web/app/components/workflow/nodes/group/node.tsx index c40e9476f5..37cd5e0419 100644 --- a/web/app/components/workflow/nodes/group/node.tsx +++ b/web/app/components/workflow/nodes/group/node.tsx @@ -24,14 +24,15 @@ const GroupNode = (props: NodeProps) => { : [] ), [data._children, data.members]) - // handler 列表:优先使用传入的 handlers,缺省时用 members 的 label 填充。 const handlers: GroupHandler[] = useMemo(() => ( data.handlers?.length ? data.handlers : members.length ? members.map(member => ({ - id: member.id, + id: `${member.id}-source`, label: member.label || member.id, + nodeId: member.id, + sourceHandle: 'source', })) : [] ), [data.handlers, members]) diff --git a/web/app/components/workflow/utils/workflow-init.ts b/web/app/components/workflow/utils/workflow-init.ts index c236049c64..4ee8390fc1 100644 --- a/web/app/components/workflow/utils/workflow-init.ts +++ b/web/app/components/workflow/utils/workflow-init.ts @@ -380,6 +380,16 @@ export const initialNodes = (originNodes: Node[], originEdges: Edge[]) => { }) } + if (node.data.type === BlockEnum.Group) { + const groupData = node.data as GroupNodeData + if (groupData.handlers?.length) { + node.data._targetBranches = groupData.handlers.map(handler => ({ + id: handler.id, + name: handler.label || handler.id, + })) + } + } + if (node.data.type === BlockEnum.Iteration) { const iterationNodeData = node.data as IterationNodeType iterationNodeData._children = iterationOrLoopNodeMap[node.id] || []