feat(flow): 实现条件选择节点功能
- 新增SwitchNode组件用于渲染条件选择节点- 新增nodeContentSwitch组件用于展示条件选择节点内容 - 修改LocalNode组件中的节点类型判断逻辑- 更新localNodeData配置文件,添加switchParameters配置 - 将ConditionEditor重命名为SwitchEditor并调整其内部实现 - 调整ConditionsTable组件以支持switch类型节点 -优化LoopNode和LoopEditor中参数更新逻辑 - 移除addNodeMenu中无用的日志输出 - 更新useFlowCallbacks中节点注册逻辑以支持SWITCH类型 - 调整节点句柄渲染逻辑以适配新结构master
parent
0a70157262
commit
4e594e1368
@ -1,24 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { NodeEditorProps } from '@/components/FlowEditor/nodeEditors';
|
|
||||||
import { Typography } from '@arco-design/web-react';
|
|
||||||
import { IconUnorderedList } from '@arco-design/web-react/icon';
|
|
||||||
import ParamsTable from './ParamsTable';
|
|
||||||
|
|
||||||
const ConditionEditor: React.FC<NodeEditorProps> = ({ nodeData, updateNodeData }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Typography.Title heading={5}><IconUnorderedList style={{ marginRight: 5 }} />输入参数</Typography.Title>
|
|
||||||
<ParamsTable
|
|
||||||
initialData={nodeData.parameters.dataIns || []}
|
|
||||||
onUpdateData={(data) => {
|
|
||||||
updateNodeData('parameters', {
|
|
||||||
...nodeData.parameters,
|
|
||||||
dataIns: data
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ConditionEditor;
|
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { NodeEditorProps } from '@/components/FlowEditor/nodeEditors';
|
||||||
|
import { Typography } from '@arco-design/web-react';
|
||||||
|
import { IconUnorderedList } from '@arco-design/web-react/icon';
|
||||||
|
import ParamsTable from './ParamsTable';
|
||||||
|
import ConditionsTable from '@/components/FlowEditor/nodeEditors/components/ConditionsTable';
|
||||||
|
|
||||||
|
const SwitchEditor: React.FC<NodeEditorProps> = ({ nodeData, updateNodeData }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography.Title heading={5}><IconUnorderedList style={{ marginRight: 5 }} />输入参数</Typography.Title>
|
||||||
|
<ParamsTable
|
||||||
|
initialData={nodeData.parameters.dataIns || []}
|
||||||
|
onUpdateData={(data) => {
|
||||||
|
updateNodeData('parameters', {
|
||||||
|
...nodeData.parameters,
|
||||||
|
dataIns: data
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography.Title heading={5}>
|
||||||
|
<IconUnorderedList style={{ marginRight: 5, marginTop: 20 }} />
|
||||||
|
条件表达式
|
||||||
|
</Typography.Title>
|
||||||
|
<ConditionsTable
|
||||||
|
initialData={nodeData.component || {}}
|
||||||
|
nodeData={nodeData || null}
|
||||||
|
onUpdateData={(data) => {
|
||||||
|
updateNodeData('component', {
|
||||||
|
...data.component
|
||||||
|
});
|
||||||
|
updateNodeData('parameters', {
|
||||||
|
...nodeData.parameters,
|
||||||
|
apiOuts: data.parameters.apiOuts
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
type="switch"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SwitchEditor;
|
||||||
@ -0,0 +1,176 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styles from '@/components/FlowEditor/node/style/baseOther.module.less';
|
||||||
|
import { Handle, Position, useStore } from '@xyflow/react';
|
||||||
|
|
||||||
|
interface NodeContentData {
|
||||||
|
parameters?: {
|
||||||
|
dataIns?: any[];
|
||||||
|
dataOuts?: any[];
|
||||||
|
apiIns?: any[];
|
||||||
|
apiOuts?: any[];
|
||||||
|
};
|
||||||
|
showFooter?: boolean;
|
||||||
|
type?: string;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义通用的句柄样式
|
||||||
|
const handleStyles = {
|
||||||
|
mainSource: {
|
||||||
|
background: '#2290f6',
|
||||||
|
width: '8px',
|
||||||
|
height: '8px',
|
||||||
|
border: '2px solid #fff',
|
||||||
|
boxShadow: '0 0 4px rgba(0,0,0,0.2)'
|
||||||
|
},
|
||||||
|
mainTarget: {
|
||||||
|
background: '#2290f6',
|
||||||
|
width: '8px',
|
||||||
|
height: '8px',
|
||||||
|
border: '2px solid #fff',
|
||||||
|
boxShadow: '0 0 4px rgba(0,0,0,0.2)'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
background: '#555',
|
||||||
|
width: '6px',
|
||||||
|
height: '6px',
|
||||||
|
border: '1px solid #fff',
|
||||||
|
boxShadow: '0 0 2px rgba(0,0,0,0.2)'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 渲染普通节点的句柄
|
||||||
|
const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[], apiOuts: any[]) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{apiOuts.map((_, index) => (
|
||||||
|
<Handle
|
||||||
|
key={`api-output-handle-${index}`}
|
||||||
|
type="source"
|
||||||
|
position={Position.Right}
|
||||||
|
id={apiOuts[index].name || apiOuts[index].id || `output-${index}`}
|
||||||
|
style={{
|
||||||
|
...handleStyles.mainSource,
|
||||||
|
top: `${35 + index * 20}px`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{apiIns.map((_, index) => (
|
||||||
|
<Handle
|
||||||
|
key={`api-input-handle-${index}`}
|
||||||
|
type="target"
|
||||||
|
position={Position.Left}
|
||||||
|
id={apiIns[index].name || apiIns[index].id || `input-${index}`}
|
||||||
|
style={{
|
||||||
|
...handleStyles.mainTarget,
|
||||||
|
top: `${35 + index * 20}px`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* 输入参数连接端点 */}
|
||||||
|
{dataIns.map((_, index) => (
|
||||||
|
<Handle
|
||||||
|
key={`data-input-handle-${index}`}
|
||||||
|
type="target"
|
||||||
|
position={Position.Left}
|
||||||
|
id={dataIns[index].name || dataIns[index].id || `input-${index}`}
|
||||||
|
style={{
|
||||||
|
...handleStyles.data,
|
||||||
|
top: `${65 + (apiOuts.length + index) * 20}px`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* 输出参数连接端点 */}
|
||||||
|
{dataOuts.map((_, index) => (
|
||||||
|
<Handle
|
||||||
|
key={`data-output-handle-${index}`}
|
||||||
|
type="source"
|
||||||
|
position={Position.Right}
|
||||||
|
id={dataOuts[index].name || dataOuts[index].id || `output-${index}`}
|
||||||
|
style={{
|
||||||
|
...handleStyles.data,
|
||||||
|
top: `${65 + (apiOuts.length + index) * 20}px`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const NodeContent = ({ data }: { data: NodeContentData }) => {
|
||||||
|
const apiIns = data.parameters?.apiIns || [];
|
||||||
|
const apiOuts = data.parameters?.apiOuts || [];
|
||||||
|
const dataIns = data.parameters?.dataIns || [];
|
||||||
|
const dataOuts = data.parameters?.dataOuts || [];
|
||||||
|
|
||||||
|
// 判断节点类型
|
||||||
|
const isStartNode = data.type === 'start';
|
||||||
|
const isEndNode = data.type === 'end';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/*content栏-api部分*/}
|
||||||
|
<div className={styles['node-api-box']}>
|
||||||
|
<div className={styles['node-content-api']}>
|
||||||
|
{apiIns.length > 0 && (
|
||||||
|
<div className={styles['node-inputs']}>
|
||||||
|
{apiIns.map((input, index) => (
|
||||||
|
<div key={input.id || `input-${index}`} className={styles['node-input-label']}>
|
||||||
|
{input.desc}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{apiOuts.length > 0 && (
|
||||||
|
<div className={styles['node-outputs-api']}>
|
||||||
|
{apiOuts.map((output, index) => (
|
||||||
|
<div key={output.id || `output-${index}`} className={styles['node-input-label']}>
|
||||||
|
{output.desc || output.id || output.name}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{(dataIns.length > 0 || dataOuts.length > 0) && (
|
||||||
|
<>
|
||||||
|
{/*分割*/}
|
||||||
|
<div className={styles['node-split-line']}></div>
|
||||||
|
|
||||||
|
{/*content栏-data部分*/}
|
||||||
|
<div className={styles['node-data-box']}>
|
||||||
|
<div className={styles['node-content']}>
|
||||||
|
{dataIns.length > 0 && !isStartNode && (
|
||||||
|
<div className={styles['node-inputs']}>
|
||||||
|
{dataIns.map((input, index) => (
|
||||||
|
<div key={input.id || `input-${index}`} className={styles['node-input-label']}>
|
||||||
|
{input.id || `输入${index + 1}`}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{dataOuts.length > 0 && !isEndNode && (
|
||||||
|
<div className={styles['node-outputs']}>
|
||||||
|
{dataOuts.map((output, index) => (
|
||||||
|
<div key={output.id || `output-${index}`} className={styles['node-input-label']}>
|
||||||
|
{output.id || `输出${index + 1}`}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NodeContent;
|
||||||
Loading…
Reference in New Issue