1
This commit is contained in:
parent
3315114522
commit
1cd09a9501
@ -3,8 +3,7 @@ import type { WorkflowDefinition, WorkflowDefinitionQuery, WorkflowCategory } fr
|
|||||||
import type { Page } from '@/types/base';
|
import type { Page } from '@/types/base';
|
||||||
|
|
||||||
// API 基础路径
|
// API 基础路径
|
||||||
const DEFINITION_URL = '/api/v1/workflow-definitions';
|
const DEFINITION_URL = '/api/v1/workflow/definition';
|
||||||
const CATEGORY_URL = '/api/v1/workflow-categories';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取工作流定义列表
|
* 获取工作流定义列表
|
||||||
@ -19,9 +18,15 @@ export const getDefinitionDetail = (id: number) =>
|
|||||||
request.get<WorkflowDefinition>(`${DEFINITION_URL}/${id}`);
|
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);
|
request.post<WorkflowDefinition>(DEFINITION_URL, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,23 +45,28 @@ export const deleteDefinition = (id: number) =>
|
|||||||
* 发布工作流定义
|
* 发布工作流定义
|
||||||
*/
|
*/
|
||||||
export const publishDefinition = (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>) =>
|
export const startWorkflowInstance = (processKey: string, categoryCode: string) =>
|
||||||
request.post<void>(`/api/v1/workflow-instances/start`, {
|
request.post<void>(`/api/v1/workflow/instance/start`, {
|
||||||
definitionKey: key,
|
processKey,
|
||||||
category,
|
businessKey: `${categoryCode}_${Date.now()}`,
|
||||||
variables
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取工作流分类
|
* 获取工作流分类
|
||||||
*/
|
*/
|
||||||
export const getWorkflowCategories = () =>
|
export const getWorkflowCategories = () =>
|
||||||
request.get<WorkflowCategory[]>(CATEGORY_URL);
|
request.get<WorkflowCategory[]>(`${DEFINITION_URL}/categories`);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存工作流图形数据
|
* 保存工作流图形数据
|
||||||
|
|||||||
@ -1,21 +1,26 @@
|
|||||||
import { BaseResponse, BaseQuery } from '@/types/base';
|
import { BaseResponse, BaseQuery } from '@/types/base';
|
||||||
|
|
||||||
// 复用现有的工作流定义类型,保持API兼容性
|
// 复用现有的工作流定义类型,保持API兼容性
|
||||||
export interface WorkflowDefinition extends BaseResponse {
|
export interface WorkflowDefinition {
|
||||||
id: number;
|
id: number;
|
||||||
|
createTime: string | null;
|
||||||
|
createBy: string | null;
|
||||||
|
updateTime: string | null;
|
||||||
|
updateBy: string | null;
|
||||||
|
version: number;
|
||||||
|
deleted: boolean;
|
||||||
|
extraData: any;
|
||||||
name: string;
|
name: string;
|
||||||
key: string;
|
key: string;
|
||||||
description?: string;
|
description: string;
|
||||||
flowVersion?: number;
|
|
||||||
status?: string;
|
|
||||||
category: string;
|
category: string;
|
||||||
triggers: string[];
|
triggers: string[];
|
||||||
|
flowVersion: number;
|
||||||
|
bpmnXml: string | null;
|
||||||
|
status: string;
|
||||||
graph: {
|
graph: {
|
||||||
nodes: WorkflowDefinitionNode[];
|
nodes: WorkflowDefinitionNode[];
|
||||||
edges: any[];
|
edges: WorkflowDefinitionEdge[];
|
||||||
};
|
|
||||||
formConfig: {
|
|
||||||
formItems: any[];
|
|
||||||
};
|
};
|
||||||
formVariablesSchema?: {
|
formVariablesSchema?: {
|
||||||
type: string;
|
type: string;
|
||||||
@ -36,17 +41,31 @@ export interface WorkflowDefinition extends BaseResponse {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
bpmnXml?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkflowDefinitionNode {
|
export interface WorkflowDefinitionNode {
|
||||||
id: number;
|
id: string;
|
||||||
nodeCode: string;
|
nodeCode: string;
|
||||||
nodeType: string;
|
nodeType: string;
|
||||||
nodeName: string;
|
nodeName: string;
|
||||||
uiVariables: JSON;
|
position: {
|
||||||
panelVariables: JSON;
|
x: number;
|
||||||
localVariables: JSON;
|
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 {
|
export interface WorkflowDefinitionQuery extends BaseQuery {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { message } from 'antd';
|
|||||||
import * as definitionService from '../../Definition/service';
|
import * as definitionService from '../../Definition/service';
|
||||||
import type { FlowNode, FlowEdge } from '../types';
|
import type { FlowNode, FlowEdge } from '../types';
|
||||||
import type { WorkflowDefinition } from '../../Definition/types';
|
import type { WorkflowDefinition } from '../../Definition/types';
|
||||||
|
import { NODE_DEFINITIONS } from '../nodes/definitions';
|
||||||
|
|
||||||
interface LoadedWorkflowData {
|
interface LoadedWorkflowData {
|
||||||
nodes: FlowNode[];
|
nodes: FlowNode[];
|
||||||
@ -16,40 +17,39 @@ export const useWorkflowLoad = () => {
|
|||||||
|
|
||||||
// 从后端数据转换为React Flow格式
|
// 从后端数据转换为React Flow格式
|
||||||
const convertToFlowFormat = useCallback((definition: WorkflowDefinition): LoadedWorkflowData => {
|
const convertToFlowFormat = useCallback((definition: WorkflowDefinition): LoadedWorkflowData => {
|
||||||
const nodes: FlowNode[] = (definition.graph?.nodes || []).map(node => ({
|
const nodes: FlowNode[] = (definition.graph?.nodes || []).map(node => {
|
||||||
|
// 根据nodeType查找对应的节点定义
|
||||||
|
const nodeDefinition = NODE_DEFINITIONS.find(def => def.nodeType === node.nodeType);
|
||||||
|
|
||||||
|
return {
|
||||||
id: node.id.toString(),
|
id: node.id.toString(),
|
||||||
type: node.nodeType,
|
type: node.nodeType,
|
||||||
position: node.uiVariables?.position || { x: 100, y: 100 },
|
position: node.position || { x: 100, y: 100 },
|
||||||
data: {
|
data: {
|
||||||
label: node.nodeName,
|
label: node.nodeName,
|
||||||
nodeType: node.nodeType as any,
|
nodeType: node.nodeType as any,
|
||||||
category: 'TASK' as any, // 默认分类,可以根据nodeType推导
|
category: getNodeCategory(node.nodeType) as any,
|
||||||
icon: getNodeIcon(node.nodeType),
|
icon: nodeDefinition?.uiConfig.style.icon || getNodeIcon(node.nodeType),
|
||||||
color: getNodeColor(node.nodeType),
|
color: nodeDefinition?.uiConfig.style.fill || getNodeColor(node.nodeType),
|
||||||
configs: node.panelVariables || {},
|
configs: node.configs || {},
|
||||||
inputMapping: node.localVariables?.inputMapping || {},
|
inputMapping: node.inputMapping || {},
|
||||||
outputMapping: node.localVariables?.outputMapping || {},
|
outputMapping: node.outputMapping || {},
|
||||||
nodeDefinition: {
|
// 添加节点定义引用,用于配置弹窗
|
||||||
nodeCode: node.nodeCode,
|
nodeDefinition
|
||||||
nodeName: node.nodeName,
|
|
||||||
nodeType: node.nodeType as any,
|
|
||||||
category: 'TASK' as any,
|
|
||||||
icon: getNodeIcon(node.nodeType),
|
|
||||||
color: getNodeColor(node.nodeType)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
selected: node.uiVariables?.selected || false,
|
selected: false,
|
||||||
dragging: node.uiVariables?.dragging || false
|
dragging: false
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const edges: FlowEdge[] = (definition.graph?.edges || []).map(edge => ({
|
const edges: FlowEdge[] = (definition.graph?.edges || []).map(edge => ({
|
||||||
id: edge.id,
|
id: edge.id,
|
||||||
source: edge.source,
|
source: edge.from, // 后端使用from字段作为source
|
||||||
target: edge.target,
|
target: edge.to, // 后端使用to字段作为target
|
||||||
type: edge.type || 'default',
|
type: 'default',
|
||||||
animated: edge.animated || true,
|
animated: true,
|
||||||
data: edge.data || {
|
data: {
|
||||||
label: '连接',
|
label: edge.name || '连接',
|
||||||
condition: {
|
condition: {
|
||||||
type: 'DEFAULT',
|
type: 'DEFAULT',
|
||||||
priority: 0
|
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 getNodeIcon = (nodeType: string): string => {
|
||||||
const iconMap: Record<string, string> = {
|
const iconMap: Record<string, string> = {
|
||||||
'START_EVENT': '▶️',
|
'START_EVENT': 'play-circle',
|
||||||
'END_EVENT': '⏹️',
|
'END_EVENT': 'stop-circle',
|
||||||
'USER_TASK': '👤',
|
'USER_TASK': 'user',
|
||||||
'SERVICE_TASK': '⚙️',
|
'SERVICE_TASK': 'api',
|
||||||
'SCRIPT_TASK': '📜',
|
'SCRIPT_TASK': 'code',
|
||||||
'DEPLOY_NODE': '🚀',
|
'DEPLOY_NODE': 'build',
|
||||||
'JENKINS_BUILD': '🔨',
|
'JENKINS_BUILD': 'jenkins',
|
||||||
'GATEWAY_NODE': '◇',
|
'GATEWAY_NODE': 'gateway',
|
||||||
'SUB_PROCESS': '📦',
|
'SUB_PROCESS': 'container',
|
||||||
'CALL_ACTIVITY': '📞'
|
'CALL_ACTIVITY': 'phone'
|
||||||
};
|
};
|
||||||
return iconMap[nodeType] || '📋';
|
return iconMap[nodeType] || 'api';
|
||||||
};
|
};
|
||||||
|
|
||||||
// 根据节点类型获取颜色
|
// 根据节点类型获取颜色
|
||||||
const getNodeColor = (nodeType: string): string => {
|
const getNodeColor = (nodeType: string): string => {
|
||||||
const colorMap: Record<string, string> = {
|
const colorMap: Record<string, string> = {
|
||||||
'START_EVENT': '#10b981',
|
'START_EVENT': '#52c41a',
|
||||||
'END_EVENT': '#ef4444',
|
'END_EVENT': '#ff4d4f',
|
||||||
'USER_TASK': '#6366f1',
|
'USER_TASK': '#722ed1',
|
||||||
'SERVICE_TASK': '#f59e0b',
|
'SERVICE_TASK': '#fa8c16',
|
||||||
'SCRIPT_TASK': '#8b5cf6',
|
'SCRIPT_TASK': '#8b5cf6',
|
||||||
'DEPLOY_NODE': '#06b6d4',
|
'DEPLOY_NODE': '#1890ff',
|
||||||
'JENKINS_BUILD': '#f97316',
|
'JENKINS_BUILD': '#f97316',
|
||||||
'GATEWAY_NODE': '#84cc16',
|
'GATEWAY_NODE': '#84cc16',
|
||||||
'SUB_PROCESS': '#ec4899',
|
'SUB_PROCESS': '#ec4899',
|
||||||
@ -123,18 +140,24 @@ export const useWorkflowLoad = () => {
|
|||||||
const createNewWorkflow = useCallback((): LoadedWorkflowData => {
|
const createNewWorkflow = useCallback((): LoadedWorkflowData => {
|
||||||
const emptyDefinition: WorkflowDefinition = {
|
const emptyDefinition: WorkflowDefinition = {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
createTime: null,
|
||||||
|
createBy: null,
|
||||||
|
updateTime: null,
|
||||||
|
updateBy: null,
|
||||||
|
version: 1,
|
||||||
|
deleted: false,
|
||||||
|
extraData: null,
|
||||||
name: '新建工作流',
|
name: '新建工作流',
|
||||||
key: `workflow_${Date.now()}`,
|
key: `workflow_${Date.now()}`,
|
||||||
description: '',
|
description: '',
|
||||||
category: 'GENERAL',
|
category: 'GENERAL',
|
||||||
triggers: ['MANUAL'],
|
triggers: [],
|
||||||
|
flowVersion: 1,
|
||||||
|
bpmnXml: null,
|
||||||
status: 'DRAFT',
|
status: 'DRAFT',
|
||||||
graph: {
|
graph: {
|
||||||
nodes: [],
|
nodes: [],
|
||||||
edges: []
|
edges: []
|
||||||
},
|
|
||||||
formConfig: {
|
|
||||||
formItems: []
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { useCallback, useState } from 'react';
|
|||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import * as definitionService from '../../Definition/service';
|
import * as definitionService from '../../Definition/service';
|
||||||
import type { FlowNode, FlowEdge } from '../types';
|
import type { FlowNode, FlowEdge } from '../types';
|
||||||
import type { WorkflowDefinition } from '../../Definition/types';
|
|
||||||
|
|
||||||
interface WorkflowSaveData {
|
interface WorkflowSaveData {
|
||||||
nodes: FlowNode[];
|
nodes: FlowNode[];
|
||||||
@ -10,6 +9,7 @@ interface WorkflowSaveData {
|
|||||||
workflowId?: number;
|
workflowId?: number;
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
definitionData?: any; // 原始工作流定义数据
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useWorkflowSave = () => {
|
export const useWorkflowSave = () => {
|
||||||
@ -25,57 +25,53 @@ export const useWorkflowSave = () => {
|
|||||||
// 转换节点和边数据为后端格式
|
// 转换节点和边数据为后端格式
|
||||||
const graph = {
|
const graph = {
|
||||||
nodes: data.nodes.map(node => ({
|
nodes: data.nodes.map(node => ({
|
||||||
id: parseInt(node.id) || 0,
|
id: node.id, // 使用React Flow的节点ID
|
||||||
nodeCode: `node_${node.id}`,
|
nodeCode: node.id, // nodeCode与ID相同
|
||||||
nodeType: node.data.nodeType,
|
nodeType: node.data.nodeType,
|
||||||
nodeName: node.data.label,
|
nodeName: node.data.label,
|
||||||
uiVariables: {
|
position: {
|
||||||
position: node.position,
|
x: Math.round(node.position.x),
|
||||||
selected: node.selected || false,
|
y: Math.round(node.position.y)
|
||||||
dragging: node.dragging || false
|
|
||||||
},
|
},
|
||||||
panelVariables: node.data.configs || {},
|
configs: node.data.configs || {},
|
||||||
localVariables: {
|
|
||||||
inputMapping: node.data.inputMapping || {},
|
inputMapping: node.data.inputMapping || {},
|
||||||
outputMapping: node.data.outputMapping || {}
|
outputMapping: node.data.outputMapping || {}
|
||||||
}
|
|
||||||
})),
|
})),
|
||||||
edges: data.edges.map(edge => ({
|
edges: data.edges.map(edge => ({
|
||||||
id: edge.id,
|
id: edge.id,
|
||||||
source: edge.source,
|
from: edge.source, // 后端使用from字段
|
||||||
target: edge.target,
|
to: edge.target, // 后端使用to字段
|
||||||
type: edge.type || 'default',
|
name: edge.data?.label || "", // 边的名称
|
||||||
animated: edge.animated || false,
|
config: {
|
||||||
data: edge.data || {}
|
type: "sequence" // 固定为sequence类型
|
||||||
|
},
|
||||||
|
vertices: [] // 暂时为空数组
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
const workflowData: Partial<WorkflowDefinition> = {
|
// 构建保存数据 - 完全模仿原始系统的逻辑
|
||||||
name: data.name || '未命名工作流',
|
const workflowData = data.definitionData
|
||||||
description: data.description || '',
|
? {
|
||||||
graph,
|
// 如果有原始定义数据,展开它并覆盖 graph
|
||||||
// 生成简单的表单配置
|
...data.definitionData,
|
||||||
formConfig: {
|
graph
|
||||||
formItems: []
|
|
||||||
}
|
}
|
||||||
};
|
: {
|
||||||
|
// 如果没有原始定义数据,创建新的工作流
|
||||||
let result: WorkflowDefinition;
|
name: data.name || '新建工作流',
|
||||||
|
description: data.description || '',
|
||||||
if (data.workflowId) {
|
|
||||||
// 更新现有工作流
|
|
||||||
result = await definitionService.updateDefinition(data.workflowId, workflowData);
|
|
||||||
message.success('工作流保存成功');
|
|
||||||
} else {
|
|
||||||
// 创建新工作流
|
|
||||||
result = await definitionService.createDefinition({
|
|
||||||
...workflowData,
|
|
||||||
key: `workflow_${Date.now()}`,
|
key: `workflow_${Date.now()}`,
|
||||||
category: 'GENERAL',
|
category: 'GENERAL',
|
||||||
triggers: ['MANUAL']
|
triggers: [],
|
||||||
} as any);
|
flowVersion: 1,
|
||||||
message.success('工作流创建成功');
|
bpmnXml: null,
|
||||||
}
|
status: 'DRAFT',
|
||||||
|
graph
|
||||||
|
};
|
||||||
|
|
||||||
|
// 无论新建还是更新,都调用 saveDefinition (POST 请求)
|
||||||
|
await definitionService.saveDefinition(workflowData as any);
|
||||||
|
message.success('工作流保存成功');
|
||||||
|
|
||||||
setLastSaved(new Date());
|
setLastSaved(new Date());
|
||||||
setHasUnsavedChanges(false);
|
setHasUnsavedChanges(false);
|
||||||
|
|||||||
@ -158,7 +158,8 @@ const WorkflowDesignInner: React.FC = () => {
|
|||||||
edges,
|
edges,
|
||||||
workflowId: currentWorkflowId,
|
workflowId: currentWorkflowId,
|
||||||
name: workflowTitle,
|
name: workflowTitle,
|
||||||
description: workflowDefinition?.description || ''
|
description: workflowDefinition?.description || '',
|
||||||
|
definitionData: workflowDefinition // 传递原始定义数据
|
||||||
});
|
});
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user