import React, { useCallback, useEffect } from 'react'; import { applyNodeChanges, applyEdgeChanges, addEdge, reconnectEdge, Node, Edge } from '@xyflow/react'; import { getAppInfoNew, setMainFlowNew, setSubFlowNew } from '@/api/appRes'; import { Message } from '@arco-design/web-react'; import { nodeTypeMap, registerNodeType } from '@/components/FlowEditor/node'; import { convertFlowData, reverseConvertFlowData, revertFlowData } from '@/utils/convertFlowData'; import { localNodeData } from '@/pages/flowEditor/sideBar/config/localNodeData'; import { useAlignmentGuidelines } from '@/hooks/useAlignmentGuidelines'; import LocalNode from '@/components/FlowEditor/node/localNode/LocalNode'; import LoopNode from '@/components/FlowEditor/node/loopNode/LoopNode'; import { updateCanvasDataMap, resetNodeStatus, updateIsRunning, updateEventListOld, addRuntimeLog, clearRuntimeLogs, updateRuntimeId, updateFlowData } from '@/store/ideContainer'; import { validateAllNodes, showValidationErrors, validateAllEdges } from '@/components/FlowEditor/nodeEditors/validators/nodeValidators'; import { getHandleType, validateDataType, getNodeComponent } from '@/utils/flowCommon'; import { projectFlowHandle } from '@/pages/flowEditor/utils/projectFlowHandle'; import { appFLowHandle } from '@/pages/flowEditor/utils/appFlowhandle'; import { handelEventNodeList, updateEvent, upDatePublish } from '@/pages/flowEditor/utils/common'; import { Dispatch } from 'redux'; import { runMainFlow, stopApp } from '@/api/apps'; import store from '@/store'; import { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent'; import { sleep } from '@/utils/common'; import { queryEventItemBySceneIdOld, deleteEventSub, deleteEventPub } from '@/api/event'; export const useFlowCallbacks = ( nodes: Node[], setNodes: React.Dispatch>, edges: Edge[], setEdges: React.Dispatch>, useDefault: boolean, reactFlowInstance: any, canvasDataMap: any, dispatch: Dispatch, updateCanvasDataMapDebounced: ( dispatch: Dispatch, canvasDataMap: any, id: string, nodes: Node[], edges: Edge[] ) => void, initialData: any, historyTimeoutRef: React.MutableRefObject, setHistoryInitialized: React.Dispatch>, editingNode: Node | null, setEditingNode: React.Dispatch>, setIsEditModalOpen: React.Dispatch>, edgeForNodeAdd: Edge | null, setEdgeForNodeAdd: React.Dispatch>, positionForNodeAdd: { x: number, y: number } | null, setPositionForNodeAdd: React.Dispatch>, setIsDelete: React.Dispatch> ) => { const { getGuidelines, clearGuidelines } = useAlignmentGuidelines(); // region 画布操作 // 节点变更处理,添加防抖机制 const onNodesChange = useCallback((changes: any) => { // 深度克隆节点数组以避免修改冻结的对象 const clonedNodes = JSON.parse(JSON.stringify(nodes)); const newNodes = applyNodeChanges(changes, clonedNodes); setNodes(newNodes); // 如果需要在节点变化时执行某些操作,可以在这里添加 // 只有当变化是节点位置变化时才不立即记录历史 const isPositionChange = changes.some((change: any) => change.type === 'position' && change.dragging === false ); // 如果是位置变化结束或者不是位置变化,则记录历史 if (isPositionChange || !changes.some((change: any) => change.type === 'position')) { // 清除之前的定时器 if (historyTimeoutRef.current) { clearTimeout(historyTimeoutRef.current); } // 设置新的定时器,延迟记录历史记录 historyTimeoutRef.current = setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...newNodes], edges: [...edges] } }); document.dispatchEvent(event); }, 100); } }, [nodes, edges]); // 边变更处理 const onEdgesChange = useCallback((changes: any) => { const newEdges = applyEdgeChanges(changes, edges); setEdges(newEdges); // 如果需要在边变化时执行某些操作,可以在这里添加 // 边的变化立即记录历史 const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...nodes], edges: [...newEdges] } }); document.dispatchEvent(event); }, [edges, nodes]); // 节点删除处理 const onNodesDelete = useCallback((deletedNodes: any) => { setIsDelete(true); }, []); // 边连接处理 const onConnect = useCallback((params: any) => { // 获取源节点和目标节点 const sourceNode = nodes.find(node => node.id === params.source); const targetNode = nodes.find(node => node.id === params.target); // 如果找不到节点,不创建连接 if (!sourceNode || !targetNode) { return; } // 获取源节点和目标节点的参数信息 const sourceParams: any = sourceNode.data?.parameters || {}; const targetParams: any = targetNode.data?.parameters || {}; // 获取源handle和目标handle的类型 (api或data) const sourceHandleType = getHandleType(params.sourceHandle, sourceParams); const targetHandleType = getHandleType(params.targetHandle, targetParams); // 验证连接类型是否匹配 (api只能连api, data只能连data) if (sourceHandleType !== targetHandleType) { console.warn('连接类型不匹配: ', sourceHandleType, targetHandleType); return; } // 验证数据类型是否匹配 if (!validateDataType(sourceNode, targetNode, params.sourceHandle, params.targetHandle)) { console.warn('数据类型不匹配'); return; } // 如果验证通过,创建连接 setEdges((edgesSnapshot: Edge[]) => { // 创建带有事件信息的连接 const edgeParams = { ...params, type: 'custom' }; // 添加lineType字段,用于区分API连接和数据连接 edgeParams.data = { ...edgeParams.data, lineType: sourceHandleType // 'api' 或 'data' }; // 对于数据类型的边,需要额外验证dataIns和dataOuts中的数据类型是否一致 if (sourceHandleType === 'data') { // 查找源节点的dataOuts中对应的数据 const sourceDataOut = (sourceParams.dataOuts || []).find((dataOut: any) => dataOut.name === params.sourceHandle || dataOut.id === params.sourceHandle); // 查找目标节点的dataIns中对应的数据 const targetDataIn = (targetParams.dataIns || []).find((dataIn: any) => dataIn.name === params.targetHandle || dataIn.id === params.targetHandle); // 验证数据类型是否一致 if (sourceDataOut && targetDataIn && sourceDataOut.dataType !== targetDataIn.dataType) { console.warn('数据类型不匹配,源节点数据类型:', sourceDataOut.dataType, '目标节点数据类型:', targetDataIn.dataType); Message.warning(`数据类型不匹配,源节点数据类型: ${sourceDataOut.dataType},目标节点数据类型: ${targetDataIn.dataType}`); return edgesSnapshot; // 不创建连接 } } // 检查源节点和目标节点是否都有事件信息 const sourceApi = (sourceParams.apiOuts || []).find((api: any) => (api?.eventId || api.name || api.id) === params.sourceHandle); const targetApi = (targetParams.apiIns || []).find((api: any) => (api?.eventId || api.name || api.id) === params.targetHandle); // 如果源节点有事件topic信息 if (sourceApi && sourceApi.topic) { // 如果目标节点的topic是**empty**或没有topic,则使用源节点的事件信息 if (!targetApi || !targetApi.topic || targetApi.topic.includes('**empty**')) { edgeParams.data = { ...edgeParams.data, lineType: 'api', displayData: { name: sourceApi.eventName, eventId: sourceApi.eventId, topic: sourceApi.topic } }; } // 如果两个节点都有非empty的topic,则以源节点为准 else if (sourceApi.topic && targetApi.topic && !sourceApi.topic.includes('**empty**') && !targetApi.topic.includes('**empty**')) { edgeParams.data = { ...edgeParams.data, lineType: 'api', displayData: { name: sourceApi.eventName, eventId: sourceApi.eventId, topic: sourceApi.topic } }; } } // 如果源节点没有事件信息,但目标节点有 else if (targetApi && targetApi.topic && !targetApi.topic.includes('**empty**')) { edgeParams.data = { ...edgeParams.data, lineType: 'api', displayData: { name: targetApi.eventName, eventId: targetApi.eventId, topic: targetApi.topic } }; } const newEdges = addEdge(edgeParams, edgesSnapshot); // 连接建立后记录历史 setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...nodes], edges: [...newEdges] } }); document.dispatchEvent(event); }, 0); return newEdges; }); }, [nodes]); // 边重新连接处理 const onReconnect = useCallback((oldEdge: Edge, newConnection: any) => { // 获取源节点和目标节点 const sourceNode = nodes.find(node => node.id === newConnection.source); const targetNode = nodes.find(node => node.id === newConnection.target); // 如果找不到节点,不创建连接 if (!sourceNode || !targetNode) { return; } // 获取源节点和目标节点的参数信息 const sourceParams = sourceNode.data?.parameters || {}; const targetParams = targetNode.data?.parameters || {}; // 获取源handle和目标handle的类型 (api或data) const sourceHandleType = getHandleType(newConnection.sourceHandle, sourceParams); const targetHandleType = getHandleType(newConnection.targetHandle, targetParams); // 验证连接类型是否匹配 (api只能连api, data只能连data) if (sourceHandleType !== targetHandleType) { console.warn('连接类型不匹配: ', sourceHandleType, targetHandleType); return; } // 验证数据类型是否匹配 if (!validateDataType(sourceNode, targetNode, newConnection.sourceHandle, newConnection.targetHandle)) { console.warn('数据类型不匹配'); return; } // 如果验证通过,重新连接 setEdges((els) => reconnectEdge(oldEdge, newConnection, els)); }, [nodes]); // 拖动处理 const onDragOver = useCallback((event: React.DragEvent) => { event.preventDefault(); event.dataTransfer.dropEffect = 'move'; }, []); // 侧边栏节点实例 const onDrop = useCallback((event: React.DragEvent) => { event.preventDefault(); if (!reactFlowInstance) return; const callBack = event.dataTransfer.getData('application/reactflow'); const nodeData = JSON.parse(callBack); if (typeof nodeData.nodeType === 'undefined' || !nodeData.nodeType) { return; } const position = reactFlowInstance.screenToFlowPosition({ x: event.clientX, y: event.clientY }); // 特殊处理循环节点,添加开始和结束节点 if (nodeData.nodeType === 'LOOP') { addLoopNodeWithStartEnd(position, nodeData); return; } const newNode = { id: `${nodeData.nodeType}-${Date.now()}`, type: nodeData.nodeType, position, data: { ...nodeData.data, title: nodeData.nodeName, type: nodeData.nodeType } }; // 将未定义的节点动态追加进nodeTypes const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key)); // 目前默认添加的都是系统组件/本地组件 if (!nodeMap.includes(nodeData.nodeType)) { registerNodeType(nodeData.nodeType, LocalNode, nodeData.nodeName); } setNodes((nds: Node[]) => { const newNodes = nds.concat(newNode); // 添加节点后记录历史 setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...newNodes], edges: [...edges] } }); document.dispatchEvent(event); }, 0); return newNodes; }); }, [reactFlowInstance, edges]); // 添加循环节点及其开始和结束节点 const addLoopNodeWithStartEnd = useCallback((position: { x: number, y: number }, nodeData: any) => { // 创建循环开始节点 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 // 这里的参数是为了提供在组件内部处理数据是使用,最后这个字段要序列化后放进customDef } } }; loopStartNode.data.component = { type: 'LOOP_START', customDef: JSON.stringify({ loopEndNodeId: loopEndNode.id }) }; // 创建连接边(连接循环开始和结束节点的顶部连接点) const newEdges = [ { id: `${loopStartNode.id}-${loopEndNode.id}-group`, source: loopStartNode.id, target: loopEndNode.id, sourceHandle: `${loopStartNode.id}-group`, targetHandle: `${loopEndNode.id}-group`, 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, ...newEdges] } }); document.dispatchEvent(event); }, 0); return newNodes; }); setEdges((eds: Edge[]) => { const updatedEdges = [...eds, ...newEdges]; // 添加边后记录历史 setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...nodes, loopStartNode, loopEndNode], edges: [...updatedEdges] } }); document.dispatchEvent(event); }, 0); return updatedEdges; }); }, [nodes, edges]); // 节点拖拽处理 const onNodeDrag = useCallback((_: any, node: Node) => { // 获取对齐线 getGuidelines(node, nodes); }, [nodes, getGuidelines]); // 节点拖拽结束处理 const onNodeDragStop = useCallback(() => { // 清除对齐线 clearGuidelines(); }, [clearGuidelines]); // endregion // region 画布数据处理 // 初始化画布数据 const initializeCanvasData = useCallback(() => { if (canvasDataMap[initialData?.appId]) { const { edges, nodes } = canvasDataMap[initialData?.appId]; setNodes(nodes); setEdges(edges); } else { // 首次进入 if (useDefault) projectFlowHandle(initialData, useDefault, setNodes, setEdges, dispatch, canvasDataMap); else appFLowHandle(initialData, useDefault, setNodes, setEdges, dispatch); } // 标记历史记录已初始化 setHistoryInitialized(true); }, [initialData, canvasDataMap]); // 实时更新 canvasDataMap const updateCanvasDataMapEffect = useCallback(() => { if (initialData?.appId) { updateCanvasDataMapDebounced(dispatch, canvasDataMap, initialData.appId, nodes, edges); } // 清理函数,在组件卸载时取消防抖 return () => { // 取消防抖函数 }; }, [nodes, edges, initialData?.appId, dispatch, canvasDataMap]); // 关闭编辑弹窗 const closeEditModal = useCallback(() => { setIsEditModalOpen(false); setEditingNode(null); }, []); // 保存节点编辑 const saveNodeEdit = useCallback((updatedData: any) => { console.log('updatedData:', updatedData); const updatedNodes = nodes.map((node) => { if (node.id === editingNode?.id) { return { ...node, data: { ...node.data, ...updatedData } }; } return node; }); setNodes(updatedNodes); closeEditModal(); }, [nodes, editingNode, closeEditModal]); // 编辑节点 const editNode = useCallback((node: Node) => { setEditingNode(node); setIsEditModalOpen(true); }, []); // 编辑边 const editEdge = useCallback((edge: Edge) => { // 这里可以实现边编辑逻辑 console.log('编辑边:', edge); }, []); // 复制节点 const copyNode = useCallback((node: Node) => { // 将节点数据存储到localStorage中,以便粘贴时使用 const nodeData = { ...node, // 清除可能存在的运行时状态 selected: false, dragging: false, // 添加应用标识信息 appId: initialData?.appId }; localStorage.setItem('copiedNode', JSON.stringify(nodeData)); }, [initialData?.appId]); // 粘贴节点 const pasteNode = useCallback((position: { x: number; y: number }) => { const copiedNodeStr = localStorage.getItem('copiedNode'); if (!copiedNodeStr) { console.warn('没有找到复制的节点数据'); return; } try { const copiedNode = JSON.parse(copiedNodeStr); // 检查是否为同一应用,如果不是则不允许粘贴 if (copiedNode.appId && initialData?.appId && copiedNode.appId !== initialData.appId) { console.warn('不能在不同应用之间粘贴节点'); // Message.warning('不能在不同应用之间粘贴节点'); return; } // 创建新节点,更新ID和位置 const newNode = { ...copiedNode, id: `${copiedNode.type}-${Date.now()}`, // 生成新的唯一ID position, // 使用传入的位置 selected: false, dragging: false, // 移除应用标识信息,避免存储在节点数据中 appId: undefined }; // 特殊处理循环节点 if (copiedNode.type === 'LOOP') { // 对于循环节点,我们需要特殊处理 setNodes((nds: Node[]) => { const newNodes = [...nds, newNode]; // 添加节点后记录历史 setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...newNodes], edges: [...edges] } }); document.dispatchEvent(event); }, 0); return newNodes; }); return; } // 将未定义的节点动态追加进nodeTypes const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key)); if (!nodeMap.includes(newNode.type)) { registerNodeType(newNode.type, getNodeComponent(newNode.type), newNode.data?.title || newNode.type); } setNodes((nds: Node[]) => { const newNodes = [...nds, newNode]; // 添加节点后记录历史 setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...newNodes], edges: [...edges] } }); document.dispatchEvent(event); }, 0); return newNodes; }); } catch (error) { console.error('粘贴节点时出错:', error); } }, [edges, initialData?.appId]); // endregion // region 节点/边操作 // 删除节点函数 const deleteNode = useCallback((node: Node) => { // 在应用编排模式下(useDefault为false)不允许删除节点 if (!useDefault) { console.warn('在应用编排模式下不允许删除节点'); return; } // 开始和结束节点不允许删除 if (node.type === 'start' || node.type === 'end') { console.warn('开始和结束节点不允许删除'); return; } // 处理循环节点删除逻辑 if (node.data?.type === 'LOOP_START' || node.data?.type === 'LOOP_END') { // 获取关联的另一个循环节点 let relatedNodeId = null; // 类型断言,将component从unknown转换为具有customDef属性的对象 const component = node.data?.component as { customDef?: string } | undefined; if (node.data?.type === 'LOOP_START' && component?.customDef) { try { const customDef = JSON.parse(component.customDef); relatedNodeId = customDef.loopEndNodeId; } catch (e) { console.error('解析循环开始节点数据失败:', e); } } else if (node.data?.type === 'LOOP_END' && component?.customDef) { try { const customDef = JSON.parse(component.customDef); relatedNodeId = customDef.loopStartNodeId; } catch (e) { console.error('解析循环结束节点数据失败:', e); } } // 删除两个节点及相关边 setNodes((nds: Node[]) => { const updatedNodes = nds.filter((n) => n.id !== node.id && n.id !== relatedNodeId ); return updatedNodes; }); setEdges((eds: Edge[]) => { const updatedEdges = eds.filter((e) => e.source !== node.id && e.target !== node.id && e.source !== relatedNodeId && e.target !== relatedNodeId ); return updatedEdges; }); // 删除节点后记录历史 setTimeout(() => { const updatedNodes = nodes.filter((n) => n.id !== node.id && n.id !== relatedNodeId ); const updatedEdges = edges.filter((e) => e.source !== node.id && e.target !== node.id && e.source !== relatedNodeId && e.target !== relatedNodeId ); const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...updatedNodes], edges: [...updatedEdges] } }); document.dispatchEvent(event); }, 0); return; } // 普通节点删除逻辑 setNodes((nds: Node[]) => nds.filter((n) => n.id !== node.id)); setEdges((eds: Edge[]) => eds.filter((e) => e.source !== node.id && e.target !== node.id)); // 删除节点后记录历史 setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...nodes.filter((n) => n.id !== node.id)], edges: [...edges.filter((e) => e.source !== node.id && e.target !== node.id)] } }); document.dispatchEvent(event); }, 0); }, [nodes, edges, useDefault]); // 删除边函数 const deleteEdge = useCallback((edge: Edge) => { // 获取当前应用的运行状态 const { appRuntimeData, currentAppData } = store.getState().ideContainer; const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id] ? appRuntimeData[currentAppData.id].isRunning : false; // 在运行时禁止删除边 if (currentAppIsRunning) { console.warn('在运行时不允许删除边'); return; } setEdges((eds: Edge[]) => eds.filter((e) => e.id !== edge.id)); // 删除边后记录历史 setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...nodes], edges: [...edges.filter((e) => e.id !== edge.id)] } }); document.dispatchEvent(event); }, 0); }, [nodes, edges]); // 在边上添加节点的具体实现 const addNodeOnEdge = useCallback((nodeType: string, node: any) => { const { currentAppData, flowData } = store.getState().ideContainer; if (!edgeForNodeAdd || !reactFlowInstance) return; // 查找节点定义 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', lineType: 'api', data: { lineType: 'api' } }, { id: `e${loopEndNode.id}-${edgeForNodeAdd.target}`, source: loopEndNode.id, target: edgeForNodeAdd.target, sourceHandle: 'break', // 循环结束节点的输出句柄 targetHandle: edgeForNodeAdd.targetHandle, type: 'custom', lineType: 'api', data: { lineType: 'api' } } ]; // 将未定义的节点动态追加进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); if (!sourceNode || !targetNode) return; // 计算中点位置 const position = { x: (sourceNode.position.x + targetNode.position.x) / 2, y: (sourceNode.position.y + targetNode.position.y) / 2 }; // 创建新节点 const newNode = { id: `${nodeType}-${Date.now()}`, type: nodeType, position, data: { ...nodeDefinition.data, title: nodeDefinition.nodeName, type: nodeType } }; // 有组件ID的节点就追加组件id数据 if (nodeDefinition.id || nodeDefinition?.flowHousVO?.id) newNode.data.compId = nodeDefinition.id || nodeDefinition?.flowHousVO?.id; if (nodeType === 'SWITCH') { newNode.data.component = { customDef: JSON.stringify({ apiOutIds: ['default'], conditions: [] }) }; } else if (nodeType === 'SUB') { const flowSubMap = flowData[currentAppData.id]?.subMap || {}; const sameData: any = flowSubMap[newNode.data.compId]; if (sameData) { newNode.data.component = { type: nodeType, compId: newNode.data.compId, customDef: JSON.stringify({ dataIns: newNode.data.parameters.dataIns, dataOuts: newNode.data.parameters.dataOuts, subflowId: sameData, name: newNode.data.title }) }; } else { newNode.data.component = { type: nodeType, compId: newNode.data.compId }; } } else if (nodeType === 'EVENTSEND' || nodeType === 'EVENTLISTENE') { const { eventList } = store.getState().ideContainer; const emptyEvent = eventList.find(item => item.topic.includes('**empty**')); newNode.data.component = { type: nodeType, customDef: { eventId: emptyEvent.id, name: emptyEvent.name, topic: emptyEvent.topic } }; } else if (nodeType === 'BASIC') { newNode.data.component = { type: nodeType, compId: nodeDefinition.id }; } // 将未定义的节点动态追加进nodeTypes const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key)); if (!nodeMap.includes(nodeType)) { registerNodeType(nodeType, getNodeComponent(nodeType), nodeDefinition.nodeName); } // 添加新节点 setNodes((nds: Node[]) => [...nds, newNode]); // 删除旧边 setEdges((eds: Edge[]) => eds.filter(e => e.id !== edgeForNodeAdd.id)); // 确定新边的句柄 // 对于第一条边 (source -> new node): 使用原始边的 sourceHandle,目标句柄根据节点类型确定 // 对于第二条边 (new node -> target): 源句柄根据节点类型确定,使用原始边的 targetHandle // 获取新节点的默认句柄 let newNodeSourceHandle = 'done'; // 默认源句柄 let newNodeTargetHandle = 'start'; // 默认目标句柄 // 如果新节点有参数定义,尝试获取更准确的句柄信息 if (newNode.data?.parameters) { const { apiOuts, apiIns } = newNode.data.parameters; // 获取第一个api输出作为源句柄(如果存在) if (apiOuts && apiOuts.length > 0) { newNodeSourceHandle = apiOuts[0].name || apiOuts[0].id || newNodeSourceHandle; } // 获取第一个api输入作为目标句柄(如果存在) if (apiIns && apiIns.length > 0) { newNodeTargetHandle = apiIns[0].name || apiIns[0].id || newNodeTargetHandle; } } // 创建新边: source -> new node, new node -> target const newEdges = [ ...edges.filter(e => e.id !== edgeForNodeAdd.id), { id: `e${edgeForNodeAdd.source}-${newNode.id}`, source: edgeForNodeAdd.source, target: newNode.id, sourceHandle: edgeForNodeAdd.sourceHandle, targetHandle: newNodeTargetHandle, type: 'custom', lineType: 'api', data: { lineType: 'api' } }, { id: `e${newNode.id}-${edgeForNodeAdd.target}`, source: newNode.id, target: edgeForNodeAdd.target, sourceHandle: newNodeSourceHandle, targetHandle: edgeForNodeAdd.targetHandle, type: 'custom', lineType: 'api', data: { lineType: 'api' } } ]; setEdges(newEdges); // 关闭菜单 setEdgeForNodeAdd(null); setPositionForNodeAdd(null); // 添加节点后记录历史 setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...nodes, newNode], edges: [...newEdges] } }); document.dispatchEvent(event); }, 0); }, [edgeForNodeAdd, nodes, reactFlowInstance, edges, addLoopNodeWithStartEnd]); // 在画布上添加节点 const addNodeOnPane = useCallback((nodeType: string, position: { x: number; y: number }, node?: any) => { const { currentAppData, flowData } = store.getState().ideContainer; if (!reactFlowInstance) return; // 查找节点定义 const nodeDefinition = localNodeData.find(n => n.nodeType === nodeType) || node; if (!nodeDefinition) return; // 特殊处理循环节点,添加开始和结束节点 if (nodeType === 'LOOP') { addLoopNodeWithStartEnd(position, nodeDefinition); return; } // 创建新节点 const newNode = { id: `${nodeType}-${Date.now()}`, type: nodeType, position, data: { ...nodeDefinition.data, title: nodeDefinition.nodeName, type: nodeType } }; if (nodeDefinition.id || nodeDefinition?.flowHousVO?.id) newNode.data.compId = nodeDefinition.id || nodeDefinition?.flowHousVO?.id; if (nodeType === 'SWITCH') { newNode.data.component = { customDef: JSON.stringify({ apiOutIds: ['default'], conditions: [] }) }; } else if (nodeType === 'SUB') { const flowSubMap = flowData[currentAppData.id]?.subMap || {}; const sameData: any = flowSubMap[newNode.data.compId]; if (sameData) { newNode.data.component = { type: nodeType, compId: newNode.data.compId, customDef: JSON.stringify({ dataIns: newNode.data.parameters.dataIns, dataOuts: newNode.data.parameters.dataOuts, subflowId: sameData, name: newNode.data.title }) }; } else { newNode.data.component = { type: nodeType, compId: newNode.data.compId }; } } else if (nodeType === 'EVENTSEND' || nodeType === 'EVENTLISTENE') { const { eventList } = store.getState().ideContainer; const emptyEvent = eventList.find(item => item.topic.includes('**empty**')); newNode.data.component = { type: nodeType, customDef: { eventId: emptyEvent?.eventId ?? null, name: emptyEvent.name, topic: emptyEvent.topic } }; } else if (nodeType === 'BASIC') { newNode.data.component = { type: nodeType, compId: nodeDefinition.id }; } // 将未定义的节点动态追加进nodeTypes const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key)); // 目前默认添加的都是系统组件/本地组件 if (!nodeMap.includes(nodeType)) { registerNodeType(nodeType, getNodeComponent(nodeType), nodeDefinition.nodeName); } setNodes((nds: Node[]) => { const newNodes = [...nds, newNode]; // 添加节点后记录历史 setTimeout(() => { const event = new CustomEvent('takeSnapshot', { detail: { nodes: [...newNodes], edges: [...edges] } }); document.dispatchEvent(event); }, 0); return newNodes; }); }, [reactFlowInstance, edges, addLoopNodeWithStartEnd]); // 处理添加节点的统一方法 const handleAddNode = useCallback((nodeType: string, node: any) => { // 如果是通过边添加节点 if (edgeForNodeAdd) { addNodeOnEdge(nodeType, node); } // 如果是通过画布添加节点 else if (positionForNodeAdd) { addNodeOnPane(nodeType, positionForNodeAdd, node); } // 清除状态 setEdgeForNodeAdd(null); setPositionForNodeAdd(null); }, [edgeForNodeAdd, positionForNodeAdd, addNodeOnEdge, addNodeOnPane]); // endregion const saveFlowDataToServer = useCallback(async () => { if (useDefault) { try { // 首先校验所有节点数据是否完整 const nodeValidation = validateAllNodes(nodes); if (!nodeValidation.isValid) { showValidationErrors(nodeValidation.errors); return; } // 然后校验所有连接线是否有效 const edgeValidation = validateAllEdges(edges, nodes); if (!edgeValidation.isValid) { showValidationErrors(edgeValidation.errors); return; } // 转换会原始数据类型 const revertedData = revertFlowData(nodes, edges); const upDatePublishCB = await upDatePublish(revertedData.nodeConfigs); const newRevertedData = reverseConvertFlowData(nodes, edges, upDatePublishCB); const { flowData, currentAppData, info } = store.getState().ideContainer; const { deleteEventSendNodeList, deleteEventlisteneList } = handelEventNodeList(newRevertedData); let params = {}; // 更新复合组件/子流程 if (currentAppData.key.includes('sub')) { const appEventDefinition = updateEvent(revertedData.nodeConfigs, initialData.appId); params = { ...currentAppData?.compData || {}, components: newRevertedData, appEventDefinition, sceneId: info.id }; const res: any = await setSubFlowNew(params, currentAppData.parentAppId); if (res.code === 200) { Message.success('保存成功'); // 更新事件枚举表 const res1: any = await queryEventItemBySceneIdOld(info.id); if (res1.code === 200) dispatch(updateEventListOld(res1.data)); // 更新缓存数据 - 与主流程保持一致 dispatch(updateCanvasDataMap({ ...canvasDataMap, [currentAppData.id]: { nodes, edges } })); const appRes: any = await getAppInfoNew(currentAppData.id); // 更新 flowData 中的数据 dispatch(updateFlowData({ [currentAppData.id]: appRes.data })); // 同步更新到 canvasDataMap if (appRes.data.main?.components) { const { nodes, edges } = convertFlowData(appRes.data.main.components, true); setNodes(nodes); setEdges(edges); dispatch(updateCanvasDataMap({ ...canvasDataMap, [currentAppData.id]: { nodes, edges } })); } } else { Message.error(res.message); } } // 更新主流程 else { const appEventDefinition = updateEvent(revertedData.nodeConfigs, initialData.appId); params = { ...flowData[currentAppData.id]?.main || {}, components: newRevertedData, appEventDefinition, sceneId: info.id }; const res: any = await setMainFlowNew(params, initialData.appId); if (res.code === 200) { Message.success('保存成功'); // 更新事件枚举表 const res1: any = await queryEventItemBySceneIdOld(info.id); if (res1.code === 200) dispatch(updateEventListOld(res1.data)); // 更新缓存数据 dispatch(updateCanvasDataMap({ ...canvasDataMap, [currentAppData.id]: { nodes, edges } })); const appRes: any = await getAppInfoNew(currentAppData.id); // 更新 flowData 中的数据 dispatch(updateFlowData({ [currentAppData.id]: appRes.data })); // 同步更新到 canvasDataMap if (appRes.data.main?.components) { const { nodes, edges } = convertFlowData(appRes.data.main.components, true); setNodes(nodes); setEdges(edges); dispatch(updateCanvasDataMap({ ...canvasDataMap, [currentAppData.id]: { nodes, edges } })); } } else { Message.error(res.message); } } // 事件节点变动数据有长度就通知后端,主流程和子流程(复合节点)通用 if (deleteEventSendNodeList.length > 0 || deleteEventlisteneList.length > 0) { deleteEventSendNodeList.length > 0 && deleteEventPub({ appId: currentAppData.id, topics: deleteEventSendNodeList }); deleteEventlisteneList.length > 0 && deleteEventSub({ appId: currentAppData.id, topics: deleteEventlisteneList }); } } catch (error) { console.error('Error saving flow data:', error); Message.error('保存失败'); } } else { const appFlowParams = { appEventList: {}, eventEdges: [] }; nodes.forEach(node => { appFlowParams.appEventList[node.id] = { x: node.position.x, y: node.position.y }; }); const eventMap = new Map(); edges.forEach((edge: any) => { // 处理事件连线 appFlowParams.eventEdges.push({ id: edge.id, source: edge.source, target: edge.target, lineType: 'data', data: { displayData: { ...edge.data.displayData } } }); // 应用组件的桩点id就是事件id const sourceId = edge.sourceHandle; const targetId = edge.targetHandle; const topic = edge.data.displayData.topic; if (eventMap.has(topic)) { // 如果topic已存在,将eventId添加到数组中 eventMap.get(topic).eventId.push(sourceId); eventMap.get(topic).eventId.push(targetId); } else { // 如果topic不存在,创建新的条目 eventMap.set(topic, { eventId: [sourceId, targetId], topic: topic }); } }); // 对eventId数组进行去重处理 const appEventParams = Array.from(eventMap.values()).map(item => ({ ...item, eventId: Array.from(new Set(item.eventId)) })); try { updateAppFlowData(appFlowParams); if (appEventParams.length > 0) { for (const item of appEventParams) { await sleep(500); await updateAppEventChannel(item); } } Message.success('保存成功'); } catch (error) { console.error('保存失败:', error); Message.error('保存失败: ' + (error.message)); } } }, [nodes, edges, initialData?.appId]); // 运行处理函数 const handleRun = useCallback(async (running: boolean) => { const { currentAppData, socketId, appRuntimeData } = store.getState().ideContainer; if (running) { // 启动运行 const params = { appId: currentAppData.id, socketId }; const res: any = await runMainFlow(params); if (res.code === 200) { // 设置运行状态为true dispatch(updateIsRunning(true)); // 重置节点状态 dispatch(resetNodeStatus()); // 更新运行ID dispatch(updateRuntimeId(res.data)); // 开始运行时动画 setEdges((eds) => eds.map(edge => ({ ...edge, data: { ...edge.data, isRunning: true, animationProgress: 0 } }))); } else { Message.error(res.message); } } else { // 设置运行状态为false dispatch(updateIsRunning(false)); await stopApp(appRuntimeData[currentAppData.id].runId); // 更新运行ID dispatch(updateRuntimeId('')); // 停止运行 setEdges((eds) => eds.map(edge => ({ ...edge, data: { ...edge.data, isRunning: false, animationProgress: 0 } }))); // 清空当前应用的运行日志 dispatch(clearRuntimeLogs({ appId: currentAppData.id })); } }, [initialData?.appId]); return { // Event handlers onNodesChange, onEdgesChange, onConnect, onReconnect, onDragOver, onDrop, onNodeDrag, onNodeDragStop, onNodesDelete, // Menu handlers closeEditModal, saveNodeEdit, deleteNode, deleteEdge, editNode, editEdge, copyNode, pasteNode, // 添加粘贴节点功能 // Node operations addNodeOnEdge, addNodeOnPane, handleAddNode, // Initialization initializeCanvasData, updateCanvasDataMapEffect, // Actions saveFlowDataToServer, handleRun }; }; export default useFlowCallbacks;