From 61661e2a0999b2e6ff2ca1e71788c0d042a83bd5 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Thu, 12 Dec 2024 18:04:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B7=A5=E5=85=B7=E6=A0=8F?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Design/components/NodePanel.tsx | 241 ++++++++++++++++ .../Workflow/Definition/Design/index.tsx | 263 ++++++++++++++++++ .../Workflow/Definition/Design/service.ts | 11 + .../pages/Workflow/Definition/Design/types.ts | 77 +++++ .../pages/Workflow/Definition/List/index.tsx | 100 ------- .../Workflow/Definition/components/edit.tsx | 90 ++++++ .../src/pages/Workflow/Definition/index.tsx | 196 +++++++++++++ .../src/pages/Workflow/Definition/service.ts | 23 ++ .../src/pages/Workflow/Definition/types.ts | 22 ++ frontend/src/router/index.tsx | 10 +- frontend/src/types/base.ts | 6 + frontend/src/utils/page.ts | 10 +- 12 files changed, 939 insertions(+), 110 deletions(-) create mode 100644 frontend/src/pages/Workflow/Definition/Design/components/NodePanel.tsx create mode 100644 frontend/src/pages/Workflow/Definition/Design/index.tsx create mode 100644 frontend/src/pages/Workflow/Definition/Design/service.ts create mode 100644 frontend/src/pages/Workflow/Definition/Design/types.ts delete mode 100644 frontend/src/pages/Workflow/Definition/List/index.tsx create mode 100644 frontend/src/pages/Workflow/Definition/components/edit.tsx create mode 100644 frontend/src/pages/Workflow/Definition/index.tsx create mode 100644 frontend/src/pages/Workflow/Definition/service.ts create mode 100644 frontend/src/pages/Workflow/Definition/types.ts diff --git a/frontend/src/pages/Workflow/Definition/Design/components/NodePanel.tsx b/frontend/src/pages/Workflow/Definition/Design/components/NodePanel.tsx new file mode 100644 index 00000000..803ca005 --- /dev/null +++ b/frontend/src/pages/Workflow/Definition/Design/components/NodePanel.tsx @@ -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 = { + 'play-circle': PlayCircleOutlined, + 'stop': StopOutlined, + 'user': UserOutlined, + 'api': ApiOutlined, + 'code': CodeOutlined, + 'fork': NodeIndexOutlined, + 'branches': SplitCellsOutlined, + 'apartment': AppstoreOutlined +}; + +// 节点类型到图标的映射 +const typeIconMap: Record = { + '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 = { + 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 = ({onNodeDragStart}) => { + const [loading, setLoading] = useState(false); + const [nodeDefinitions, setNodeDefinitions] = useState([]); + + // 加载节点定义列表 + 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); + + // 处理节点拖拽开始事件 + 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 ( + + ); + }; + + 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: {label}, + children: ( +
+ {groupedNodes[category as NodeCategory]?.map(node => ( + +
{node.description}
+
+
功能特点:
+
    + {node.graphConfig.details.features.map((feature, index) => ( +
  • {feature}
  • + ))} +
+
+
+ } + overlayStyle={tooltipStyle} + overlayInnerStyle={tooltipOverlayInnerStyle} + placement="right" + arrow={false} + > +
handleDragStart(node, e)} + style={getNodeItemStyle(node)} + > +
+ {renderNodeIcon(node)} + {node.name} +
+
+ + ))} + + ), + })); + + return ( + + + + ); +}; + +export default NodePanel; diff --git a/frontend/src/pages/Workflow/Definition/Design/index.tsx b/frontend/src/pages/Workflow/Definition/Design/index.tsx new file mode 100644 index 00000000..d1c9d1bf --- /dev/null +++ b/frontend/src/pages/Workflow/Definition/Design/index.tsx @@ -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('工作流设计'); + const graphContainerRef = useRef(null); + const [graph, setGraph] = useState(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 ( +
+ + + + + + } + > + + + + + + +
+ + + + +
+ ); +}; + +export default WorkflowDesign; diff --git a/frontend/src/pages/Workflow/Definition/Design/service.ts b/frontend/src/pages/Workflow/Definition/Design/service.ts new file mode 100644 index 00000000..4380fec5 --- /dev/null +++ b/frontend/src/pages/Workflow/Definition/Design/service.ts @@ -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`); diff --git a/frontend/src/pages/Workflow/Definition/Design/types.ts b/frontend/src/pages/Workflow/Definition/Design/types.ts new file mode 100644 index 00000000..96f3d931 --- /dev/null +++ b/frontend/src/pages/Workflow/Definition/Design/types.ts @@ -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; + 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; +} diff --git a/frontend/src/pages/Workflow/Definition/List/index.tsx b/frontend/src/pages/Workflow/Definition/List/index.tsx deleted file mode 100644 index 8d293ea4..00000000 --- a/frontend/src/pages/Workflow/Definition/List/index.tsx +++ /dev/null @@ -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) => ( - history.push(`/workflow/definition/detail/${record.id}`)}>{text} - ), - }, - { - title: '流程标识', - dataIndex: 'key', - key: 'key', - }, - { - title: '版本', - dataIndex: 'flowVersion', - key: 'flowVersion', - }, - { - title: '状态', - dataIndex: 'status', - key: 'status', - render: (status: string) => ( - - {status === 'DRAFT' ? '草稿' : '已发布'} - - ), - }, - { - title: '描述', - dataIndex: 'description', - key: 'description', - ellipsis: true, - }, - { - title: '操作', - key: 'action', - render: (_: any, record: any) => ( - - history.push(`/workflow/definition/edit/${record.id}`)}>编辑 - handleDeploy(record.id)}>发布 - handleDelete(record.id)}>删除 - - ), - }, - ]; - - const handleDeploy = async (id: number) => { - message.success('发布成功'); - refresh(); - }; - - const handleDelete = async (id: number) => { - message.success('删除成功'); - refresh(); - }; - - return ( - } - onClick={() => history.push('/workflow/definition/create')} - > - 新建流程 - - } - > - - - ); -}; - -export default WorkflowDefinitionList; diff --git a/frontend/src/pages/Workflow/Definition/components/edit.tsx b/frontend/src/pages/Workflow/Definition/components/edit.tsx new file mode 100644 index 00000000..710bc12f --- /dev/null +++ b/frontend/src/pages/Workflow/Definition/components/edit.tsx @@ -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 = ({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 ( + { + onClose(); + form.resetFields(); + }} + destroyOnClose + > +
+ + + + + + + + + + +
+ ); +}; + +export default EditModal; diff --git a/frontend/src/pages/Workflow/Definition/index.tsx b/frontend/src/pages/Workflow/Definition/index.tsx new file mode 100644 index 00000000..f17d4c86 --- /dev/null +++ b/frontend/src/pages/Workflow/Definition/index.tsx @@ -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(); + const [query, setQuery] = useState({ + 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) => ( + navigate(`/workflow-definitions/${record.id}`)}>{text} + ), + }, + { + title: '流程标识', + dataIndex: 'key', + key: 'key', + }, + { + title: '版本', + dataIndex: 'flowVersion', + key: 'flowVersion', + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + render: (status: string) => ( + + {status === 'DRAFT' ? '草稿' : '已发布'} + + ), + }, + { + title: '描述', + dataIndex: 'description', + key: 'description', + ellipsis: true, + }, + { + title: '操作', + key: 'action', + render: (_: any, record: WorkflowDefinition) => ( + + {record.status === 'DRAFT' && ( + <> + handleEditFlow(record)}>编辑 + handleDesignFlow(record)}>设计 + + )} + {record.status === 'DRAFT' && ( + handleDeploy(record.id)}>发布 + )} + handleDelete(record.id)}>删除 + + ), + }, + ]; + + const handleDeploy = async (id: number) => { + confirm({ + title: '确认发布', + icon: , + 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: , + content: '确定要删除该流程定义吗?删除后不可恢复。', + onOk: async () => { + try { + await service.deleteDefinition(id); + message.success('删除成功'); + loadData(query); + } catch (error) { + if (error instanceof Error) { + message.error(error.message); + } + } + }, + }); + }; + + return ( + } + onClick={handleCreateFlow} + > + 新建流程 + + } + > +
{ + setQuery({...query, pageNum: page - 1, pageSize}); + }, + }} + /> + loadData(query)} + record={currentRecord} + /> + + ); +}; + +export default WorkflowDefinitionList; \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/service.ts b/frontend/src/pages/Workflow/Definition/service.ts new file mode 100644 index 00000000..2d9c082a --- /dev/null +++ b/frontend/src/pages/Workflow/Definition/service.ts @@ -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>(`${DEFINITION_URL}/page`, {params}); + +export const getDefinitionDetail = (id: number) => + request.get(`${DEFINITION_URL}/${id}`); + +export const deployDefinition = (id: number) => + request.post(`${DEFINITION_URL}/${id}/deploy`); + +export const deleteDefinition = (id: number) => + request.delete(`${DEFINITION_URL}/${id}`); + +export const saveDefinition = (data: WorkflowDefinition) => + request.post(`${DEFINITION_URL}`, data); + +export const updateDefinition = (id: number, data: WorkflowDefinition) => + request.put(`${DEFINITION_URL}/${id}`, data); \ No newline at end of file diff --git a/frontend/src/pages/Workflow/Definition/types.ts b/frontend/src/pages/Workflow/Definition/types.ts new file mode 100644 index 00000000..f589a8e5 --- /dev/null +++ b/frontend/src/pages/Workflow/Definition/types.ts @@ -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; +} \ No newline at end of file diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index 21cc1c8a..07a42440 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -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: ( }> - + ) }, { - path: 'designer/:id?', + path: ':id/design', element: ( }> - + ) } diff --git a/frontend/src/types/base.ts b/frontend/src/types/base.ts index 7b09fbb3..ad49e6a8 100644 --- a/frontend/src/types/base.ts +++ b/frontend/src/types/base.ts @@ -26,6 +26,12 @@ export interface BaseRequest { enabled?: boolean; } +export interface PageParams { + pageNum: number; // 页码(从1开始) + pageSize: number; // 每页大小 + sortField?: string; // 排序字段 + sortOrder?: string; // 排序方向 +} // 分页响应数据 export interface Page { diff --git a/frontend/src/utils/page.ts b/frontend/src/utils/page.ts index ed37bd25..39a62d0e 100644 --- a/frontend/src/utils/page.ts +++ b/frontend/src/utils/page.ts @@ -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, @@ -26,4 +26,4 @@ export const convertToPageInfo = (page?: Page) => ({ current: (page?.number || 0) + 1, pageSize: page?.size || DEFAULT_PAGE_SIZE, total: page?.totalElements || 0 -}); \ No newline at end of file +}); \ No newline at end of file