This commit is contained in:
dengqichen 2025-10-20 22:45:43 +08:00
parent 3315114522
commit 1cd09a9501
5 changed files with 165 additions and 116 deletions

View File

@ -3,8 +3,7 @@ import type { WorkflowDefinition, WorkflowDefinitionQuery, WorkflowCategory } fr
import type { Page } from '@/types/base';
// API 基础路径
const DEFINITION_URL = '/api/v1/workflow-definitions';
const CATEGORY_URL = '/api/v1/workflow-categories';
const DEFINITION_URL = '/api/v1/workflow/definition';
/**
*
@ -19,9 +18,15 @@ export const getDefinitionDetail = (id: number) =>
request.get<WorkflowDefinition>(`${DEFINITION_URL}/${id}`);
/**
*
*
*/
export const createDefinition = (data: Partial<WorkflowDefinition>) =>
export const getPublishedDefinitions = () =>
request.get<WorkflowDefinition[]>(`${DEFINITION_URL}/published`);
/**
*
*/
export const saveDefinition = (data: WorkflowDefinition) =>
request.post<WorkflowDefinition>(DEFINITION_URL, data);
/**
@ -40,23 +45,28 @@ export const deleteDefinition = (id: number) =>
*
*/
export const publishDefinition = (id: number) =>
request.post<void>(`${DEFINITION_URL}/${id}/publish`);
request.post<void>(`${DEFINITION_URL}/${id}/published`);
/**
*
*/
export const deployDefinition = (id: number) =>
request.post<void>(`${DEFINITION_URL}/${id}/deploy`);
/**
*
*/
export const startWorkflowInstance = (key: string, category: string, variables?: Record<string, any>) =>
request.post<void>(`/api/v1/workflow-instances/start`, {
definitionKey: key,
category,
variables
export const startWorkflowInstance = (processKey: string, categoryCode: string) =>
request.post<void>(`/api/v1/workflow/instance/start`, {
processKey,
businessKey: `${categoryCode}_${Date.now()}`,
});
/**
*
*/
export const getWorkflowCategories = () =>
request.get<WorkflowCategory[]>(CATEGORY_URL);
request.get<WorkflowCategory[]>(`${DEFINITION_URL}/categories`);
/**
*

View File

@ -1,21 +1,26 @@
import { BaseResponse, BaseQuery } from '@/types/base';
// 复用现有的工作流定义类型保持API兼容性
export interface WorkflowDefinition extends BaseResponse {
export interface WorkflowDefinition {
id: number;
createTime: string | null;
createBy: string | null;
updateTime: string | null;
updateBy: string | null;
version: number;
deleted: boolean;
extraData: any;
name: string;
key: string;
description?: string;
flowVersion?: number;
status?: string;
description: string;
category: string;
triggers: string[];
flowVersion: number;
bpmnXml: string | null;
status: string;
graph: {
nodes: WorkflowDefinitionNode[];
edges: any[];
};
formConfig: {
formItems: any[];
edges: WorkflowDefinitionEdge[];
};
formVariablesSchema?: {
type: string;
@ -36,17 +41,31 @@ export interface WorkflowDefinition extends BaseResponse {
};
};
};
bpmnXml?: string;
}
export interface WorkflowDefinitionNode {
id: number;
id: string;
nodeCode: string;
nodeType: string;
nodeName: string;
uiVariables: JSON;
panelVariables: JSON;
localVariables: JSON;
position: {
x: number;
y: number;
};
configs: Record<string, any>;
inputMapping: Record<string, any>;
outputMapping: Record<string, any>;
}
export interface WorkflowDefinitionEdge {
id: string;
from: string;
to: string;
name: string;
config: {
type: string;
};
vertices: any[];
}
export interface WorkflowDefinitionQuery extends BaseQuery {

View File

@ -3,6 +3,7 @@ import { message } from 'antd';
import * as definitionService from '../../Definition/service';
import type { FlowNode, FlowEdge } from '../types';
import type { WorkflowDefinition } from '../../Definition/types';
import { NODE_DEFINITIONS } from '../nodes/definitions';
interface LoadedWorkflowData {
nodes: FlowNode[];
@ -16,40 +17,39 @@ export const useWorkflowLoad = () => {
// 从后端数据转换为React Flow格式
const convertToFlowFormat = useCallback((definition: WorkflowDefinition): LoadedWorkflowData => {
const nodes: FlowNode[] = (definition.graph?.nodes || []).map(node => ({
id: node.id.toString(),
type: node.nodeType,
position: node.uiVariables?.position || { x: 100, y: 100 },
data: {
label: node.nodeName,
nodeType: node.nodeType as any,
category: 'TASK' as any, // 默认分类可以根据nodeType推导
icon: getNodeIcon(node.nodeType),
color: getNodeColor(node.nodeType),
configs: node.panelVariables || {},
inputMapping: node.localVariables?.inputMapping || {},
outputMapping: node.localVariables?.outputMapping || {},
nodeDefinition: {
nodeCode: node.nodeCode,
nodeName: node.nodeName,
const nodes: FlowNode[] = (definition.graph?.nodes || []).map(node => {
// 根据nodeType查找对应的节点定义
const nodeDefinition = NODE_DEFINITIONS.find(def => def.nodeType === node.nodeType);
return {
id: node.id.toString(),
type: node.nodeType,
position: node.position || { x: 100, y: 100 },
data: {
label: node.nodeName,
nodeType: node.nodeType as any,
category: 'TASK' as any,
icon: getNodeIcon(node.nodeType),
color: getNodeColor(node.nodeType)
}
},
selected: node.uiVariables?.selected || false,
dragging: node.uiVariables?.dragging || false
}));
category: getNodeCategory(node.nodeType) as any,
icon: nodeDefinition?.uiConfig.style.icon || getNodeIcon(node.nodeType),
color: nodeDefinition?.uiConfig.style.fill || getNodeColor(node.nodeType),
configs: node.configs || {},
inputMapping: node.inputMapping || {},
outputMapping: node.outputMapping || {},
// 添加节点定义引用,用于配置弹窗
nodeDefinition
},
selected: false,
dragging: false
};
});
const edges: FlowEdge[] = (definition.graph?.edges || []).map(edge => ({
id: edge.id,
source: edge.source,
target: edge.target,
type: edge.type || 'default',
animated: edge.animated || true,
data: edge.data || {
label: '连接',
source: edge.from, // 后端使用from字段作为source
target: edge.to, // 后端使用to字段作为target
type: 'default',
animated: true,
data: {
label: edge.name || '连接',
condition: {
type: 'DEFAULT',
priority: 0
@ -64,32 +64,49 @@ export const useWorkflowLoad = () => {
};
}, []);
// 根据节点类型获取图标
// 根据节点类型获取分类
const getNodeCategory = (nodeType: string): string => {
const categoryMap: Record<string, string> = {
'START_EVENT': 'EVENT',
'END_EVENT': 'EVENT',
'USER_TASK': 'TASK',
'SERVICE_TASK': 'TASK',
'SCRIPT_TASK': 'TASK',
'DEPLOY_NODE': 'TASK',
'JENKINS_BUILD': 'TASK',
'GATEWAY_NODE': 'GATEWAY',
'SUB_PROCESS': 'CONTAINER',
'CALL_ACTIVITY': 'CONTAINER'
};
return categoryMap[nodeType] || 'TASK';
};
// 根据节点类型获取图标(图标名称格式)
const getNodeIcon = (nodeType: string): string => {
const iconMap: Record<string, string> = {
'START_EVENT': '▶️',
'END_EVENT': '⏹️',
'USER_TASK': '👤',
'SERVICE_TASK': '⚙️',
'SCRIPT_TASK': '📜',
'DEPLOY_NODE': '🚀',
'JENKINS_BUILD': '🔨',
'GATEWAY_NODE': '◇',
'SUB_PROCESS': '📦',
'CALL_ACTIVITY': '📞'
'START_EVENT': 'play-circle',
'END_EVENT': 'stop-circle',
'USER_TASK': 'user',
'SERVICE_TASK': 'api',
'SCRIPT_TASK': 'code',
'DEPLOY_NODE': 'build',
'JENKINS_BUILD': 'jenkins',
'GATEWAY_NODE': 'gateway',
'SUB_PROCESS': 'container',
'CALL_ACTIVITY': 'phone'
};
return iconMap[nodeType] || '📋';
return iconMap[nodeType] || 'api';
};
// 根据节点类型获取颜色
const getNodeColor = (nodeType: string): string => {
const colorMap: Record<string, string> = {
'START_EVENT': '#10b981',
'END_EVENT': '#ef4444',
'USER_TASK': '#6366f1',
'SERVICE_TASK': '#f59e0b',
'START_EVENT': '#52c41a',
'END_EVENT': '#ff4d4f',
'USER_TASK': '#722ed1',
'SERVICE_TASK': '#fa8c16',
'SCRIPT_TASK': '#8b5cf6',
'DEPLOY_NODE': '#06b6d4',
'DEPLOY_NODE': '#1890ff',
'JENKINS_BUILD': '#f97316',
'GATEWAY_NODE': '#84cc16',
'SUB_PROCESS': '#ec4899',
@ -123,18 +140,24 @@ export const useWorkflowLoad = () => {
const createNewWorkflow = useCallback((): LoadedWorkflowData => {
const emptyDefinition: WorkflowDefinition = {
id: 0,
createTime: null,
createBy: null,
updateTime: null,
updateBy: null,
version: 1,
deleted: false,
extraData: null,
name: '新建工作流',
key: `workflow_${Date.now()}`,
description: '',
category: 'GENERAL',
triggers: ['MANUAL'],
triggers: [],
flowVersion: 1,
bpmnXml: null,
status: 'DRAFT',
graph: {
nodes: [],
edges: []
},
formConfig: {
formItems: []
}
};

View File

@ -2,7 +2,6 @@ import { useCallback, useState } from 'react';
import { message } from 'antd';
import * as definitionService from '../../Definition/service';
import type { FlowNode, FlowEdge } from '../types';
import type { WorkflowDefinition } from '../../Definition/types';
interface WorkflowSaveData {
nodes: FlowNode[];
@ -10,6 +9,7 @@ interface WorkflowSaveData {
workflowId?: number;
name?: string;
description?: string;
definitionData?: any; // 原始工作流定义数据
}
export const useWorkflowSave = () => {
@ -25,57 +25,53 @@ export const useWorkflowSave = () => {
// 转换节点和边数据为后端格式
const graph = {
nodes: data.nodes.map(node => ({
id: parseInt(node.id) || 0,
nodeCode: `node_${node.id}`,
id: node.id, // 使用React Flow的节点ID
nodeCode: node.id, // nodeCode与ID相同
nodeType: node.data.nodeType,
nodeName: node.data.label,
uiVariables: {
position: node.position,
selected: node.selected || false,
dragging: node.dragging || false
position: {
x: Math.round(node.position.x),
y: Math.round(node.position.y)
},
panelVariables: node.data.configs || {},
localVariables: {
inputMapping: node.data.inputMapping || {},
outputMapping: node.data.outputMapping || {}
}
configs: node.data.configs || {},
inputMapping: node.data.inputMapping || {},
outputMapping: node.data.outputMapping || {}
})),
edges: data.edges.map(edge => ({
id: edge.id,
source: edge.source,
target: edge.target,
type: edge.type || 'default',
animated: edge.animated || false,
data: edge.data || {}
from: edge.source, // 后端使用from字段
to: edge.target, // 后端使用to字段
name: edge.data?.label || "", // 边的名称
config: {
type: "sequence" // 固定为sequence类型
},
vertices: [] // 暂时为空数组
}))
};
const workflowData: Partial<WorkflowDefinition> = {
name: data.name || '未命名工作流',
description: data.description || '',
graph,
// 生成简单的表单配置
formConfig: {
formItems: []
// 构建保存数据 - 完全模仿原始系统的逻辑
const workflowData = data.definitionData
? {
// 如果有原始定义数据,展开它并覆盖 graph
...data.definitionData,
graph
}
};
let result: WorkflowDefinition;
if (data.workflowId) {
// 更新现有工作流
result = await definitionService.updateDefinition(data.workflowId, workflowData);
message.success('工作流保存成功');
} else {
// 创建新工作流
result = await definitionService.createDefinition({
...workflowData,
: {
// 如果没有原始定义数据,创建新的工作流
name: data.name || '新建工作流',
description: data.description || '',
key: `workflow_${Date.now()}`,
category: 'GENERAL',
triggers: ['MANUAL']
} as any);
message.success('工作流创建成功');
}
triggers: [],
flowVersion: 1,
bpmnXml: null,
status: 'DRAFT',
graph
};
// 无论新建还是更新,都调用 saveDefinition (POST 请求)
await definitionService.saveDefinition(workflowData as any);
message.success('工作流保存成功');
setLastSaved(new Date());
setHasUnsavedChanges(false);

View File

@ -158,7 +158,8 @@ const WorkflowDesignInner: React.FC = () => {
edges,
workflowId: currentWorkflowId,
name: workflowTitle,
description: workflowDefinition?.description || ''
description: workflowDefinition?.description || '',
definitionData: workflowDefinition // 传递原始定义数据
});
if (success) {