|
|
|
|
@ -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, isJSON } from '@/utils/common';
|
|
|
|
|
@ -13,6 +13,7 @@ interface NodeContentData {
|
|
|
|
|
};
|
|
|
|
|
showFooter?: boolean;
|
|
|
|
|
type?: string;
|
|
|
|
|
component?: any;
|
|
|
|
|
|
|
|
|
|
[key: string]: any;
|
|
|
|
|
}
|
|
|
|
|
@ -178,29 +179,112 @@ const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[]
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const formatFooter = (data: any) => {
|
|
|
|
|
try {
|
|
|
|
|
switch (data?.type) {
|
|
|
|
|
case 'WAIT':
|
|
|
|
|
const { duration } = deserializeValue(data.customDef);
|
|
|
|
|
const hours = Math.floor(duration / 3600);
|
|
|
|
|
const minutes = Math.floor((duration % 3600) / 60);
|
|
|
|
|
const seconds = Math.floor(duration % 60);
|
|
|
|
|
return `${hours}小时${minutes}分钟${seconds}秒`;
|
|
|
|
|
case 'CYCLE':
|
|
|
|
|
const { intervalSeconds } = deserializeValue(data.customDef);
|
|
|
|
|
return cronstrue.toString(intervalSeconds, { locale: 'zh_CN' });
|
|
|
|
|
case 'EVENTSEND':
|
|
|
|
|
case 'EVENTLISTENE':
|
|
|
|
|
const { name, topic } = isJSON(data.customDef) ? JSON.parse(data.customDef) : data.customDef;
|
|
|
|
|
if (topic.includes('**empty**')) return '';
|
|
|
|
|
return `事件: ${name}`;
|
|
|
|
|
default:
|
|
|
|
|
return '';
|
|
|
|
|
// 检查是否为有效的API(非empty)
|
|
|
|
|
const isValidApi = (api: any) => {
|
|
|
|
|
return api && !api.topic?.includes('**empty**') && !api.name?.includes('**empty**');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 检查是否为有效的数据(非empty)
|
|
|
|
|
const isValidData = (data: any) => {
|
|
|
|
|
return data && !data.topic?.includes('**empty**') && !data.name?.includes('**empty**');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 为每组关联的数据定义颜色
|
|
|
|
|
const getGroupColor = (groupId: number) => {
|
|
|
|
|
const colors = [
|
|
|
|
|
'#1890ff', // 蓝色
|
|
|
|
|
'#52c41a', // 绿色
|
|
|
|
|
'#faad14', // 黄色
|
|
|
|
|
'#f5222d', // 红色
|
|
|
|
|
'#722ed1', // 紫色
|
|
|
|
|
'#13c2c2' // 青色
|
|
|
|
|
];
|
|
|
|
|
return colors[groupId % colors.length];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 从customDef中提取事件分组信息
|
|
|
|
|
const useEventGroups = (component: any) => {
|
|
|
|
|
return useMemo(() => {
|
|
|
|
|
if (!component?.customDef) return {};
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const customDef = isJSON(component.customDef)
|
|
|
|
|
? JSON.parse(component.customDef)
|
|
|
|
|
: component.customDef;
|
|
|
|
|
|
|
|
|
|
const groups: Record<string, { event: any; color: string; index: number }> = {};
|
|
|
|
|
let groupIndex = 0;
|
|
|
|
|
|
|
|
|
|
// 处理eventListenes(对应apiOuts)
|
|
|
|
|
if (Array.isArray(customDef.eventListenes)) {
|
|
|
|
|
customDef.eventListenes
|
|
|
|
|
.filter(event => event && !event.topic?.includes('**empty**'))
|
|
|
|
|
.forEach(event => {
|
|
|
|
|
groups[`eventListenes-${event.eventId}`] = {
|
|
|
|
|
event,
|
|
|
|
|
color: getGroupColor(groupIndex++),
|
|
|
|
|
index: groupIndex - 1
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理eventSends(对应apiIns)
|
|
|
|
|
if (Array.isArray(customDef.eventSends)) {
|
|
|
|
|
customDef.eventSends
|
|
|
|
|
.filter(event => event && !event.topic?.includes('**empty**'))
|
|
|
|
|
.forEach(event => {
|
|
|
|
|
groups[`eventSends-${event.eventId}`] = {
|
|
|
|
|
event,
|
|
|
|
|
color: getGroupColor(groupIndex++),
|
|
|
|
|
index: groupIndex - 1
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
console.log('groups:', groups);
|
|
|
|
|
return groups;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('解析customDef时出错:', e);
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
}, [component?.customDef]);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 根据数据ID查找关联的事件分组
|
|
|
|
|
const findRelatedEventGroup = (dataItem: any, eventGroups: Record<string, any>, eventType: string) => {
|
|
|
|
|
if (!dataItem || !eventGroups) return null;
|
|
|
|
|
|
|
|
|
|
// 查找与数据项关联的事件
|
|
|
|
|
for (const key in eventGroups) {
|
|
|
|
|
const group = eventGroups[key];
|
|
|
|
|
const eventDataIns = group.event?.dataIns || [];
|
|
|
|
|
const eventDataOuts = group.event?.dataOuts || [];
|
|
|
|
|
|
|
|
|
|
// 检查数据项是否属于当前事件的数据输入或输出
|
|
|
|
|
if (eventType === 'eventListenes' && eventDataIns.some((input: any) => input.id === dataItem.id)) {
|
|
|
|
|
return group;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (eventType === 'eventSends' && eventDataOuts.some((output: any) => output.id === dataItem.id)) {
|
|
|
|
|
return group;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.log(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 根据topic查找API关联的事件分组
|
|
|
|
|
const findApiGroupByTopic = (apiItem: any, eventGroups: Record<string, any>) => {
|
|
|
|
|
if (!apiItem || !apiItem.topic || !eventGroups) return null;
|
|
|
|
|
|
|
|
|
|
// 查找与API项topic关联的事件
|
|
|
|
|
for (const key in eventGroups) {
|
|
|
|
|
const group = eventGroups[key];
|
|
|
|
|
if (group.event.topic === apiItem.topic) {
|
|
|
|
|
return group;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const NodeContent = ({ data }: { data: NodeContentData }) => {
|
|
|
|
|
@ -208,9 +292,8 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
|
|
|
|
|
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) || {};
|
|
|
|
|
// console.log(apiIns, apiOuts, dataIns, dataOuts);
|
|
|
|
|
// 获取事件分组信息
|
|
|
|
|
const eventGroups = useEventGroups(data.component);
|
|
|
|
|
|
|
|
|
|
// 判断节点类型
|
|
|
|
|
const isStartNode = data.type === 'start';
|
|
|
|
|
@ -224,21 +307,41 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
|
|
|
|
|
<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.topic.includes('**empty') ? input.name : ''}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
{apiIns.map((input, index) => {
|
|
|
|
|
// 查找关联的事件分组
|
|
|
|
|
const group = findApiGroupByTopic(input, eventGroups);
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={input.id || `input-${index}`}
|
|
|
|
|
className={styles['node-input-label']}
|
|
|
|
|
style={{
|
|
|
|
|
color: group ? group.color : '#000'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{isValidApi(input) ? input.name : ''}
|
|
|
|
|
</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.topic.includes('**empty') ? output.name : ''}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
{apiOuts.map((output, index) => {
|
|
|
|
|
// 查找关联的事件分组
|
|
|
|
|
const group = findApiGroupByTopic(output, eventGroups);
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={output.id || `output-${index}`}
|
|
|
|
|
className={styles['node-input-label']}
|
|
|
|
|
style={{
|
|
|
|
|
color: group ? group.color : '#000'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{isValidApi(output) ? output.name : ''}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
@ -253,21 +356,42 @@ const NodeContent = ({ data }: { data: NodeContentData }) => {
|
|
|
|
|
<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>
|
|
|
|
|
))}
|
|
|
|
|
{dataIns.map((input, index) => {
|
|
|
|
|
// 查找关联的事件分组
|
|
|
|
|
const group = findRelatedEventGroup(input, eventGroups, 'eventSends');
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={input.id || `input-${index}`}
|
|
|
|
|
className={styles['node-input-label']}
|
|
|
|
|
style={{
|
|
|
|
|
color: group ? group.color : '#000'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{isValidData(input) ? (input.name || 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>
|
|
|
|
|
))}
|
|
|
|
|
{dataOuts.map((output, index) => {
|
|
|
|
|
// 查找关联的事件分组
|
|
|
|
|
const group = findRelatedEventGroup(output, eventGroups, 'eventListenes');
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={output.id || `output-${index}`}
|
|
|
|
|
className={styles['node-input-label']}
|
|
|
|
|
style={{
|
|
|
|
|
color: group ? group.color : '#000'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{isValidData(output) ? (output.name || output.id || `输出${index + 1}`) : ''}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|