feat(flowEditor): 优化节点句柄渲染逻辑

- 引入 useMemo 钩子提升性能
- 添加获取连接边和数据字段的辅助函数
- 仅渲染已连接 API 对应的 data 输入输出句柄
- 调整句柄位置计算方式以适应新布局
- 更新节点内容展示逻辑,只显示有连接的 data 字段
- 为 API 输入输出句柄添加注释说明
master
钟良源 2 months ago
parent 9881a9afed
commit 5cc13cba69

@ -1,4 +1,4 @@
import React from 'react'; import React, { useMemo } from 'react';
import styles from '@/components/FlowEditor/node/style/baseOther.module.less'; import styles from '@/components/FlowEditor/node/style/baseOther.module.less';
import { Handle, Position, useStore } from '@xyflow/react'; import { Handle, Position, useStore } from '@xyflow/react';
import { deserializeValue, formatDataType, isJSON } from '@/utils/common'; import { deserializeValue, formatDataType, isJSON } from '@/utils/common';
@ -14,6 +14,7 @@ interface NodeContentData {
}; };
showFooter?: boolean; showFooter?: boolean;
type?: string; type?: string;
id?: string;
[key: string]: any; [key: string]: any;
} }
@ -119,10 +120,40 @@ const renderSpecialNodeHandles = (isStartNode: boolean, isEndNode: boolean, data
); );
}; };
// 获取连接到特定 API 句柄的 edges
const getConnectedEdgesForHandle = (edges: any[], nodeId: string, handleId: string, handleType: 'source' | 'target') => {
return edges.filter(edge => {
if (handleType === 'target') {
return edge.targetHandle === handleId;
}
else {
return edge.sourceHandle === handleId;
}
});
};
// 根据 apiIns 中的 fieldIns/fieldOuts 获取对应的 dataIns/dataOuts
const getDataFieldsForApi = (apiItem: any, dataFields: any[], type: string) => {
if (!apiItem.fieldIns && !apiItem.fieldOuts) return [];
const fieldNames = type === 'in' ? apiItem.fieldIns : apiItem.fieldOuts;
return fieldNames
.map((fieldName: string) => dataFields.find(df => df.name === fieldName || df.id === fieldName))
.filter(Boolean);
};
// 渲染普通节点的句柄 // 渲染普通节点的句柄
const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[], apiOuts: any[]) => { const renderRegularNodeHandles = (
dataIns: any[],
dataOuts: any[],
apiIns: any[],
apiOuts: any[],
connectedDataIns: any[],
connectedDataOuts: any[]
) => {
return ( return (
<> <>
{/* API 输出连接点 */}
{apiOuts.map((_, index) => ( {apiOuts.map((_, index) => (
<Handle <Handle
key={`api-output-handle-${index}`} key={`api-output-handle-${index}`}
@ -135,6 +166,8 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[]
}} }}
/> />
))} ))}
{/* API 输入连接点 */}
{apiIns.map((_, index) => ( {apiIns.map((_, index) => (
<Handle <Handle
key={`api-input-handle-${index}`} key={`api-input-handle-${index}`}
@ -148,30 +181,30 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[]
/> />
))} ))}
{/* 输入参数连接端点 */} {/* Data 输入参数连接端点 - 只渲染已连接API的data字段 */}
{dataIns.map((_, index) => ( {connectedDataIns.map((dataIn, index) => (
<Handle <Handle
key={`data-input-handle-${index}`} key={`data-input-handle-${dataIn.name || dataIn.id || index}`}
type="target" type="target"
position={Position.Left} position={Position.Left}
id={dataIns[index].name || dataIns[index].id || `input-${index}`} id={dataIn.name || dataIn.id || `data-input-${index}`}
style={{ style={{
...handleStyles.data, ...handleStyles.data,
top: `${65 + (apiIns.length + index) * 22}px` top: `${65 + (apiIns.length) * 22 + index * 22}px`
}} }}
/> />
))} ))}
{/* 输出参数连接端点 */} {/* Data 输出参数连接端点 - 只渲染已连接API的data字段 */}
{dataOuts.map((_, index) => ( {connectedDataOuts.map((dataOut, index) => (
<Handle <Handle
key={`data-output-handle-${index}`} key={`data-output-handle-${dataOut.name || dataOut.id || index}`}
type="source" type="source"
position={Position.Right} position={Position.Right}
id={dataOuts[index].name || dataOuts[index].id || `output-${index}`} id={dataOut.name || dataOut.id || `data-output-${index}`}
style={{ style={{
...handleStyles.data, ...handleStyles.data,
top: `${65 + (apiIns.length + index) * 22}px` top: `${65 + (apiIns.length) * 22 + index * 22}px`
}} }}
/> />
))} ))}
@ -222,18 +255,46 @@ const formatTitle = (text) => {
const NodeContent = ({ data }: { data: NodeContentData }) => { const NodeContent = ({ data }: { data: NodeContentData }) => {
const { eventListOld } = useSelector((state) => state.ideContainer); const { eventListOld } = useSelector((state) => state.ideContainer);
// 使用 useStore 获取 edges 信息
const edges = useStore((state) => state.edges);
const apiIns = data.parameters?.apiIns || []; const apiIns = data.parameters?.apiIns || [];
const apiOuts = data.parameters?.apiOuts || []; const apiOuts = data.parameters?.apiOuts || [];
const dataIns = data.parameters?.dataIns || []; const dataIns = data.parameters?.dataIns || [];
const dataOuts = data.parameters?.dataOuts || []; const dataOuts = data.parameters?.dataOuts || [];
const showFooter = formatFooter(data.component) || false; const showFooter = formatFooter(data.component) || false;
const footerData = (showFooter && data.component) || {}; const footerData = (showFooter && data.component) || {};
const nodeId = data.id;
// 判断节点类型 // 判断节点类型
const isStartNode = data.type === 'start'; const isStartNode = data.type === 'start';
const isEndNode = data.type === 'end'; const isEndNode = data.type === 'end';
const isSpecialNode = isStartNode || isEndNode; const isSpecialNode = isStartNode || isEndNode;
// 获取连接到当前节点 API 句柄的 data 字段
const connectedDataFields = useMemo(() => {
const connectedIns: any[] = [];
const connectedOuts: any[] = [];
// 通过api数据获取对应的data输入输出
apiIns.forEach(apiIn => {
const handleIdIn = apiIn.name || apiIn.id;
const hasConnectionIn = getConnectedEdgesForHandle(edges, nodeId, handleIdIn, 'target').length > 0;
if (hasConnectionIn) {
const relatedDataIn = getDataFieldsForApi(apiIn, dataIns, 'in');
connectedIns.push(...relatedDataIn);
const relatedDataOut = getDataFieldsForApi(apiIn, dataOuts, 'out');
// console.log("relatedDataOut:",relatedDataOut);
connectedOuts.push(...relatedDataOut);
}
});
return { connectedIns, connectedOuts };
}, [edges, nodeId, apiIns, apiOuts, dataIns, dataOuts]);
return ( return (
<> <>
{/*content栏-api部分*/} {/*content栏-api部分*/}
@ -260,36 +321,36 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
)} )}
</div> </div>
</div> </div>
{(dataIns.length > 0 || dataOuts.length > 0) && (
{/* Data 区域 - 仅当有连接的 API 时显示对应的 data 字段 */}
{(connectedDataFields.connectedIns.length > 0 || connectedDataFields.connectedOuts.length > 0) && (
<> <>
{/*分割*/} {/*分割*/}
<div <div className={styles['node-split-line']}></div>
className={styles['node-split-line']}
>
</div>
{/*content栏-data部分*/} {/*content栏-data部分*/}
<div className={styles['node-data-box']}> <div className={styles['node-data-box']}>
<div className={styles['node-content']}> <div className={styles['node-content']}>
<div className={styles['node-inputs']}> {/* Data 输入 */}
{dataIns.map((input, index) => ( {connectedDataFields.connectedIns.length > 0 && (
<div key={input.id || `input-${index}`} className={styles['node-input-label']}> <div className={styles['node-inputs']}>
<span {connectedDataFields.connectedIns.map((input, index) => (
className={styles['node-data-type']} <div key={input.id || `input-${index}`} className={styles['node-input-label']}>
>{input.id || `输入${index + 1}`} {formatDataType(input.dataType)} <span className={styles['node-data-type']}>
</span> {input.name || input.id || `输入${index + 1}`} {formatDataType(input.dataType)}
</div> </span>
))} </div>
</div> ))}
</div>
{dataOuts.length > 0 && !isEndNode && ( )}
{/* Data 输出 */}
{connectedDataFields.connectedOuts.length > 0 && !isEndNode && (
<div className={styles['node-outputs']}> <div className={styles['node-outputs']}>
{dataOuts.map((output, index) => ( {connectedDataFields.connectedOuts.map((output, index) => (
<div key={output.id || `output-${index}`} className={styles['node-output-label']}> <div key={output.id || `output-${index}`} className={styles['node-output-label']}>
<span <span className={styles['node-data-type']}>
className={styles['node-data-type']} {formatDataType(output.dataType)} {output.name || output.id || `输出${index + 1}`}
>{formatDataType(output.dataType)} {output.id || `输出${index + 1}`}
</span> </span>
</div> </div>
))} ))}
@ -310,7 +371,14 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
{/* 根据节点类型渲染不同的句柄 */} {/* 根据节点类型渲染不同的句柄 */}
{isSpecialNode {isSpecialNode
? renderSpecialNodeHandles(isStartNode, isEndNode, dataIns, dataOuts, apiIns, apiOuts) ? renderSpecialNodeHandles(isStartNode, isEndNode, dataIns, dataOuts, apiIns, apiOuts)
: renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)} : renderRegularNodeHandles(
dataIns,
dataOuts,
apiIns,
apiOuts,
connectedDataFields.connectedIns,
connectedDataFields.connectedOuts
)}
</> </>
); );
}; };

Loading…
Cancel
Save