mirror of https://github.com/langgenius/dify.git
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.
This commit is contained in:
parent
50bed78d7a
commit
9012dced6a
|
|
@ -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<string>()
|
||||
|
||||
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<string, GroupHandler>()
|
||||
|
||||
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({
|
||||
|
|
|
|||
|
|
@ -24,14 +24,15 @@ const GroupNode = (props: NodeProps<GroupNodeData>) => {
|
|||
: []
|
||||
), [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])
|
||||
|
|
|
|||
|
|
@ -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] || []
|
||||
|
|
|
|||
Loading…
Reference in New Issue