引入 Notion 风格的 DESIGN.md 设计系统文件,并全面重构前端 UI: - 主色从 Indigo (#4F46E5) 迁移到 Notion Blue (#0075de) - 页面背景从冷灰 (#F1F5F9) 迁移到暖白 (#f6f5f4) - 侧边栏从深色 (#0F172A) 迁移到白色,活跃项用蓝色指示 - 文字从 Slate 冷色迁移到暖灰系列 (Warm Gray 500/300) - 圆角从 8px 缩小到 4px(按钮/输入),8px(卡片) - 阴影改为多层超轻 Notion 风格(最大 opacity 0.05) - 字体优先使用 Inter,保留中文回退 - 暗色模式适配暖黑色调 (#191918) - 更新 27 个前端文件的硬编码颜色值
200 lines
5.4 KiB
TypeScript
200 lines
5.4 KiB
TypeScript
import { useEffect, useState, useCallback } from 'react';
|
|
import { Button, message, Modal, Space, Table, Tag, theme } from 'antd';
|
|
import { PlusOutlined } from '@ant-design/icons';
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
import {
|
|
listProcessDefinitions,
|
|
createProcessDefinition,
|
|
updateProcessDefinition,
|
|
publishProcessDefinition,
|
|
type ProcessDefinitionInfo,
|
|
type CreateProcessDefinitionRequest,
|
|
} from '../../api/workflowDefinitions';
|
|
import ProcessDesigner from './ProcessDesigner';
|
|
|
|
const statusColors: Record<string, { bg: string; color: string; text: string }> = {
|
|
draft: { bg: '#f6f5f4', color: '#615d59', text: '草稿' },
|
|
published: { bg: '#ecfdf5', color: '#1aae39', text: '已发布' },
|
|
deprecated: { bg: '#fef2f2', color: '#e5534b', text: '已弃用' },
|
|
};
|
|
|
|
export default function ProcessDefinitions() {
|
|
const [data, setData] = useState<ProcessDefinitionInfo[]>([]);
|
|
const [total, setTotal] = useState(0);
|
|
const [page, setPage] = useState(1);
|
|
const [loading, setLoading] = useState(false);
|
|
const [designerOpen, setDesignerOpen] = useState(false);
|
|
const [editingId, setEditingId] = useState<string | null>(null);
|
|
const { token } = theme.useToken();
|
|
const isDark = token.colorBgContainer === '#111827' || token.colorBgContainer === 'rgb(17, 24, 39)';
|
|
|
|
const fetchData = useCallback(async (p = page) => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await listProcessDefinitions(p, 20);
|
|
setData(res.data);
|
|
setTotal(res.total);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [page]);
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, [fetchData]);
|
|
|
|
const handleCreate = () => {
|
|
setEditingId(null);
|
|
setDesignerOpen(true);
|
|
};
|
|
|
|
const handleEdit = (id: string) => {
|
|
setEditingId(id);
|
|
setDesignerOpen(true);
|
|
};
|
|
|
|
const handlePublish = async (id: string) => {
|
|
try {
|
|
await publishProcessDefinition(id);
|
|
message.success('发布成功');
|
|
fetchData();
|
|
} catch {
|
|
message.error('发布失败');
|
|
}
|
|
};
|
|
|
|
const handleSave = async (req: CreateProcessDefinitionRequest, id?: string) => {
|
|
try {
|
|
if (id) {
|
|
await updateProcessDefinition(id, req);
|
|
message.success('更新成功');
|
|
} else {
|
|
await createProcessDefinition(req);
|
|
message.success('创建成功');
|
|
}
|
|
setDesignerOpen(false);
|
|
fetchData();
|
|
} catch {
|
|
message.error(id ? '更新失败' : '创建失败');
|
|
}
|
|
};
|
|
|
|
const columns: ColumnsType<ProcessDefinitionInfo> = [
|
|
{
|
|
title: '名称',
|
|
dataIndex: 'name',
|
|
key: 'name',
|
|
render: (v: string) => <span style={{ fontWeight: 500 }}>{v}</span>,
|
|
},
|
|
{
|
|
title: '编码',
|
|
dataIndex: 'key',
|
|
key: 'key',
|
|
render: (v: string) => (
|
|
<Tag style={{
|
|
background: isDark ? '#1E293B' : '#f6f5f4',
|
|
border: 'none',
|
|
color: isDark ? '#a39e98' : '#615d59',
|
|
fontFamily: 'monospace',
|
|
fontSize: 12,
|
|
}}>
|
|
{v}
|
|
</Tag>
|
|
),
|
|
},
|
|
{ title: '版本', dataIndex: 'version', key: 'version', width: 80 },
|
|
{ title: '分类', dataIndex: 'category', key: 'category', width: 120 },
|
|
{
|
|
title: '状态',
|
|
dataIndex: 'status',
|
|
key: 'status',
|
|
width: 100,
|
|
render: (s: string) => {
|
|
const info = statusColors[s] || { bg: '#f6f5f4', color: '#615d59', text: s };
|
|
return (
|
|
<Tag style={{
|
|
background: info.bg,
|
|
border: 'none',
|
|
color: info.color,
|
|
fontWeight: 500,
|
|
}}>
|
|
{info.text}
|
|
</Tag>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: '操作',
|
|
key: 'action',
|
|
width: 200,
|
|
render: (_, record) => (
|
|
<Space size={4}>
|
|
{record.status === 'draft' && (
|
|
<>
|
|
<Button size="small" type="text" onClick={() => handleEdit(record.id)}>
|
|
编辑
|
|
</Button>
|
|
<Button size="small" type="primary" onClick={() => handlePublish(record.id)}>
|
|
发布
|
|
</Button>
|
|
</>
|
|
)}
|
|
</Space>
|
|
),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<>
|
|
<div style={{
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginBottom: 16,
|
|
}}>
|
|
<span style={{ fontSize: 13, color: isDark ? '#615d59' : '#a39e98' }}>
|
|
共 {total} 个流程定义
|
|
</span>
|
|
<Button type="primary" icon={<PlusOutlined />} onClick={handleCreate}>
|
|
新建流程
|
|
</Button>
|
|
</div>
|
|
|
|
<div style={{
|
|
background: isDark ? '#111827' : '#FFFFFF',
|
|
borderRadius: 12,
|
|
border: `1px solid ${isDark ? '#1E293B' : '#f6f5f4'}`,
|
|
overflow: 'hidden',
|
|
}}>
|
|
<Table
|
|
rowKey="id"
|
|
columns={columns}
|
|
dataSource={data}
|
|
loading={loading}
|
|
pagination={{
|
|
current: page,
|
|
total,
|
|
pageSize: 20,
|
|
onChange: setPage,
|
|
showTotal: (t) => `共 ${t} 条记录`,
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<Modal
|
|
title={editingId ? '编辑流程' : '新建流程'}
|
|
open={designerOpen}
|
|
onCancel={() => setDesignerOpen(false)}
|
|
footer={null}
|
|
width={1200}
|
|
destroyOnHidden
|
|
>
|
|
<ProcessDesigner
|
|
definitionId={editingId}
|
|
onSave={handleSave}
|
|
/>
|
|
</Modal>
|
|
</>
|
|
);
|
|
}
|