增加工具栏提示。

This commit is contained in:
dengqichen 2024-12-12 18:04:47 +08:00
parent 4f938e1c1c
commit 61661e2a09
12 changed files with 939 additions and 110 deletions

View File

@ -0,0 +1,241 @@
import React, {useState, useEffect} from 'react';
import {Card, Collapse, Tooltip, message} from 'antd';
import type {NodeDefinition, NodeCategory} from '../types';
import {
PlayCircleOutlined,
StopOutlined,
UserOutlined,
ApiOutlined,
CodeOutlined,
NodeIndexOutlined,
SplitCellsOutlined,
AppstoreOutlined,
BranchesOutlined
} from '@ant-design/icons';
import {getNodeDefinitionList} from "@/pages/Workflow/Definition/Design/service";
const {Panel} = Collapse;
// 图标映射配置
const iconMap: Record<string, any> = {
'play-circle': PlayCircleOutlined,
'stop': StopOutlined,
'user': UserOutlined,
'api': ApiOutlined,
'code': CodeOutlined,
'fork': NodeIndexOutlined,
'branches': SplitCellsOutlined,
'apartment': AppstoreOutlined
};
// 节点类型到图标的映射
const typeIconMap: Record<string, any> = {
'START_EVENT': PlayCircleOutlined,
'END_EVENT': StopOutlined,
'USER_TASK': UserOutlined,
'SERVICE_TASK': ApiOutlined,
'SCRIPT_TASK': CodeOutlined,
'EXCLUSIVE_GATEWAY': NodeIndexOutlined,
'PARALLEL_GATEWAY': SplitCellsOutlined,
'SUB_PROCESS': AppstoreOutlined,
'CALL_ACTIVITY': BranchesOutlined
};
// 节点分类配置
const categoryConfig: Record<NodeCategory, {
label: string;
key: string;
}> = {
EVENT: {label: '事件节点', key: '1'},
TASK: {label: '任务节点', key: '2'},
GATEWAY: {label: '网关节点', key: '3'},
CONTAINER: {label: '容器节点', key: '4'},
};
interface NodePanelProps {
onNodeDragStart?: (node: NodeDefinition, e: React.DragEvent) => void;
}
const NodePanel: React.FC<NodePanelProps> = ({onNodeDragStart}) => {
const [loading, setLoading] = useState(false);
const [nodeDefinitions, setNodeDefinitions] = useState<NodeDefinition[]>([]);
// 加载节点定义列表
const loadNodeDefinitions = async () => {
setLoading(true);
try {
const data = await getNodeDefinitionList();
setNodeDefinitions(data);
} catch (error) {
if (error instanceof Error) {
message.error(error.message);
}
} finally {
setLoading(false);
}
};
useEffect(() => {
loadNodeDefinitions();
}, []);
// 按分类对节点进行分组
const groupedNodes = nodeDefinitions.reduce((acc, node) => {
if (!acc[node.category]) {
acc[node.category] = [];
}
acc[node.category].push(node);
return acc;
}, {} as Record<NodeCategory, NodeDefinition[]>);
// 处理节点拖拽开始事件
const handleDragStart = (node: NodeDefinition, e: React.DragEvent) => {
e.dataTransfer.setData('node', JSON.stringify(node));
onNodeDragStart?.(node, e);
};
// 渲染节点图标
const renderNodeIcon = (node: NodeDefinition) => {
const iconName = node.graphConfig.uiSchema.style.icon;
// 首先尝试使用配置的图标
let IconComponent = iconMap[iconName];
// 如果没有找到对应的图标,使用节点类型对应的默认图标
if (!IconComponent) {
IconComponent = typeIconMap[node.type] || AppstoreOutlined;
}
return (
<IconComponent
style={{
color: node.graphConfig.uiSchema.style.iconColor || '#1890ff',
fontSize: '16px',
marginRight: '6px'
}}
/>
);
};
const getNodeItemStyle = (node: NodeDefinition) => ({
width: '100%',
padding: '10px 12px',
border: `1px solid ${node.graphConfig.uiSchema.style.stroke}`,
borderRadius: '6px',
cursor: 'move',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '10px',
background: node.graphConfig.uiSchema.style.fill,
transition: 'all 0.3s',
boxShadow: '0 1px 2px rgba(0,0,0,0.05)',
'&:hover': {
transform: 'translateY(-1px)',
boxShadow: '0 3px 6px rgba(0,0,0,0.1)',
}
});
const tooltipStyle = {
maxWidth: '300px'
};
const tooltipOverlayInnerStyle = {
padding: '12px 16px'
};
// 构建折叠面板的 items
const collapseItems = Object.entries(categoryConfig).map(([category, {label, key}]) => ({
key,
label: <span style={{ fontSize: '14px', fontWeight: 500 }}>{label}</span>,
children: (
<div style={{
display: 'flex',
flexDirection: 'column',
gap: '12px',
padding: '8px 4px'
}}>
{groupedNodes[category as NodeCategory]?.map(node => (
<Tooltip
key={node.id}
title={
<div>
<div style={{ fontSize: '14px', fontWeight: 500 }}>{node.description}</div>
<div style={{ marginTop: 12 }}>
<div style={{ fontSize: '13px', color: '#8c8c8c' }}></div>
<ul style={{
paddingLeft: 16,
margin: '8px 0',
fontSize: '13px',
color: '#595959'
}}>
{node.graphConfig.details.features.map((feature, index) => (
<li key={index}>{feature}</li>
))}
</ul>
</div>
</div>
}
overlayStyle={tooltipStyle}
overlayInnerStyle={tooltipOverlayInnerStyle}
placement="right"
arrow={false}
>
<div
draggable
onDragStart={(e) => handleDragStart(node, e)}
style={getNodeItemStyle(node)}
>
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '6px'
}}>
{renderNodeIcon(node)}
<span style={{
fontSize: '13px',
color: '#262626',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}>{node.name}</span>
</div>
</div>
</Tooltip>
))}
</div>
),
}));
return (
<Card
title="流程节点"
size="small"
loading={loading}
styles={{
header: {
padding: '12px 16px',
fontSize: '15px',
fontWeight: 500,
borderBottom: '1px solid #f0f0f0',
},
body: {
padding: '12px',
height: 'calc(100vh - 200px)',
overflowY: 'auto'
}
}}
>
<Collapse
defaultActiveKey={['1', '2', '3', '4']}
ghost
items={collapseItems}
style={{
background: 'transparent',
}}
/>
</Card>
);
};
export default NodePanel;

View File

@ -0,0 +1,263 @@
import React, { useEffect, useState, useRef } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { Button, Space, Card, Row, Col } from 'antd';
import { ArrowLeftOutlined, SaveOutlined, PlayCircleOutlined } from '@ant-design/icons';
import { Graph } from '@antv/x6';
import { getDefinitionDetail } from '../service';
import NodePanel from './components/NodePanel';
import { NodeDefinition } from './types';
const WorkflowDesign: React.FC = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const [title, setTitle] = useState<string>('工作流设计');
const graphContainerRef = useRef<HTMLDivElement>(null);
const [graph, setGraph] = useState<Graph | null>(null);
useEffect(() => {
if (id) {
loadDefinitionDetail();
}
}, [id]);
useEffect(() => {
if (graphContainerRef.current) {
const graph = new Graph({
container: graphContainerRef.current,
grid: {
size: 10,
visible: true,
type: 'dot',
args: {
color: '#a0a0a0',
thickness: 1,
},
},
connecting: {
router: 'manhattan',
connector: {
name: 'rounded',
args: {
radius: 8,
},
},
anchor: 'center',
connectionPoint: 'anchor',
allowBlank: false,
snap: true,
createEdge() {
return this.createEdge({
attrs: {
line: {
stroke: '#1890ff',
strokeWidth: 2,
targetMarker: {
name: 'classic',
size: 8,
},
},
},
});
},
},
highlighting: {
magnetAvailable: {
name: 'stroke',
args: {
padding: 4,
attrs: {
strokeWidth: 4,
stroke: '#52c41a',
},
},
},
},
snapline: true,
history: true,
clipboard: true,
selecting: true,
keyboard: true,
background: {
color: '#f5f5f5',
},
});
setGraph(graph);
return () => {
graph.dispose();
};
}
}, [graphContainerRef]);
const loadDefinitionDetail = async () => {
try {
const response = await getDefinitionDetail(id);
if (response.success) {
setTitle(`工作流设计 - ${response.data.name}`);
}
} catch (error) {
console.error('Failed to load workflow definition:', error);
}
};
const handleNodeDragStart = (node: NodeDefinition, e: React.DragEvent) => {
if (graph) {
const { clientX, clientY } = e;
const point = graph.clientToLocal({ x: clientX, y: clientY });
const nodeConfig = {
shape: node.graphConfig.uiSchema.shape,
width: node.graphConfig.uiSchema.size.width,
height: node.graphConfig.uiSchema.size.height,
attrs: {
body: {
fill: node.graphConfig.uiSchema.style.fill,
stroke: node.graphConfig.uiSchema.style.stroke,
strokeWidth: node.graphConfig.uiSchema.style.strokeWidth,
},
label: {
text: node.name,
fill: '#000000',
fontSize: 12,
},
},
ports: {
groups: {
top: {
position: 'top',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
right: {
position: 'right',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
left: {
position: 'left',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
},
items: [
{ group: 'top' },
{ group: 'right' },
{ group: 'bottom' },
{ group: 'left' },
],
},
};
graph.addNode({
...nodeConfig,
x: point.x,
y: point.y,
});
}
};
return (
<div style={{ padding: '24px' }}>
<Card
title={title}
extra={
<Space>
<Button
icon={<SaveOutlined />}
type="primary"
onClick={() => console.log('Save workflow')}
>
</Button>
<Button
icon={<PlayCircleOutlined />}
onClick={() => console.log('Deploy workflow')}
>
</Button>
<Button
icon={<ArrowLeftOutlined />}
onClick={() => navigate('/workflow/definition')}
>
</Button>
</Space>
}
>
<Row gutter={16}>
<Col span={6}>
<NodePanel onNodeDragStart={handleNodeDragStart} />
</Col>
<Col span={18}>
<Card
size="small"
bodyStyle={{
padding: 0,
height: 'calc(100vh - 250px)',
background: '#f5f5f5',
border: '1px solid #d9d9d9',
borderRadius: '4px',
}}
>
<div
ref={graphContainerRef}
style={{
width: '100%',
height: '100%',
}}
/>
</Card>
</Col>
</Row>
</Card>
</div>
);
};
export default WorkflowDesign;

View File

@ -0,0 +1,11 @@
// 该文件已不再需要,可以删除
import request from '@/utils/request';
const NODE_DEFINITION_URL = '/api/v1/workflow/node-definition';
/**
*
*/
export const getNodeDefinitionList = () =>
request.get(`${NODE_DEFINITION_URL}/list`);

View File

@ -0,0 +1,77 @@
// 节点类型
export type NodeType = 'START_EVENT' | 'END_EVENT' | 'USER_TASK' | 'SERVICE_TASK' | 'SCRIPT_TASK' | 'EXCLUSIVE_GATEWAY' | 'PARALLEL_GATEWAY' | 'SUB_PROCESS' | 'CALL_ACTIVITY';
// 节点分类
export type NodeCategory = 'EVENT' | 'TASK' | 'GATEWAY' | 'CONTAINER';
// 节点定义
export interface NodeDefinition {
id: number;
type: NodeType;
name: string;
description: string;
category: NodeCategory;
graphConfig: {
code: string;
name: string;
description: string;
details: {
description: string;
features: string[];
scenarios: string[];
};
configSchema: {
type: string;
properties: Record<string, any>;
required: string[];
};
uiSchema: {
shape: 'circle' | 'rectangle' | 'diamond';
size: {
width: number;
height: number;
};
style: {
fill: string;
stroke: string;
strokeWidth: number;
icon: string;
iconColor: string;
};
ports: {
groups: {
in?: {
position: string;
attrs: {
circle: {
r: number;
fill: string;
stroke: string;
};
};
};
out?: {
position: string;
attrs: {
circle: {
r: number;
fill: string;
stroke: string;
};
};
};
};
};
};
};
orderNum: number;
enabled: boolean;
}
// 节点列表响应
export interface NodeDefinitionResponse {
success: boolean;
message: string;
data: NodeDefinition[];
code: number;
}

View File

@ -1,100 +0,0 @@
import React from 'react';
import { Table, Card, Button, Space, Tag, message } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { useRequest } from 'ahooks';
import { history } from 'umi';
import { queryWorkflowDefinitions } from './service';
const WorkflowDefinitionList: React.FC = () => {
const { data, loading, refresh } = useRequest(queryWorkflowDefinitions, {
defaultParams: [{ current: 1, pageSize: 10 }],
});
const columns = [
{
title: '流程名称',
dataIndex: 'name',
key: 'name',
render: (text: string, record: any) => (
<a onClick={() => history.push(`/workflow/definition/detail/${record.id}`)}>{text}</a>
),
},
{
title: '流程标识',
dataIndex: 'key',
key: 'key',
},
{
title: '版本',
dataIndex: 'flowVersion',
key: 'flowVersion',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => (
<Tag color={status === 'DRAFT' ? 'orange' : 'green'}>
{status === 'DRAFT' ? '草稿' : '已发布'}
</Tag>
),
},
{
title: '描述',
dataIndex: 'description',
key: 'description',
ellipsis: true,
},
{
title: '操作',
key: 'action',
render: (_: any, record: any) => (
<Space size="middle">
<a onClick={() => history.push(`/workflow/definition/edit/${record.id}`)}></a>
<a onClick={() => handleDeploy(record.id)}></a>
<a onClick={() => handleDelete(record.id)}></a>
</Space>
),
},
];
const handleDeploy = async (id: number) => {
message.success('发布成功');
refresh();
};
const handleDelete = async (id: number) => {
message.success('删除成功');
refresh();
};
return (
<Card
title="流程定义列表"
extra={
<Button
type="primary"
icon={<PlusOutlined />}
onClick={() => history.push('/workflow/definition/create')}
>
</Button>
}
>
<Table
columns={columns}
dataSource={data?.content}
loading={loading}
rowKey="id"
pagination={{
total: data?.totalElements,
pageSize: 10,
showSizeChanger: true,
showQuickJumper: true,
}}
/>
</Card>
);
};
export default WorkflowDefinitionList;

View File

@ -0,0 +1,90 @@
import React, {useEffect} from 'react';
import {Modal, Form, Input, message} from 'antd';
import type {WorkflowDefinition} from '../types';
import {saveDefinition, updateDefinition} from '../service';
interface EditModalProps {
visible: boolean;
onClose: () => void;
onSuccess?: () => void;
record?: WorkflowDefinition;
}
const EditModal: React.FC<EditModalProps> = ({visible, onClose, onSuccess, record}) => {
const [form] = Form.useForm();
const isEdit = !!record;
useEffect(() => {
if (visible && record) {
form.setFieldsValue(record);
}
}, [visible, record]);
const handleOk = async () => {
try {
const values = await form.validateFields();
if (isEdit) {
await updateDefinition(record.id, {
...record,
...values,
});
} else {
await saveDefinition({
...values,
flowVersion: 1,
status: 'DRAFT'
} as WorkflowDefinition);
}
message.success(isEdit ? '更新成功' : '保存成功');
onSuccess?.();
onClose();
form.resetFields();
} catch (error) {
if (error instanceof Error) {
message.error(error.message);
}
}
};
return (
<Modal
title={isEdit ? '编辑流程' : '新建流程'}
open={visible}
onOk={handleOk}
onCancel={() => {
onClose();
form.resetFields();
}}
destroyOnClose
>
<Form
form={form}
layout="vertical"
preserve={false}
>
<Form.Item
name="name"
label="流程名称"
rules={[{required: true, message: '请输入流程名称'}]}
>
<Input placeholder="请输入流程名称"/>
</Form.Item>
<Form.Item
name="key"
label="流程标识"
rules={[{required: true, message: '请输入流程标识'}]}
>
<Input placeholder="请输入流程标识" disabled={isEdit}/>
</Form.Item>
<Form.Item
name="description"
label="描述"
>
<Input.TextArea placeholder="请输入流程描述"/>
</Form.Item>
</Form>
</Modal>
);
};
export default EditModal;

View File

@ -0,0 +1,196 @@
import React, {useState, useEffect} from 'react';
import {Table, Card, Button, Space, Tag, message, Modal} from 'antd';
import {PlusOutlined, ExclamationCircleOutlined} from '@ant-design/icons';
import {useNavigate} from 'react-router-dom';
import * as service from './service';
import type {WorkflowDefinition, WorkflowDefinitionQuery} from './types';
import {DEFAULT_PAGE_SIZE, DEFAULT_CURRENT} from '@/utils/page';
import EditModal from './components/Edit';
const {confirm} = Modal;
const WorkflowDefinitionList: React.FC = () => {
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [pageData, setPageData] = useState<{
content: WorkflowDefinition[];
totalElements: number;
size: number;
number: number;
} | null>(null);
const [modalVisible, setModalVisible] = useState(false);
const [currentRecord, setCurrentRecord] = useState<WorkflowDefinition>();
const [query, setQuery] = useState<WorkflowDefinitionQuery>({
pageNum: DEFAULT_CURRENT - 1,
pageSize: DEFAULT_PAGE_SIZE
});
const loadData = async (params: WorkflowDefinitionQuery) => {
setLoading(true);
try {
const data = await service.getDefinitions(params);
setPageData(data);
} catch (error) {
if (error instanceof Error) {
message.error(error.message);
}
} finally {
setLoading(false);
}
};
useEffect(() => {
loadData(query);
}, [query]);
const handleCreateFlow = () => {
setCurrentRecord(undefined);
setModalVisible(true);
};
const handleEditFlow = (record: WorkflowDefinition) => {
setCurrentRecord(record);
setModalVisible(true);
};
const handleDesignFlow = (record: WorkflowDefinition) => {
navigate(`/workflow/definition/${record.id}/design`);
};
const handleModalClose = () => {
setModalVisible(false);
setCurrentRecord(undefined);
};
const columns = [
{
title: '流程名称',
dataIndex: 'name',
key: 'name',
render: (text: string, record: WorkflowDefinition) => (
<a onClick={() => navigate(`/workflow-definitions/${record.id}`)}>{text}</a>
),
},
{
title: '流程标识',
dataIndex: 'key',
key: 'key',
},
{
title: '版本',
dataIndex: 'flowVersion',
key: 'flowVersion',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => (
<Tag color={status === 'DRAFT' ? 'orange' : 'green'}>
{status === 'DRAFT' ? '草稿' : '已发布'}
</Tag>
),
},
{
title: '描述',
dataIndex: 'description',
key: 'description',
ellipsis: true,
},
{
title: '操作',
key: 'action',
render: (_: any, record: WorkflowDefinition) => (
<Space size="middle">
{record.status === 'DRAFT' && (
<>
<a onClick={() => handleEditFlow(record)}></a>
<a onClick={() => handleDesignFlow(record)}></a>
</>
)}
{record.status === 'DRAFT' && (
<a onClick={() => handleDeploy(record.id)}></a>
)}
<a onClick={() => handleDelete(record.id)}></a>
</Space>
),
},
];
const handleDeploy = async (id: number) => {
confirm({
title: '确认发布',
icon: <ExclamationCircleOutlined/>,
content: '确定要发布该流程定义吗?发布后将不能修改。',
onOk: async () => {
try {
await service.deployDefinition(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 (
<Card
title={
<Button
type="primary"
icon={<PlusOutlined/>}
onClick={handleCreateFlow}
>
</Button>
}
>
<Table
columns={columns}
dataSource={pageData?.content}
loading={loading}
rowKey="id"
pagination={{
total: pageData?.totalElements,
pageSize: pageData?.size || DEFAULT_PAGE_SIZE,
current: (pageData?.number || 0) + 1,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page, pageSize) => {
setQuery({...query, pageNum: page - 1, pageSize});
},
}}
/>
<EditModal
visible={modalVisible}
onClose={handleModalClose}
onSuccess={() => loadData(query)}
record={currentRecord}
/>
</Card>
);
};
export default WorkflowDefinitionList;

View File

@ -0,0 +1,23 @@
import request from '@/utils/request';
import {WorkflowDefinition, WorkflowDefinitionQuery} from './types';
import {Page} from '@/types/base';
const DEFINITION_URL = '/api/v1/workflow/definition';
export const getDefinitions = (params?: WorkflowDefinitionQuery) =>
request.get<Page<WorkflowDefinition>>(`${DEFINITION_URL}/page`, {params});
export const getDefinitionDetail = (id: number) =>
request.get<WorkflowDefinition>(`${DEFINITION_URL}/${id}`);
export const deployDefinition = (id: number) =>
request.post<void>(`${DEFINITION_URL}/${id}/deploy`);
export const deleteDefinition = (id: number) =>
request.delete<void>(`${DEFINITION_URL}/${id}`);
export const saveDefinition = (data: WorkflowDefinition) =>
request.post<WorkflowDefinition>(`${DEFINITION_URL}`, data);
export const updateDefinition = (id: number, data: WorkflowDefinition) =>
request.put<WorkflowDefinition>(`${DEFINITION_URL}/${id}`, data);

View File

@ -0,0 +1,22 @@
import { BaseResponse, BaseQuery } from '@/types/base';
export interface WorkflowDefinition extends BaseResponse {
name: string;
key: string;
flowVersion: number;
status: string;
description: string;
graph: {
nodes: any[];
edges: any[];
};
formConfig: {
formItems: any[];
};
}
export interface WorkflowDefinitionQuery extends BaseQuery {
name?: string;
key?: string;
status?: string;
}

View File

@ -32,10 +32,10 @@ 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 WorkflowDefinitionList = lazy(() => import('../pages/Workflow/Definition'));
const WorkflowDesign = lazy(() => import('../pages/Workflow/Definition/Design'));
const WorkflowInstance = lazy(() => import('../pages/Workflow/Instance'));
const WorkflowMonitor = lazy(() => import('../pages/Workflow/Monitor'));
const FlowDesigner = lazy(() => import('../pages/Workflow/Definition/Designer'));
const LogStreamPage = lazy(() => import('../pages/LogStream'));
// 创建路由
@ -127,15 +127,15 @@ const router = createBrowserRouter([
path: '',
element: (
<Suspense fallback={<LoadingComponent/>}>
<WorkflowDefinition/>
<WorkflowDefinitionList/>
</Suspense>
)
},
{
path: 'designer/:id?',
path: ':id/design',
element: (
<Suspense fallback={<LoadingComponent/>}>
<FlowDesigner/>
<WorkflowDesign/>
</Suspense>
)
}

View File

@ -26,6 +26,12 @@ export interface BaseRequest {
enabled?: boolean;
}
export interface PageParams {
pageNum: number; // 页码(从1开始)
pageSize: number; // 每页大小
sortField?: string; // 排序字段
sortOrder?: string; // 排序方向
}
// 分页响应数据
export interface Page<T> {

View File

@ -1,8 +1,8 @@
import type {Page, PageParams} from '@/types/base/page';
import type {Page, PageParams} from '@/types/base';
// 默认分页参数
const DEFAULT_PAGE_SIZE = 10;
const DEFAULT_CURRENT = 1;
export const DEFAULT_PAGE_SIZE = 10;
export const DEFAULT_CURRENT = 1;
/**
*
@ -12,7 +12,7 @@ export const convertToPageParams = (params?: {
pageSize?: number;
sortField?: string;
sortOrder?: string;
}): PageParams => ({
}): { sortOrder: any; sortField: string | undefined; pageSize: number; pageNum: number } => ({
pageNum: Math.max(1, params?.current || DEFAULT_CURRENT) - 1, // 转换为从0开始的页码
pageSize: params?.pageSize || DEFAULT_PAGE_SIZE,
sortField: params?.sortField,