feat(flow): 添加图片展示节点和相关逻辑

master
钟良源 4 months ago
parent 31649d0370
commit 1fb71b5446

@ -0,0 +1,31 @@
import React from 'react';
import { useStore } from '@xyflow/react';
// import styles from '@/pages/flowEditor/node/style/base.module.less';
import styles from '@/components/FlowEditor/node/style/baseOther.module.less';
import DynamicIcon from '@/components/DynamicIcon';
import NodeContentImage from '@/pages/flowEditor/components/nodeContentImage';
const setIcon = () => {
return <DynamicIcon type="IconImage" style={{ fontSize: '16px', marginRight: '5px' }} />;
};
const ImageNode = ({ data, id }: { data: any; id: string }) => {
const title = data.title || '图片展示';
// 获取节点选中状态 - 适配React Flow v12 API
const isSelected = useStore((state) =>
state.nodeLookup.get(id)?.selected || false
);
return (
<div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}>
<div className={styles['node-header']} style={{ backgroundColor: '#1890ff' }}>
{setIcon()}
{title}
</div>
<NodeContentImage data={data} />
</div>
);
};
export default ImageNode;

@ -20,10 +20,25 @@ import LocalNode from '@/components/FlowEditor/node/localNode/LocalNode';
import LoopNode from '@/components/FlowEditor/node/loopNode/LoopNode';
import BasicNode from '@/components/FlowEditor/node/basicNode/BasicNode';
import SwitchNode from '@/components/FlowEditor/node/switchNode/SwitchNode';
import ImageNode from '@/components/FlowEditor/node/imageNode/ImageNode';
import { updateCanvasDataMap } from '@/store/ideContainer';
import { Dispatch } from 'redux';
// 根据节点类型获取对应的节点组件
const getNodeComponent = (nodeType: string) => {
switch (nodeType) {
case 'BASIC':
return BasicNode;
case 'SWITCH':
return SwitchNode;
case 'IMAGE':
return ImageNode;
default:
return LocalNode;
}
};
export const useFlowCallbacks = (
nodes: Node[],
setNodes: React.Dispatch<React.SetStateAction<Node[]>>,
@ -512,14 +527,10 @@ export const useFlowCallbacks = (
setNodes(updatedNodes);
closeEditModal();
// TODO 如果需要在节点编辑后立即保存到服务器,可以调用保存函数
// saveFlowDataToServer();
}, [nodes, editingNode, closeEditModal]);
// 删除节点函数
const deleteNode = useCallback((node: Node) => {
console.log('node:', node);
setNodes((nds: Node[]) => nds.filter((n) => n.id !== node.id));
setEdges((eds: Edge[]) => eds.filter((e) => e.source !== node.id && e.target !== node.id));
@ -615,7 +626,7 @@ export const useFlowCallbacks = (
// 将未定义的节点动态追加进nodeTypes
const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key));
if (!nodeMap.includes(nodeType)) {
registerNodeType(nodeType, nodeType === 'BASIC' ? BasicNode : nodeType === 'SWITCH' ? SwitchNode : LocalNode, nodeDefinition.nodeName);
registerNodeType(nodeType, getNodeComponent(nodeType), nodeDefinition.nodeName);
}
// 添加新节点
@ -727,7 +738,8 @@ export const useFlowCallbacks = (
const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key));
// 目前默认添加的都是系统组件/本地组件
if (!nodeMap.includes(nodeType)) {
registerNodeType(nodeType, nodeType === 'BASIC' ? BasicNode : nodeType === 'SWITCH' ? SwitchNode : LocalNode, nodeDefinition.nodeName);
// console.log('getRegisterNodes():', getRegisterNodes(nodeType));
registerNodeType(nodeType, getNodeComponent(nodeType), nodeDefinition.nodeName);
}
setNodes((nds: Node[]) => {

@ -0,0 +1,189 @@
import React from 'react';
import styles from '@/components/FlowEditor/node/style/baseOther.module.less';
import { Handle, Position, useStore } from '@xyflow/react';
import { Image } from '@arco-design/web-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: `${70 + (apiIns.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: `${70 + (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';
const isSpecialNode = isStartNode || isEndNode;
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}
</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.desc} ${input.dataType}` || `输入${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>
</>
)}
{/*图片展示 TODO 需要对接接口*/}
{/*<div className={styles['node-image-box']}>*/}
{/* <Image*/}
{/* width={150}*/}
{/* src=""*/}
{/* alt="lamp"*/}
{/* />*/}
{/*</div>*/}
{/* 根据节点类型渲染不同的句柄 */}
{renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)}
</>
);
};
export default NodeContent;

@ -62,6 +62,28 @@ const switchParameters = {
dataIns: [],
dataOuts: []
};
const imageParameters = {
apiIns: [{
name: 'start',
desc: '',
dataType: '',
defaultValue: ''
}],
apiOuts: [{
name: 'done',
desc: '',
dataType: '',
defaultValue: ''
}],
dataIns: [{
name: 'in',
desc: 'url',
dataType: 'STRING',
defaultValue: '',
arrayType: ''
}],
dataOuts: []
};
// 定义节点基本信息 画布中添加的组件列表依赖这里
const nodeDefinitions = [
@ -96,6 +118,9 @@ export const localNodeData = nodeDefinitions.map(({ nodeName, nodeType, nodeGrou
else if (nodeType === 'SWITCH') {
parameters = switchParameters;
}
else if (nodeType === 'IMAGE') {
parameters = imageParameters;
}
return {
nodeName,

Loading…
Cancel
Save