|
|
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为空,则返回默认的开始和结束节点
|
|
|
const timestamp = Date.now();
|
|
|
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-${timestamp}`,
|
|
|
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.includes('start')) {
|
|
|
nodeType = 'start';
|
|
|
}
|
|
|
else if (nodeId.includes('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 (nodeConfig.status) {
|
|
|
node.data.status = nodeConfig.status;
|
|
|
}
|
|
|
if (nodeConfig.isStatusVisible !== undefined) {
|
|
|
node.data.isStatusVisible = nodeConfig.isStatusVisible;
|
|
|
}
|
|
|
|
|
|
// 注册循环节点类型
|
|
|
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<string>();
|
|
|
|
|
|
// 创建一个映射来存储所有连接信息
|
|
|
const connections = new Map<string, { source: string; target: string; sourceHandle: string; targetHandle: string }>();
|
|
|
|
|
|
// 遍历所有节点,收集连接信息
|
|
|
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'
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
console.log('nodes, edges:', nodes, edges);
|
|
|
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.includes('start')) {
|
|
|
nodeType = 'start';
|
|
|
}
|
|
|
else if (nodeId.includes('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/nodeId: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.data.component.customDef) {
|
|
|
let subflowId = '';
|
|
|
if (complexKV && node.data.compId) {
|
|
|
// 遍历complexKV找到匹配的条目,使用节点ID进行匹配
|
|
|
for (const key in complexKV) {
|
|
|
if (key.includes('/')) {
|
|
|
const [numericId, nodeId] = key.split('/');
|
|
|
// 使用节点ID进行匹配
|
|
|
if (nodeId === node.id) {
|
|
|
subflowId = complexKV[key];
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
nodeConfig.component = {
|
|
|
type: 'SUB',
|
|
|
compId: node.data.compId,
|
|
|
customDef: JSON.stringify({
|
|
|
dataIns: node.data.parameters.dataIns,
|
|
|
dataOuts: node.data.parameters.dataOuts,
|
|
|
subflowId: subflowId,
|
|
|
name: node.data.title
|
|
|
})
|
|
|
};
|
|
|
}
|
|
|
else if (node.data?.component) {
|
|
|
nodeConfig.component = { ...node.data.component };
|
|
|
}
|
|
|
else {
|
|
|
nodeConfig.component = {
|
|
|
type: node.type
|
|
|
};
|
|
|
}
|
|
|
|
|
|
// 处理参数信息
|
|
|
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[]) => {
|
|
|
// JSON2STR 和 STR2JSON 不需要 API 输入
|
|
|
if (nodeConfig.component?.type === 'JSON2STR' || nodeConfig.component?.type === 'STR2JSON') {
|
|
|
return [];
|
|
|
}
|
|
|
|
|
|
// 对于特定类型的节点使用预定义值
|
|
|
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.includes('end')) {
|
|
|
return [{ name: 'end', desc: '', dataType: '', defaultValue: '' }];
|
|
|
}
|
|
|
else if (nodeConfig.component?.type === 'SUB') {
|
|
|
return [{ name: 'start', desc: '', dataType: '', defaultValue: '' }];
|
|
|
}
|
|
|
else {
|
|
|
const comp = currentProjectCompData.filter(item => {
|
|
|
return (item.identifier || item?.comp?.identifier) === nodeConfig?.component?.compIdentifier;
|
|
|
});
|
|
|
if (comp && comp.length > 0) {
|
|
|
const apiIns = comp[0]?.def?.apis || comp[0]?.comp?.def?.apis || [];
|
|
|
return apiIns.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[]) => {
|
|
|
// JSON2STR 和 STR2JSON 不需要 API 输出
|
|
|
if (nodeConfig.component?.type === 'JSON2STR' || nodeConfig.component?.type === 'STR2JSON') {
|
|
|
return [];
|
|
|
}
|
|
|
|
|
|
// 对于特定类型的节点使用预定义值
|
|
|
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.includes('start')) {
|
|
|
return [{ name: 'start', desc: '', dataType: '', defaultValue: '' }];
|
|
|
}
|
|
|
else if (nodeId.includes('end')) {
|
|
|
return [];
|
|
|
}
|
|
|
else if (nodeConfig.component?.type === 'SUB') {
|
|
|
return [{ name: 'done', desc: '', dataType: '', defaultValue: '' }];
|
|
|
}
|
|
|
else {
|
|
|
const comp = currentProjectCompData.filter(item => item.identifier === nodeConfig?.component?.compIdentifier);
|
|
|
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.includes('start')) {
|
|
|
return 'start';
|
|
|
}
|
|
|
else if (nodeId.includes('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'
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 如果从 store 中没有获取到数据,尝试从 sessionStorage 中获取
|
|
|
if (result.length === 0) {
|
|
|
try {
|
|
|
const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
|
|
|
const compLibsKey = `compLibs${userInfo.userId}`;
|
|
|
const compLibsData = sessionStorage.getItem(compLibsKey);
|
|
|
|
|
|
if (compLibsData) {
|
|
|
const { myLibs = [], pubLibs = [], teamLibs = [], myFlow = [], pubFlow = [] } = JSON.parse(compLibsData);
|
|
|
|
|
|
// 处理 myLibs(我的组件库)
|
|
|
if (Array.isArray(myLibs)) {
|
|
|
myLibs.forEach((lib: any) => {
|
|
|
if (lib.children && Array.isArray(lib.children)) {
|
|
|
lib.children.forEach((item: any) => {
|
|
|
result.push({
|
|
|
...item,
|
|
|
type: 'mineComp'
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 处理 pubLibs(公共组件库)
|
|
|
if (Array.isArray(pubLibs)) {
|
|
|
pubLibs.forEach((lib: any) => {
|
|
|
if (lib.children && Array.isArray(lib.children)) {
|
|
|
lib.children.forEach((item: any) => {
|
|
|
result.push({
|
|
|
...item,
|
|
|
type: 'pubComp'
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 处理 teamLibs(协同组件库)
|
|
|
if (Array.isArray(teamLibs)) {
|
|
|
teamLibs.forEach((lib: any) => {
|
|
|
if (lib.children && Array.isArray(lib.children)) {
|
|
|
lib.children.forEach((item: any) => {
|
|
|
result.push({
|
|
|
...item,
|
|
|
type: 'teamWorkComp'
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 处理 myFlow(我的流程)
|
|
|
if (Array.isArray(myFlow)) {
|
|
|
myFlow.forEach((item: any) => {
|
|
|
result.push({
|
|
|
...item,
|
|
|
type: 'mineFlow'
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 处理 pubFlow(公共流程)
|
|
|
if (Array.isArray(pubFlow)) {
|
|
|
pubFlow.forEach((item: any) => {
|
|
|
result.push({
|
|
|
...item,
|
|
|
type: 'pubFlow'
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('从 sessionStorage 获取组件数据失败:', error);
|
|
|
}
|
|
|
}
|
|
|
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;
|
|
|
}
|
|
|
}; |