From f8b1019625f34ea373edcdade70c41780935fe12 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Thu, 12 Dec 2024 14:25:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B7=A5=E5=85=B7=E6=A0=8F?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.windsurfrules | 3 + .../Designer/components/NodePanel/index.less | 103 ++++----- .../Designer/components/NodePanel/index.tsx | 154 ++++++++----- .../Designer/components/NodePanel/types.ts | 98 ++++++++ .../Definition/Designer/configs/nodeConfig.ts | 99 -------- .../Workflow/Definition/Designer/index.tsx | 31 ++- .../Workflow/Definition/Designer/service.ts | 61 ----- .../Workflow/Definition/Designer/types.ts | 134 ++++++++--- .../Definition/Designer/types/base.ts | 63 ----- .../Definition/Designer/types/edge.ts | 42 ---- .../Definition/Designer/types/graph.ts | 97 -------- .../Definition/Designer/types/index.ts | 4 - .../Definition/Designer/types/node.ts | 90 -------- .../Definition/Designer/types/workflow.ts | 44 ---- .../Designer/utils/loadGraphData.ts | 71 ------ .../Workflow/Definition/Designer/validate.ts | 64 ++++-- .../pages/Workflow/Definition/constants.ts | 17 -- .../src/pages/Workflow/Definition/index.tsx | 2 +- .../src/pages/Workflow/Definition/service.ts | 129 ----------- .../src/pages/Workflow/Definition/types.ts | 216 +++++++++++++++++- frontend/src/pages/Workflow/types.ts | 171 ++------------ 21 files changed, 650 insertions(+), 1043 deletions(-) create mode 100644 frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/types.ts delete mode 100644 frontend/src/pages/Workflow/Definition/Designer/configs/nodeConfig.ts delete mode 100644 frontend/src/pages/Workflow/Definition/Designer/types/base.ts delete mode 100644 frontend/src/pages/Workflow/Definition/Designer/types/edge.ts delete mode 100644 frontend/src/pages/Workflow/Definition/Designer/types/graph.ts delete mode 100644 frontend/src/pages/Workflow/Definition/Designer/types/index.ts delete mode 100644 frontend/src/pages/Workflow/Definition/Designer/types/node.ts delete mode 100644 frontend/src/pages/Workflow/Definition/Designer/types/workflow.ts delete mode 100644 frontend/src/pages/Workflow/Definition/Designer/utils/loadGraphData.ts delete mode 100644 frontend/src/pages/Workflow/Definition/constants.ts diff --git a/frontend/.windsurfrules b/frontend/.windsurfrules index a241965e..4c9ca183 100644 --- a/frontend/.windsurfrules +++ b/frontend/.windsurfrules @@ -15,4 +15,7 @@ - 一次将所有修改应用于单个文件 - 请勿修改不相关的文件 +4. 代码注释: +- 尽量都编写代码注释,写明实现的方式 + 记住要始终考虑每个项目的背景和特定需求。 \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/index.less b/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/index.less index 178d6615..e5545a38 100644 --- a/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/index.less +++ b/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/index.less @@ -1,27 +1,21 @@ .node-panel { height: 100%; border-right: 1px solid #e8e8e8; + background-color: #fff; - &-loading { - display: flex; - justify-content: center; - align-items: center; + &-tabs { height: 100%; - } - - :global { - .ant-tabs { - height: 100%; - + + :global { .ant-tabs-nav { margin: 0; - padding: 0 16px; + padding: 0 12px; background-color: #fafafa; border-bottom: 1px solid #f0f0f0; .ant-tabs-tab { - padding: 8px 16px !important; - margin: 0 !important; + padding: 8px 12px; + margin: 0 4px !important; transition: all 0.3s; border-radius: 4px 4px 0 0; @@ -32,6 +26,7 @@ &.ant-tabs-tab-active { background: #fff; + border-bottom-color: #fff; .ant-tabs-tab-btn { color: #1890ff; @@ -41,70 +36,70 @@ } } - .ant-tabs-content-holder { - flex: 1; + .ant-tabs-content { + height: calc(100% - 44px); + padding: 12px; overflow: auto; - - .ant-tabs-content { - height: calc(100% - 44px); - } } } } +} - .node-tabs { - height: 100%; - - :global(.ant-tabs-content) { - height: 100%; - } - } - - .node-panel-content { - padding: 8px; - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 8px; - height: 100%; - overflow-y: auto; - } - - .node-card { +.node-panel-content { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 16px; + padding: 8px; + + .node-item { + position: relative; display: flex; flex-direction: column; align-items: center; - padding: 12px; - border: 1px solid #e8e8e8; + justify-content: center; + min-height: 90px; + padding: 16px; border-radius: 4px; cursor: move; transition: all 0.3s; - background: #fff; - + user-select: none; + &:hover { - border-color: #1890ff; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + box-shadow: 0 2px 8px rgba(24, 144, 255, 0.15); + transform: translateY(-1px); + } + + &:active { + transform: translateY(0); } .node-icon { + display: flex; + align-items: center; + justify-content: center; margin-bottom: 8px; - color: #5F95FF; - + font-size: 24px; + line-height: 1; + .anticon { - font-size: 32px; + display: block; } } - + .node-name { + margin-bottom: 4px; + color: #262626; font-size: 14px; font-weight: 500; - color: #333; - margin-bottom: 4px; - } - - .node-desc { - font-size: 12px; - color: #666; text-align: center; + line-height: 1.4; + } + + .node-desc { + color: #8c8c8c; + font-size: 12px; + text-align: center; + line-height: 1.4; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; diff --git a/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/index.tsx b/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/index.tsx index 38f8b264..9f66ce60 100644 --- a/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/index.tsx +++ b/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/index.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import { Tabs, Spin } from 'antd'; -import { NodeType, NodeCategory } from '../../../../types'; +import * as Icons from '@ant-design/icons'; +import { NodeType, NodeCategory } from './types'; import { getNodeTypes } from '../../../../service'; import './index.less'; @@ -17,9 +18,22 @@ const NodePanel: React.FC = ({ onNodeDragStart }) => { setLoading(true); try { const response = await getNodeTypes({ enabled: true }); - console.log('节点类型列表:', response); + console.log('节点类型原始数据:', response); if (response?.content) { - setNodeTypes(response.content); + // 处理每个节点的样式 + const processedTypes = response.content.map(node => ({ + ...node, + // 确保graphConfig中的style存在 + style: node.graphConfig?.style || { + fill: '#ffffff', + stroke: '#1890ff', + strokeWidth: 2, + icon: 'api', + iconColor: '#1890ff' + } + })); + console.log('处理后的节点数据:', processedTypes); + setNodeTypes(processedTypes); } } catch (error) { console.error('获取节点类型失败:', error); @@ -31,15 +45,64 @@ const NodePanel: React.FC = ({ onNodeDragStart }) => { fetchNodeTypes(); }, []); + // 获取图标组件 + const getIcon = (iconName: string) => { + if (!iconName) return null; + + // 转换图标名称为大驼峰格式 + const formatIconName = (name: string) => { + return name + .split('-') + .map(part => part.charAt(0).toUpperCase() + part.slice(1)) + .join(''); + }; + + // 尝试多种图标命名方式 + const iconVariants = [ + formatIconName(iconName), + `${formatIconName(iconName)}Outlined`, + `${formatIconName(iconName)}Filled`, + `${formatIconName(iconName)}TwoTone` + ]; + + console.log('尝试加载图标:', iconName, iconVariants); + + for (const variant of iconVariants) { + const IconComponent = (Icons as any)[variant]; + if (IconComponent) { + console.log('成功找到图标组件:', variant); + return React.createElement(IconComponent); + } + } + console.warn('未找到图标组件:', iconName, iconVariants); + return null; + }; + // 按类别分组节点类型 - const groupedNodeTypes = Array.isArray(nodeTypes) ? nodeTypes.reduce((acc, nodeType) => { - const category = nodeType.category; + const groupedNodeTypes = nodeTypes.reduce((acc, nodeType) => { + const category = nodeType.category || NodeCategory.TASK; if (!acc[category]) { acc[category] = []; } acc[category].push(nodeType); return acc; - }, {} as Record) : {}; + }, {} as Record); + + // 获取类别标签 + const getCategoryLabel = (category: string): string => { + switch (category) { + case NodeCategory.EVENT: + return '事件节点'; + case NodeCategory.TASK: + return '任务节点'; + case NodeCategory.GATEWAY: + return '网关节点'; + case NodeCategory.CONTAINER: + return '容器节点'; + default: + return '其他节点'; + } + }; // 将分组转换为 Tabs 项 const tabItems = Object.entries(groupedNodeTypes).map(([category, types]) => ({ @@ -48,36 +111,41 @@ const NodePanel: React.FC = ({ onNodeDragStart }) => { children: (
{types.map((nodeType) => { - const graphConfig = typeof nodeType.graphConfig === 'string' - ? JSON.parse(nodeType.graphConfig) - : nodeType.graphConfig; - + // 从graphConfig中获取样式 + const style = nodeType.graphConfig?.style || nodeType.style || {}; + console.log('节点样式:', nodeType.type, style); + const icon = getIcon(style.icon); + + const nodeStyle = { + backgroundColor: style.fill || '#ffffff', + borderColor: style.stroke || '#d9d9d9', + borderWidth: `${style.strokeWidth || 1}px`, + borderStyle: 'solid' + }; + + console.log('应用的样式:', nodeType.type, nodeStyle); + return (
{ - e.dataTransfer.setData('node-type', JSON.stringify({ - ...nodeType, - graphConfig - })); + e.dataTransfer.effectAllowed = 'move'; onNodeDragStart(nodeType); }} + style={nodeStyle} > -
- {graphConfig.properties?.shape?.const === 'serviceTask' && ( - - - - - - - - )} -
-
{nodeType.name}
-
{nodeType.description}
+ {icon && ( + + {icon} + + )} + {nodeType.name} + {nodeType.description}
); })} @@ -85,33 +153,17 @@ const NodePanel: React.FC = ({ onNodeDragStart }) => { ), })); - if (loading) { - return ( -
- -
- ); - } - return (
- + + +
); }; -// 获取类别标签 -function getCategoryLabel(category: string): string { - const categoryMap: Record = { - [NodeCategory.TASK]: '任务节点', - [NodeCategory.EVENT]: '事件节点', - [NodeCategory.GATEWAY]: '网关节点' - }; - return categoryMap[category] || category; -} - export default NodePanel; \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/types.ts b/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/types.ts new file mode 100644 index 00000000..b0dc0296 --- /dev/null +++ b/frontend/src/pages/Workflow/Definition/Designer/components/NodePanel/types.ts @@ -0,0 +1,98 @@ +import { BaseResponse } from '@/types/base/response'; +import { NodeGraph } from '../../../types'; + +/** + * 节点样式 + */ +export interface NodeStyle { + fill: string; // 填充颜色 + stroke: string; // 边框颜色 + strokeWidth: number; // 边框宽度 + icon: string; // 图标名称 + iconColor: string; // 图标颜色 +} + +/** + * 节点类别 + */ +export enum NodeCategory { + EVENT = 'EVENT', // 事件节点 + TASK = 'TASK', // 任务节点 + GATEWAY = 'GATEWAY', // 网关节点 + CONTAINER = 'CONTAINER' // 容器节点 +} + +/** + * 节点类型 + */ +export enum NodeTypeEnum { + START_EVENT = 'START_EVENT', // 开始节点 + END_EVENT = 'END_EVENT', // 结束节点 + USER_TASK = 'USER_TASK', // 用户任务 + SERVICE_TASK = 'SERVICE_TASK', // 服务任务 + SCRIPT_TASK = 'SCRIPT_TASK', // 脚本任务 + EXCLUSIVE_GATEWAY = 'EXCLUSIVE_GATEWAY', // 排他网关 + PARALLEL_GATEWAY = 'PARALLEL_GATEWAY', // 并行网关 + SUB_PROCESS = 'SUB_PROCESS', // 子流程 + CALL_ACTIVITY = 'CALL_ACTIVITY' // 调用活动 +} + +/** + * 节点详情 + */ +export interface NodeDetails { + description: string; // 详细描述 + features: string[]; // 功能特性 + scenarios: string[]; // 使用场景 +} + +/** + * JSON Schema属性 + */ +export interface JsonSchemaProperty { + type: string; + title: string; + description: string; + format?: string; + default?: any; + enum?: string[]; + enumNames?: string[]; +} + +/** + * 节点配置Schema + */ +export interface NodeConfigSchema { + type: string; + properties: Record; + required: string[]; +} + +/** + * 节点图形配置 + */ +export interface NodeGraphConfig { + code: string; // 节点代码 + name: string; // 节点名称 + description: string; // 节点描述 + details: NodeDetails; // 节点详情 + configSchema: NodeConfigSchema; // 配置模式 + uiSchema: NodeGraph; // UI渲染模式 +} + +/** + * 节点类型定义 + */ +export interface NodeType extends BaseResponse { + type: NodeTypeEnum; // 节点类型 + name: string; // 节点名称 + description: string; // 节点描述 + category: NodeCategory; // 节点分类 + flowableConfig: any; // 工作流引擎配置 + graphConfig: NodeGraphConfig; // 图形配置 + style: NodeStyle; // 节点样式 + orderNum: number; // 排序号 + enabled: boolean; // 是否启用 + deleted: boolean; // 是否删除 + extraData: any; // 扩展数据 +} diff --git a/frontend/src/pages/Workflow/Definition/Designer/configs/nodeConfig.ts b/frontend/src/pages/Workflow/Definition/Designer/configs/nodeConfig.ts deleted file mode 100644 index 37932261..00000000 --- a/frontend/src/pages/Workflow/Definition/Designer/configs/nodeConfig.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { NodeConfig } from '../types'; - -export const NODE_CONFIG: Record = { - startEvent: { - size: { width: 80, height: 80 }, - shape: 'circle', - theme: { - fill: '#f6ffed', - stroke: '#52c41a' - }, - label: '开始节点', - extras: { - icon: { - 'xlink:href': 'data:image/svg+xml;base64,PHN2ZyB0PSIxNzAzODQ4NTM1NDY5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQxNjEiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNNTEyIDY0QzI2NC42IDY0IDY0IDI2NC42IDY0IDUxMnMyMDAuNiA0NDggNDQ4IDQ0OCA0NDgtMjAwLjYgNDQ4LTQ0OFM3NTkuNCA2NCA1MTIgNjR6bTE5MiAzMzZjMCA0LjQtMy42IDgtOCA4SDU0NHYxNTJjMCA0LjQtMy42IDgtOCA4aC00OGMtNC40IDAtOC0zLjYtOC04VjQwOEgzMjhjLTQuNCAwLTgtMy42LTgtOHYtNDhjMC00LjQgMy42LTggOC04aDE1MlYxOTJjMC00LjQgMy42LTggOC04aDQ4YzQuNCAwIDggMy42IDggOHYxNTJoMTUyYzQuNCAwIDggMy42IDggOHY0OHoiIGZpbGw9IiM1MmM0MWEiIHAtaWQ9IjQxNjIiPjwvcGF0aD48L3N2Zz4=', - width: 32, - height: 32, - x: 24, - y: 8 - } - } - }, - endEvent: { - size: { width: 80, height: 80 }, - shape: 'circle', - theme: { - fill: '#fff1f0', - stroke: '#ff4d4f' - }, - label: '结束节点', - extras: { - icon: { - 'xlink:href': 'data:image/svg+xml;base64,PHN2ZyB0PSIxNzAzODQ4NTM1NDY5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQxNjEiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNNTEyIDY0QzI2NC42IDY0IDY0IDI2NC42IDY0IDUxMnMyMDAuNiA0NDggNDQ4IDQ0OCA0NDgtMjAwLjYgNDQ4LTQ0OFM3NTkuNCA2NCA1MTIgNjR6bTE5MiAyODhjMCA0LjQtMy42IDgtOCA4SDMyOGMtNC40IDAtOC0zLjYtOC04di00OGMwLTQuNCAzLjYtOCA4LThoMzY4YzQuNCAwIDggMy42IDggOHY0OHoiIGZpbGw9IiNmZjRkNGYiIHAtaWQ9IjQxNjIiPjwvcGF0aD48L3N2Zz4=', - width: 32, - height: 32, - x: 24, - y: 8 - } - } - }, - userTask: { - size: { width: 200, height: 80 }, - shape: 'rect', - theme: { - fill: '#fff7e6', - stroke: '#fa8c16' - }, - label: '用户任务', - extras: { - rx: 4, - ry: 4, - icon: { - 'xlink:href': 'data:image/svg+xml;base64,PHN2ZyB0PSIxNzAzODQ4NTM1NDY5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQxNjEiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNODU4LjUgNzYzLjZjLTE4LjktMjYuMi00Ny45LTQxLjctNzkuOS00MS43SDI0Ni40Yy0zMiAwLTYxIDI1LjUtNzkuOSA0MS43LTE4LjkgMTYuMi0yOS45IDM3LjItMjkuOSA1OS42IDAgMjIuNCAyNi44IDQwLjYgNTkuOCA0MC42aDYzMi4yYzMzIDAgNTkuOC0xOC4yIDU5LjgtNDAuNiAwLTIyLjQtMTEtNDMuNC0yOS45LTU5LjZ6TTUxMiAyNTZjODguNCAwIDE2MCA3MS42IDE2MCAxNjBzLTcxLjYgMTYwLTE2MCAxNjAtMTYwLTcxLjYtMTYwLTE2MCA3MS42LTE2MCAxNjAtMTYweiIgZmlsbD0iI2ZhOGMxNiIgcC1pZD0iNDE2MiI+PC9wYXRoPjwvc3ZnPg==', - width: 32, - height: 32, - x: 8, - y: 24 - } - } - }, - shellTask: { - size: { width: 200, height: 80 }, - shape: 'rect', - theme: { - fill: '#e6f7ff', - stroke: '#1890ff' - }, - label: 'Shell脚本', - extras: { - rx: 4, - ry: 4, - icon: { - 'xlink:href': 'data:image/svg+xml;base64,PHN2ZyB0PSIxNzAzODQ4NTM1NDY5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQxNjEiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNMTYwIDI1NnY1MTJoNzA0VjI1NkgxNjB6IG02NDAgNDQ4SDE5MlYyODhoNjA4djQxNnpNMjI0IDQ4MGwxMjgtMTI4IDQ1LjMgNDUuMy04Mi43IDgyLjcgODIuNyA4Mi43TDM1MiA2MDhsLTEyOC0xMjh6IG0yMzEuMSAxOTBsLTQ1LjMtNDUuMyAxNTItMTUyIDQ1LjMgNDUuM2wtMTUyIDE1MnoiIGZpbGw9IiMxODkwZmYiIHAtaWQ9IjQxNjIiPjwvcGF0aD48L3N2Zz4=', - width: 32, - height: 32, - x: 8, - y: 24 - } - } - }, - exclusiveGateway: { - size: { width: 60, height: 60 }, - shape: 'polygon', - theme: { - fill: '#f9f0ff', - stroke: '#722ed1' - }, - label: '排他网关', - extras: { - refPoints: '0,30 30,0 60,30 30,60', - icon: { - 'xlink:href': 'data:image/svg+xml;base64,PHN2ZyB0PSIxNzAzODQ4NzAzODY0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjY1NTUiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNNzgyLjcgNDQxLjRMNTQ1IDMwNC44YzAuMy0wLjMgMC40LTAuNyAwLjctMUw3ODIuNyA0NDEuNHogbTExOC45IDQzMi45TDIxNy43IDE1OC4xQzIwMi4xIDE0Mi41IDE3NiAxNDIuNSAxNjAuNCAxNTguMWMtMTUuNiAxNS42LTE1LjYgNDEuNyAwIDU3LjNsNjgzLjkgNzE2LjJjMTUuNiAxNS42IDQxLjcgMTUuNiA1Ny4zIDBzMTUuNi00MS43IDAtNTcuM3ogbS00MjYtMjQuNmMtMC4zIDAuMy0wLjQgMC43LTAuNyAxTDIzNy42IDU4Mi42YzAuMy0wLjMgMC40LTAuNyAwLjctMWwyMzcuMyAyNjguMXogbTIzNy4zLTI2Ny4xTDQ3NS42IDg1MC43YzAuMy0wLjMgMC40LTAuNyAwLjctMUw3MTMuNiA1ODIuNnoiIGZpbGw9IiM3MjJlZDEiIHAtaWQ9IjY1NTYiPjwvcGF0aD48L3N2Zz4=', - width: 32, - height: 32, - x: 14, - y: 14 - } - } - } -}; diff --git a/frontend/src/pages/Workflow/Definition/Designer/index.tsx b/frontend/src/pages/Workflow/Definition/Designer/index.tsx index 90ed6ed0..90ab2aac 100644 --- a/frontend/src/pages/Workflow/Definition/Designer/index.tsx +++ b/frontend/src/pages/Workflow/Definition/Designer/index.tsx @@ -3,8 +3,8 @@ import {useNavigate, useParams} from 'react-router-dom'; import {Button, Card, Layout, message, Space, Spin, Drawer, Form, Dropdown} from 'antd'; import {ArrowLeftOutlined, SaveOutlined, DeleteOutlined, CopyOutlined, SettingOutlined, ClearOutlined, FullscreenOutlined} from '@ant-design/icons'; import {getDefinition, updateDefinition, getNodeTypes} from '../../service'; -import {WorkflowStatus} from '../../../Workflow/types'; -import {Graph, Node, Cell, Edge, Shape} from '@antv/x6'; +import {WorkflowDefinition, WorkflowStatus} from '../../../Workflow/types'; +import {Graph, Node, Cell, Edge} from '@antv/x6'; import '@antv/x6-react-shape'; import './index.module.less'; import NodePanel from './components/NodePanel'; @@ -12,11 +12,9 @@ import NodeConfig from './components/NodeConfig'; import Toolbar from './components/Toolbar'; import EdgeConfig from './components/EdgeConfig'; import { validateFlow, hasCycle } from './validate'; -import { generateNodeStyle, generatePorts, calculateNodePosition, calculateCanvasPosition, getNodeShape, getNodeSize } from './utils/nodeUtils'; -import { NODE_CONFIG } from './configs/nodeConfig'; -import { isWorkflowError } from './utils/errors'; +import { generateNodeStyle, generatePorts, calculateCanvasPosition, getNodeShape, getNodeSize } from './utils/nodeUtils'; import { initGraph } from './utils/graphUtils'; -import { NodeType, NodeData, EdgeData, WorkflowDefinition, WorkflowGraph } from './types'; +import { NodeType, NodeData, WorkflowGraph } from './types'; const {Sider, Content} = Layout; @@ -105,16 +103,17 @@ const FlowDesigner: React.FC = () => { if (contextMenu.cell && contextMenu.cell.isNode()) { setCurrentNode(contextMenu.cell); const data = contextMenu.cell.getData() as NodeData; - const nodeType = nodeTypes.find(type => type.type === data.type); - if (nodeType) { - setCurrentNodeType(nodeType); - const formValues = { - name: data.name || nodeType.name, - description: data.description, - ...data.config - }; - form.setFieldsValue(formValues); - } + console.log(data) + // const nodeType = nodeTypes.find(type => type.type === data.type); + // if (nodeType) { + // setCurrentNodeType(nodeType); + // const formValues = { + // name: data.name || nodeType.name, + // description: data.description, + // ...data.config + // }; + // form.setFieldsValue(formValues); + // } setConfigVisible(true); } setContextMenu(prev => ({ ...prev, visible: false })); diff --git a/frontend/src/pages/Workflow/Definition/Designer/service.ts b/frontend/src/pages/Workflow/Definition/Designer/service.ts index 8b63eb44..e69de29b 100644 --- a/frontend/src/pages/Workflow/Definition/Designer/service.ts +++ b/frontend/src/pages/Workflow/Definition/Designer/service.ts @@ -1,61 +0,0 @@ -import request from '@/utils/request'; -import { NodeCategory } from '../../types'; - -export interface NodeExecutor { - code: string; - name: string; - description: string; - configSchema: string; - defaultConfig?: string; -} - -export interface JsonSchemaProperty { - type: string; - title: string; - description?: string; - minLength?: number; - maxLength?: number; - minimum?: number; - maximum?: number; - default?: any; - format?: string; - pattern?: string; - enum?: string[]; - enumNames?: string[]; -} - -export interface JsonSchema { - type: string; - properties: Record; - required?: string[]; -} - -export interface NodeType { - id: number; - code: string; - name: string; - category: NodeCategory; - description: string; - enabled: boolean; - icon: string; - color: string; - executors: NodeExecutor[]; - configSchema: string; - defaultConfig: string; - createTime: string; - updateTime: string; - version: number; - deleted: boolean; - createBy: string; - updateBy: string; - extraData: any; -} - -export interface NodeTypeQuery { - enabled?: boolean; - category?: NodeCategory; -} - -// 获取节点类型列表 -export const getNodeTypes = (params?: NodeTypeQuery) => - request.get('/api/v1/node-types', { params }); \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/Designer/types.ts b/frontend/src/pages/Workflow/Definition/Designer/types.ts index 95a2bf36..1c3b238a 100644 --- a/frontend/src/pages/Workflow/Definition/Designer/types.ts +++ b/frontend/src/pages/Workflow/Definition/Designer/types.ts @@ -1,21 +1,45 @@ +/** + * 位置信息 + */ export interface Position { x: number; y: number; } +/** + * 尺寸信息 + */ export interface Size { width: number; height: number; } +/** + * 节点类型枚举 + */ +export enum NodeType { + START = 'startEvent', + END = 'endEvent', + USER_TASK = 'userTask', + SERVICE_TASK = 'serviceTask', + SCRIPT_TASK = 'scriptTask', + EXCLUSIVE_GATEWAY = 'exclusiveGateway', + PARALLEL_GATEWAY = 'parallelGateway', + SUBPROCESS = 'subProcess', + CALL_ACTIVITY = 'callActivity' +} + +/** + * 节点配置 + */ export interface NodeConfig { - size: Size; - shape: 'circle' | 'rect' | 'polygon'; - theme: { + size?: Size; + shape?: 'circle' | 'rect' | 'polygon'; + theme?: { fill: string; stroke: string; }; - label: string; + label?: string; extras?: { rx?: number; ry?: number; @@ -27,46 +51,94 @@ export interface NodeConfig { y: number; }; }; + assignee?: string; + candidateUsers?: string[]; + candidateGroups?: string[]; + dueDate?: string; + priority?: number; + formKey?: string; + executor?: string; + retryTimes?: number; + retryInterval?: number; + script?: string; + timeout?: number; + workingDirectory?: string; + environment?: string; + successExitCode?: string; + [key: string]: any; } -export interface NodeType { - id?: number; - code: string; + +/** + * 工作流节点 + */ +export interface WorkflowNode { + id: string; + type: NodeType; name: string; - category: string; - color?: string; - icon?: string; - enabled?: boolean; - description?: string; - config: NodeConfig; + position: Position; + size: Size; + config?: NodeConfig; + properties?: Record; } +/** + * 工作流边 + */ +export interface WorkflowEdge { + id: string; + source: string; + target: string; + sourcePortId?: string; + targetPortId?: string; + name?: string; + condition?: string; + description?: string; + priority?: number; + config?: { + condition?: string; + expression?: string; + type?: 'sequence' | 'message' | 'association'; + [key: string]: any; + }; + properties?: Record; +} + +/** + * 工作流图形 + */ +export interface WorkflowGraph { + nodes: WorkflowNode[]; + edges: WorkflowEdge[]; + properties?: WorkflowProperties; +} + +/** + * 工作流属性 + */ +export interface WorkflowProperties { + name: string; + key?: string; + description?: string; + version?: number; + category?: string; +} + +/** + * 节点数据 + */ export interface NodeData { type: string; name?: string; description?: string; - config: { - executor?: string; - retryTimes?: number; - retryInterval?: number; - script?: string; - timeout?: number; - workingDirectory?: string; - environment?: string; - successExitCode?: string; - [key: string]: any; - }; + config?: NodeConfig; } +/** + * 边数据 + */ export interface EdgeData { condition?: string; description?: string; priority?: number; } - -export interface WorkflowDefinition { - id: number; - name: string; - description?: string; - bpmnJson: string; -} diff --git a/frontend/src/pages/Workflow/Definition/Designer/types/base.ts b/frontend/src/pages/Workflow/Definition/Designer/types/base.ts deleted file mode 100644 index 9e0330ee..00000000 --- a/frontend/src/pages/Workflow/Definition/Designer/types/base.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Graph, Node, Edge } from '@antv/x6'; - -// 基础实体类型 -export interface BaseEntity { - id: number; - createTime?: string; - createBy?: string; - updateTime?: string; - updateBy?: string; - version: number; - deleted: boolean; - extraData?: any; -} - -// 位置类型 -export interface Position { - x: number; - y: number; -} - -// 尺寸类型 -export interface Size { - width: number; - height: number; -} - -// 工作流状态 -export enum WorkflowStatus { - DRAFT = 'DRAFT', // 草稿 - PUBLISHED = 'PUBLISHED', // 已发布 - DISABLED = 'DISABLED' // 已禁用 -} - -// 节点类别 -export enum NodeCategory { - EVENT = 'EVENT', // 事件节点 - TASK = 'TASK', // 任务节点 - GATEWAY = 'GATEWAY' // 网关节点 -} - -// 图形属性 -export interface GraphAttrs { - body?: { - fill?: string; - stroke?: string; - strokeWidth?: number; - rx?: number; - ry?: number; - }; - label?: { - text?: string; - fill?: string; - fontSize?: number; - fontWeight?: string; - }; - image?: { - 'xlink:href'?: string; - width?: number; - height?: number; - x?: number; - y?: number; - }; -} diff --git a/frontend/src/pages/Workflow/Definition/Designer/types/edge.ts b/frontend/src/pages/Workflow/Definition/Designer/types/edge.ts deleted file mode 100644 index d0f0125a..00000000 --- a/frontend/src/pages/Workflow/Definition/Designer/types/edge.ts +++ /dev/null @@ -1,42 +0,0 @@ -// 连线数据 -export interface EdgeData { - id: string; - source: string; - target: string; - name?: string; - config: { - type: 'sequenceFlow'; - condition?: string; - defaultFlow?: boolean; - }; - properties: Record; -} - -// 连线验证错误 -export interface EdgeValidationError { - edgeId: string; - property: string; - message: string; -} - -// 连线样式 -export interface EdgeStyle { - line: { - stroke: string; - strokeWidth: number; - targetMarker?: { - name: string; - size: number; - }; - sourceMarker?: { - name: string; - size: number; - }; - }; - label?: { - text: string; - fill: string; - fontSize: number; - fontWeight?: string; - }; -} diff --git a/frontend/src/pages/Workflow/Definition/Designer/types/graph.ts b/frontend/src/pages/Workflow/Definition/Designer/types/graph.ts deleted file mode 100644 index 8cea55a3..00000000 --- a/frontend/src/pages/Workflow/Definition/Designer/types/graph.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * 工作流图形定义类型 - */ -export interface WorkflowGraph { - nodes: WorkflowNode[]; - edges: WorkflowEdge[]; - properties?: WorkflowProperties; -} - -/** - * 工作流属性 - */ -export interface WorkflowProperties { - name: string; - key?: string; - description?: string; - version?: number; - category?: string; -} - -/** - * 工作流节点 - */ -export interface WorkflowNode { - id: string; - type: NodeType; - name: string; - position: Position; - config?: NodeConfig; - properties?: Record; - size?: { - width: number; - height: number; - }; -} - -/** - * 节点类型枚举 - */ -export enum NodeType { - START = 'start', - END = 'end', - USER_TASK = 'userTask', - SERVICE_TASK = 'serviceTask', - SCRIPT_TASK = 'scriptTask', - EXCLUSIVE_GATEWAY = 'exclusiveGateway', - PARALLEL_GATEWAY = 'parallelGateway', - SUBPROCESS = 'subProcess', - CALL_ACTIVITY = 'callActivity' -} - -/** - * 工作流边 - */ -export interface WorkflowEdge { - id: string; - source: string; - target: string; - name?: string; - config?: EdgeConfig; - properties?: Record; -} - -/** - * 边配置 - */ -export interface EdgeConfig { - condition?: string; - expression?: string; - type?: 'sequence' | 'message' | 'association'; -} - -/** - * 节点配置 - */ -export interface NodeConfig { - type?: string; - implementation?: string; - fields?: Record; - assignee?: string; - candidateUsers?: string[]; - candidateGroups?: string[]; - dueDate?: string; - priority?: number; - formKey?: string; - skipExpression?: string; - isAsync?: boolean; - exclusive?: boolean; -} - -/** - * 位置信息 - */ -export interface Position { - x: number; - y: number; -} diff --git a/frontend/src/pages/Workflow/Definition/Designer/types/index.ts b/frontend/src/pages/Workflow/Definition/Designer/types/index.ts deleted file mode 100644 index e33e8cde..00000000 --- a/frontend/src/pages/Workflow/Definition/Designer/types/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './base'; -export * from './node'; -export * from './edge'; -export * from './workflow'; diff --git a/frontend/src/pages/Workflow/Definition/Designer/types/node.ts b/frontend/src/pages/Workflow/Definition/Designer/types/node.ts deleted file mode 100644 index 1726d0af..00000000 --- a/frontend/src/pages/Workflow/Definition/Designer/types/node.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { BaseEntity, NodeCategory, Position, Size, GraphAttrs } from './base'; - -// 端口组配置 -export interface PortGroup { - position: string; - attrs: { - circle: { - r: number; - magnet: boolean; - stroke?: string; - strokeWidth?: number; - fill?: string; - }; - }; -} - -// 图形配置 -export interface GraphConfig { - shape: 'circle' | 'rect' | 'diamond'; - width: number; - height: number; - ports: { - groups: Record; - }; - attrs: GraphAttrs; -} - -// 表单属性 -export interface FormProperty { - name: string; - label: string; - type: string; - required?: boolean; - default?: any; - options?: Array<{ - label: string; - value: any; - }>; -} - -// 表单配置 -export interface FormConfig { - properties: FormProperty[]; -} - -// 节点类型定义 -export interface NodeType extends BaseEntity { - type: string; - name: string; - description: string; - category: NodeCategory; - flowableConfig: Record; - graphConfig: GraphConfig; - formConfig: FormConfig; - orderNum: number; - enabled: boolean; -} - -// 节点数据 -export interface NodeData { - id: string; - type: string; - name: string; - position: Position; - size: Size; - config: { - type: string; - implementation?: string; - fields?: Record; - // Shell任务特有配置 - script?: string; - workDir?: string; - // 用户任务特有配置 - assignee?: string; - candidateUsers?: string; - candidateGroups?: string; - dueDate?: string; - }; - properties: Record; -} - -// 节点验证错误 -export interface NodeValidationError { - nodeId: string; - property: string; - message: string; -} diff --git a/frontend/src/pages/Workflow/Definition/Designer/types/workflow.ts b/frontend/src/pages/Workflow/Definition/Designer/types/workflow.ts deleted file mode 100644 index 0472f614..00000000 --- a/frontend/src/pages/Workflow/Definition/Designer/types/workflow.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { BaseEntity, WorkflowStatus } from './base'; -import { NodeData } from './node'; -import { EdgeData } from './edge'; - -// 图形数据 -export interface GraphData { - nodes: NodeData[]; - edges: EdgeData[]; - properties: Record; -} - -// 表单配置 -export interface WorkflowFormConfig { - formItems: Array; // 根据实际表单配置类型定义 -} - -// 工作流定义 -export interface WorkflowDefinition extends BaseEntity { - name: string; - key: string; - flowVersion: number; - description?: string; - status: WorkflowStatus; - bpmnXml: string; - graph: GraphData; - formConfig: WorkflowFormConfig; -} - -// 工作流验证错误 -export interface WorkflowValidationError { - type: 'node' | 'edge' | 'workflow'; - id?: string; - message: string; -} - -// 工作流查询参数 -export interface WorkflowDefinitionQuery { - keyword?: string; - status?: WorkflowStatus; - startTime?: string; - endTime?: string; - pageSize?: number; - current?: number; -} diff --git a/frontend/src/pages/Workflow/Definition/Designer/utils/loadGraphData.ts b/frontend/src/pages/Workflow/Definition/Designer/utils/loadGraphData.ts deleted file mode 100644 index c8e899a7..00000000 --- a/frontend/src/pages/Workflow/Definition/Designer/utils/loadGraphData.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Graph } from '@antv/x6'; -import { NodeData } from '../types'; - -export interface GraphJsonCell { - id: string; - shape: string; - data?: { - label: string; - serviceTask?: { - type: string; - implementation: string; - fields: any; - }; - }; - position?: { - x: number; - y: number; - }; - source?: string; - target?: string; -} - -export interface GraphJson { - cells: GraphJsonCell[]; -} - -export const loadGraphData = (graph: Graph, graphJson: GraphJson) => { - // 清空现有图形 - graph.clearCells(); - - // 先添加所有节点 - const nodes = graphJson.cells.filter(cell => cell.shape !== 'edge'); - nodes.forEach(node => { - graph.addNode({ - id: node.id, - shape: node.shape, - x: node.position?.x || 0, - y: node.position?.y || 0, - label: node.data?.label || '', - data: { - ...node.data, - type: node.shape, - }, - }); - }); - - // 再添加所有边 - const edges = graphJson.cells.filter(cell => cell.shape === 'edge'); - edges.forEach(edge => { - graph.addEdge({ - id: edge.id, - source: edge.source, - target: edge.target, - label: edge.data?.label || '', - attrs: { - line: { - stroke: '#5F95FF', - strokeWidth: 1, - targetMarker: { - name: 'classic', - size: 8, - }, - }, - }, - }); - }); - - // 自动布局 - graph.centerContent(); - graph.zoomToFit({ padding: 20 }); -}; diff --git a/frontend/src/pages/Workflow/Definition/Designer/validate.ts b/frontend/src/pages/Workflow/Definition/Designer/validate.ts index a14ff809..8f41eeca 100644 --- a/frontend/src/pages/Workflow/Definition/Designer/validate.ts +++ b/frontend/src/pages/Workflow/Definition/Designer/validate.ts @@ -1,6 +1,7 @@ import { Graph } from '@antv/x6'; +import { NodeType } from './types'; -interface ValidationResult { +export interface ValidationResult { valid: boolean; errors: string[]; } @@ -14,8 +15,15 @@ export const validateFlow = (graph: Graph): ValidationResult => { const nodes = graph.getNodes(); const edges = graph.getEdges(); + // 验证是否有节点 + if (nodes.length === 0) { + result.errors.push('流程必须包含至少一个节点'); + result.valid = false; + return result; + } + // 验证开始节点 - const startNodes = nodes.filter(node => node.getData()?.type === 'startEvent'); + const startNodes = nodes.filter(node => node.getData()?.type === NodeType.START); if (startNodes.length === 0) { result.errors.push('流程必须包含一个开始节点'); result.valid = false; @@ -25,10 +33,13 @@ export const validateFlow = (graph: Graph): ValidationResult => { } // 验证结束节点 - const endNodes = nodes.filter(node => node.getData()?.type === 'endEvent'); + const endNodes = nodes.filter(node => node.getData()?.type === NodeType.END); if (endNodes.length === 0) { result.errors.push('流程必须包含至少一个结束节点'); result.valid = false; + } else if (endNodes.length > 1) { + result.errors.push('流程只能包含一个结束节点'); + result.valid = false; } // 验证孤立节点 @@ -39,17 +50,17 @@ export const validateFlow = (graph: Graph): ValidationResult => { const nodeType = nodeData?.type; const nodeName = nodeData?.name || '未命名'; - if (nodeType === 'startEvent' && incomingEdges.length > 0) { + if (nodeType === NodeType.START && incomingEdges.length > 0) { result.errors.push('开始节点不能有入边'); result.valid = false; } - if (nodeType === 'endEvent' && outgoingEdges.length > 0) { + if (nodeType === NodeType.END && outgoingEdges.length > 0) { result.errors.push('结束节点不能有出边'); result.valid = false; } - if (nodeType !== 'startEvent' && nodeType !== 'endEvent' && + if (nodeType !== NodeType.START && nodeType !== NodeType.END && (incomingEdges.length === 0 || outgoingEdges.length === 0)) { result.errors.push(`节点 "${nodeName}" 未完全连接`); result.valid = false; @@ -57,28 +68,51 @@ export const validateFlow = (graph: Graph): ValidationResult => { }); // 验证网关配对 - const validateGatewayPairs = () => { - const gatewayNodes = nodes.filter(node => node.getData()?.type === 'exclusiveGateway'); - gatewayNodes.forEach(gateway => { + const validateGateways = () => { + const exclusiveGateways = nodes.filter( + node => node.getData()?.type === NodeType.EXCLUSIVE_GATEWAY + ); + const parallelGateways = nodes.filter( + node => node.getData()?.type === NodeType.PARALLEL_GATEWAY + ); + + // 验证排他网关 + exclusiveGateways.forEach(gateway => { const outgoingEdges = graph.getOutgoingEdges(gateway) || []; const incomingEdges = graph.getIncomingEdges(gateway) || []; const gatewayData = gateway.getData(); const gatewayName = gatewayData?.name || '未命名'; - // 排他网关必须至少有一个出口 - if (outgoingEdges.length < 1) { - result.errors.push(`排他网关 "${gatewayName}" 必须至少有一个出口`); + if (outgoingEdges.length < 2) { + result.errors.push(`排他网关 "${gatewayName}" 必须至少有两个出口`); result.valid = false; } - // 排他网关必须有入口 if (incomingEdges.length < 1) { result.errors.push(`排他网关 "${gatewayName}" 必须至少有一个入口`); result.valid = false; } }); + + // 验证并行网关 + parallelGateways.forEach(gateway => { + const outgoingEdges = graph.getOutgoingEdges(gateway) || []; + const incomingEdges = graph.getIncomingEdges(gateway) || []; + const gatewayData = gateway.getData(); + const gatewayName = gatewayData?.name || '未命名'; + + if (outgoingEdges.length < 2) { + result.errors.push(`并行网关 "${gatewayName}" 必须至少有两个出口`); + result.valid = false; + } + + if (incomingEdges.length < 1) { + result.errors.push(`并行网关 "${gatewayName}" 必须至少有一个入口`); + result.valid = false; + } + }); }; - validateGatewayPairs(); + validateGateways(); return result; }; @@ -118,4 +152,4 @@ export const hasCycle = (graph: Graph): boolean => { } return false; -}; \ No newline at end of file +}; \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/constants.ts b/frontend/src/pages/Workflow/Definition/constants.ts deleted file mode 100644 index c77d90c4..00000000 --- a/frontend/src/pages/Workflow/Definition/constants.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NodeCategory } from './types'; - -// 节点分类配置 -export const NODE_CATEGORY_CONFIG = { - [NodeCategory.TASK]: { - icon: 'CodeOutlined', - label: '任务节点', - }, - [NodeCategory.EVENT]: { - icon: 'ThunderboltOutlined', - label: '事件节点', - }, - [NodeCategory.GATEWAY]: { - icon: 'ForkOutlined', - label: '网关节点', - }, -} as const; \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/index.tsx b/frontend/src/pages/Workflow/Definition/index.tsx index 511972ac..ed4fcb4c 100644 --- a/frontend/src/pages/Workflow/Definition/index.tsx +++ b/frontend/src/pages/Workflow/Definition/index.tsx @@ -15,7 +15,7 @@ import { WorkflowStatus, WorkflowDefinitionBase } from '../types'; -import {DeleteOutlined, EditOutlined, PlusOutlined} from '@ant-design/icons'; +import {DeleteOutlined, PlusOutlined} from '@ant-design/icons'; const {confirm} = Modal; diff --git a/frontend/src/pages/Workflow/Definition/service.ts b/frontend/src/pages/Workflow/Definition/service.ts index aaced46e..e69de29b 100644 --- a/frontend/src/pages/Workflow/Definition/service.ts +++ b/frontend/src/pages/Workflow/Definition/service.ts @@ -1,129 +0,0 @@ -import { NodeCategory, NodeType } from '../types'; - -// Mock 节点类型数据 -const mockNodeTypes: NodeType[] = [ - { - id: 1, - code: 'start', - name: '开始节点', - color: '#67C23A', - icon: 'PlayCircleOutlined', - category: NodeCategory.EVENT, - description: '流程的开始节点', - enabled: true, - executors: [], - configSchema: '{}', - defaultConfig: '{}', - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - }, - { - id: 2, - code: 'end', - name: '结束节点', - color: '#F56C6C', - icon: 'StopOutlined', - category: NodeCategory.EVENT, - description: '流程的结束节点', - enabled: true, - executors: [], - configSchema: '{}', - defaultConfig: '{}', - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - }, - { - id: 3, - code: 'approval', - name: '审批节点', - color: '#409EFF', - icon: 'AuditOutlined', - category: NodeCategory.TASK, - description: '人工审批节点', - enabled: true, - executors: [], - configSchema: '{}', - defaultConfig: '{}', - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - }, - { - id: 4, - code: 'script', - name: '脚本节点', - color: '#909399', - icon: 'CodeOutlined', - category: NodeCategory.TASK, - description: '执行自定义脚本', - enabled: true, - executors: [], - configSchema: '{}', - defaultConfig: '{}', - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - }, - { - id: 5, - code: 'webhook', - name: 'Webhook', - color: '#9C27B0', - icon: 'ApiOutlined', - category: NodeCategory.TASK, - description: '调用外部 Webhook', - enabled: true, - executors: [], - configSchema: '{}', - defaultConfig: '{}', - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - }, - { - id: 6, - code: 'exclusive', - name: '排他网关', - color: '#FF9800', - icon: 'ForkOutlined', - category: NodeCategory.GATEWAY, - description: '根据条件选择一条分支执行', - enabled: true, - executors: [], - configSchema: '{}', - defaultConfig: '{}', - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - }, - { - id: 7, - code: 'parallel', - name: '并行网关', - color: '#795548', - icon: 'PartitionOutlined', - category: NodeCategory.GATEWAY, - description: '同时执行所有分支', - enabled: true, - executors: [], - configSchema: '{}', - defaultConfig: '{}', - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - } -]; - -interface GetNodeTypesParams { - enabled?: boolean; -} - -// 获取节点类型列表 -export const getNodeTypes = async (params?: GetNodeTypesParams): Promise => { - // 模拟 API 延迟 - await new Promise(resolve => setTimeout(resolve, 500)); - - let result = [...mockNodeTypes]; - - // 根据启用状态筛选 - if (params?.enabled !== undefined) { - result = result.filter(node => node.enabled === params.enabled); - } - - return result; -}; \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/types.ts b/frontend/src/pages/Workflow/Definition/types.ts index 14d713e7..b8f2948f 100644 --- a/frontend/src/pages/Workflow/Definition/types.ts +++ b/frontend/src/pages/Workflow/Definition/types.ts @@ -1,15 +1,209 @@ -export enum NodeCategory { - TASK = 'TASK', - EVENT = 'EVENT', - GATEWAY = 'GATEWAY' +import { BaseResponse } from '@/types/base/response'; + +/** + * 节点图形配置 + */ +interface NodeGraphStyle { + fill: string; + stroke: string; + icon?: string; + iconColor?: string; + strokeWidth?: number; } -export interface NodeType { - code: string; +/** + * 节点端口组配置 + */ +interface PortGroup { + position: 'left' | 'right'; + attrs: { + circle: { + r: number; + fill: string; + stroke: string; + }; + }; +} + +/** + * 节点端口配置 + */ +interface NodePorts { + groups: { + in?: PortGroup; + out?: PortGroup; + }; + types: ('in' | 'out')[]; +} + +/** + * 节点图形定义 + */ +interface NodeGraph { + shape: 'circle' | 'rectangle'; + size: { + width: number; + height: number; + }; + style: NodeGraphStyle; + ports: NodePorts; +} + +/** + * 工作流节点配置 + */ +interface NodeConfig { name: string; - color: string; - icon: string; - category: NodeCategory; description?: string; - enabled?: boolean; -} \ No newline at end of file + script?: string; + language?: string; + [key: string]: any; +} + +/** + * 工作流节点定义 + */ +interface WorkflowNode { + id: string; + code: string; + type: string; + name: string; + graph: NodeGraph; + config: NodeConfig; +} + +/** + * 工作流边配置 + */ +interface EdgeConfig { + type: 'sequence'; + [key: string]: any; +} + +/** + * 工作流边定义 + */ +interface WorkflowEdge { + id: string; + from: string; + to: string; + name: string; + config: EdgeConfig; + properties?: Record; +} + +/** + * 工作流状态 + */ +export enum WorkflowStatus { + DRAFT = 'DRAFT', // 草稿 + PUBLISHED = 'PUBLISHED', // 已发布 + DISABLED = 'DISABLED' // 已禁用 +} + +/** + * 工作流图形定义 + */ +export interface WorkflowGraph { + nodes: WorkflowNode[]; + edges: WorkflowEdge[]; +} + +/** + * 工作流定义基本信息 + */ +export interface WorkflowDefinitionBase { + name: string; // 工作流名称 + key: string; // 工作流标识 + flowVersion?: number; // 流程版本 + description?: string; // 描述信息 +} + +/** + * 创建工作流定义请求 + */ +export interface CreateWorkflowDefinitionRequest extends WorkflowDefinitionBase { + status: WorkflowStatus; // 工作流状态 +} + +/** + * 更新工作流定义请求 + */ +export interface UpdateWorkflowDefinitionRequest extends WorkflowDefinitionBase { + +} + +/** + * 表单配置 + */ +export interface FormConfig { + formItems: any[]; // 表单项配置 +} + +/** + * 工作流定义 + */ +export interface WorkflowDefinition extends BaseResponse { + name: string; // 工作流名称 + key: string; // 工作流标识 + flowVersion: number; // 流程版本 + bpmnXml: string | null; // BPMN XML定义 + graph: WorkflowGraph; // 图形定义 + formConfig: FormConfig; // 表单配置 + status: WorkflowStatus; // 工作流状态 + description: string; // 描述信息 + deleted: boolean; // 是否删除 + extraData: any; // 扩展数据 +} + +/** + * 工作流配置的解析和序列化工具 + */ +export const WorkflowConfigUtils = { + /** + * 解析节点配置 + */ + parseNodeConfig: (config: string): Record => { + try { + return JSON.parse(config); + } catch { + return {nodes: []}; + } + }, + + /** + * 解析转换配置 + */ + parseTransitionConfig: (config: string): Record => { + try { + return JSON.parse(config); + } catch { + return {transitions: []}; + } + }, + + /** + * 解析表单定义 + */ + parseFormDefinition: (config: string): Record => { + try { + return JSON.parse(config); + } catch { + return {}; + } + }, + + /** + * 解析图形定义 + */ + parseGraphDefinition: (config: string): Record => { + try { + return JSON.parse(config); + } catch { + return {}; + } + } +}; + +// 导出NodePanel中的所有类型 +export * from './Designer/components/NodePanel/types'; \ No newline at end of file diff --git a/frontend/src/pages/Workflow/types.ts b/frontend/src/pages/Workflow/types.ts index a8ee89f1..47055e77 100644 --- a/frontend/src/pages/Workflow/types.ts +++ b/frontend/src/pages/Workflow/types.ts @@ -1,152 +1,29 @@ -import {Page} from '../../types/base'; +import { Page } from '@/types/base'; +import { BaseQuery } from '@/types/base/query'; +import type { WorkflowDefinition, WorkflowStatus } from './Definition/types'; -// 工作流状态枚举 -export enum WorkflowStatus { - DRAFT = 'DRAFT', // 草稿 - PUBLISHED = 'PUBLISHED', // 已发布 - DISABLED = 'DISABLED' // 已禁用 +/** + * 工作流定义查询参数 + */ +export interface WorkflowDefinitionQuery extends BaseQuery { + keyword?: string; // 关键字搜索 + status?: WorkflowStatus; // 工作流状态 + enabled?: boolean; // 是否启用 } -// 节点类型分类枚举 -export enum NodeCategory { - TASK = 'TASK', // 任务节点 - EVENT = 'EVENT', // 事件节点 - GATEWAY = 'GATEWAY' // 网关节点 -} - -// 节点类型查询参数 -export interface NodeTypeQuery { - enabled?: boolean; - category?: NodeCategory; -} - -// 节点执行器 -export interface NodeExecutor { - code: string; - name: string; - description: string; - configSchema: string; // JSON Schema - defaultConfig?: string; -} - -// 节点类型 -export interface NodeType { - id: number; - type: string; - name: string; - description: string; - category: NodeCategory; - flowableConfig: string; // JSON string - graphConfig: string; // JSON string - formConfig: string; // JSON string - orderNum: number; - enabled: boolean; - createTime: string; - updateTime: string; - createBy: string; - updateBy: string; - version: number; - deleted: boolean; - extraData: any; -} - -// 工作流定义查询参数 -export interface WorkflowDefinitionQuery { - page?: number; - size?: number; - sort?: string[]; - keyword?: string; - status?: WorkflowStatus; - enabled?: boolean; -} - -// 节点配置 -export interface NodeConfig { - nodes: { - id: string; - type: string; - name: string; - config: Record; - }[]; -} - -// 流转配置 -export interface TransitionConfig { - transitions: { - from: string; - to: string; - condition: string; - }[]; -} - -// 工作流定义基本信息 -export interface WorkflowDefinitionBase { - name: string; - key: string; - flowVersion?: number; - status?: WorkflowStatus; - description?: string; -} - -// 工作流定义 -export interface WorkflowDefinition extends WorkflowDefinitionBase { - id: number; - status: WorkflowStatus; - enabled: boolean; - nodeConfig: string; // JSON string of NodeConfig - transitionConfig: string; // JSON string of TransitionConfig - formDefinition: string; // JSON string - graphDefinition: string; // JSON string - createTime: string; - updateTime: string; - createBy: string; - updateBy: string; -} - -// 创建工作流定义请求 -export interface CreateWorkflowDefinitionRequest extends WorkflowDefinitionBase { - status: WorkflowStatus; -} - -// 更新工作流定义请求 -export interface UpdateWorkflowDefinitionRequest extends WorkflowDefinitionBase { - nodeConfig?: string; // JSON string of NodeConfig - transitionConfig?: string; // JSON string of TransitionConfig - formDefinition?: string; // JSON string - graphDefinition?: string; // JSON string -} - -// 分页响应 +/** + * 工作流定义分页响应 + */ export type WorkflowDefinitionPage = Page; -// 工作流配置的解析和序列化工具 -export const WorkflowConfigUtils = { - parseNodeConfig: (config: string): NodeConfig => { - try { - return JSON.parse(config); - } catch { - return { nodes: [] }; - } - }, - parseTransitionConfig: (config: string): TransitionConfig => { - try { - return JSON.parse(config); - } catch { - return { transitions: [] }; - } - }, - parseFormDefinition: (config: string): Record => { - try { - return JSON.parse(config); - } catch { - return {}; - } - }, - parseGraphDefinition: (config: string): Record => { - try { - return JSON.parse(config); - } catch { - return {}; - } - } -}; \ No newline at end of file +/** + * 节点类型查询参数 + */ +export interface NodeTypeQuery extends BaseQuery { + enabled?: boolean; // 是否启用 + category?: string; // 节点类别 +} + +// 重新导出工作流相关类型 +export type { WorkflowDefinition } from './Definition/types'; +export { WorkflowStatus } from './Definition/types';