refactor(flowEditor): 重构节点渲染逻辑并优化数据转换

- 重构了 DraggableNode 组件,使用新设计的 NodeContent组件来渲染节点内容
-优化了节点类型判断和处理逻辑,支持开始和结束节点的特殊处理
- 改进了节点参数的渲染方式,根据节点类型动态显示输入和输出端点
- 新增 convertFlowData 工具函数,用于将原始数据结构转换为 flow editor 可用的节点和边数据
production
钟良源 8 months ago
parent ebc6de6965
commit f084c93b39

@ -2,7 +2,6 @@ import React from 'react';
import styles from '@/pages/flowEditor/node/style/base.module.less'; import styles from '@/pages/flowEditor/node/style/base.module.less';
import { Handle, Position } from '@xyflow/react'; import { Handle, Position } from '@xyflow/react';
interface NodeContentData { interface NodeContentData {
parameters?: { parameters?: {
inputs?: any[]; inputs?: any[];
@ -18,11 +17,17 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
const inputs = data.parameters?.inputs || []; const inputs = data.parameters?.inputs || [];
const outputs = data.parameters?.outputs || []; const outputs = data.parameters?.outputs || [];
const showFooter = data.showFooter || false; const showFooter = data.showFooter || false;
// 判断节点类型
const isStartNode = data.type === 'start';
const isEndNode = data.type === 'end';
const isSpecialNode = isStartNode || isEndNode;
return ( return (
<> <>
{/*content栏*/} {/*content栏*/}
<div className={styles['node-content']}> <div className={styles['node-content']}>
{inputs.length > 0 && ( {inputs.length > 0 && !isStartNode && (
<div className={styles['node-inputs']}> <div className={styles['node-inputs']}>
{inputs.map((input, index) => ( {inputs.map((input, index) => (
<div key={`input-${index}`} className={styles['node-input-label']}> <div key={`input-${index}`} className={styles['node-input-label']}>
@ -32,7 +37,7 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
</div> </div>
)} )}
{outputs.length > 0 && ( {outputs.length > 0 && !isEndNode && (
<div className={styles['node-outputs']}> <div className={styles['node-outputs']}>
{outputs.map((output, index) => ( {outputs.map((output, index) => (
<div key={`output-${index}`} style={{ fontSize: '12px', padding: '2px 0' }}> <div key={`output-${index}`} style={{ fontSize: '12px', padding: '2px 0' }}>
@ -50,27 +55,59 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
</div> </div>
)} )}
{/* 默认连接端点*/} {/* 流程传输(开始/结束)只在一侧显示端点 */}
{isSpecialNode ? (
<>
<Handle <Handle
type={data.type === 'start' ? 'source' : 'target'} type={isStartNode ? 'source' : 'target'}
position={data.type === 'start' ? Position.Right : Position.Left} position={isStartNode ? Position.Right : Position.Left}
id={data.type === 'start' ? 'start-source' : 'end-target'} id={isStartNode ? 'start-source' : 'end-target'}
style={{ style={{
background: '#555', background: '#555',
top: '40px' top: '40px'
}} }}
/> />
{/* 为流程传输的参数也添加句柄 */}
{isStartNode && outputs.map((_, index) => (
<Handle
key={`output-handle-${index}`}
type="source"
position={Position.Right}
id={outputs[index].name || `output-${index}`}
style={{
background: '#555',
top: `${60 + index * 20}px`
}}
/>
))}
{isEndNode && inputs.map((_, index) => (
<Handle
key={`input-handle-${index}`}
type="target"
position={Position.Left}
id={inputs[index].name || `input-${index}`}
style={{
background: '#555',
top: `${60 + index * 20}px`
}}
/>
))}
</>
) : (
/* 普通节点可以在两侧显示多个端点 */
<>
{/* 输入参数连接端点 */} {/* 输入参数连接端点 */}
{inputs.map((_, index) => ( {inputs.map((_, index) => (
<Handle <Handle
key={`input-handle-${index}`} key={`input-handle-${index}`}
type="target" type="target"
position={Position.Left} position={Position.Left}
id={`input-${index}`} id={inputs[index].name || `input-${index}`}
style={{ style={{
background: '#555', background: '#555',
top: `${60 + index * 20}px` top: `${40 + index * 20}px`
}} }}
/> />
))} ))}
@ -81,15 +118,16 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
key={`output-handle-${index}`} key={`output-handle-${index}`}
type="source" type="source"
position={Position.Right} position={Position.Right}
id={`output-${index}`} id={outputs[index].name || `output-${index}`}
style={{ style={{
background: '#555', background: '#555',
// top: `${80 + inputs.length * 20 + index * 20}px` top: `${40 + index * 20}px`
top: `${60 + index * 20}px`
}} }}
/> />
))} ))}
</> </>
)}
</>
); );
}; };

@ -1,28 +1,17 @@
import React from 'react'; import React from 'react';
import { Handle, Position } from '@xyflow/react'; import { Handle, Position } from '@xyflow/react';
import styles from '@/pages/flowEditor/node/style/base.module.less';
import NodeContent from '@/pages/flowEditor/components/nodeContent';
const DraggableNode = ({ data }: { data: any }) => { const DraggableNode = ({ data }: { data: any }) => {
const title = data.title || '任务节点';
return ( return (
<div style={{ <div className={styles['node-container']}>
padding: '10px 20px', <div className={styles['node-header']} style={{ backgroundColor: '#1890ff' }}>
backgroundColor: '#1890ff', {title}
borderRadius: '5px', </div>
color: 'white',
fontWeight: 'bold' <NodeContent data={data} />
}}>
<div>{data.label || '任务节点'}</div>
<Handle
type="target"
position={Position.Left}
id="task-target"
style={{ background: '#555' }}
/>
<Handle
type="source"
position={Position.Right}
id="task-source"
style={{ background: '#555' }}
/>
</div> </div>
); );
}; };

@ -0,0 +1,81 @@
/**
* 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 = 'draggable';
if (nodeConfig.nodeId === 'start') {
nodeType = 'start';
} else if (nodeConfig.nodeId === 'end') {
nodeType = 'end';
}
// 解析位置信息
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,
type: nodeType, // 添加节点类型到data中供NodeContent使用
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);
}
return { nodes, edges };
};

@ -52,7 +52,7 @@ export const convertFlowData = (flowData: any) => {
defaultValue: output.defaultValue defaultValue: output.defaultValue
})) || [] })) || []
}, },
type: nodeType, type: nodeType
} }
}; };

Loading…
Cancel
Save