feat(flowEditor): 重构节点参数处理逻辑(现阶段数据结构需要重新设计,旧数据无法回显连线)

master
钟良源 5 months ago
parent bbe554db8a
commit d837db46ab

@ -1,12 +1,13 @@
import React from 'react'; 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, useStore } from '@xyflow/react'; import { Handle, Position, useStore } from '@xyflow/react';
import { Divider } from '@arco-design/web-react';
interface NodeContentData { interface NodeContentData {
parameters?: { parameters?: {
inputs?: any[]; dataIns?: any[];
outputs?: any[]; dataOuts?: any[];
apiIns?: any[];
apiOuts?: any[];
}; };
showFooter?: boolean; showFooter?: boolean;
type?: string; type?: string;
@ -15,16 +16,19 @@ interface NodeContentData {
} }
// 渲染特殊节点(开始/结束节点)的句柄 // 渲染特殊节点(开始/结束节点)的句柄
const renderSpecialNodeHandles = (isStartNode: boolean, isEndNode: boolean, inputs: any[], outputs: any[]) => { const renderSpecialNodeHandles = (isStartNode: boolean, isEndNode: boolean, dataIns: any[], dataOuts: any[], apiIns: any[], apiOuts: any[]) => {
return ( return (
<> <>
{isStartNode ?
apiOuts.map((_, index) => (
<Handle <Handle
type={isStartNode ? 'source' : 'target'} key={`start-output-handle-${index}`}
position={isStartNode ? Position.Right : Position.Left} type="source"
id={isStartNode ? 'start-source' : 'end-target'} position={Position.Right}
id={apiOuts[index].name || `start-output-${index}`}
style={{ style={{
background: '#2290f6', background: '#2290f6',
top: '40px', top: `${40 + index * 20}px`,
width: '8px', width: '8px',
height: '8px', height: '8px',
border: '2px solid #fff', border: '2px solid #fff',
@ -32,14 +36,34 @@ const renderSpecialNodeHandles = (isStartNode: boolean, isEndNode: boolean, inpu
}} }}
className="node-handle" className="node-handle"
/> />
))
:
apiIns.map((_, index) => (
<Handle
key={`end-input-handle-${index}`}
type="target"
position={Position.Left}
id={apiIns[index].name || `end-input-${index}`}
style={{
background: '#2290f6',
top: `${40 + index * 20}px`,
width: '8px',
height: '8px',
border: '2px solid #fff',
boxShadow: '0 0 4px rgba(0,0,0,0.2)'
}}
className="node-handle"
/>
))
}
{/* 为特殊节点的参数也添加句柄 */} {/* 为特殊节点的参数也添加句柄 */}
{isStartNode && outputs.map((_, index) => ( {isStartNode && dataOuts.map((_, index) => (
<Handle <Handle
key={`output-handle-${index}`} key={`output-handle-${index}`}
type="source" type="source"
position={Position.Right} position={Position.Right}
id={outputs[index].name || `output-${index}`} id={dataOuts[index].name || `output-${index}`}
style={{ style={{
background: '#555', background: '#555',
top: `${60 + index * 20}px`, top: `${60 + index * 20}px`,
@ -52,12 +76,12 @@ const renderSpecialNodeHandles = (isStartNode: boolean, isEndNode: boolean, inpu
/> />
))} ))}
{isEndNode && inputs.map((_, index) => ( {isEndNode && dataIns.map((_, index) => (
<Handle <Handle
key={`input-handle-${index}`} key={`input-handle-${index}`}
type="target" type="target"
position={Position.Left} position={Position.Left}
id={inputs[index].name || `input-${index}`} id={dataIns[index].name || `input-${index}`}
style={{ style={{
background: '#555', background: '#555',
top: `${60 + index * 20}px`, top: `${60 + index * 20}px`,
@ -74,16 +98,18 @@ const renderSpecialNodeHandles = (isStartNode: boolean, isEndNode: boolean, inpu
}; };
// 渲染普通节点的句柄 // 渲染普通节点的句柄
const renderRegularNodeHandles = (inputs: any[], outputs: any[]) => { const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[], apiOuts: any[]) => {
return ( return (
<> <>
{apiOuts.map((_, index) => (
<Handle <Handle
key={`output-handle-${index}`}
type="source" type="source"
position={Position.Right} position={Position.Right}
id="start-source" id={apiOuts[index].name || `output-${index}`}
style={{ style={{
background: '#2290f6', background: '#2290f6',
top: '40px', top: `${40 + index * 20}px`,
width: '8px', width: '8px',
height: '8px', height: '8px',
border: '2px solid #fff', border: '2px solid #fff',
@ -91,13 +117,16 @@ const renderRegularNodeHandles = (inputs: any[], outputs: any[]) => {
}} }}
className="node-handle" className="node-handle"
/> />
))}
{apiIns.map((_, index) => (
<Handle <Handle
key={`input-handle-${index}`}
type="target" type="target"
position={Position.Left} position={Position.Left}
id="end-target" id={apiIns[index].name || `input-${index}`}
style={{ style={{
background: '#2290f6', background: '#2290f6',
top: '40px', top: `${40 + index * 20}px`,
width: '8px', width: '8px',
height: '8px', height: '8px',
border: '2px solid #fff', border: '2px solid #fff',
@ -105,14 +134,15 @@ const renderRegularNodeHandles = (inputs: any[], outputs: any[]) => {
}} }}
className="node-handle" className="node-handle"
/> />
))}
{/* 输入参数连接端点 */} {/* 输入参数连接端点 */}
{inputs.map((_, index) => ( {dataIns.map((_, index) => (
<Handle <Handle
key={`input-handle-${index}`} key={`input-handle-${index}`}
type="target" type="target"
position={Position.Left} position={Position.Left}
id={inputs[index].name || `input-${index}`} id={dataIns[index].name || `input-${index}`}
style={{ style={{
background: '#555', background: '#555',
top: `${40 + (index + 1) * 20}px`, top: `${40 + (index + 1) * 20}px`,
@ -126,12 +156,12 @@ const renderRegularNodeHandles = (inputs: any[], outputs: any[]) => {
))} ))}
{/* 输出参数连接端点 */} {/* 输出参数连接端点 */}
{outputs.map((_, index) => ( {dataOuts.map((_, index) => (
<Handle <Handle
key={`output-handle-${index}`} key={`output-handle-${index}`}
type="source" type="source"
position={Position.Right} position={Position.Right}
id={outputs[index].name || `output-${index}`} id={dataOuts[index].name || `output-${index}`}
style={{ style={{
background: '#555', background: '#555',
top: `${40 + (index + 1) * 20}px`, top: `${40 + (index + 1) * 20}px`,
@ -148,8 +178,10 @@ const renderRegularNodeHandles = (inputs: any[], outputs: any[]) => {
}; };
const NodeContent = ({ data }: { data: NodeContentData }) => { const NodeContent = ({ data }: { data: NodeContentData }) => {
const inputs = data.parameters?.inputs || []; const apiIns = data.parameters?.apiIns || [];
const outputs = data.parameters?.outputs || []; const apiOuts = data.parameters?.apiOuts || [];
const dataIns = data.parameters?.dataIns || [];
const dataOuts = data.parameters?.dataOuts || [];
const showFooter = data.showFooter || false; const showFooter = data.showFooter || false;
// 判断节点类型 // 判断节点类型
@ -161,9 +193,9 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
<> <>
{/*content栏*/} {/*content栏*/}
<div className={styles['node-content']}> <div className={styles['node-content']}>
{inputs.length > 0 && !isStartNode && ( {dataIns.length > 0 && !isStartNode && (
<div className={styles['node-inputs']}> <div className={styles['node-inputs']}>
{inputs.map((input, index) => ( {dataIns.map((input, index) => (
<div key={`input-${index}`} className={styles['node-input-label']}> <div key={`input-${index}`} className={styles['node-input-label']}>
{input.name || `输入${index + 1}`} {input.name || `输入${index + 1}`}
</div> </div>
@ -171,9 +203,9 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
</div> </div>
)} )}
{outputs.length > 0 && !isEndNode && ( {dataOuts.length > 0 && !isEndNode && (
<div className={styles['node-outputs']}> <div className={styles['node-outputs']}>
{outputs.map((output, index) => ( {dataOuts.map((output, index) => (
<div key={`output-${index}`} style={{ fontSize: '12px', padding: '2px 0' }}> <div key={`output-${index}`} style={{ fontSize: '12px', padding: '2px 0' }}>
{output.name || `输出${index + 1}`} {output.name || `输出${index + 1}`}
</div> </div>
@ -191,8 +223,8 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
{/* 根据节点类型渲染不同的句柄 */} {/* 根据节点类型渲染不同的句柄 */}
{isSpecialNode {isSpecialNode
? renderSpecialNodeHandles(isStartNode, isEndNode, inputs, outputs) ? renderSpecialNodeHandles(isStartNode, isEndNode, dataIns, dataOuts, apiIns, apiOuts)
: renderRegularNodeHandles(inputs, outputs)} : renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)}
</> </>
); );
}; };

@ -1,4 +1,4 @@
import React, { useState, useCallback, useRef } from 'react'; import React, { useState, useCallback, useRef, useEffect } from 'react';
import { import {
ReactFlow, ReactFlow,
applyNodeChanges, applyNodeChanges,
@ -12,7 +12,6 @@ import {
useReactFlow, useReactFlow,
NodeTypes, NodeTypes,
EdgeTypes, EdgeTypes,
Panel,
SelectionMode SelectionMode
} from '@xyflow/react'; } from '@xyflow/react';
import '@xyflow/react/dist/style.css'; import '@xyflow/react/dist/style.css';
@ -23,7 +22,7 @@ import DraggableNode from './node/draggableNode/DraggableNode';
import BasicNode from './node/basicNode/BasicNode'; import BasicNode from './node/basicNode/BasicNode';
import SideBar from './sideBar/sideBar'; import SideBar from './sideBar/sideBar';
import { convertFlowData } from '@/utils/convertFlowData'; import { convertFlowData } from '@/utils/convertFlowData';
import { exampleFlowData } from '@/pages/flowEditor/test/exampleFlowData'; import { exampleFlowData, mineList } from '@/pages/flowEditor/test/exampleFlowData';
import CustomEdge from './components/customEdge'; import CustomEdge from './components/customEdge';
const nodeTypes: NodeTypes = { const nodeTypes: NodeTypes = {
@ -38,14 +37,6 @@ const edgeTypes: EdgeTypes = {
custom: CustomEdge custom: CustomEdge
}; };
const { nodes: convertedNodes, edges: convertedEdges } = convertFlowData(exampleFlowData);
// 为所有边添加类型
const initialEdges: Edge[] = convertedEdges.map(edge => ({
...edge,
type: 'custom'
}));
const FlowEditorWithProvider: React.FC = () => { const FlowEditorWithProvider: React.FC = () => {
return ( return (
<div style={{ width: '100%', height: '91vh', display: 'flex' }}> <div style={{ width: '100%', height: '91vh', display: 'flex' }}>
@ -58,8 +49,8 @@ const FlowEditorWithProvider: React.FC = () => {
}; };
const FlowEditor: React.FC = () => { const FlowEditor: React.FC = () => {
const [nodes, setNodes] = useState<Node[]>(convertedNodes); const [nodes, setNodes] = useState<Node[]>([]);
const [edges, setEdges] = useState<Edge[]>(initialEdges); const [edges, setEdges] = useState<Edge[]>([]);
const reactFlowInstance = useReactFlow(); const reactFlowInstance = useReactFlow();
const reactFlowWrapper = useRef<HTMLDivElement>(null); const reactFlowWrapper = useRef<HTMLDivElement>(null);
@ -109,6 +100,17 @@ const FlowEditor: React.FC = () => {
[reactFlowInstance] [reactFlowInstance]
); );
useEffect(() => {
const { nodes: convertedNodes, edges: convertedEdges } = convertFlowData(exampleFlowData);
// 为所有边添加类型
const initialEdges: Edge[] = convertedEdges.map(edge => ({
...edge,
type: 'custom'
}));
setNodes(convertedNodes);
setEdges(initialEdges);
}, []);
return ( return (
<div ref={reactFlowWrapper} style={{ width: '100%', height: '100%' }}> <div ref={reactFlowWrapper} style={{ width: '100%', height: '100%' }}>
<ReactFlow <ReactFlow

File diff suppressed because it is too large Load Diff

@ -39,13 +39,25 @@ export const convertFlowData = (flowData: any) => {
data: { data: {
title: nodeConfig.nodeName || nodeConfig.nodeId, title: nodeConfig.nodeName || nodeConfig.nodeId,
parameters: { parameters: {
inputs: nodeConfig.dataIns?.map((input: any) => ({ apiIns: [{
name: 'start',
desc: '',
dataType: '',
defaultValue: ''
}],
apiOuts: [{
name: nodeConfig.nodeId === 'end' ? 'end' : 'done',
desc: '',
dataType: '',
defaultValue: ''
}],
dataIns: nodeConfig.dataIns?.map((input: any) => ({
name: input.id, name: input.id,
desc: input.desc, desc: input.desc,
dataType: input.dataType, dataType: input.dataType,
defaultValue: input.defaultValue defaultValue: input.defaultValue
})) || [], })) || [],
outputs: nodeConfig.dataOuts?.map((output: any) => ({ dataOuts: nodeConfig.dataOuts?.map((output: any) => ({
name: output.id, name: output.id,
desc: output.desc, desc: output.desc,
dataType: output.dataType, dataType: output.dataType,

Loading…
Cancel
Save