|
|
|
|
@ -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
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|