|
|
|
|
@ -8,56 +8,38 @@ import {
|
|
|
|
|
Edge
|
|
|
|
|
} from '@xyflow/react';
|
|
|
|
|
import { setMainFlow } from '@/api/appRes';
|
|
|
|
|
import { getUserToken } from '@/api/user';
|
|
|
|
|
import { Message } from '@arco-design/web-react';
|
|
|
|
|
import { nodeTypeMap, registerNodeType } from '@/components/FlowEditor/node';
|
|
|
|
|
import { convertFlowData, revertFlowData } from '@/utils/convertFlowData';
|
|
|
|
|
import { localNodeData } from '@/pages/flowEditor/sideBar/config/localNodeData';
|
|
|
|
|
import { defaultNodeTypes } from '@/components/FlowEditor/node/types/defaultType';
|
|
|
|
|
import useWebSocket from '@/hooks/useWebSocket';
|
|
|
|
|
import { useAlignmentGuidelines } from '@/hooks/useAlignmentGuidelines';
|
|
|
|
|
import LocalNode from '@/components/FlowEditor/node/localNode/LocalNode';
|
|
|
|
|
import LoopNode from '@/components/FlowEditor/node/loopNode/LoopNode';
|
|
|
|
|
import BasicNode from '@/components/FlowEditor/node/basicNode/BasicNode';
|
|
|
|
|
import SwitchNode from '@/components/FlowEditor/node/switchNode/SwitchNode';
|
|
|
|
|
import ImageNode from '@/components/FlowEditor/node/imageNode/ImageNode';
|
|
|
|
|
import CodeNode from '@/components/FlowEditor/node/codeNode/CodeNode';
|
|
|
|
|
import RestNode from '@/components/FlowEditor/node/restNode/RestNode';
|
|
|
|
|
import { updateCanvasDataMap } 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 { Dispatch } from 'redux';
|
|
|
|
|
import { runMainFlow } from '@/api/apps';
|
|
|
|
|
import store from '@/store';
|
|
|
|
|
import { updateAppEvent } from '@/api/appEvent';
|
|
|
|
|
|
|
|
|
|
// 根据节点类型获取对应的节点组件
|
|
|
|
|
const getNodeComponent = (nodeType: string) => {
|
|
|
|
|
switch (nodeType) {
|
|
|
|
|
case 'BASIC':
|
|
|
|
|
return BasicNode;
|
|
|
|
|
case 'SWITCH':
|
|
|
|
|
return SwitchNode;
|
|
|
|
|
case 'IMAGE':
|
|
|
|
|
return ImageNode;
|
|
|
|
|
case 'CODE':
|
|
|
|
|
return CodeNode;
|
|
|
|
|
case 'REST':
|
|
|
|
|
return RestNode;
|
|
|
|
|
default:
|
|
|
|
|
return LocalNode;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useFlowCallbacks = (
|
|
|
|
|
nodes: Node[],
|
|
|
|
|
setNodes: React.Dispatch<React.SetStateAction<Node[]>>,
|
|
|
|
|
edges: Edge[],
|
|
|
|
|
setEdges: React.Dispatch<React.SetStateAction<Edge[]>>,
|
|
|
|
|
useDefault: boolean,
|
|
|
|
|
reactFlowInstance: any,
|
|
|
|
|
canvasDataMap: any,
|
|
|
|
|
dispatch: Dispatch<any>,
|
|
|
|
|
@ -82,278 +64,188 @@ export const useFlowCallbacks = (
|
|
|
|
|
setIsRunning: React.Dispatch<React.SetStateAction<boolean>>
|
|
|
|
|
) => {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取handle类型 (api或data)
|
|
|
|
|
const getHandleType = (handleId: string, nodeParams: any) => {
|
|
|
|
|
// 检查是否为api类型的handle
|
|
|
|
|
const apiOuts = nodeParams.apiOuts || [];
|
|
|
|
|
const apiIns = nodeParams.apiIns || [];
|
|
|
|
|
|
|
|
|
|
if (apiOuts.some((api: any) => (api.name || api.id) === handleId) ||
|
|
|
|
|
apiIns.some((api: any) => (api.name || api.id) === handleId) || (handleId.includes('loop'))) {
|
|
|
|
|
return 'api';
|
|
|
|
|
// 设置新的定时器,延迟记录历史记录
|
|
|
|
|
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);
|
|
|
|
|
// 如果需要在边变化时执行某些操作,可以在这里添加
|
|
|
|
|
|
|
|
|
|
// 检查是否为data类型的handle
|
|
|
|
|
const dataOuts = nodeParams.dataOuts || [];
|
|
|
|
|
const dataIns = nodeParams.dataIns || [];
|
|
|
|
|
// 边的变化立即记录历史
|
|
|
|
|
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 (dataOuts.some((data: any) => (data.name || data.id) === handleId) ||
|
|
|
|
|
dataIns.some((data: any) => (data.name || data.id) === handleId)) {
|
|
|
|
|
return 'data';
|
|
|
|
|
// 如果找不到节点,不创建连接
|
|
|
|
|
if (!sourceNode || !targetNode) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 默认为data类型
|
|
|
|
|
return 'data';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 验证数据类型是否匹配
|
|
|
|
|
const validateDataType = (sourceNode: defaultNodeTypes, targetNode: defaultNodeTypes, sourceHandleId: string, targetHandleId: string) => {
|
|
|
|
|
// 获取源节点和目标节点的参数信息
|
|
|
|
|
const sourceParams = sourceNode.data?.parameters || {};
|
|
|
|
|
const targetParams = targetNode.data?.parameters || {};
|
|
|
|
|
|
|
|
|
|
// 获取源节点的输出参数
|
|
|
|
|
let sourceDataType = '';
|
|
|
|
|
const sourceApiOuts = sourceParams.apiOuts || [];
|
|
|
|
|
const sourceDataOuts = sourceParams.dataOuts || [];
|
|
|
|
|
|
|
|
|
|
// 查找源handle的数据类型
|
|
|
|
|
const sourceApi = sourceApiOuts.find((api: any) => api.name === sourceHandleId);
|
|
|
|
|
const sourceData = sourceDataOuts.find((data: any) => data.name === sourceHandleId);
|
|
|
|
|
// 获取源handle和目标handle的类型 (api或data)
|
|
|
|
|
const sourceHandleType = getHandleType(params.sourceHandle, sourceParams);
|
|
|
|
|
const targetHandleType = getHandleType(params.targetHandle, targetParams);
|
|
|
|
|
|
|
|
|
|
if (sourceApi) {
|
|
|
|
|
sourceDataType = sourceApi.dataType || '';
|
|
|
|
|
}
|
|
|
|
|
else if (sourceData) {
|
|
|
|
|
sourceDataType = sourceData.dataType || '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取目标节点的输入参数
|
|
|
|
|
let targetDataType = '';
|
|
|
|
|
const targetApiIns = targetParams.apiIns || [];
|
|
|
|
|
const targetDataIns = targetParams.dataIns || [];
|
|
|
|
|
|
|
|
|
|
// 查找目标handle的数据类型
|
|
|
|
|
const targetApi = targetApiIns.find((api: any) => api.name === targetHandleId);
|
|
|
|
|
const targetData = targetDataIns.find((data: any) => data.name === targetHandleId);
|
|
|
|
|
|
|
|
|
|
if (targetApi) {
|
|
|
|
|
targetDataType = targetApi.dataType || '';
|
|
|
|
|
}
|
|
|
|
|
else if (targetData) {
|
|
|
|
|
targetDataType = targetData.dataType || '';
|
|
|
|
|
// 验证连接类型是否匹配 (api只能连api, data只能连data)
|
|
|
|
|
if (sourceHandleType !== targetHandleType) {
|
|
|
|
|
console.warn('连接类型不匹配: ', sourceHandleType, targetHandleType);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果任一数据类型为空,则允许连接
|
|
|
|
|
if (!sourceDataType || !targetDataType) {
|
|
|
|
|
return true;
|
|
|
|
|
// 验证数据类型是否匹配
|
|
|
|
|
if (!validateDataType(sourceNode, targetNode, params.sourceHandle, params.targetHandle)) {
|
|
|
|
|
console.warn('数据类型不匹配');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 比较数据类型是否匹配
|
|
|
|
|
return sourceDataType === targetDataType;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// onNodesChange 函数,添加防抖机制
|
|
|
|
|
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]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// onEdgesChange 函数
|
|
|
|
|
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);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// onConnect 函数
|
|
|
|
|
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 = sourceNode.data?.parameters || {};
|
|
|
|
|
const targetParams = 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;
|
|
|
|
|
}
|
|
|
|
|
// 如果验证通过,创建连接
|
|
|
|
|
setEdges((edgesSnapshot: Edge[]) => {
|
|
|
|
|
const newEdges = addEdge({ ...params, type: 'custom' }, edgesSnapshot);
|
|
|
|
|
|
|
|
|
|
// 验证数据类型是否匹配
|
|
|
|
|
if (!validateDataType(sourceNode, targetNode, params.sourceHandle, params.targetHandle)) {
|
|
|
|
|
console.warn('数据类型不匹配');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果验证通过,创建连接
|
|
|
|
|
setEdges((edgesSnapshot: Edge[]) => {
|
|
|
|
|
const newEdges = addEdge({ ...params, type: 'custom' }, edgesSnapshot);
|
|
|
|
|
|
|
|
|
|
// 连接建立后记录历史
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
const event = new CustomEvent('takeSnapshot', {
|
|
|
|
|
detail: { nodes: [...nodes], edges: [...newEdges] }
|
|
|
|
|
});
|
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
|
|
return newEdges;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
[nodes]
|
|
|
|
|
);
|
|
|
|
|
// 连接建立后记录历史
|
|
|
|
|
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 onReconnect = useCallback((oldEdge: Edge, newConnection: any) => {
|
|
|
|
|
// 获取源节点和目标节点
|
|
|
|
|
const sourceNode = nodes.find(node => node.id === newConnection.source);
|
|
|
|
|
const targetNode = nodes.find(node => node.id === newConnection.target);
|
|
|
|
|
|
|
|
|
|
// 获取源节点和目标节点的参数信息
|
|
|
|
|
const sourceParams = sourceNode.data?.parameters || {};
|
|
|
|
|
const targetParams = targetNode.data?.parameters || {};
|
|
|
|
|
// 如果找不到节点,不创建连接
|
|
|
|
|
if (!sourceNode || !targetNode) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取源handle和目标handle的类型 (api或data)
|
|
|
|
|
const sourceHandleType = getHandleType(newConnection.sourceHandle, sourceParams);
|
|
|
|
|
const targetHandleType = getHandleType(newConnection.targetHandle, targetParams);
|
|
|
|
|
// 获取源节点和目标节点的参数信息
|
|
|
|
|
const sourceParams = sourceNode.data?.parameters || {};
|
|
|
|
|
const targetParams = targetNode.data?.parameters || {};
|
|
|
|
|
|
|
|
|
|
// 验证连接类型是否匹配 (api只能连api, data只能连data)
|
|
|
|
|
if (sourceHandleType !== targetHandleType) {
|
|
|
|
|
console.warn('连接类型不匹配: ', sourceHandleType, targetHandleType);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 获取源handle和目标handle的类型 (api或data)
|
|
|
|
|
const sourceHandleType = getHandleType(newConnection.sourceHandle, sourceParams);
|
|
|
|
|
const targetHandleType = getHandleType(newConnection.targetHandle, targetParams);
|
|
|
|
|
|
|
|
|
|
// 验证数据类型是否匹配
|
|
|
|
|
if (!validateDataType(sourceNode, targetNode, newConnection.sourceHandle, newConnection.targetHandle)) {
|
|
|
|
|
console.warn('数据类型不匹配');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 验证连接类型是否匹配 (api只能连api, data只能连data)
|
|
|
|
|
if (sourceHandleType !== targetHandleType) {
|
|
|
|
|
console.warn('连接类型不匹配: ', sourceHandleType, targetHandleType);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果验证通过,重新连接
|
|
|
|
|
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
|
|
|
|
|
},
|
|
|
|
|
[nodes]
|
|
|
|
|
);
|
|
|
|
|
// 验证数据类型是否匹配
|
|
|
|
|
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 onDrop = useCallback((event: React.DragEvent) => {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
|
|
const callBack = event.dataTransfer.getData('application/reactflow');
|
|
|
|
|
const nodeData = JSON.parse(callBack);
|
|
|
|
|
if (typeof nodeData.nodeType === 'undefined' || !nodeData.nodeType) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!reactFlowInstance) return;
|
|
|
|
|
|
|
|
|
|
const position = reactFlowInstance.screenToFlowPosition({
|
|
|
|
|
x: event.clientX,
|
|
|
|
|
y: event.clientY
|
|
|
|
|
});
|
|
|
|
|
const callBack = event.dataTransfer.getData('application/reactflow');
|
|
|
|
|
const nodeData = JSON.parse(callBack);
|
|
|
|
|
if (typeof nodeData.nodeType === 'undefined' || !nodeData.nodeType) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 特殊处理循环节点,添加开始和结束节点
|
|
|
|
|
if (nodeData.nodeType === 'LOOP') {
|
|
|
|
|
addLoopNodeWithStartEnd(position, nodeData);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const position = reactFlowInstance.screenToFlowPosition({
|
|
|
|
|
x: event.clientX,
|
|
|
|
|
y: event.clientY
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const newNode = {
|
|
|
|
|
id: `${nodeData.nodeType}-${Date.now()}`,
|
|
|
|
|
type: nodeData.nodeType,
|
|
|
|
|
position,
|
|
|
|
|
data: { ...nodeData.data, title: nodeData.nodeName, type: nodeData.nodeType }
|
|
|
|
|
};
|
|
|
|
|
// 特殊处理循环节点,添加开始和结束节点
|
|
|
|
|
if (nodeData.nodeType === 'LOOP') {
|
|
|
|
|
addLoopNodeWithStartEnd(position, nodeData);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将未定义的节点动态追加进nodeTypes
|
|
|
|
|
const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key));
|
|
|
|
|
// 目前默认添加的都是系统组件/本地组件
|
|
|
|
|
if (!nodeMap.includes(nodeData.nodeType)) {
|
|
|
|
|
registerNodeType(nodeData.nodeType, LocalNode, nodeData.nodeName);
|
|
|
|
|
}
|
|
|
|
|
const newNode = {
|
|
|
|
|
id: `${nodeData.nodeType}-${Date.now()}`,
|
|
|
|
|
type: nodeData.nodeType,
|
|
|
|
|
position,
|
|
|
|
|
data: { ...nodeData.data, title: nodeData.nodeName, type: nodeData.nodeType }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setNodes((nds: Node[]) => {
|
|
|
|
|
const newNodes = nds.concat(newNode);
|
|
|
|
|
// 将未定义的节点动态追加进nodeTypes
|
|
|
|
|
const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key));
|
|
|
|
|
// 目前默认添加的都是系统组件/本地组件
|
|
|
|
|
if (!nodeMap.includes(nodeData.nodeType)) {
|
|
|
|
|
registerNodeType(nodeData.nodeType, LocalNode, nodeData.nodeName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加节点后记录历史
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
const event = new CustomEvent('takeSnapshot', {
|
|
|
|
|
detail: { nodes: [...newNodes], edges: [...edges] }
|
|
|
|
|
});
|
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
}, 0);
|
|
|
|
|
setNodes((nds: Node[]) => {
|
|
|
|
|
const newNodes = nds.concat(newNode);
|
|
|
|
|
|
|
|
|
|
return newNodes;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
[reactFlowInstance, edges]
|
|
|
|
|
);
|
|
|
|
|
// 添加节点后记录历史
|
|
|
|
|
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) => {
|
|
|
|
|
// 创建循环开始节点
|
|
|
|
|
@ -458,21 +350,19 @@ export const useFlowCallbacks = (
|
|
|
|
|
return updatedEdges;
|
|
|
|
|
});
|
|
|
|
|
}, [nodes, edges]);
|
|
|
|
|
|
|
|
|
|
const onNodeDrag = useCallback(
|
|
|
|
|
(_: any, node: Node) => {
|
|
|
|
|
// 获取对齐线
|
|
|
|
|
getGuidelines(node, nodes);
|
|
|
|
|
},
|
|
|
|
|
[nodes, getGuidelines]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 节点拖拽处理
|
|
|
|
|
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]) {
|
|
|
|
|
@ -482,30 +372,13 @@ export const useFlowCallbacks = (
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// 首次进入
|
|
|
|
|
const { nodes: convertedNodes, edges: convertedEdges } = convertFlowData(initialData?.main.components, true);
|
|
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
// 为所有边添加类型
|
|
|
|
|
const initialEdges: Edge[] = convertedEdges.map(edge => ({
|
|
|
|
|
...edge,
|
|
|
|
|
type: 'custom'
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
setNodes(convertedNodes);
|
|
|
|
|
setEdges(initialEdges);
|
|
|
|
|
|
|
|
|
|
if (initialData?.appId) {
|
|
|
|
|
dispatch(updateCanvasDataMap({
|
|
|
|
|
...canvasDataMap,
|
|
|
|
|
[initialData.appId]: { nodes: convertedNodes, edges: initialEdges }
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
|
@ -517,13 +390,11 @@ export const useFlowCallbacks = (
|
|
|
|
|
// 取消防抖函数
|
|
|
|
|
};
|
|
|
|
|
}, [nodes, edges, initialData?.appId, dispatch, canvasDataMap]);
|
|
|
|
|
|
|
|
|
|
// 关闭编辑弹窗
|
|
|
|
|
const closeEditModal = useCallback(() => {
|
|
|
|
|
setIsEditModalOpen(false);
|
|
|
|
|
setEditingNode(null);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// 保存节点编辑
|
|
|
|
|
const saveNodeEdit = useCallback((updatedData: any) => {
|
|
|
|
|
console.log('updatedData:', updatedData);
|
|
|
|
|
@ -540,7 +411,24 @@ export const useFlowCallbacks = (
|
|
|
|
|
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) => {
|
|
|
|
|
// 这里可以实现节点复制逻辑
|
|
|
|
|
console.log('复制节点:', node);
|
|
|
|
|
}, []);
|
|
|
|
|
// endregion
|
|
|
|
|
|
|
|
|
|
// region 节点/边操作
|
|
|
|
|
// 删除节点函数
|
|
|
|
|
const deleteNode = useCallback((node: Node) => {
|
|
|
|
|
|
|
|
|
|
@ -558,7 +446,6 @@ export const useFlowCallbacks = (
|
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
}, 0);
|
|
|
|
|
}, [nodes, edges]);
|
|
|
|
|
|
|
|
|
|
// 删除边函数
|
|
|
|
|
const deleteEdge = useCallback((edge: Edge) => {
|
|
|
|
|
setEdges((eds: Edge[]) => eds.filter((e) => e.id !== edge.id));
|
|
|
|
|
@ -574,25 +461,6 @@ export const useFlowCallbacks = (
|
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
}, 0);
|
|
|
|
|
}, [nodes, edges]);
|
|
|
|
|
|
|
|
|
|
// 编辑节点
|
|
|
|
|
const editNode = useCallback((node: Node) => {
|
|
|
|
|
setEditingNode(node);
|
|
|
|
|
setIsEditModalOpen(true);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// 编辑边
|
|
|
|
|
const editEdge = useCallback((edge: Edge) => {
|
|
|
|
|
// 这里可以实现边编辑逻辑
|
|
|
|
|
console.log('编辑边:', edge);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// 复制节点
|
|
|
|
|
const copyNode = useCallback((node: Node) => {
|
|
|
|
|
// 这里可以实现节点复制逻辑
|
|
|
|
|
console.log('复制节点:', node);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// 在边上添加节点的具体实现
|
|
|
|
|
const addNodeOnEdge = useCallback((nodeType: string, node: any) => {
|
|
|
|
|
if (!edgeForNodeAdd || !reactFlowInstance) return;
|
|
|
|
|
@ -859,7 +727,6 @@ export const useFlowCallbacks = (
|
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
}, 0);
|
|
|
|
|
}, [edgeForNodeAdd, nodes, reactFlowInstance, edges, addLoopNodeWithStartEnd]);
|
|
|
|
|
|
|
|
|
|
// 在画布上添加节点
|
|
|
|
|
const addNodeOnPane = useCallback((nodeType: string, position: { x: number; y: number }, node?: any) => {
|
|
|
|
|
if (!reactFlowInstance) return;
|
|
|
|
|
@ -926,7 +793,6 @@ export const useFlowCallbacks = (
|
|
|
|
|
return newNodes;
|
|
|
|
|
});
|
|
|
|
|
}, [reactFlowInstance, edges, addLoopNodeWithStartEnd]);
|
|
|
|
|
|
|
|
|
|
// 处理添加节点的统一方法
|
|
|
|
|
const handleAddNode = useCallback((nodeType: string, node: any) => {
|
|
|
|
|
// 如果是通过边添加节点
|
|
|
|
|
@ -942,43 +808,9 @@ export const useFlowCallbacks = (
|
|
|
|
|
setEdgeForNodeAdd(null);
|
|
|
|
|
setPositionForNodeAdd(null);
|
|
|
|
|
}, [edgeForNodeAdd, positionForNodeAdd, addNodeOnEdge, addNodeOnPane]);
|
|
|
|
|
// endregion
|
|
|
|
|
|
|
|
|
|
// 保存所有节点和边数据到服务器
|
|
|
|
|
const saveFlowDataToServer = useCallback(async () => {
|
|
|
|
|
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);
|
|
|
|
|
console.log('revertedData(中断):', revertedData);
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
|
|
updateEvent(revertedData.nodeConfigs);
|
|
|
|
|
const res: any = await setMainFlow(revertedData, initialData.appId);
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
Message.success('保存成功');
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Message.error(res.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error saving flow data:', error);
|
|
|
|
|
Message.error('保存失败');
|
|
|
|
|
}
|
|
|
|
|
}, [nodes, edges, initialData?.appId]);
|
|
|
|
|
|
|
|
|
|
// 保存所有节点和边数据到服务器,更新事件相关数据
|
|
|
|
|
const updateEvent = (revertedData) => {
|
|
|
|
|
// 初始化参数对象
|
|
|
|
|
const params: any = {
|
|
|
|
|
@ -1034,8 +866,49 @@ export const useFlowCallbacks = (
|
|
|
|
|
updateAppEvent(initialData.appId, params);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
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);
|
|
|
|
|
console.log('revertedData(中断):', revertedData);
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
|
|
updateEvent(revertedData.nodeConfigs);
|
|
|
|
|
const res: any = await setMainFlow(revertedData, initialData.appId);
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
Message.success('保存成功');
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Message.error(res.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error saving flow data:', error);
|
|
|
|
|
Message.error('保存失败');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const params = {
|
|
|
|
|
nodes,
|
|
|
|
|
edges
|
|
|
|
|
};
|
|
|
|
|
console.log('params:', params);
|
|
|
|
|
}
|
|
|
|
|
}, [nodes, edges, initialData?.appId]);
|
|
|
|
|
// 运行处理函数
|
|
|
|
|
const handleRun = useCallback(async (running: boolean) => {
|
|
|
|
|
if (running) {
|
|
|
|
|
@ -1084,10 +957,6 @@ export const useFlowCallbacks = (
|
|
|
|
|
|
|
|
|
|
// Actions
|
|
|
|
|
saveFlowDataToServer,
|
|
|
|
|
handleRun,
|
|
|
|
|
|
|
|
|
|
// Utilities
|
|
|
|
|
getHandleType,
|
|
|
|
|
validateDataType
|
|
|
|
|
handleRun
|
|
|
|
|
};
|
|
|
|
|
};
|