diff --git a/src/pages/flowEditor/components/nodeContentOther.tsx b/src/pages/flowEditor/components/nodeContentOther.tsx
new file mode 100644
index 0000000..1aea57f
--- /dev/null
+++ b/src/pages/flowEditor/components/nodeContentOther.tsx
@@ -0,0 +1,288 @@
+import React from 'react';
+import styles from '@/pages/flowEditor/node/style/baseOther.module.less';
+import { Handle, Position, useStore } from '@xyflow/react';
+import { isJSON } from '@/utils/common';
+import cronstrue from 'cronstrue/i18n';
+
+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 renderSpecialNodeHandles = (isStartNode: boolean, isEndNode: boolean, dataIns: any[], dataOuts: any[], apiIns: any[], apiOuts: any[]) => {
+ const renderStartNodeHandles = () => {
+ if (!isStartNode) return null;
+
+ return (
+ <>
+ {apiOuts.map((_, index) => (
+
+ ))}
+ {dataOuts.map((_, index) => (
+
+ ))}
+ >
+ );
+ };
+
+ const renderEndNodeHandles = () => {
+ if (!isEndNode) return null;
+
+ return (
+ <>
+ {apiIns.map((_, index) => (
+
+ ))}
+ {dataIns.map((_, index) => (
+
+ ))}
+ >
+ );
+ };
+
+ return (
+ <>
+ {renderStartNodeHandles()}
+ {renderEndNodeHandles()}
+ >
+ );
+};
+
+// 渲染普通节点的句柄
+const renderRegularNodeHandles = (dataIns: any[], dataOuts: any[], apiIns: any[], apiOuts: any[]) => {
+ return (
+ <>
+ {apiOuts.map((_, index) => (
+
+ ))}
+ {apiIns.map((_, index) => (
+
+ ))}
+
+ {/* 输入参数连接端点 */}
+ {dataIns.map((_, index) => (
+
+ ))}
+
+ {/* 输出参数连接端点 */}
+ {dataOuts.map((_, index) => (
+
+ ))}
+ >
+ );
+};
+
+const formatFooter = (data: any) => {
+ console.log('formatFooter:', data);
+ switch (data.type) {
+ case 'WAIT':
+ const { duration } = 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 } = data.customDef;
+ return cronstrue.toString(intervalSeconds, { locale: 'zh_CN' });
+ case 'EVENTSEND':
+ case 'EVENTLISTENE':
+ const { name } = data.customDef;
+ return `事件: ${name}`;
+ default:
+ return '这个类型还没开发';
+ }
+};
+
+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 showFooter = data?.component?.customDef || false;
+ const footerData = (showFooter && data.component) || {};
+
+ // 判断节点类型
+ const isStartNode = data.type === 'start';
+ const isEndNode = data.type === 'end';
+ const isSpecialNode = isStartNode || isEndNode;
+
+ return (
+ <>
+ {/*content栏-api部分*/}
+
+
+ {apiIns.length > 0 && (
+
+ {apiIns.map((input, index) => (
+
+ {input.desc}
+
+ ))}
+
+ )}
+
+ {apiOuts.length > 0 && (
+
+ {apiOuts.map((output, index) => (
+
+ {output.desc}
+
+ ))}
+
+ )}
+
+
+
+
+ 中间分割
+
+
+ {/*content栏-data部分*/}
+
+
+ {dataIns.length > 0 && !isStartNode && (
+
+ {dataIns.map((input, index) => (
+
+ {input.id || `输入${index + 1}`}
+
+ ))}
+
+ )}
+
+ {dataOuts.length > 0 && !isEndNode && (
+
+ {dataOuts.map((output, index) => (
+
+ {output.id || `输出${index + 1}`}
+
+ ))}
+
+ )}
+
+
+
+ {/*footer栏*/}
+ {showFooter && (
+
+ {formatFooter(footerData)}
+
+ )}
+
+ {/* 根据节点类型渲染不同的句柄 */}
+ {isSpecialNode
+ ? renderSpecialNodeHandles(isStartNode, isEndNode, dataIns, dataOuts, apiIns, apiOuts)
+ : renderRegularNodeHandles(dataIns, dataOuts, apiIns, apiOuts)}
+ >
+ );
+};
+
+export default NodeContent;
\ No newline at end of file
diff --git a/src/pages/flowEditor/node/basicNode/BasicNode.tsx b/src/pages/flowEditor/node/basicNode/BasicNode.tsx
index c302fe0..8976c54 100644
--- a/src/pages/flowEditor/node/basicNode/BasicNode.tsx
+++ b/src/pages/flowEditor/node/basicNode/BasicNode.tsx
@@ -1,6 +1,8 @@
import React from 'react';
import styles from '@/pages/flowEditor/node/style/base.module.less';
+// import styles from '@/pages/flowEditor/node/style/baseOther.module.less';
import NodeContent from '@/pages/flowEditor/components/nodeContent';
+import NodeContentOther from '@/pages/flowEditor/components/nodeContentOther';
import { useStore } from '@xyflow/react';
import { defaultNodeTypes } from '@/pages/flowEditor/node/types/defaultType';
@@ -9,7 +11,7 @@ const BasicNode = ({ data, id }: { data: defaultNodeTypes; id: string }) => {
const title = data.title || '基础节点';
// 获取节点选中状态 - 适配React Flow v12 API
- const isSelected = useStore((state) =>
+ const isSelected = useStore((state) =>
state.nodeLookup.get(id)?.selected || false
);
@@ -20,6 +22,7 @@ const BasicNode = ({ data, id }: { data: defaultNodeTypes; id: string }) => {
+ {/**/}
);
};
diff --git a/src/pages/flowEditor/node/style/baseOther.module.less b/src/pages/flowEditor/node/style/baseOther.module.less
new file mode 100644
index 0000000..d5b0b04
--- /dev/null
+++ b/src/pages/flowEditor/node/style/baseOther.module.less
@@ -0,0 +1,93 @@
+.node-container {
+ color: white;
+ min-width: 150px;
+ font-size: 14px;
+ border: 2px solid transparent;
+
+ &.selected {
+ border-color: #1890ff;
+ box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
+ }
+
+ .node-header {
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+ color: #000000;
+ text-align: center;
+ }
+
+ .node-api-box,
+ .node-data-box {
+ //box-shadow: 3px 3px 5px #cccccc;
+ }
+
+ .node-data-box {
+ margin-top: -1px;
+ }
+
+ .node-split-line {
+ z-index: 230;
+ position: relative;
+ width: 90%;
+ background-color: #ffffff;
+ margin: -1px auto; // -1是为了遮住上面盒子的部分边框
+ color: #000000;
+ text-align: center;
+ height: 20px;
+ line-height: 20px;
+ font-size: 12px;
+ border: 1px solid #cccccc;
+ border-bottom-color: #ffffff;
+ border-top-color: #ffffff;
+ }
+
+ .node-content,
+ .node-content-api {
+ display: flex;
+ background-color: #ffffff;
+ color: #000000;
+ padding: 0 5px;
+ border: 1px solid #cccccc;
+ border-radius: 3px;
+
+
+ .node-inputs,
+ .node-outputs,
+ .node-outputs-api {
+ flex: 1;
+
+ .node-input-label {
+ font-size: 12px;
+ padding: 1px 0;
+ height: 20px;
+ line-height: 20px;
+ }
+ }
+
+ .node-inputs {
+ margin-bottom: 5px;
+ margin-right: 30px;
+ }
+
+ .node-outputs,
+ .node-outputs-api {
+ text-align: right;
+ }
+ }
+
+ .node-content-api {
+ min-height: 30px;
+ }
+
+ .node-content {
+ min-height: 10px;
+ }
+
+ .node-footer {
+ background-color: #ffffff;
+ color: #000000;
+ padding: 5px 20px;
+ border-top: 1px solid rgba(204, 204, 204, 0.18);
+ min-height: 20px;
+ text-align: center;
+ }
+}
\ No newline at end of file