refactor(flowEditor): 重构流程编辑器节点组件
- 提取公共组件 NodeContent 用于渲染节点内容和连接端点 -重构 StartNode 和 EndNode 组件,使用新的 NodeContent 组件- 添加 convertFlowData 工具函数用于转换流程数据 - 新增 exampleFlowData 作为测试数据 - 更新 FlowEditor 组件,使用转换后的流程数据初始化节点和边master
parent
b784cb158b
commit
b9b5ec2f19
@ -0,0 +1,96 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styles from '@/pages/flowEditor/node/style/base.module.less';
|
||||||
|
import { Handle, Position } from '@xyflow/react';
|
||||||
|
|
||||||
|
|
||||||
|
interface NodeContentData {
|
||||||
|
parameters?: {
|
||||||
|
inputs?: any[];
|
||||||
|
outputs?: any[];
|
||||||
|
};
|
||||||
|
showFooter?: boolean;
|
||||||
|
type?: string;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeContent = ({ data }: { data: NodeContentData }) => {
|
||||||
|
const inputs = data.parameters?.inputs || [];
|
||||||
|
const outputs = data.parameters?.outputs || [];
|
||||||
|
const showFooter = data.showFooter || false;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/*content栏*/}
|
||||||
|
<div className={styles['node-content']}>
|
||||||
|
{inputs.length > 0 && (
|
||||||
|
<div className={styles['node-inputs']}>
|
||||||
|
{inputs.map((input, index) => (
|
||||||
|
<div key={`input-${index}`} className={styles['node-input-label']}>
|
||||||
|
{input.name || `输入${index + 1}`}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{outputs.length > 0 && (
|
||||||
|
<div className={styles['node-outputs']}>
|
||||||
|
{outputs.map((output, index) => (
|
||||||
|
<div key={`output-${index}`} style={{ fontSize: '12px', padding: '2px 0' }}>
|
||||||
|
{output.name || `输出${index + 1}`}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/*footer栏*/}
|
||||||
|
{showFooter && (
|
||||||
|
<div className={styles['node-footer']}>
|
||||||
|
暂定的footer
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 默认连接端点*/}
|
||||||
|
<Handle
|
||||||
|
type={data.type === 'start' ? 'source' : 'target'}
|
||||||
|
position={data.type === 'start' ? Position.Right : Position.Left}
|
||||||
|
id={data.type === 'start' ? 'start-source' : 'end-target'}
|
||||||
|
style={{
|
||||||
|
background: '#555',
|
||||||
|
top: '40px'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/*输入参数连接端点*/}
|
||||||
|
{inputs.map((_, index) => (
|
||||||
|
<Handle
|
||||||
|
key={`input-handle-${index}`}
|
||||||
|
type="target"
|
||||||
|
position={Position.Left}
|
||||||
|
id={`input-${index}`}
|
||||||
|
style={{
|
||||||
|
background: '#555',
|
||||||
|
top: `${60 + index * 20}px`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/*输出参数连接端点*/}
|
||||||
|
{outputs.map((_, index) => (
|
||||||
|
<Handle
|
||||||
|
key={`output-handle-${index}`}
|
||||||
|
type="source"
|
||||||
|
position={Position.Right}
|
||||||
|
id={`output-${index}`}
|
||||||
|
style={{
|
||||||
|
background: '#555',
|
||||||
|
// top: `${80 + inputs.length * 20 + index * 20}px`
|
||||||
|
top: `${60 + index * 20}px`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NodeContent;
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* 将提供的数据结构转换为适用于 flow editor 的 nodes 和 edges
|
||||||
|
* @param flowData - 原始数据结构
|
||||||
|
* @returns 包含 nodes 和 edges 的对象
|
||||||
|
*/
|
||||||
|
export const convertFlowData = (flowData: any) => {
|
||||||
|
const nodes: any[] = [];
|
||||||
|
const edges: any[] = [];
|
||||||
|
|
||||||
|
// 处理节点配置
|
||||||
|
const nodeConfigs = flowData.main?.nodeConfigs || [];
|
||||||
|
for (const nodeConfig of nodeConfigs) {
|
||||||
|
// 确定节点类型
|
||||||
|
let nodeType = 'BASIC';
|
||||||
|
if (nodeConfig.nodeId === 'start') {
|
||||||
|
nodeType = 'start';
|
||||||
|
}
|
||||||
|
else if (nodeConfig.nodeId === 'end') {
|
||||||
|
nodeType = 'end';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nodeType = nodeConfig.component.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析位置信息
|
||||||
|
let position = { x: 0, y: 0 };
|
||||||
|
try {
|
||||||
|
const x6Data = JSON.parse(nodeConfig.x6);
|
||||||
|
position = x6Data.position || { x: 0, y: 0 };
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to parse position for node:', nodeConfig.nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造节点数据
|
||||||
|
const node: any = {
|
||||||
|
id: nodeConfig.nodeId,
|
||||||
|
type: nodeType,
|
||||||
|
position,
|
||||||
|
data: {
|
||||||
|
title: nodeConfig.nodeName || nodeConfig.nodeId,
|
||||||
|
parameters: {
|
||||||
|
inputs: nodeConfig.dataIns?.map((input: any) => ({
|
||||||
|
name: input.id,
|
||||||
|
desc: input.desc,
|
||||||
|
dataType: input.dataType,
|
||||||
|
defaultValue: input.defaultValue
|
||||||
|
})) || [],
|
||||||
|
outputs: nodeConfig.dataOuts?.map((output: any) => ({
|
||||||
|
name: output.id,
|
||||||
|
desc: output.desc,
|
||||||
|
dataType: output.dataType,
|
||||||
|
defaultValue: output.defaultValue
|
||||||
|
})) || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果是机械臂节点,添加组件标识信息
|
||||||
|
if (nodeConfig.component) {
|
||||||
|
node.data.component = {
|
||||||
|
compIdentifier: nodeConfig.component.compIdentifier,
|
||||||
|
compInstanceIdentifier: nodeConfig.component.compInstanceIdentifier
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理连线配置
|
||||||
|
const lineConfigs = flowData.main?.lineConfigs || [];
|
||||||
|
for (const lineConfig of lineConfigs) {
|
||||||
|
const edge: any = {
|
||||||
|
id: lineConfig.id,
|
||||||
|
source: lineConfig.prev.nodeId,
|
||||||
|
target: lineConfig.next.nodeId,
|
||||||
|
sourceHandle: lineConfig.prev.endpointId,
|
||||||
|
targetHandle: lineConfig.next.endpointId
|
||||||
|
};
|
||||||
|
|
||||||
|
edges.push(edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('nodes, edges:', nodes, edges);
|
||||||
|
|
||||||
|
return { nodes, edges };
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue