You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

807 lines
25 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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';
/**
* 将提供的数据结构转换为适用于 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 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];
// 确定节点类型
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<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'
});
}
}
// 处理数据下游连接
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'
});
}
}
}
});
}
}
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对照表 {数字IDsub_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 }
};
console.log('node:', node);
// 处理 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;
}
};