1
This commit is contained in:
parent
c7bbdb3444
commit
483a3b96e7
@ -13,11 +13,11 @@ import {
|
|||||||
Panel
|
Panel
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
import '@xyflow/react/dist/style.css';
|
import '@xyflow/react/dist/style.css';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
import type { FlowNode, FlowEdge } from '../types';
|
import type { FlowNode, FlowEdge } from '../types';
|
||||||
import { nodeTypes } from '../nodes';
|
import { nodeTypes } from '../nodes';
|
||||||
import CustomEdge from './CustomEdge';
|
import CustomEdge from './CustomEdge';
|
||||||
|
import { generateEdgeId } from '../utils/idGenerator';
|
||||||
|
|
||||||
interface FlowCanvasProps {
|
interface FlowCanvasProps {
|
||||||
initialNodes?: FlowNode[];
|
initialNodes?: FlowNode[];
|
||||||
@ -51,7 +51,7 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
|
|||||||
const onConnect = useCallback(
|
const onConnect = useCallback(
|
||||||
(params: Connection | Edge) => {
|
(params: Connection | Edge) => {
|
||||||
const newEdge: FlowEdge = {
|
const newEdge: FlowEdge = {
|
||||||
id: uuidv4(), // 使用 UUID 生成唯一 ID
|
id: generateEdgeId(), // ✅ 生成格式: eid_xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
|
||||||
source: params.source!,
|
source: params.source!,
|
||||||
target: params.target!,
|
target: params.target!,
|
||||||
type: 'smoothstep',
|
type: 'smoothstep',
|
||||||
|
|||||||
@ -37,6 +37,11 @@ export const useWorkflowLoad = () => {
|
|||||||
// 根据nodeType查找对应的节点定义
|
// 根据nodeType查找对应的节点定义
|
||||||
const nodeDefinition = NODE_DEFINITIONS.find(def => def.nodeType === node.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 {
|
return {
|
||||||
id: node.id.toString(),
|
id: node.id.toString(),
|
||||||
type: node.nodeType,
|
type: node.nodeType,
|
||||||
@ -44,9 +49,9 @@ export const useWorkflowLoad = () => {
|
|||||||
data: {
|
data: {
|
||||||
label: node.nodeName,
|
label: node.nodeName,
|
||||||
nodeType: node.nodeType as any,
|
nodeType: node.nodeType as any,
|
||||||
category: getNodeCategory(node.nodeType) as any,
|
category,
|
||||||
icon: nodeDefinition?.renderConfig.icon.content || getNodeIcon(node.nodeType),
|
icon,
|
||||||
color: nodeDefinition?.renderConfig.theme.primary || getNodeColor(node.nodeType),
|
color,
|
||||||
configs: node.configs || {},
|
configs: node.configs || {},
|
||||||
inputMapping: node.inputMapping || {},
|
inputMapping: node.inputMapping || {},
|
||||||
outputs: node.outputs || [], // ✅ 从后端加载的输出能力定义
|
outputs: node.outputs || [], // ✅ 从后端加载的输出能力定义
|
||||||
@ -105,39 +110,8 @@ export const useWorkflowLoad = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 根据节点类型获取图标(图标名称格式)
|
// ✅ 已删除硬编码的 getNodeIcon 和 getNodeColor 函数
|
||||||
const getNodeIcon = (nodeType: string): string => {
|
// 现在直接从节点定义的 renderConfig 中获取图标和颜色
|
||||||
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';
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载工作流定义
|
// 加载工作流定义
|
||||||
const loadWorkflow = useCallback(async (workflowId: number): Promise<LoadedWorkflowData | null> => {
|
const loadWorkflow = useCallback(async (workflowId: number): Promise<LoadedWorkflowData | null> => {
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import React, { useState, useCallback, useRef, useEffect } from 'react';
|
|||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import { ReactFlowProvider, useReactFlow } from '@xyflow/react';
|
import { ReactFlowProvider, useReactFlow } from '@xyflow/react';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
import WorkflowToolbar from './components/WorkflowToolbar';
|
import WorkflowToolbar from './components/WorkflowToolbar';
|
||||||
import NodePanel from './components/NodePanel';
|
import NodePanel from './components/NodePanel';
|
||||||
@ -15,6 +14,7 @@ import { isConfigurableNode } from './nodes/types';
|
|||||||
import { useWorkflowSave } from './hooks/useWorkflowSave';
|
import { useWorkflowSave } from './hooks/useWorkflowSave';
|
||||||
import { useWorkflowLoad } from './hooks/useWorkflowLoad';
|
import { useWorkflowLoad } from './hooks/useWorkflowLoad';
|
||||||
import { useHistory } from './hooks/useHistory';
|
import { useHistory } from './hooks/useHistory';
|
||||||
|
import { generateNodeId } from './utils/idGenerator';
|
||||||
|
|
||||||
// 样式
|
// 样式
|
||||||
import '@xyflow/react/dist/style.css';
|
import '@xyflow/react/dist/style.css';
|
||||||
@ -285,7 +285,7 @@ const WorkflowDesignInner: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const newNode: FlowNode = {
|
const newNode: FlowNode = {
|
||||||
id: uuidv4(), // 使用 UUID 生成唯一 ID
|
id: generateNodeId(), // ✅ 生成格式: sid_xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
|
||||||
type: nodeType,
|
type: nodeType,
|
||||||
position,
|
position,
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
48
frontend/src/pages/Workflow/Design/utils/idGenerator.ts
Normal file
48
frontend/src/pages/Workflow/Design/utils/idGenerator.ts
Normal 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);
|
||||||
|
};
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user