feat(flow): 实现循环节点的自动添加功能

- 添加对 LOOP 类型节点的特殊处理逻辑
- 自动创建循环开始和结束节点- 计算并设置节点位置在源节点和目标节点之间
- 创建连接原有节点与循环节点的边
- 注册缺失的 LOOP 节点类型到 nodeTypes
- 更新节点和边的状态管理逻辑
- 添加循环节点后触发历史记录快照
-优化依赖数组以包含新增的回调函数
master
钟良源 4 months ago
parent a925326557
commit ec07db1da2

@ -598,6 +598,149 @@ export const useFlowCallbacks = (
const nodeDefinition = localNodeData.find(n => n.nodeType === nodeType) || node;
if (!nodeDefinition) return;
// 特殊处理循环节点,添加开始和结束节点
if (nodeType === 'LOOP') {
// 获取源节点和目标节点的位置
const sourceNode = nodes.find(n => n.id === edgeForNodeAdd.source);
const targetNode = nodes.find(n => n.id === edgeForNodeAdd.target);
if (!sourceNode || !targetNode) return;
// 计算中点位置
const position = {
x: (sourceNode.position.x + targetNode.position.x) / 2,
y: (sourceNode.position.y + targetNode.position.y) / 2
};
// 创建循环开始和结束节点
const loopStartNode = {
id: `LOOP_START-${Date.now()}`,
type: 'LOOP',
position: { x: position.x, y: position.y },
data: {
title: '循环开始',
type: 'LOOP_START',
parameters: {
apiIns: [{ name: 'start', desc: '', dataType: '', defaultValue: '' }],
apiOuts: [{ name: 'done', desc: '', dataType: '', defaultValue: '' }],
dataIns: [],
dataOuts: []
},
component: {}
}
};
const loopEndNode = {
id: `LOOP_END-${Date.now()}`,
type: 'LOOP',
position: { x: position.x + 400, y: position.y },
data: {
title: '循环结束',
type: 'LOOP_END',
parameters: {
apiIns: [{ name: 'continue', desc: '', dataType: '', defaultValue: '' }],
apiOuts: [{ name: 'break', desc: '', dataType: '', defaultValue: '' }],
dataIns: [{
'arrayType': null,
'dataType': 'INTEGER',
'defaultValue': 10,
'desc': '最大循环次数',
'id': 'maxTime'
}],
dataOuts: []
},
component: {
type: 'LOOP_END',
customDef: JSON.stringify({
apiOutIds: ['continue', 'break'],
conditions: [],
loopStartNodeId: loopStartNode.id
}),
loopStartNodeId: loopStartNode.id
}
}
};
loopStartNode.data.component = {
type: 'LOOP_START',
customDef: JSON.stringify({ loopEndNodeId: loopEndNode.id })
};
// 创建连接边(连接循环开始和结束节点的顶部连接点)
const groupEdge = {
id: `${loopStartNode.id}-${loopEndNode.id}-group`,
source: loopStartNode.id,
target: loopEndNode.id,
sourceHandle: `${loopStartNode.id}-group`,
targetHandle: `${loopEndNode.id}-group`,
type: 'custom'
};
// 创建连接原有节点到循环开始节点,以及循环结束节点到目标节点的边
const connectionEdges = [
{
id: `e${edgeForNodeAdd.source}-${loopStartNode.id}`,
source: edgeForNodeAdd.source,
target: loopStartNode.id,
sourceHandle: edgeForNodeAdd.sourceHandle,
targetHandle: 'start', // 循环开始节点的输入句柄
type: 'custom'
},
{
id: `e${loopEndNode.id}-${edgeForNodeAdd.target}`,
source: loopEndNode.id,
target: edgeForNodeAdd.target,
sourceHandle: 'break', // 循环结束节点的输出句柄
targetHandle: edgeForNodeAdd.targetHandle,
type: 'custom'
}
];
// 将未定义的节点动态追加进nodeTypes
const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key));
if (!nodeMap.includes('LOOP')) {
registerNodeType('LOOP', LoopNode, '循环');
}
// 更新节点和边
setNodes((nds: Node[]) => {
const newNodes = [...nds, loopStartNode, loopEndNode];
// 添加节点后记录历史
setTimeout(() => {
const event = new CustomEvent('takeSnapshot', {
detail: { nodes: [...newNodes], edges: [...edges, groupEdge, ...connectionEdges] }
});
document.dispatchEvent(event);
}, 0);
return newNodes;
});
setEdges((eds: Edge[]) => {
// 删除原来的边,添加新的边
const updatedEdges = [...eds.filter(e => e.id !== edgeForNodeAdd.id), groupEdge, ...connectionEdges];
// 添加边后记录历史
setTimeout(() => {
const event = new CustomEvent('takeSnapshot', {
detail: {
nodes: [...nodes, loopStartNode, loopEndNode],
edges: [...updatedEdges]
}
});
document.dispatchEvent(event);
}, 0);
return updatedEdges;
});
// 关闭菜单
setEdgeForNodeAdd(null);
setPositionForNodeAdd(null);
return;
}
// 获取源节点和目标节点
const sourceNode = nodes.find(n => n.id === edgeForNodeAdd.source);
const targetNode = nodes.find(n => n.id === edgeForNodeAdd.target);
@ -704,7 +847,7 @@ export const useFlowCallbacks = (
});
document.dispatchEvent(event);
}, 0);
}, [edgeForNodeAdd, nodes, reactFlowInstance, edges]);
}, [edgeForNodeAdd, nodes, reactFlowInstance, edges, addLoopNodeWithStartEnd]);
// 在画布上添加节点
const addNodeOnPane = useCallback((nodeType: string, position: { x: number; y: number }, node?: any) => {

Loading…
Cancel
Save