节点面板
This commit is contained in:
parent
80ddeb0ecb
commit
db1fb56ced
1
frontend/package-lock.json
generated
1
frontend/package-lock.json
generated
@ -3989,7 +3989,6 @@
|
||||
"resolved": "https://registry.npmmirror.com/less/-/less-4.2.1.tgz",
|
||||
"integrity": "sha512-CasaJidTIhWmjcqv0Uj5vccMI7pJgfD9lMkKtlnTHAdJdYK/7l8pM9tumLyJ0zhbD4KJLo/YvTj+xznQd5NBhg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"copy-anything": "^2.0.1",
|
||||
"parse-node-version": "^1.0.1",
|
||||
|
||||
@ -0,0 +1,125 @@
|
||||
.nodeTabs {
|
||||
height: 100%;
|
||||
|
||||
:global {
|
||||
.ant-tabs {
|
||||
height: 100%;
|
||||
|
||||
.ant-tabs-nav {
|
||||
margin-bottom: 12px;
|
||||
padding: 0 12px;
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.ant-tabs-tab {
|
||||
padding: 12px 0;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.anticon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab-active {
|
||||
.ant-tabs-tab-btn {
|
||||
color: #1890ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-ink-bar {
|
||||
background: #1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-content {
|
||||
height: calc(100% - 46px);
|
||||
padding: 0 12px 12px;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background: #999;
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-empty {
|
||||
margin: 32px 0;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nodeCard {
|
||||
margin-bottom: 16px;
|
||||
cursor: move;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
background: #fff;
|
||||
border: 1px solid #f0f0f0;
|
||||
padding: 12px;
|
||||
|
||||
&:hover {
|
||||
border-color: #1890ff;
|
||||
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.15);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.nodeIcon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 8px;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
transition: all 0.3s;
|
||||
|
||||
.anticon {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .nodeIcon {
|
||||
transform: scale(1.1);
|
||||
|
||||
.anticon {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.nodeName {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
13
frontend/src/pages/Workflow/Definition/Designer/NodePanel.module.less.d.ts
vendored
Normal file
13
frontend/src/pages/Workflow/Definition/Designer/NodePanel.module.less.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
declare namespace NodePanelModuleLessNamespace {
|
||||
export interface INodePanelModuleLess {
|
||||
nodeTabs: string;
|
||||
nodeCard: string;
|
||||
nodeIcon: string;
|
||||
nodeName: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '*.less' {
|
||||
const content: NodePanelModuleLessNamespace.INodePanelModuleLess;
|
||||
export default content;
|
||||
}
|
||||
121
frontend/src/pages/Workflow/Definition/Designer/NodePanel.tsx
Normal file
121
frontend/src/pages/Workflow/Definition/Designer/NodePanel.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Card, Empty, Spin, Tabs, Tooltip} from 'antd';
|
||||
import {NodeCategory, NodeType} from '../../types';
|
||||
import {getNodeTypes} from '../../service';
|
||||
import * as Icons from '@ant-design/icons';
|
||||
import styles from './NodePanel.module.less';
|
||||
|
||||
interface NodePanelProps {
|
||||
onNodeDragStart: (nodeType: NodeType) => void;
|
||||
}
|
||||
|
||||
const NodePanel: React.FC<NodePanelProps> = ({onNodeDragStart}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [nodeTypes, setNodeTypes] = useState<NodeType[]>([]);
|
||||
|
||||
// 获取节点类型列表
|
||||
const fetchNodeTypes = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await getNodeTypes({enabled: true});
|
||||
if (response) {
|
||||
setNodeTypes(response);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchNodeTypes();
|
||||
}, []);
|
||||
|
||||
// 按分类分组节点类型
|
||||
const groupedNodeTypes = nodeTypes.reduce((acc, nodeType) => {
|
||||
const category = nodeType.category;
|
||||
if (!acc[category]) {
|
||||
acc[category] = [];
|
||||
}
|
||||
acc[category].push(nodeType);
|
||||
return acc;
|
||||
}, {} as Record<NodeCategory, NodeType[]>);
|
||||
|
||||
// 渲染图标
|
||||
const renderIcon = (iconName: string) => {
|
||||
// @ts-ignore
|
||||
const Icon = Icons[iconName];
|
||||
return Icon ? <Icon/> : null;
|
||||
};
|
||||
|
||||
// 渲染节点类型卡片
|
||||
const renderNodeTypeCard = (nodeType: NodeType) => (
|
||||
<Tooltip
|
||||
key={nodeType.code}
|
||||
title={nodeType.description}
|
||||
placement="right"
|
||||
mouseEnterDelay={0.5}
|
||||
>
|
||||
<Card
|
||||
className={styles.nodeCard}
|
||||
draggable
|
||||
onDragStart={(e) => {
|
||||
e.dataTransfer.setData('nodeType', JSON.stringify(nodeType));
|
||||
onNodeDragStart(nodeType);
|
||||
}}
|
||||
>
|
||||
<div className={styles.nodeIcon} style={{backgroundColor: nodeType.color}}>
|
||||
{renderIcon(nodeType.icon)}
|
||||
</div>
|
||||
<div className={styles.nodeName}>{nodeType.name}</div>
|
||||
</Card>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
const categoryIcons = {
|
||||
[NodeCategory.TASK]: renderIcon('CodeOutlined'),
|
||||
[NodeCategory.EVENT]: renderIcon('ThunderboltOutlined'),
|
||||
[NodeCategory.GATEWAY]: renderIcon('ForkOutlined')
|
||||
};
|
||||
|
||||
const categoryLabels = {
|
||||
[NodeCategory.TASK]: '任务节点',
|
||||
[NodeCategory.EVENT]: '事件节点',
|
||||
[NodeCategory.GATEWAY]: '网关节点'
|
||||
};
|
||||
|
||||
const tabItems = Object.values(NodeCategory).map(category => ({
|
||||
key: category,
|
||||
label: (
|
||||
<span>
|
||||
{categoryIcons[category]} {categoryLabels[category]}
|
||||
<span style={{color: '#999', fontSize: 12, marginLeft: 4}}>
|
||||
({groupedNodeTypes[category]?.length || 0})
|
||||
</span>
|
||||
</span>
|
||||
),
|
||||
children: groupedNodeTypes[category]?.map(renderNodeTypeCard) || (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description={`暂无${categoryLabels[category]}`}
|
||||
/>
|
||||
)
|
||||
}));
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div style={{textAlign: 'center', padding: '20px 0'}}>
|
||||
<Spin/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
defaultActiveKey={NodeCategory.TASK}
|
||||
items={tabItems}
|
||||
className={styles.nodeTabs}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default NodePanel;
|
||||
@ -0,0 +1,30 @@
|
||||
.container {
|
||||
height: calc(100vh - 180px);
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 2px;
|
||||
background: #fff;
|
||||
|
||||
.sider {
|
||||
border-right: 1px solid #f0f0f0;
|
||||
background: #fff;
|
||||
|
||||
.nodePanel {
|
||||
padding: 16px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
background: #fafafa;
|
||||
|
||||
.graph {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
frontend/src/pages/Workflow/Definition/Designer/index.module.less.d.ts
vendored
Normal file
14
frontend/src/pages/Workflow/Definition/Designer/index.module.less.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
declare namespace IndexModuleLessNamespace {
|
||||
export interface IIndexModuleLess {
|
||||
container: string;
|
||||
sider: string;
|
||||
nodePanel: string;
|
||||
content: string;
|
||||
graph: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '*.less' {
|
||||
const content: IndexModuleLessNamespace.IIndexModuleLess;
|
||||
export default content;
|
||||
}
|
||||
235
frontend/src/pages/Workflow/Definition/Designer/index.tsx
Normal file
235
frontend/src/pages/Workflow/Definition/Designer/index.tsx
Normal file
@ -0,0 +1,235 @@
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import {useNavigate, useParams} from 'react-router-dom';
|
||||
import {Button, Card, Layout, message, Space, Spin} from 'antd';
|
||||
import {ArrowLeftOutlined, SaveOutlined} from '@ant-design/icons';
|
||||
import {getDefinition, updateDefinition} from '../../service';
|
||||
import {WorkflowDefinition, WorkflowStatus, NodeType} from '../../types';
|
||||
import {Graph} from '@antv/x6';
|
||||
import '@antv/x6-react-shape';
|
||||
import styles from './index.module.less';
|
||||
import NodePanel from './NodePanel';
|
||||
|
||||
const {Sider, Content} = Layout;
|
||||
|
||||
const FlowDesigner: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const {id} = useParams<{ id: string }>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [detail, setDetail] = useState<WorkflowDefinition>();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const graphRef = useRef<Graph>();
|
||||
|
||||
// 初始化图形
|
||||
const initGraph = () => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
// 创建画布
|
||||
const graph = new Graph({
|
||||
container: containerRef.current,
|
||||
width: 800,
|
||||
height: 600,
|
||||
grid: {
|
||||
size: 10,
|
||||
visible: true,
|
||||
type: 'dot',
|
||||
args: {
|
||||
color: '#ccc',
|
||||
thickness: 1,
|
||||
},
|
||||
},
|
||||
connecting: {
|
||||
router: 'manhattan',
|
||||
connector: {
|
||||
name: 'rounded',
|
||||
args: {
|
||||
radius: 8,
|
||||
},
|
||||
},
|
||||
anchor: 'center',
|
||||
connectionPoint: 'anchor',
|
||||
allowBlank: false,
|
||||
snap: {
|
||||
radius: 20,
|
||||
},
|
||||
createEdge() {
|
||||
return this.createEdge({
|
||||
attrs: {
|
||||
line: {
|
||||
stroke: '#5F95FF',
|
||||
strokeWidth: 1,
|
||||
targetMarker: {
|
||||
name: 'classic',
|
||||
size: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
router: {
|
||||
name: 'manhattan',
|
||||
},
|
||||
connector: {
|
||||
name: 'rounded',
|
||||
args: {
|
||||
radius: 8,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
highlighting: {
|
||||
magnetAvailable: {
|
||||
name: 'stroke',
|
||||
args: {
|
||||
padding: 4,
|
||||
attrs: {
|
||||
strokeWidth: 4,
|
||||
stroke: '#52c41a',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mousewheel: {
|
||||
enabled: true,
|
||||
modifiers: ['ctrl', 'meta'],
|
||||
minScale: 0.5,
|
||||
maxScale: 2,
|
||||
},
|
||||
interacting: {
|
||||
nodeMovable: true,
|
||||
edgeMovable: true,
|
||||
edgeLabelMovable: true,
|
||||
arrowheadMovable: true,
|
||||
vertexMovable: true,
|
||||
vertexAddable: true,
|
||||
vertexDeletable: true,
|
||||
},
|
||||
scroller: {
|
||||
enabled: true,
|
||||
pannable: true,
|
||||
pageVisible: false,
|
||||
pageBreak: false,
|
||||
},
|
||||
history: {
|
||||
enabled: true,
|
||||
},
|
||||
clipboard: {
|
||||
enabled: true,
|
||||
},
|
||||
keyboard: {
|
||||
enabled: true,
|
||||
},
|
||||
selecting: {
|
||||
enabled: true,
|
||||
multiple: true,
|
||||
rubberband: true,
|
||||
movable: true,
|
||||
showNodeSelectionBox: true,
|
||||
},
|
||||
});
|
||||
|
||||
graphRef.current = graph;
|
||||
|
||||
// 加载流程图数据
|
||||
if (detail) {
|
||||
try {
|
||||
const graphData = JSON.parse(detail.graphDefinition);
|
||||
graph.fromJSON(graphData);
|
||||
} catch (error) {
|
||||
message.error('加载流程图数据失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取详情
|
||||
const fetchDetail = async () => {
|
||||
if (!id) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await getDefinition(parseInt(id));
|
||||
if (response) {
|
||||
setDetail(response);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理保存
|
||||
const handleSave = async () => {
|
||||
if (!id || !detail || !graphRef.current || detail.status !== WorkflowStatus.DRAFT) return;
|
||||
|
||||
try {
|
||||
const graphData = graphRef.current.toJSON();
|
||||
const data = {
|
||||
...detail,
|
||||
graphDefinition: JSON.stringify(graphData)
|
||||
};
|
||||
await updateDefinition(parseInt(id), data);
|
||||
message.success('保存成功');
|
||||
} catch (error) {
|
||||
// 错误已在请求拦截器中处理
|
||||
}
|
||||
};
|
||||
|
||||
// 处理返回
|
||||
const handleBack = () => {
|
||||
navigate('/workflow/definition');
|
||||
};
|
||||
|
||||
// 首次加载
|
||||
useEffect(() => {
|
||||
fetchDetail();
|
||||
}, [id]);
|
||||
|
||||
// 初始化图形
|
||||
useEffect(() => {
|
||||
if (detail && containerRef.current) {
|
||||
initGraph();
|
||||
}
|
||||
}, [detail, containerRef.current]);
|
||||
|
||||
// 处理节点拖拽开始
|
||||
const handleNodeDragStart = (nodeType: NodeType) => {
|
||||
// TODO: 实现节点拖拽创建
|
||||
console.log('Node drag start:', nodeType);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div style={{textAlign: 'center', padding: 100}}>
|
||||
<Spin size="large"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="流程设计器"
|
||||
extra={
|
||||
<Space>
|
||||
<Button
|
||||
icon={<SaveOutlined/>}
|
||||
type="primary"
|
||||
onClick={handleSave}
|
||||
disabled={detail?.status !== WorkflowStatus.DRAFT}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
<Button icon={<ArrowLeftOutlined/>} onClick={handleBack}>
|
||||
返回
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Layout className={styles.container}>
|
||||
<Sider width={280} className={styles.sider}>
|
||||
<NodePanel onNodeDragStart={handleNodeDragStart}/>
|
||||
</Sider>
|
||||
<Content className={styles.content}>
|
||||
<div ref={containerRef} className={styles.graph}/>
|
||||
</Content>
|
||||
</Layout>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default FlowDesigner;
|
||||
159
frontend/src/pages/Workflow/Definition/Edit/index.tsx
Normal file
159
frontend/src/pages/Workflow/Definition/Edit/index.tsx
Normal file
@ -0,0 +1,159 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useNavigate, useParams} from 'react-router-dom';
|
||||
import {Button, Card, Form, Input, message, Space, Spin} from 'antd';
|
||||
import {getDefinition, updateDefinition} from '../../service';
|
||||
import {UpdateWorkflowDefinitionRequest, WorkflowDefinition, WorkflowStatus} from '../../types';
|
||||
import {ArrowLeftOutlined} from '@ant-design/icons';
|
||||
import {WorkflowConfigUtils} from '../../../Workflow/utils';
|
||||
|
||||
const WorkflowDefinitionEdit: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const {id} = useParams<{ id: string }>();
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [detail, setDetail] = useState<WorkflowDefinition>();
|
||||
|
||||
// 获取详情
|
||||
const fetchDetail = async () => {
|
||||
if (!id) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await getDefinition(parseInt(id));
|
||||
if (response) {
|
||||
setDetail(response);
|
||||
// 设置表单初始值
|
||||
form.setFieldsValue({
|
||||
code: response.code,
|
||||
name: response.name,
|
||||
description: response.description,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 首次加载
|
||||
useEffect(() => {
|
||||
fetchDetail();
|
||||
}, [id]);
|
||||
|
||||
// 处理保存
|
||||
const handleSave = async (values: any) => {
|
||||
if (!id || !detail) return;
|
||||
try {
|
||||
// 保留原有配置和状态
|
||||
const data: UpdateWorkflowDefinitionRequest = {
|
||||
...values,
|
||||
status: detail.status,
|
||||
version: detail.version,
|
||||
enabled: detail.enabled,
|
||||
nodeConfig: JSON.stringify(WorkflowConfigUtils.parseNodeConfig(detail.nodeConfig)),
|
||||
transitionConfig: JSON.stringify(WorkflowConfigUtils.parseTransitionConfig(detail.transitionConfig)),
|
||||
formDefinition: JSON.stringify(WorkflowConfigUtils.parseFormDefinition(detail.formDefinition)),
|
||||
graphDefinition: JSON.stringify(WorkflowConfigUtils.parseGraphDefinition(detail.graphDefinition))
|
||||
};
|
||||
await updateDefinition(parseInt(id), data);
|
||||
message.success('保存成功');
|
||||
navigate('/workflow/definition');
|
||||
} catch (error) {
|
||||
// 错误已在请求拦截器中处理
|
||||
}
|
||||
};
|
||||
|
||||
// 处理取消
|
||||
const handleCancel = () => {
|
||||
navigate('/workflow/definition');
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div style={{textAlign: 'center', padding: 100}}>
|
||||
<Spin size="large"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="编辑流程定义"
|
||||
extra={
|
||||
<Button icon={<ArrowLeftOutlined/>} onClick={handleCancel}>
|
||||
返回
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<div style={{maxWidth: 800, margin: '0 auto'}}>
|
||||
{detail?.status !== WorkflowStatus.DRAFT && (
|
||||
<div style={{marginBottom: 24, padding: '12px 24px', backgroundColor: '#fff2f0', border: '1px solid #ffccc7', borderRadius: 4, color: '#ff4d4f'}}>
|
||||
注意:非草稿状态的流程定义不能修改!
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
labelCol={{span: 6}}
|
||||
wrapperCol={{span: 18}}
|
||||
onFinish={handleSave}
|
||||
layout="horizontal"
|
||||
size="large"
|
||||
>
|
||||
<Form.Item
|
||||
name="code"
|
||||
label="流程编码"
|
||||
rules={[
|
||||
{required: true, message: '请输入流程编码'},
|
||||
{pattern: /^[A-Z_]+$/, message: '编码只能包含大写字母和下划线'},
|
||||
]}
|
||||
extra="编码只能包含大写字母和下划线,例如:DEPLOY_WORKFLOW"
|
||||
>
|
||||
<Input
|
||||
placeholder="请输入流程编码"
|
||||
disabled={detail?.status !== WorkflowStatus.DRAFT}
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="流程名称"
|
||||
rules={[{required: true, message: '请输入流程名称'}]}
|
||||
extra="给流程起一个简单易懂的名称"
|
||||
>
|
||||
<Input
|
||||
placeholder="请输入流程名称"
|
||||
disabled={detail?.status !== WorkflowStatus.DRAFT}
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="description"
|
||||
label="流程描述"
|
||||
extra="详细描述流程的用途、步骤和注意事项"
|
||||
>
|
||||
<Input.TextArea
|
||||
placeholder="请输入流程描述"
|
||||
disabled={detail?.status !== WorkflowStatus.DRAFT}
|
||||
allowClear
|
||||
rows={4}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item wrapperCol={{offset: 6, span: 18}}>
|
||||
<Space size="large">
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
disabled={detail?.status !== WorkflowStatus.DRAFT}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
<Button onClick={handleCancel}>取消</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkflowDefinitionEdit;
|
||||
366
frontend/src/pages/Workflow/Definition/index.tsx
Normal file
366
frontend/src/pages/Workflow/Definition/index.tsx
Normal file
@ -0,0 +1,366 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Button, Card, Form, Input, message, Modal, Select, Space, Table, Tag} from 'antd';
|
||||
import {useNavigate} from 'react-router-dom';
|
||||
import {
|
||||
createDefinition,
|
||||
deleteDefinition,
|
||||
disableDefinition,
|
||||
enableDefinition,
|
||||
getDefinitions,
|
||||
publishDefinition
|
||||
} from '../service';
|
||||
import {
|
||||
CreateWorkflowDefinitionRequest,
|
||||
WorkflowDefinition,
|
||||
WorkflowStatus,
|
||||
WorkflowDefinitionBase
|
||||
} from '../types';
|
||||
import {DeleteOutlined, EditOutlined, PlusOutlined} from '@ant-design/icons';
|
||||
|
||||
const {confirm} = Modal;
|
||||
|
||||
const WorkflowDefinitionList: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [list, setList] = useState<WorkflowDefinition[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [current, setCurrent] = useState(1);
|
||||
const [size, setSize] = useState(10);
|
||||
const [createModalVisible, setCreateModalVisible] = useState(false);
|
||||
|
||||
// 获取列表数据
|
||||
const fetchList = async (page = current, pageSize = size) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = {
|
||||
page: page - 1, // 后端页码从0开始
|
||||
size: pageSize,
|
||||
...form.getFieldsValue()
|
||||
};
|
||||
const response = await getDefinitions(params);
|
||||
if (response?.content) {
|
||||
setList(response.content);
|
||||
setTotal(response.totalElements);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 首次加载
|
||||
useEffect(() => {
|
||||
fetchList();
|
||||
}, []);
|
||||
|
||||
// 处理表格变化
|
||||
const handleTableChange = (pagination: any) => {
|
||||
setCurrent(pagination.current);
|
||||
setSize(pagination.pageSize);
|
||||
fetchList(pagination.current, pagination.pageSize);
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = () => {
|
||||
setCurrent(1);
|
||||
fetchList(1);
|
||||
};
|
||||
|
||||
// 处理重置
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
setCurrent(1);
|
||||
fetchList(1);
|
||||
};
|
||||
|
||||
// 处理创建
|
||||
const handleCreate = async (values: WorkflowDefinitionBase) => {
|
||||
try {
|
||||
// 设置系统字段的默认值
|
||||
const data = {
|
||||
...values,
|
||||
status: WorkflowStatus.DRAFT,
|
||||
version: 1,
|
||||
enabled: true,
|
||||
nodeConfig: JSON.stringify({
|
||||
nodes: []
|
||||
}),
|
||||
transitionConfig: JSON.stringify({
|
||||
transitions: []
|
||||
}),
|
||||
formDefinition: JSON.stringify({}),
|
||||
graphDefinition: JSON.stringify({})
|
||||
};
|
||||
await createDefinition(data);
|
||||
message.success('创建成功');
|
||||
setCreateModalVisible(false);
|
||||
fetchList();
|
||||
} catch (error) {
|
||||
// 错误已在请求拦截器中处理
|
||||
}
|
||||
};
|
||||
|
||||
// 处理删除
|
||||
const handleDelete = (record: WorkflowDefinition) => {
|
||||
confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除工作流"${record.name}"吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await deleteDefinition(record.id);
|
||||
message.success('删除成功');
|
||||
fetchList();
|
||||
} catch (error) {
|
||||
// 错误已在请求拦截器中处理
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 处理发布
|
||||
const handlePublish = async (record: WorkflowDefinition) => {
|
||||
try {
|
||||
await publishDefinition(record.id);
|
||||
message.success('发布成功');
|
||||
fetchList();
|
||||
} catch (error) {
|
||||
// 错误已在请求拦截器中处理
|
||||
}
|
||||
};
|
||||
|
||||
// 处理启用/禁用
|
||||
const handleToggleEnable = async (record: WorkflowDefinition) => {
|
||||
try {
|
||||
if (record.enabled) {
|
||||
await disableDefinition(record.id);
|
||||
message.success('禁用成功');
|
||||
} else {
|
||||
await enableDefinition(record.id);
|
||||
message.success('启用成功');
|
||||
}
|
||||
fetchList();
|
||||
} catch (error) {
|
||||
// 错误已在请求拦截器中处理
|
||||
}
|
||||
};
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '编码',
|
||||
dataIndex: 'code',
|
||||
key: 'code',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '版本',
|
||||
dataIndex: 'version',
|
||||
key: 'version',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (status: WorkflowStatus) => {
|
||||
const statusMap = {
|
||||
[WorkflowStatus.DRAFT]: {color: 'default', text: '草稿'},
|
||||
[WorkflowStatus.PUBLISHED]: {color: 'success', text: '已发布'},
|
||||
[WorkflowStatus.DISABLED]: {color: 'error', text: '已禁用'},
|
||||
};
|
||||
const {color, text} = statusMap[status];
|
||||
return <Tag color={color}>{text}</Tag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '是否启用',
|
||||
dataIndex: 'enabled',
|
||||
key: 'enabled',
|
||||
width: 100,
|
||||
render: (enabled: boolean) => (
|
||||
<Tag color={enabled ? 'success' : 'default'}>{enabled ? '已启用' : '已禁用'}</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right' as const,
|
||||
width: 380,
|
||||
render: (_: any, record: WorkflowDefinition) => (
|
||||
<Space>
|
||||
<Button
|
||||
type="link"
|
||||
icon={<EditOutlined/>}
|
||||
onClick={() => navigate(`/workflow/definition/edit/${record.id}`)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => navigate(`/workflow/definition/designer/${record.id}`)}
|
||||
>
|
||||
设计
|
||||
</Button>
|
||||
{record.status === WorkflowStatus.DRAFT && (
|
||||
<Button type="link" onClick={() => handlePublish(record)}>
|
||||
发布
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => handleToggleEnable(record)}
|
||||
>
|
||||
{record.enabled ? '禁用' : '启用'}
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
icon={<DeleteOutlined/>}
|
||||
onClick={() => handleDelete(record)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card title="流程定义">
|
||||
{/* 搜索表单 */}
|
||||
<Form form={form} layout="inline" style={{marginBottom: 16}}>
|
||||
<Form.Item name="keyword" label="关键字">
|
||||
<Input placeholder="请输入编码或名称" allowClear/>
|
||||
</Form.Item>
|
||||
<Form.Item name="status" label="状态">
|
||||
<Select
|
||||
placeholder="请选择状态"
|
||||
allowClear
|
||||
style={{width: 150}}
|
||||
options={[
|
||||
{label: '草稿', value: WorkflowStatus.DRAFT},
|
||||
{label: '已发布', value: WorkflowStatus.PUBLISHED},
|
||||
{label: '已禁用', value: WorkflowStatus.DISABLED},
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="enabled" label="是否启用">
|
||||
<Select
|
||||
placeholder="请选择是否启用"
|
||||
allowClear
|
||||
style={{width: 150}}
|
||||
options={[
|
||||
{label: '已启用', value: true},
|
||||
{label: '已禁用', value: false},
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" onClick={handleSearch}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button onClick={handleReset}>重置</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
{/* 工具栏 */}
|
||||
<div style={{marginBottom: 16}}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined/>}
|
||||
onClick={() => setCreateModalVisible(true)}
|
||||
>
|
||||
新建流程
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 列表 */}
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={list}
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
scroll={{ x: 1500 }}
|
||||
pagination={{
|
||||
current,
|
||||
pageSize: size,
|
||||
total,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
|
||||
{/* 创建表单弹窗 */}
|
||||
<Modal
|
||||
title="新建流程"
|
||||
open={createModalVisible}
|
||||
onCancel={() => setCreateModalVisible(false)}
|
||||
footer={null}
|
||||
>
|
||||
<Form
|
||||
labelCol={{span: 4}}
|
||||
wrapperCol={{span: 20}}
|
||||
onFinish={handleCreate}
|
||||
>
|
||||
<Form.Item
|
||||
name="code"
|
||||
label="编码"
|
||||
rules={[
|
||||
{required: true, message: '请输入流程编码'},
|
||||
{pattern: /^[A-Z_]+$/, message: '编码只能包含大写字母和下划线'},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入流程编码"/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="名称"
|
||||
rules={[{required: true, message: '请输入流程名称'}]}
|
||||
>
|
||||
<Input placeholder="请输入流程名称"/>
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="描述">
|
||||
<Input.TextArea placeholder="请输入流程描述"/>
|
||||
</Form.Item>
|
||||
<Form.Item wrapperCol={{offset: 4, span: 20}}>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit">
|
||||
确定
|
||||
</Button>
|
||||
<Button onClick={() => setCreateModalVisible(false)}>
|
||||
取消
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkflowDefinitionList;
|
||||
12
frontend/src/pages/Workflow/Instance/index.tsx
Normal file
12
frontend/src/pages/Workflow/Instance/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import {Card} from 'antd';
|
||||
|
||||
const WorkflowInstance: React.FC = () => {
|
||||
return (
|
||||
<Card title="流程实例">
|
||||
<div>流程实例列表页面</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkflowInstance;
|
||||
12
frontend/src/pages/Workflow/Monitor/index.tsx
Normal file
12
frontend/src/pages/Workflow/Monitor/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import {Card} from 'antd';
|
||||
|
||||
const WorkflowMonitor: React.FC = () => {
|
||||
return (
|
||||
<Card title="流程监控">
|
||||
<div>流程监控页面</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkflowMonitor;
|
||||
56
frontend/src/pages/Workflow/service.ts
Normal file
56
frontend/src/pages/Workflow/service.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import request from '../../utils/request';
|
||||
import {
|
||||
CreateWorkflowDefinitionRequest,
|
||||
UpdateWorkflowDefinitionRequest,
|
||||
WorkflowDefinition,
|
||||
WorkflowDefinitionPage,
|
||||
WorkflowDefinitionQuery,
|
||||
NodeType,
|
||||
NodeTypeQuery
|
||||
} from './types';
|
||||
|
||||
const WORKFLOW_DEFINITION_URL = '/api/v1/workflow-definitions';
|
||||
const NODE_TYPE_URL = '/api/v1/node-types';
|
||||
|
||||
// 创建工作流定义
|
||||
export const createDefinition = (data: CreateWorkflowDefinitionRequest) =>
|
||||
request.post<WorkflowDefinition>(WORKFLOW_DEFINITION_URL, data);
|
||||
|
||||
// 更新工作流定义
|
||||
export const updateDefinition = (id: number, data: UpdateWorkflowDefinitionRequest) =>
|
||||
request.put<WorkflowDefinition>(`${WORKFLOW_DEFINITION_URL}/${id}`, data);
|
||||
|
||||
// 获取工作流定义列表
|
||||
export const getDefinitions = (params?: WorkflowDefinitionQuery) =>
|
||||
request.get<WorkflowDefinitionPage>(`${WORKFLOW_DEFINITION_URL}/page`, { params });
|
||||
|
||||
// 获取工作流定义详情
|
||||
export const getDefinition = (id: number) =>
|
||||
request.get<WorkflowDefinition>(`${WORKFLOW_DEFINITION_URL}/${id}`);
|
||||
|
||||
// 删除工作流定义
|
||||
export const deleteDefinition = (id: number) =>
|
||||
request.delete(`${WORKFLOW_DEFINITION_URL}/${id}`);
|
||||
|
||||
// 发布工作流定义
|
||||
export const publishDefinition = (id: number) =>
|
||||
request.post<WorkflowDefinition>(`${WORKFLOW_DEFINITION_URL}/${id}/publish`);
|
||||
|
||||
// 禁用工作流定义
|
||||
export const disableDefinition = (id: number) =>
|
||||
request.post<WorkflowDefinition>(`${WORKFLOW_DEFINITION_URL}/${id}/disable`);
|
||||
|
||||
// 启用工作流定义
|
||||
export const enableDefinition = (id: number) =>
|
||||
request.post<WorkflowDefinition>(`${WORKFLOW_DEFINITION_URL}/${id}/enable`);
|
||||
|
||||
// 创建新版本
|
||||
export const createVersion = (id: number) =>
|
||||
request.post<WorkflowDefinition>(`${WORKFLOW_DEFINITION_URL}/${id}/versions`);
|
||||
|
||||
// 节点类型相关接口
|
||||
export const getNodeTypes = (params?: NodeTypeQuery) =>
|
||||
request.get<NodeType[]>(NODE_TYPE_URL, { params });
|
||||
|
||||
export const getNodeTypeExecutors = (code: string) =>
|
||||
request.get<NodeType>(`${NODE_TYPE_URL}/${code}/executors`);
|
||||
151
frontend/src/pages/Workflow/types.ts
Normal file
151
frontend/src/pages/Workflow/types.ts
Normal file
@ -0,0 +1,151 @@
|
||||
import {Page} from '../../types/base';
|
||||
|
||||
// 工作流状态枚举
|
||||
export enum WorkflowStatus {
|
||||
DRAFT = 'DRAFT', // 草稿
|
||||
PUBLISHED = 'PUBLISHED', // 已发布
|
||||
DISABLED = 'DISABLED' // 已禁用
|
||||
}
|
||||
|
||||
// 节点类型分类枚举
|
||||
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;
|
||||
code: string;
|
||||
name: string;
|
||||
category: NodeCategory;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
icon: string;
|
||||
color: string;
|
||||
executors: NodeExecutor[];
|
||||
configSchema: string; // JSON Schema
|
||||
defaultConfig: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
}
|
||||
|
||||
// 工作流定义查询参数
|
||||
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 {
|
||||
code: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// 工作流定义
|
||||
export interface WorkflowDefinition extends WorkflowDefinitionBase {
|
||||
id: number;
|
||||
version: 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;
|
||||
}
|
||||
|
||||
// 创建工作流定义请求
|
||||
export interface CreateWorkflowDefinitionRequest extends WorkflowDefinitionBase {
|
||||
status: WorkflowStatus;
|
||||
version: number;
|
||||
enabled: boolean;
|
||||
nodeConfig: string; // JSON string of NodeConfig
|
||||
transitionConfig: string; // JSON string of TransitionConfig
|
||||
formDefinition: string; // JSON string
|
||||
graphDefinition: string; // JSON string
|
||||
}
|
||||
|
||||
// 更新工作流定义请求
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
};
|
||||
33
frontend/src/pages/Workflow/utils.ts
Normal file
33
frontend/src/pages/Workflow/utils.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {NodeConfig, TransitionConfig} from './types';
|
||||
|
||||
// 工作流配置的解析和序列化工具
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -32,11 +32,11 @@ const Menu = lazy(() => import('../pages/System/Menu'));
|
||||
const Department = lazy(() => import('../pages/System/Department'));
|
||||
const External = lazy(() => import('../pages/System/External'));
|
||||
const X6Test = lazy(() => import('../pages/X6Test'));
|
||||
// const WorkflowDefinition = lazy(() => import('../pages/Workflow/Definition'));
|
||||
// const WorkflowDefinitionEdit = lazy(() => import('../pages/Workflow/Definition/Edit'));
|
||||
// const WorkflowInstance = lazy(() => import('../pages/Workflow/Instance'));
|
||||
// const WorkflowMonitor = lazy(() => import('../pages/Workflow/Monitor'));
|
||||
// const FlowDesigner = lazy(() => import('../pages/Workflow/Definition/Designer'));
|
||||
const WorkflowDefinition = lazy(() => import('../pages/Workflow/Definition'));
|
||||
const WorkflowDefinitionEdit = lazy(() => import('../pages/Workflow/Definition/Edit'));
|
||||
const WorkflowInstance = lazy(() => import('../pages/Workflow/Instance'));
|
||||
const WorkflowMonitor = lazy(() => import('../pages/Workflow/Monitor'));
|
||||
const FlowDesigner = lazy(() => import('../pages/Workflow/Definition/Designer'));
|
||||
|
||||
// 创建路由
|
||||
const router = createBrowserRouter([
|
||||
@ -117,56 +117,56 @@ const router = createBrowserRouter([
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
// {
|
||||
// path: 'workflow',
|
||||
// children: [
|
||||
// {
|
||||
// path: 'definition',
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// element: (
|
||||
// <Suspense fallback={<LoadingComponent/>}>
|
||||
// <WorkflowDefinition/>
|
||||
// </Suspense>
|
||||
// )
|
||||
// },
|
||||
// {
|
||||
// path: 'edit/:id?',
|
||||
// element: (
|
||||
// <Suspense fallback={<LoadingComponent/>}>
|
||||
// <WorkflowDefinitionEdit/>
|
||||
// </Suspense>
|
||||
// )
|
||||
// },
|
||||
// {
|
||||
// path: 'designer/:id?',
|
||||
// element: (
|
||||
// <Suspense fallback={<LoadingComponent/>}>
|
||||
// <FlowDesigner/>
|
||||
// </Suspense>
|
||||
// )
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: 'instance',
|
||||
// element: (
|
||||
// <Suspense fallback={<LoadingComponent/>}>
|
||||
// <WorkflowInstance/>
|
||||
// </Suspense>
|
||||
// )
|
||||
// },
|
||||
// {
|
||||
// path: 'monitor',
|
||||
// element: (
|
||||
// <Suspense fallback={<LoadingComponent/>}>
|
||||
// <WorkflowMonitor/>
|
||||
// </Suspense>
|
||||
// )
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
path: 'workflow',
|
||||
children: [
|
||||
{
|
||||
path: 'definition',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<WorkflowDefinition/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'edit/:id?',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<WorkflowDefinitionEdit/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'designer/:id?',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<FlowDesigner/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'instance',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<WorkflowInstance/>
|
||||
</Suspense>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'monitor',
|
||||
element: (
|
||||
<Suspense fallback={<LoadingComponent/>}>
|
||||
<WorkflowMonitor/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <Navigate to="/dashboard"/>
|
||||
|
||||
@ -13,4 +13,22 @@ export interface BaseQuery {
|
||||
enabled?: boolean;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
}
|
||||
|
||||
export interface Page<T> {
|
||||
content: T[];
|
||||
totalElements: number;
|
||||
totalPages: number;
|
||||
size: number;
|
||||
number: number;
|
||||
empty: boolean;
|
||||
first: boolean;
|
||||
last: boolean;
|
||||
}
|
||||
|
||||
export interface Response<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
success: boolean;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user