import { nodeTypeMap, registerNodeType } from '@/components/FlowEditor/node'; import store from '@/store/index'; import LocalNode from '@/components/FlowEditor/node/localNode/LocalNode'; import LoopNode from '@/components/FlowEditor/node/loopNode/LoopNode'; import SwitchNode from '@/components/FlowEditor/node/switchNode/SwitchNode'; import BasicNode from '@/components/FlowEditor/node/basicNode/BasicNode'; 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 { updateEventNodeList } from '@/store/ideContainer'; /** * 将提供的数据结构转换为适用于 flow editor 的 nodes 和 edges * @param flowData - 原始数据结构 * @param useDefault - 当flowData为空时是否返回默认的开始和结束节点 * @returns 包含 nodes 和 edges 的对象 */ export const convertFlowData = (flowData: any, useDefault = true) => { const nodes: any[] = []; const edges: any[] = []; const eventSendNodeList = []; const eventlisteneList = []; const currentProjectCompData = getCurrentProjectStoreData(); if (!flowData || Object.keys(flowData).length === 0) { // 如果useDefault为true且flowData为空,则返回默认的开始和结束节点 if (useDefault) { return { nodes: [ { id: 'start', type: 'start', position: { x: 200, y: 200 }, data: { title: '开始', parameters: { apiIns: [], apiOuts: [{ name: 'start', desc: '', dataType: '', defaultValue: '' }], dataIns: [], dataOuts: [] }, type: 'start' } }, { id: 'end', type: 'end', position: { x: 600, y: 200 }, data: { title: '结束', parameters: { apiIns: [{ name: 'end', desc: '', dataType: '', defaultValue: '' }], apiOuts: [], dataIns: [], dataOuts: [] }, type: 'end' } } ], edges: [] }; } // 否则返回空数组 return { nodes, edges }; } // 处理新格式的数据结构 // 先处理所有节点 const nodeEntries = Object.entries(flowData); for (const entry of nodeEntries) { const nodeId: string = entry[0]; const nodeConfig: any = entry[1]; // 更新应用中的事件节点列表 if (nodeId.includes('EVENTLISTENE')) { try { const customDef = JSON.parse(nodeConfig.component.customDef); // 使用展开运算符创建新数组,避免修改冻结对象 eventlisteneList.splice(eventlisteneList.length, 0, { [nodeId]: customDef.topic }); } catch (error) { console.log(error); } } else if (nodeId.includes('EVENTSEND')) { try { const customDef = JSON.parse(nodeConfig.component.customDef); // 使用展开运算符创建新数组,避免修改冻结对象 eventSendNodeList.splice(eventSendNodeList.length, 0, { [nodeId]: customDef.topic }); } catch (error) { console.log(error); } } if (eventlisteneList.length > 0 || eventSendNodeList.length > 0) store.dispatch(updateEventNodeList({ eventSendNodeList: [...eventSendNodeList], eventlisteneList: [...eventlisteneList] })); else { store.dispatch(updateEventNodeList({ eventSendNodeList: [], eventlisteneList: [] })); } // 确定节点类型 let nodeType = 'BASIC'; if (nodeId === 'start') { nodeType = 'start'; } else if (nodeId === 'end') { nodeType = 'end'; } else if (nodeConfig.component?.type === 'LOOP_START' || nodeConfig.component?.type === 'LOOP_END') { nodeType = 'LOOP'; } else { nodeType = nodeConfig.component?.type || 'BASIC'; } // 解析位置信息 const position = nodeConfig.position || { x: 0, y: 0 }; // 构造节点数据 const node: any = { id: nodeId, type: nodeType, position, data: { title: nodeConfig.componentName || nodeId, parameters: { apiIns: getNodeApiIns(nodeId, nodeConfig, currentProjectCompData), apiOuts: getNodeApiOuts(nodeId, nodeConfig, currentProjectCompData), dataIns: nodeConfig.dataIns || [], dataOuts: nodeConfig.dataOuts || [] }, type: nodeConfig.component?.type || nodeType } }; // 添加组件标识信息 if (nodeConfig.component) { node.data.component = { ...nodeConfig.component }; node.data.compId = nodeConfig.component.compId; } // 注册循环节点类型 if (nodeType === 'LOOP') { const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key)); if (!nodeMap.includes('LOOP')) { registerNodeType('LOOP', LoopNode, '循环'); } } // 注册其他节点类型 const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key)); if (!nodeMap.includes(nodeType) && nodeType !== 'start' && nodeType !== 'end' && nodeType !== 'LOOP') { registerNodeType(nodeType, getNodeComponent(nodeType), nodeConfig.componentName); } nodes.push(node); } // 用于存储已添加的边,避免重复 const addedEdges = new Set(); // 创建一个映射来存储所有连接信息 const connections = new Map(); // 遍历所有节点,收集连接信息 for (const entry of nodeEntries) { const nodeId: string = entry[0]; const nodeConfig: any = entry[1]; // 处理 API 下游连接 - 确定目标节点信息 if (nodeConfig.apiDownstream && Array.isArray(nodeConfig.apiDownstream)) { nodeConfig.apiDownstream.forEach((targetArray: string[]) => { if (Array.isArray(targetArray)) { targetArray.forEach(target => { if (typeof target === 'string' && target.includes('$$')) { const [targetNodeId, targetHandle] = target.split('$$'); const connectionKey = `${nodeId}-${targetNodeId}`; // 存储连接信息 if (connections.has(connectionKey)) { // 如果连接已存在,更新目标句柄 const existing = connections.get(connectionKey); if (existing) { connections.set(connectionKey, { ...existing, targetHandle: targetHandle }); } } else { // 创建新的连接信息 connections.set(connectionKey, { source: nodeId, target: targetNodeId, sourceHandle: '', // 将根据节点信息填充 targetHandle: targetHandle }); } } }); } }); } // 处理 API 上游连接 - 确定源节点信息 if (nodeConfig.apiUpstream && Array.isArray(nodeConfig.apiUpstream)) { nodeConfig.apiUpstream.forEach((sourceArray: string[]) => { if (Array.isArray(sourceArray)) { sourceArray.forEach(source => { if (typeof source === 'string' && source.includes('$$')) { const [sourceNodeId, sourceHandle] = source.split('$$'); const connectionKey = `${sourceNodeId}-${nodeId}`; // 存储连接信息 if (connections.has(connectionKey)) { // 如果连接已存在,更新源句柄 const existing = connections.get(connectionKey); if (existing) { connections.set(connectionKey, { ...existing, sourceHandle: sourceHandle }); } } else { // 创建新的连接信息 connections.set(connectionKey, { source: sourceNodeId, target: nodeId, sourceHandle: sourceHandle, targetHandle: '' // 将根据节点信息填充 }); } } }); } }); } } // 根据收集的连接信息生成实际的边 const connectionEntries = Array.from(connections.entries()); for (const [connectionKey, connectionInfo] of connectionEntries) { const { source, target, sourceHandle, targetHandle } = connectionInfo; // 获取源节点和目标节点 const sourceNode = flowData[source]; const targetNode = flowData[target]; // 确定最终的源句柄 let finalSourceHandle = sourceHandle; // 如果源句柄未指定,则根据源节点信息确定 if (!finalSourceHandle) { if (source === 'start') { finalSourceHandle = 'start'; } else if (sourceNode && sourceNode.data && sourceNode.data.parameters && sourceNode.data.parameters.apiOuts && sourceNode.data.parameters.apiOuts.length > 0) { // 查找匹配的目标句柄 const matchingApiOut = sourceNode.data.parameters.apiOuts.find( (apiOut: any) => apiOut.name === targetHandle ); if (matchingApiOut) { finalSourceHandle = matchingApiOut.name; } else { // 如果没有精确匹配,使用第一个apiOut finalSourceHandle = sourceNode.data.parameters.apiOuts[0].name; } } else if (sourceNode && sourceNode.component && sourceNode.component.type) { // 根据节点类型获取正确的源句柄 finalSourceHandle = getNodeApiOutHandle(source, sourceNode); } else { // 默认句柄 finalSourceHandle = 'done'; } } // 确定最终的目标句柄 let finalTargetHandle = targetHandle; // 如果目标句柄未指定,则根据目标节点信息确定 if (!finalTargetHandle) { if (target === 'end') { finalTargetHandle = 'end'; } else if (targetNode && targetNode.data && targetNode.data.parameters && targetNode.data.parameters.apiIns && targetNode.data.parameters.apiIns.length > 0) { // 查找匹配的源句柄 const matchingApiIn = targetNode.data.parameters.apiIns.find( (apiIn: any) => apiIn.name === sourceHandle ); if (matchingApiIn) { finalTargetHandle = matchingApiIn.name; } else { // 如果没有精确匹配,使用第一个apiIn finalTargetHandle = targetNode.data.parameters.apiIns[0].name; } } else { // 默认句柄 finalTargetHandle = 'start'; } } // 创建边的唯一标识符 const edgeId = `${source}-${target}-${finalSourceHandle}-${finalTargetHandle}`; // 检查是否已添加此边 if (!addedEdges.has(edgeId)) { addedEdges.add(edgeId); edges.push({ id: `${edgeId}`, source: source, target: target, sourceHandle: finalSourceHandle, targetHandle: finalTargetHandle, type: 'custom', lineType: 'api', data: { lineType: 'api' } }); } } // 处理数据下游连接 for (const entry of nodeEntries) { const nodeId: string = entry[0]; const nodeConfig: any = entry[1]; if (nodeConfig.dataDownstream && Array.isArray(nodeConfig.dataDownstream)) { nodeConfig.dataDownstream.forEach((connectionGroup: string[]) => { // 确保 connectionGroup 是数组并且至少包含两个元素 if (Array.isArray(connectionGroup) && connectionGroup.length >= 2) { // 第一个元素是源节点和句柄信息 const [sourceInfo, targetInfo] = connectionGroup; if (typeof sourceInfo === 'string' && sourceInfo.includes('@@') && typeof targetInfo === 'string' && targetInfo.includes('@@')) { const [sourceNodeId, sourceHandle] = sourceInfo.split('@@'); const [targetNodeId, targetHandle] = targetInfo.split('@@'); // 创建边的唯一标识符 const edgeId = `${sourceNodeId}-${targetNodeId}-${sourceHandle}-${targetHandle}`; // 检查是否已添加此边 if (!addedEdges.has(edgeId)) { addedEdges.add(edgeId); edges.push({ id: `${edgeId}`, source: sourceNodeId, target: targetNodeId, sourceHandle: sourceHandle, targetHandle: targetHandle, type: 'custom', lineType: 'data', data: { lineType: 'data' } }); } } } }); } } return { nodes, edges }; }; /** * 将 flow editor 的 nodes 和 edges 数据结构转换回原始数据结构 * @param nodes - flow editor 的节点数组 * @param edges - flow editor 的连线数组 * @returns 原始数据结构 */ export const revertFlowData = (nodes: any[], edges: any[]) => { // 初始化返回的数据结构 const flowData: any = { id: 'main', nodeConfigs: [], lineConfigs: [] }; // 转换节点数据 if (nodes && nodes.length > 0) { flowData.nodeConfigs = nodes.map(node => { // 确定 nodeId 和 nodeName const nodeId = node.id || node.name; const nodeName = node.data?.title || nodeId; // 确定节点类型 let nodeType = node.data.type; // 特殊处理 start 和 end 节点 if (nodeId === 'start') { nodeType = 'start'; } else if (nodeId === 'end') { nodeType = 'end'; } // 构造 x6 数据(位置信息) const x6 = JSON.stringify({ position: node.position }); // 构造 nodeConfig 对象 const nodeConfig: any = { nodeId, nodeName, x6 }; // 处理 component 信息 if (node.data?.component) { nodeConfig.component = { type: nodeType, compIdentifier: node.data.component.compIdentifier || '', compInstanceIdentifier: node.data.component.compInstanceIdentifier || '', compId: node.data.compId || '' }; if (node.data.component?.customDef) nodeConfig.component.customDef = node.data.component.customDef; } else if (nodeType !== 'start' && nodeType !== 'end') { // 对于非 start/end 节点,添加基本的 component 信息 nodeConfig.component = { type: nodeType }; } if (['BASIC', 'SUB'].includes(nodeType)) nodeConfig.component.compId = node.data.compId || ''; // 处理参数信息 const parameters = node.data?.parameters || {}; // 处理 dataIns(输入数据) if (parameters.dataIns && parameters.dataIns.length > 0) { nodeConfig.dataIns = parameters.dataIns.map((input: any) => ({ id: input.name || input.id, desc: input.desc, dataType: input.dataType, defaultValue: input.defaultValue, arrayType: input.arrayType || null })); } // 处理 dataOuts(输出数据) if (parameters.dataOuts && parameters.dataOuts.length > 0) { nodeConfig.dataOuts = parameters.dataOuts.map((output: any) => ({ id: output.name || output.id, desc: output.desc, dataType: output.dataType, defaultValue: output.defaultValue, arrayType: output.arrayType || null })); } return nodeConfig; }); } // 转换连线数据 if (edges && edges.length > 0) { flowData.lineConfigs = edges.map((edge, index) => { // 查找源节点和目标节点以确定连线类型 const sourceNode = nodes.find(node => node.id === edge.source); const targetNode = nodes.find(node => node.id === edge.target); let lineType = 'DATA'; // 默认为DATA类型 // 判断是否为CONVERT类型的连线 if (targetNode && ['JSONCONVERT', 'JSON2STR', 'STR2JSON'].includes(targetNode.type)) { lineType = 'CONVERT'; } // 判断是否为API类型的连线 else if (edge.sourceHandle && (edge.sourceHandle === 'apiOuts' || sourceNode?.data?.parameters?.apiOuts?.some((out: any) => (out.name || out.id) === edge.sourceHandle))) { lineType = 'API'; } else if (edge.targetHandle && (edge.targetHandle === 'apiIns' || targetNode?.data?.parameters?.apiIns?.some((inp: any) => (inp.name || inp.id) === edge.targetHandle))) { lineType = 'API'; } return { id: edge.id || `edge_${index}`, // 如果没有 id,则生成一个 lineType, // 添加lineType属性 prev: { nodeId: edge.source, endpointId: edge.sourceHandle || 'done' // 默认使用 'done' }, next: { nodeId: edge.target, endpointId: edge.targetHandle || 'start' // 默认使用 'start' } }; }); } return flowData; }; /** * 将 React Flow 的 nodes 和 edges 数据结构反向转换为 convertFlowData 可以处理的格式 * @param nodes - React Flow 节点数组 * @param edges - React Flow 边数组 * @param complexKV - 复合组件使用的组件id对照表 {数字ID:sub_ID} * @returns 可用于 convertFlowData 的数据结构 */ export const reverseConvertFlowData = (nodes: any[], edges: any[], complexKV: any) => { // 初始化返回的数据结构 const flowData: any = {}; // 转换节点数据 if (nodes && nodes.length > 0) { nodes.forEach(node => { const nodeId = node.id; // 构造节点配置对象 const nodeConfig: any = { id: nodeId, componentName: node.data?.title || nodeId, position: node.position || { x: 0, y: 0 } }; // 处理 component 信息 if (node.type === 'SUB' && !node.customDef) { nodeConfig.component = { type: 'SUB', compId: node.data.compId, customDef: JSON.stringify({ dataIns: node.data.parameters.dataIns, dataOuts: node.data.parameters.dataOuts, subflowId: complexKV[node.data.compId], name: node.data.title }) }; } else if (node.data?.component) { nodeConfig.component = { ...node.data.component }; } else { nodeConfig.component = null; } // 处理参数信息 const parameters = node.data?.parameters || {}; // 处理 apiIns(输入API) if (parameters.apiIns && parameters.apiIns.length > 0) { nodeConfig.apiIns = parameters.apiIns; } else { nodeConfig.apiIns = []; } // 处理 apiOuts(输出API) if (parameters.apiOuts && parameters.apiOuts.length > 0) { nodeConfig.apiOuts = parameters.apiOuts; } else { nodeConfig.apiOuts = []; } // 处理 dataIns(输入数据) if (parameters.dataIns && parameters.dataIns.length > 0) { nodeConfig.dataIns = parameters.dataIns; } else { nodeConfig.dataIns = []; } // 处理 dataOuts(输出数据) if (parameters.dataOuts && parameters.dataOuts.length > 0) { nodeConfig.dataOuts = parameters.dataOuts; } else { nodeConfig.dataOuts = []; } // 初始化连接数组 nodeConfig.apiDownstream = []; nodeConfig.apiUpstream = []; nodeConfig.dataDownstream = []; nodeConfig.dataUpstream = []; // 将节点配置添加到 flowData 对象中 flowData[nodeId] = nodeConfig; }); } // 处理连接关系 if (edges && edges.length > 0) { // 分析边的连接关系 edges.forEach(edge => { const sourceNode = edge.source; const targetNode = edge.target; const sourceHandle = edge.sourceHandle || 'done'; const targetHandle = edge.targetHandle || 'start'; // 确定连接类型(API 还是 DATA) const sourceNodeData = nodes.find(n => n.id === sourceNode); const targetNodeData = nodes.find(n => n.id === targetNode); const isApiConnection = (sourceNodeData?.data?.parameters?.apiOuts?.some((out: any) => (out.name || out.id) === sourceHandle)) || (targetNodeData?.data?.parameters?.apiIns?.some((inp: any) => (inp.name || inp.id) === targetHandle)) || sourceHandle === 'start' || targetHandle === 'end' || sourceHandle === 'end' || targetHandle === 'start'; if (isApiConnection) { // API 连接 // 添加下游连接 flowData[sourceNode].apiDownstream.push([`${targetNode}$$${targetHandle}`]); // 添加上游连接 flowData[targetNode].apiUpstream.push([`${sourceNode}$$${sourceHandle}`]); } else { // 数据连接 const dataConnection = [`${sourceNode}@@${sourceHandle}`, `${targetNode}@@${targetHandle}`]; flowData[sourceNode].dataDownstream.push(dataConnection); flowData[targetNode].dataUpstream.push(dataConnection); } }); } return flowData; }; // 获取节点的API输入参数 const getNodeApiIns = (nodeId: string, nodeConfig: any, currentProjectCompData: any[]) => { // 对于特定类型的节点使用预定义值 if (nodeConfig.component?.type === 'LOOP_START') { return [{ name: 'start', desc: '', dataType: '', defaultValue: '' }]; } else if (nodeConfig.component?.type === 'LOOP_END') { return [{ name: 'continue', desc: '', dataType: '', defaultValue: '' }]; } else if (nodeId === 'end') { return [{ name: 'end', desc: '', dataType: '', defaultValue: '' }]; } else { const comp = currentProjectCompData.filter(item => item.id === nodeConfig?.component?.compId); if (comp && comp.length > 0) { return comp[0].def?.apis.map(v => { return { ...v, name: v.id, desc: v.desc, dataType: v?.dataType || '', defaultValue: v?.defaultValue || '' }; }); } else { return [{ name: 'start', desc: '', dataType: '', defaultValue: '' }]; } } }; // 获取节点的API输出参数 const getNodeApiOuts = (nodeId: string, nodeConfig: any, currentProjectCompData: any[]) => { // 对于特定类型的节点使用预定义值 if (nodeConfig.component?.type === 'LOOP_START') { return [{ name: 'done', desc: '', dataType: '', defaultValue: '' }]; } else if (nodeConfig.component?.type === 'LOOP_END') { // 从customDef中获取apiOutIds数组 try { const customDef = JSON.parse(nodeConfig.component?.customDef || '{}'); const apiOutIds = customDef.apiOutIds || []; // 从"break"开始的所有项都应该作为apiOut返回 const breakIndex = apiOutIds.indexOf('break'); if (breakIndex !== -1) { // 返回从"break"开始的所有项 return apiOutIds.slice(breakIndex).map(id => ({ name: id, id: id, desc: id, dataType: '', defaultValue: '' })); } else { // 如果没有找到"break",则返回默认值 return [{ name: 'break', desc: '', dataType: '', defaultValue: '' }]; } } catch (e) { // 解析失败时返回默认值 return [{ name: 'break', desc: '', dataType: '', defaultValue: '' }]; } } else if (nodeConfig.component?.type === 'SWITCH') { // 从customDef中获取apiOutIds数组 try { const customDef = JSON.parse(nodeConfig.component?.customDef || '{}'); const apiOutIds = customDef.apiOutIds || []; // 从"break"开始的所有项都应该作为apiOut返回 const breakIndex = apiOutIds.indexOf('default'); if (breakIndex !== -1) { // 返回从"break"开始的所有项 return apiOutIds.slice(breakIndex).map(id => ({ name: id, id: id, desc: id, dataType: '', defaultValue: '' })); } else { // 如果没有找到"break",则返回默认值 return [{ name: 'default', desc: '', dataType: '', defaultValue: '' }]; } } catch (e) { // 解析失败时返回默认值 return [{ name: 'done', desc: '', dataType: '', defaultValue: '' }]; } } else if (nodeId === 'start') { return [{ name: 'start', desc: '', dataType: '', defaultValue: '' }]; } else if (nodeId === 'end') { return []; } else { const comp = currentProjectCompData.filter(item => item.id === nodeConfig?.component?.compId); if (comp && comp.length > 0) { return [{ ...comp[0].def?.apiOut, dataType: '', defaultValue: '' }]; } else { return [{ name: 'done', desc: '', dataType: '', defaultValue: '' }]; } } }; // 获取节点的API输出句柄名称 const getNodeApiOutHandle = (nodeId: string, nodeConfig: any) => { if (nodeConfig.component?.type === 'LOOP_START') { return 'done'; } else if (nodeConfig.component?.type === 'LOOP_END') { return 'break'; } else if (nodeId === 'start') { return 'start'; } else if (nodeId === 'end') { return 'end'; } return 'done'; }; // 获取当前工程下组件列表并扁平化处理 const getCurrentProjectStoreData = () => { const { info, projectComponentData } = store.getState().ideContainer; const compData = projectComponentData[info?.id] || {}; const result: any[] = []; // 处理projectCompDto中的数据 if (compData.projectCompDto) { const { mineComp = [], pubComp = [], teamWorkComp = [] } = compData.projectCompDto; // 添加mineComp数据 mineComp.forEach((item: any) => { result.push({ ...item, type: 'mineComp' }); }); // 添加pubComp数据 pubComp.forEach((item: any) => { result.push({ ...item, type: 'pubComp' }); }); // 添加teamWorkComp数据 teamWorkComp.forEach((item: any) => { result.push({ ...item, type: 'teamWorkComp' }); }); } // 处理projectFlowDto中的数据 if (compData.projectFlowDto) { const { mineFlow = [], pubFlow = [] } = compData.projectFlowDto; // 添加mineFlow数据 mineFlow.forEach((item: any) => { result.push({ ...item, type: 'mineFlow' }); }); // 添加pubFlow数据 pubFlow.forEach((item: any) => { result.push({ ...item, type: 'pubFlow' }); }); } return result; }; // 根据节点类型获取对应的节点组件 const getNodeComponent = (nodeType: string) => { switch (nodeType) { case 'BASIC': case 'SUB': return BasicNode; case 'SWITCH': return SwitchNode; case 'IMAGE': return ImageNode; case 'CODE': return CodeNode; case 'REST': return RestNode; default: return LocalNode; } };