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 { Handle, Position, useStore } from '@xyflow/react';
import { deserializeValue, formatDataType, isJSON } from '@/utils/common';
@ -14,6 +14,7 @@ interface NodeContentData {
};
showFooter?: boolean;
type?: string;
id?: string;
[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 (
<>
{/* API 输出连接点 */}
{apiOuts.map((_, index) => (
<Handle
key={`api-output-handle-${index}`}
@ -135,6 +166,8 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[]
}}
/>
))}
{/* API 输入连接点 */}
{apiIns.map((_, index) => (
<Handle
key={`api-input-handle-${index}`}
@ -148,30 +181,30 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[]
/>
))}
{/* 输入参数连接端点 */}
{dataIns.map((_, index) => (
{/* Data 输入参数连接端点 - 只渲染已连接API的data字段 */}
{connectedDataIns.map((dataIn, index) => (
<Handle
key={`data-input-handle-${index}`}
key={`data-input-handle-${dataIn.name || dataIn.id || index}`}
type="target"
position={Position.Left}
id={dataIns[index].name || dataIns[index].id || `input-${index}`}
id={dataIn.name || dataIn.id || `data-input-${index}`}
style={{
...handleStyles.data,
top: `${65 + (apiIns.length + index) * 22}px`
top: `${65 + (apiIns.length) * 22 + index * 22}px`
}}
/>
))}
{/* 输出参数连接端点 */}
{dataOuts.map((_, index) => (
{/* Data 输出参数连接端点 - 只渲染已连接API的data字段 */}
{connectedDataOuts.map((dataOut, index) => (
<Handle
key={`data-output-handle-${index}`}
key={`data-output-handle-${dataOut.name || dataOut.id || index}`}
type="source"
position={Position.Right}
id={dataOuts[index].name || dataOuts[index].id || `output-${index}`}
id={dataOut.name || dataOut.id || `data-output-${index}`}
style={{
...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 { eventListOld } = useSelector((state) => state.ideContainer);
// 使用 useStore 获取 edges 信息
const edges = useStore((state) => state.edges);
const apiIns = data.parameters?.apiIns || [];
const apiOuts = data.parameters?.apiOuts || [];
const dataIns = data.parameters?.dataIns || [];
const dataOuts = data.parameters?.dataOuts || [];
const showFooter = formatFooter(data.component) || false;
const footerData = (showFooter && data.component) || {};
const nodeId = data.id;
// 判断节点类型
const isStartNode = data.type === 'start';
const isEndNode = data.type === 'end';
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 (
<>
{/*content栏-api部分*/}
@ -260,36 +321,36 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
)}
</div>
</div>
{(dataIns.length > 0 || dataOuts.length > 0) && (
{/* Data 区域 - 仅当有连接的 API 时显示对应的 data 字段 */}
{(connectedDataFields.connectedIns.length > 0 || connectedDataFields.connectedOuts.length > 0) && (
<>
{/*分割*/}
<div
className={styles['node-split-line']}
>
</div>
<div className={styles['node-split-line']}></div>
{/*content栏-data部分*/}
<div className={styles['node-data-box']}>
<div className={styles['node-content']}>
{/* Data 输入 */}
{connectedDataFields.connectedIns.length > 0 && (
<div className={styles['node-inputs']}>
{dataIns.map((input, index) => (
{connectedDataFields.connectedIns.map((input, index) => (
<div key={input.id || `input-${index}`} className={styles['node-input-label']}>
<span
className={styles['node-data-type']}
>{input.id || `输入${index + 1}`} {formatDataType(input.dataType)}
<span className={styles['node-data-type']}>
{input.name || input.id || `输入${index + 1}`} {formatDataType(input.dataType)}
</span>
</div>
))}
</div>
)}
{dataOuts.length > 0 && !isEndNode && (
{/* Data 输出 */}
{connectedDataFields.connectedOuts.length > 0 && !isEndNode && (
<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']}>
<span
className={styles['node-data-type']}
>{formatDataType(output.dataType)} {output.id || `输出${index + 1}`}
<span className={styles['node-data-type']}>
{formatDataType(output.dataType)} {output.name || output.id || `输出${index + 1}`}
</span>
</div>
))}
@ -310,7 +371,14 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
{/* 根据节点类型渲染不同的句柄 */}
{isSpecialNode
? renderSpecialNodeHandles(isStartNode, isEndNode, dataIns, dataOuts, apiIns, apiOuts)
: renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)}
: renderRegularNodeHandles(
dataIns,
dataOuts,
apiIns,
apiOuts,
connectedDataFields.connectedIns,
connectedDataFields.connectedOuts
)}
</>
);
};

Loading…
Cancel
Save