This commit is contained in:
dengqichen 2025-10-22 17:07:16 +08:00
parent c7bbdb3444
commit 483a3b96e7
4 changed files with 62 additions and 40 deletions

View File

@ -13,11 +13,11 @@ import {
Panel
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { v4 as uuidv4 } from 'uuid';
import type { FlowNode, FlowEdge } from '../types';
import { nodeTypes } from '../nodes';
import CustomEdge from './CustomEdge';
import { generateEdgeId } from '../utils/idGenerator';
interface FlowCanvasProps {
initialNodes?: FlowNode[];
@ -51,7 +51,7 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
const onConnect = useCallback(
(params: Connection | Edge) => {
const newEdge: FlowEdge = {
id: uuidv4(), // 使用 UUID 生成唯一 ID
id: generateEdgeId(), // ✅ 生成格式: eid_xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
source: params.source!,
target: params.target!,
type: 'smoothstep',

View File

@ -37,6 +37,11 @@ export const useWorkflowLoad = () => {
// 根据nodeType查找对应的节点定义
const nodeDefinition = NODE_DEFINITIONS.find(def => def.nodeType === node.nodeType);
// ✅ 使用节点定义中的配置,避免硬编码
const icon = nodeDefinition?.renderConfig.icon.content || '❓';
const color = nodeDefinition?.renderConfig.theme.primary || '#6b7280';
const category = nodeDefinition?.category || getNodeCategory(node.nodeType) as any;
return {
id: node.id.toString(),
type: node.nodeType,
@ -44,9 +49,9 @@ export const useWorkflowLoad = () => {
data: {
label: node.nodeName,
nodeType: node.nodeType as any,
category: getNodeCategory(node.nodeType) as any,
icon: nodeDefinition?.renderConfig.icon.content || getNodeIcon(node.nodeType),
color: nodeDefinition?.renderConfig.theme.primary || getNodeColor(node.nodeType),
category,
icon,
color,
configs: node.configs || {},
inputMapping: node.inputMapping || {},
outputs: node.outputs || [], // ✅ 从后端加载的输出能力定义
@ -105,39 +110,8 @@ export const useWorkflowLoad = () => {
};
}, []);
// 根据节点类型获取图标(图标名称格式)
const getNodeIcon = (nodeType: string): string => {
const iconMap: Record<string, string> = {
[NodeType.START_EVENT]: 'play-circle',
[NodeType.END_EVENT]: 'stop-circle',
[NodeType.USER_TASK]: 'user',
[NodeType.SERVICE_TASK]: 'api',
[NodeType.SCRIPT_TASK]: 'code',
[NodeType.DEPLOY_NODE]: 'build',
[NodeType.JENKINS_BUILD]: 'jenkins',
[NodeType.GATEWAY_NODE]: 'gateway',
[NodeType.SUB_PROCESS]: 'container',
[NodeType.CALL_ACTIVITY]: 'phone'
};
return iconMap[nodeType] || 'api';
};
// 根据节点类型获取颜色
const getNodeColor = (nodeType: string): string => {
const colorMap: Record<string, string> = {
[NodeType.START_EVENT]: '#52c41a',
[NodeType.END_EVENT]: '#ff4d4f',
[NodeType.USER_TASK]: '#722ed1',
[NodeType.SERVICE_TASK]: '#fa8c16',
[NodeType.SCRIPT_TASK]: '#8b5cf6',
[NodeType.DEPLOY_NODE]: '#1890ff',
[NodeType.JENKINS_BUILD]: '#52c41a',
[NodeType.GATEWAY_NODE]: '#84cc16',
[NodeType.SUB_PROCESS]: '#ec4899',
[NodeType.CALL_ACTIVITY]: '#14b8a6'
};
return colorMap[nodeType] || '#6b7280';
};
// ✅ 已删除硬编码的 getNodeIcon 和 getNodeColor 函数
// 现在直接从节点定义的 renderConfig 中获取图标和颜色
// 加载工作流定义
const loadWorkflow = useCallback(async (workflowId: number): Promise<LoadedWorkflowData | null> => {

View File

@ -2,7 +2,6 @@ import React, { useState, useCallback, useRef, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { message } from 'antd';
import { ReactFlowProvider, useReactFlow } from '@xyflow/react';
import { v4 as uuidv4 } from 'uuid';
import WorkflowToolbar from './components/WorkflowToolbar';
import NodePanel from './components/NodePanel';
@ -15,6 +14,7 @@ import { isConfigurableNode } from './nodes/types';
import { useWorkflowSave } from './hooks/useWorkflowSave';
import { useWorkflowLoad } from './hooks/useWorkflowLoad';
import { useHistory } from './hooks/useHistory';
import { generateNodeId } from './utils/idGenerator';
// 样式
import '@xyflow/react/dist/style.css';
@ -285,7 +285,7 @@ const WorkflowDesignInner: React.FC = () => {
});
const newNode: FlowNode = {
id: uuidv4(), // 使用 UUID 生成唯一 ID
id: generateNodeId(), // ✅ 生成格式: sid_xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
type: nodeType,
position,
data: {

View File

@ -0,0 +1,48 @@
import { v4 as uuidv4 } from 'uuid';
/**
* ID
* 格式: sid_xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
*
* @returns
*
* @example
* generateNodeId() // => "sid_a1b2c3d4_5e6f_7g8h_9i0j_k1l2m3n4o5p6"
*/
export const generateNodeId = (): string => {
return `sid_${uuidv4().replace(/-/g, '_')}`;
};
/**
* ID
* 格式: eid_xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
*
* @returns
*
* @example
* generateEdgeId() // => "eid_a1b2c3d4_5e6f_7g8h_9i0j_k1l2m3n4o5p6"
*/
export const generateEdgeId = (): string => {
return `eid_${uuidv4().replace(/-/g, '_')}`;
};
/**
* ID
*
* @param id ID
* @returns ID
*/
export const isValidNodeId = (id: string): boolean => {
return /^sid_[a-f0-9_]{36}$/.test(id);
};
/**
* ID
*
* @param id ID
* @returns ID
*/
export const isValidEdgeId = (id: string): boolean => {
return /^eid_[a-f0-9_]{36}$/.test(id);
};