This commit is contained in:
dengqichen 2024-12-16 11:15:09 +08:00
parent ecf12a22a0
commit 2d3cd8db92
4 changed files with 224 additions and 72 deletions

View File

@ -1,7 +1,7 @@
import React, {useEffect} from 'react'; import React, {useEffect, useState} from 'react';
import {Modal, Form, Input, message} from 'antd'; import {Modal, Form, Input, message, Select} from 'antd';
import type {WorkflowDefinition} from '../types'; import type {WorkflowDefinition, WorkflowCategory} from '../types';
import {saveDefinition, updateDefinition} from '../service'; import {saveDefinition, updateDefinition, getWorkflowCategories} from '../service';
interface EditModalProps { interface EditModalProps {
visible: boolean; visible: boolean;
@ -13,27 +13,74 @@ interface EditModalProps {
const EditModal: React.FC<EditModalProps> = ({visible, onClose, onSuccess, record}) => { const EditModal: React.FC<EditModalProps> = ({visible, onClose, onSuccess, record}) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const isEdit = !!record; const isEdit = !!record;
const [categories, setCategories] = useState<WorkflowCategory[]>([]);
const [selectedCategory, setSelectedCategory] = useState<WorkflowCategory>();
useEffect(() => {
if (visible) {
loadCategories();
}
}, [visible]);
useEffect(() => { useEffect(() => {
if (visible && record) { if (visible && record) {
form.setFieldsValue(record); // 找到当前分类
const category = categories.find(c => c.code === record.category);
setSelectedCategory(category);
// 设置表单值使用lable显示
form.setFieldsValue({
...record,
// 分类显示lable
category: {
label: category?.lable,
value: record.category
},
// 触发器显示lable
triggers: record.triggers?.map(triggerCode => ({
label: category?.supportedTriggers.find(t => t.code === triggerCode)?.lable,
value: triggerCode
})) || []
});
} }
}, [visible, record]); }, [visible, record, categories]);
const loadCategories = async () => {
try {
const data = await getWorkflowCategories();
setCategories(data);
} catch (error) {
console.error('加载工作流分类失败:', error);
message.error('加载工作流分类失败');
}
};
const handleCategoryChange = (selected: { value: string, label: string }) => {
const category = categories.find(c => c.code === selected.value);
setSelectedCategory(category);
// 当切换分类时,清空触发器选择
form.setFieldValue('triggers', []);
};
const handleOk = async () => { const handleOk = async () => {
try { try {
const values = await form.validateFields(); const values = await form.validateFields();
const submitData = {
...values,
// 提取code值提交给后端
category: values.category.value,
triggers: values.triggers.map((t: {value: string}) => t.value),
flowVersion: isEdit ? record.flowVersion : 1,
status: isEdit ? record.status : 'DRAFT'
};
if (isEdit) { if (isEdit) {
await updateDefinition(record.id, { await updateDefinition(record.id, {
...record, ...record,
...values, ...submitData,
}); });
} else { } else {
await saveDefinition({ await saveDefinition(submitData as WorkflowDefinition);
...values,
flowVersion: 1,
status: 'DRAFT'
} as WorkflowDefinition);
} }
message.success(isEdit ? '更新成功' : '保存成功'); message.success(isEdit ? '更新成功' : '保存成功');
onSuccess?.(); onSuccess?.();
@ -62,6 +109,42 @@ const EditModal: React.FC<EditModalProps> = ({visible, onClose, onSuccess, recor
layout="vertical" layout="vertical"
preserve={false} preserve={false}
> >
<Form.Item
name="category"
label="流程分类"
rules={[{required: true, message: '请选择流程分类'}]}
>
<Select
placeholder="请选择流程分类"
onChange={handleCategoryChange}
disabled={isEdit}
labelInValue
>
{(categories || []).map(category => (
<Select.Option key={category.code} value={category.code}>
{category.lable}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name="triggers"
label="触发方式"
rules={[{required: true, message: '请选择触发方式'}]}
>
<Select
mode="multiple"
placeholder="请选择触发方式"
disabled={!selectedCategory || isEdit}
labelInValue
>
{(selectedCategory?.supportedTriggers || []).map(trigger => (
<Select.Option key={trigger.code} value={trigger.code}>
{trigger.lable}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
name="name" name="name"
label="流程名称" label="流程名称"

View File

@ -3,7 +3,7 @@ import {Table, Card, Button, Space, Tag, message, Modal} from 'antd';
import {PlusOutlined, ExclamationCircleOutlined} from '@ant-design/icons'; import {PlusOutlined, ExclamationCircleOutlined} from '@ant-design/icons';
import {useNavigate} from 'react-router-dom'; import {useNavigate} from 'react-router-dom';
import * as service from './service'; import * as service from './service';
import type {WorkflowDefinition, WorkflowDefinitionQuery} from './types'; import type {WorkflowDefinition, WorkflowDefinitionQuery, WorkflowCategory} from './types';
import {DEFAULT_PAGE_SIZE, DEFAULT_CURRENT} from '@/utils/page'; import {DEFAULT_PAGE_SIZE, DEFAULT_CURRENT} from '@/utils/page';
import EditModal from './components/EditModal'; import EditModal from './components/EditModal';
@ -20,6 +20,7 @@ const WorkflowDefinitionList: React.FC = () => {
} | null>(null); } | null>(null);
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
const [currentRecord, setCurrentRecord] = useState<WorkflowDefinition>(); const [currentRecord, setCurrentRecord] = useState<WorkflowDefinition>();
const [categories, setCategories] = useState<WorkflowCategory[]>([]);
const [query, setQuery] = useState<WorkflowDefinitionQuery>({ const [query, setQuery] = useState<WorkflowDefinitionQuery>({
pageNum: DEFAULT_CURRENT - 1, pageNum: DEFAULT_CURRENT - 1,
pageSize: DEFAULT_PAGE_SIZE pageSize: DEFAULT_PAGE_SIZE
@ -39,8 +40,19 @@ const WorkflowDefinitionList: React.FC = () => {
} }
}; };
const loadCategories = async () => {
try {
const data = await service.getWorkflowCategories();
setCategories(data);
} catch (error) {
console.error('加载工作流分类失败:', error);
message.error('加载工作流分类失败');
}
};
useEffect(() => { useEffect(() => {
loadData(query); loadData(query);
loadCategories();
}, [query]); }, [query]);
const handleCreateFlow = () => { const handleCreateFlow = () => {
@ -62,20 +74,80 @@ const WorkflowDefinitionList: React.FC = () => {
setCurrentRecord(undefined); setCurrentRecord(undefined);
}; };
const handleDeploy = async (id: number) => {
confirm({
title: '确认发布',
icon: <ExclamationCircleOutlined/>,
content: '确定要发布该流程定义吗?发布后将不能修改。',
onOk: async () => {
try {
await service.publishDefinition(id);
message.success('发布成功');
loadData(query);
} catch (error) {
if (error instanceof Error) {
message.error(error.message);
}
}
},
});
};
const handleDelete = async (id: number) => {
confirm({
title: '确认删除',
icon: <ExclamationCircleOutlined/>,
content: '确定要删除该流程定义吗?删除后不可恢复。',
onOk: async () => {
try {
await service.deleteDefinition(id);
message.success('删除成功');
loadData(query);
} catch (error) {
if (error instanceof Error) {
message.error(error.message);
}
}
},
});
};
const columns = [ const columns = [
{ {
title: '流程名称', title: '流程名称',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
render: (text: string, record: WorkflowDefinition) => (
<a onClick={() => navigate(`/workflow-definitions/${record.id}`)}>{text}</a>
),
}, },
{ {
title: '流程标识', title: '流程标识',
dataIndex: 'key', dataIndex: 'key',
key: 'key', key: 'key',
}, },
{
title: '流程分类',
dataIndex: 'category',
key: 'category',
render: (category: string) => {
const categoryInfo = categories.find(c => c.code === category);
return categoryInfo?.lable || category;
}
},
{
title: '触发方式',
dataIndex: 'triggers',
key: 'triggers',
render: (triggers: string[], record: WorkflowDefinition) => {
const categoryInfo = categories.find(c => c.code === record.category);
return (triggers || [])?.map(triggerCode => {
const triggerInfo = categoryInfo?.supportedTriggers?.find(t => t.code === triggerCode);
return (
<Tag key={triggerCode}>
{triggerInfo?.lable || triggerCode}
</Tag>
);
});
}
},
{ {
title: '版本', title: '版本',
dataIndex: 'flowVersion', dataIndex: 'flowVersion',
@ -117,70 +189,30 @@ const WorkflowDefinitionList: React.FC = () => {
}, },
]; ];
const handleDeploy = async (id: number) => {
confirm({
title: '确认发布',
icon: <ExclamationCircleOutlined/>,
content: '确定要发布该流程定义吗?发布后将不能修改。',
onOk: async () => {
try {
await service.publishDefinition(id);
message.success('发布成功');
loadData(query);
} catch (error) {
if (error instanceof Error) {
message.error(error.message);
}
}
},
});
};
const handleDelete = async (id: number) => {
confirm({
title: '确认删除',
icon: <ExclamationCircleOutlined/>,
content: '确定要删除该流程定义吗?删除后不可恢复。',
onOk: async () => {
try {
await service.deleteDefinition(id);
message.success('删除成功');
loadData(query);
} catch (error) {
if (error instanceof Error) {
message.error(error.message);
}
}
},
});
};
return ( return (
<Card <Card
title={ title={
<Button <Button type="primary" icon={<PlusOutlined/>} onClick={handleCreateFlow}>
type="primary"
icon={<PlusOutlined/>}
onClick={handleCreateFlow}
>
</Button> </Button>
} }
> >
<Table <Table
columns={columns}
dataSource={pageData?.content}
loading={loading} loading={loading}
dataSource={pageData?.content}
columns={columns}
rowKey="id" rowKey="id"
pagination={{ pagination={{
total: pageData?.totalElements, total: pageData?.totalElements,
pageSize: pageData?.size || DEFAULT_PAGE_SIZE, pageSize: query.pageSize,
current: (pageData?.number || 0) + 1, current: query.pageNum + 1,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page, pageSize) => { onChange: (page, pageSize) => {
setQuery({...query, pageNum: page - 1, pageSize}); setQuery({
}, ...query,
pageNum: page - 1,
pageSize
});
}
}} }}
/> />
<EditModal <EditModal

View File

@ -1,6 +1,7 @@
import request from '@/utils/request'; import request from '@/utils/request';
import {WorkflowDefinition, WorkflowDefinitionQuery} from './types'; import {WorkflowDefinition, WorkflowDefinitionQuery} from './types';
import {Page} from '@/types/base'; import {Page} from '@/types/base';
import {WorkflowCategory} from './types'; // Add this line
const DEFINITION_URL = '/api/v1/workflow/definition'; const DEFINITION_URL = '/api/v1/workflow/definition';
@ -28,4 +29,19 @@ export const updateDefinition = (id: number, data: WorkflowDefinition) =>
* @returns Promise<void> * @returns Promise<void>
*/ */
export const publishDefinition = (id: number) => export const publishDefinition = (id: number) =>
request.post<void>(`${DEFINITION_URL}/${id}/published`); request.post<void>(`${DEFINITION_URL}/${id}/published`);
/**
*
* @returns Promise<WorkflowCategory[]>
*/
export const getWorkflowCategories = () =>
request.get<WorkflowCategory[]>(`${DEFINITION_URL}/categories`);
/**
*
* @param id ID
* @returns Promise<WorkflowCategory>
*/
export const getWorkflowCategory = (id: number) =>
request.get<WorkflowCategory>(`${DEFINITION_URL}/categories/${id}`);

View File

@ -1,11 +1,14 @@
import { BaseResponse, BaseQuery } from '@/types/base'; import { BaseResponse, BaseQuery } from '@/types/base';
export interface WorkflowDefinition extends BaseResponse { export interface WorkflowDefinition extends BaseResponse {
id: number;
name: string; name: string;
key: string; key: string;
flowVersion: number; description?: string;
status: string; flowVersion?: number;
description: string; status?: string;
category: string;
triggers: string[];
graph: { graph: {
nodes: any[]; nodes: any[];
edges: any[]; edges: any[];
@ -19,4 +22,22 @@ export interface WorkflowDefinitionQuery extends BaseQuery {
name?: string; name?: string;
key?: string; key?: string;
status?: string; status?: string;
} }
/**
*
*/
export interface WorkflowTrigger {
code: string;
label: string;
}
/**
*
*/
export interface WorkflowCategory {
code: string;
label: string;
description: string;
supportedTriggers: WorkflowTrigger[];
}