增加工具栏提示。
This commit is contained in:
parent
09489b3e11
commit
f8b1019625
@ -15,4 +15,7 @@
|
||||
- 一次将所有修改应用于单个文件
|
||||
- 请勿修改不相关的文件
|
||||
|
||||
4. 代码注释:
|
||||
- 尽量都编写代码注释,写明实现的方式
|
||||
|
||||
记住要始终考虑每个项目的背景和特定需求。
|
||||
@ -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;
|
||||
|
||||
@ -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<NodePanelProps> = ({ 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<NodePanelProps> = ({ 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<string, NodeType[]>) : {};
|
||||
}, {} as Record<string, NodeType[]>);
|
||||
|
||||
// 获取类别标签
|
||||
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<NodePanelProps> = ({ onNodeDragStart }) => {
|
||||
children: (
|
||||
<div className="node-panel-content">
|
||||
{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 (
|
||||
<div
|
||||
key={nodeType.type}
|
||||
className="node-card"
|
||||
className="node-item"
|
||||
draggable
|
||||
onDragStart={(e) => {
|
||||
e.dataTransfer.setData('node-type', JSON.stringify({
|
||||
...nodeType,
|
||||
graphConfig
|
||||
}));
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
onNodeDragStart(nodeType);
|
||||
}}
|
||||
style={nodeStyle}
|
||||
>
|
||||
<div className="node-icon">
|
||||
{graphConfig.properties?.shape?.const === 'serviceTask' && (
|
||||
<i className="anticon">
|
||||
<svg viewBox="0 0 1024 1024" width="32" height="32">
|
||||
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="#5F95FF"/>
|
||||
<path d="M512 196c-171.4 0-316 144.6-316 316s144.6 316 316 316 316-144.6 316-316-144.6-316-316-316zm0 552c-130.1 0-236-105.9-236-236s105.9-236 236-236 236 105.9 236 236-105.9 236-236 236z" fill="#5F95FF"/>
|
||||
<path d="M512 392c-66.2 0-120 53.8-120 120s53.8 120 120 120 120-53.8 120-120-53.8-120-120-120z" fill="#5F95FF"/>
|
||||
</svg>
|
||||
</i>
|
||||
)}
|
||||
</div>
|
||||
<div className="node-name">{nodeType.name}</div>
|
||||
<div className="node-desc">{nodeType.description}</div>
|
||||
{icon && (
|
||||
<span
|
||||
className="node-icon"
|
||||
style={{ color: style.iconColor || '#1890ff' }}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
)}
|
||||
<span className="node-name">{nodeType.name}</span>
|
||||
<span className="node-desc">{nodeType.description}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@ -85,33 +153,17 @@ const NodePanel: React.FC<NodePanelProps> = ({ onNodeDragStart }) => {
|
||||
),
|
||||
}));
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="node-panel-loading">
|
||||
<Spin />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="node-panel">
|
||||
<Tabs
|
||||
className="node-tabs"
|
||||
defaultActiveKey="TASK"
|
||||
items={tabItems}
|
||||
/>
|
||||
<Spin spinning={loading}>
|
||||
<Tabs
|
||||
defaultActiveKey={NodeCategory.EVENT}
|
||||
items={tabItems}
|
||||
className="node-panel-tabs"
|
||||
/>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 获取类别标签
|
||||
function getCategoryLabel(category: string): string {
|
||||
const categoryMap: Record<string, string> = {
|
||||
[NodeCategory.TASK]: '任务节点',
|
||||
[NodeCategory.EVENT]: '事件节点',
|
||||
[NodeCategory.GATEWAY]: '网关节点'
|
||||
};
|
||||
return categoryMap[category] || category;
|
||||
}
|
||||
|
||||
export default NodePanel;
|
||||
@ -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<string, JsonSchemaProperty>;
|
||||
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; // 扩展数据
|
||||
}
|
||||
@ -1,99 +0,0 @@
|
||||
import { NodeConfig } from '../types';
|
||||
|
||||
export const NODE_CONFIG: Record<string, NodeConfig> = {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -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 }));
|
||||
|
||||
@ -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<string, JsonSchemaProperty>;
|
||||
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<NodeType[]>('/api/v1/node-types', { params });
|
||||
@ -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<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作流边
|
||||
*/
|
||||
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<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作流图形
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
// 连线数据
|
||||
export interface EdgeData {
|
||||
id: string;
|
||||
source: string;
|
||||
target: string;
|
||||
name?: string;
|
||||
config: {
|
||||
type: 'sequenceFlow';
|
||||
condition?: string;
|
||||
defaultFlow?: boolean;
|
||||
};
|
||||
properties: Record<string, any>;
|
||||
}
|
||||
|
||||
// 连线验证错误
|
||||
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;
|
||||
};
|
||||
}
|
||||
@ -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<string, any>;
|
||||
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<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 边配置
|
||||
*/
|
||||
export interface EdgeConfig {
|
||||
condition?: string;
|
||||
expression?: string;
|
||||
type?: 'sequence' | 'message' | 'association';
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点配置
|
||||
*/
|
||||
export interface NodeConfig {
|
||||
type?: string;
|
||||
implementation?: string;
|
||||
fields?: Record<string, any>;
|
||||
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;
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
export * from './base';
|
||||
export * from './node';
|
||||
export * from './edge';
|
||||
export * from './workflow';
|
||||
@ -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<string, PortGroup>;
|
||||
};
|
||||
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<string, any>;
|
||||
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<string, {
|
||||
string?: string;
|
||||
expression?: string;
|
||||
}>;
|
||||
// Shell任务特有配置
|
||||
script?: string;
|
||||
workDir?: string;
|
||||
// 用户任务特有配置
|
||||
assignee?: string;
|
||||
candidateUsers?: string;
|
||||
candidateGroups?: string;
|
||||
dueDate?: string;
|
||||
};
|
||||
properties: Record<string, any>;
|
||||
}
|
||||
|
||||
// 节点验证错误
|
||||
export interface NodeValidationError {
|
||||
nodeId: string;
|
||||
property: string;
|
||||
message: string;
|
||||
}
|
||||
@ -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<string, any>;
|
||||
}
|
||||
|
||||
// 表单配置
|
||||
export interface WorkflowFormConfig {
|
||||
formItems: Array<any>; // 根据实际表单配置类型定义
|
||||
}
|
||||
|
||||
// 工作流定义
|
||||
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;
|
||||
}
|
||||
@ -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 });
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
};
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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<NodeType[]> => {
|
||||
// 模拟 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;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
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<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作流状态
|
||||
*/
|
||||
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<string, any> => {
|
||||
try {
|
||||
return JSON.parse(config);
|
||||
} catch {
|
||||
return {nodes: []};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析转换配置
|
||||
*/
|
||||
parseTransitionConfig: (config: string): Record<string, any> => {
|
||||
try {
|
||||
return JSON.parse(config);
|
||||
} catch {
|
||||
return {transitions: []};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析表单定义
|
||||
*/
|
||||
parseFormDefinition: (config: string): Record<string, any> => {
|
||||
try {
|
||||
return JSON.parse(config);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析图形定义
|
||||
*/
|
||||
parseGraphDefinition: (config: string): Record<string, any> => {
|
||||
try {
|
||||
return JSON.parse(config);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 导出NodePanel中的所有类型
|
||||
export * from './Designer/components/NodePanel/types';
|
||||
@ -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<string, any>;
|
||||
}[];
|
||||
}
|
||||
|
||||
// 流转配置
|
||||
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<WorkflowDefinition>;
|
||||
|
||||
// 工作流配置的解析和序列化工具
|
||||
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<string, any> => {
|
||||
try {
|
||||
return JSON.parse(config);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
parseGraphDefinition: (config: string): Record<string, any> => {
|
||||
try {
|
||||
return JSON.parse(config);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 节点类型查询参数
|
||||
*/
|
||||
export interface NodeTypeQuery extends BaseQuery {
|
||||
enabled?: boolean; // 是否启用
|
||||
category?: string; // 节点类别
|
||||
}
|
||||
|
||||
// 重新导出工作流相关类型
|
||||
export type { WorkflowDefinition } from './Definition/types';
|
||||
export { WorkflowStatus } from './Definition/types';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user