From b82cf8ab00b19876b145c4dbe4ebd93769afd25f Mon Sep 17 00:00:00 2001 From: dengqichen Date: Tue, 17 Dec 2024 18:25:40 +0800 Subject: [PATCH] 1 --- .../Instance/components/HistoryModal.tsx | 142 ++++++++++++++++++ .../src/pages/Workflow/Instance/index.tsx | 23 ++- .../src/pages/Workflow/Instance/service.ts | 8 + frontend/src/pages/Workflow/Instance/types.ts | 27 +++- 4 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 frontend/src/pages/Workflow/Instance/components/HistoryModal.tsx diff --git a/frontend/src/pages/Workflow/Instance/components/HistoryModal.tsx b/frontend/src/pages/Workflow/Instance/components/HistoryModal.tsx new file mode 100644 index 00000000..b8a5dc7e --- /dev/null +++ b/frontend/src/pages/Workflow/Instance/components/HistoryModal.tsx @@ -0,0 +1,142 @@ +import React, { useState, useEffect } from 'react'; +import { Modal, Table, Tag, Button, Space } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; +import { WorkflowHistoricalInstance } from '../types'; +import { getHistoricalInstances } from '../service'; +import DetailModal from './DetailModal'; +import { DEFAULT_PAGE_SIZE, DEFAULT_CURRENT } from '@/utils/page'; + +interface HistoryModalProps { + visible: boolean; + onCancel: () => void; + workflowDefinitionId?: number; +} + +const HistoryModal: React.FC = ({ visible, onCancel, workflowDefinitionId }) => { + const [loading, setLoading] = useState(false); + const [data, setData] = useState([]); + const [total, setTotal] = useState(0); + const [query, setQuery] = useState({ + pageNum: DEFAULT_CURRENT, + pageSize: DEFAULT_PAGE_SIZE, + workflowDefinitionId + }); + const [detailVisible, setDetailVisible] = useState(false); + const [selectedInstance, setSelectedInstance] = useState(); + + const loadData = async () => { + setLoading(true); + try { + const result = await getHistoricalInstances(query); + setData(result.content || []); + setTotal(result.totalElements || 0); + } catch (error) { + console.error('加载历史记录失败:', error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + if (visible) { + loadData(); + } + }, [visible, query]); + + const handleViewDetail = (record: WorkflowHistoricalInstance) => { + setSelectedInstance(record); + setDetailVisible(true); + }; + + const getStatusTag = (status: string) => { + const statusMap: Record = { + COMPLETED: { color: 'success', text: '已完成' }, + RUNNING: { color: 'processing', text: '运行中' }, + FAILED: { color: 'error', text: '失败' }, + TERMINATED: { color: 'warning', text: '已终止' } + }; + const statusInfo = statusMap[status] || { color: 'default', text: status }; + return {statusInfo.text}; + }; + + const columns: ColumnsType = [ + { + title: '业务标识', + dataIndex: 'businessKey', + key: 'businessKey', + width: 200, + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + width: 100, + render: status => getStatusTag(status) + }, + { + title: '开始时间', + dataIndex: 'startTime', + key: 'startTime', + width: 180, + }, + { + title: '结束时间', + dataIndex: 'endTime', + key: 'endTime', + width: 180, + render: time => time || '暂无' + }, + { + title: '操作', + key: 'action', + fixed: 'right', + width: 100, + render: (_, record) => ( + + ), + }, + ]; + + return ( + <> + + setQuery({ + ...query, + pageNum: page - 1, + pageSize + }), + showSizeChanger: true, + showQuickJumper: true, + }} + /> + + {selectedInstance && ( + setDetailVisible(false)} + instanceData={selectedInstance} + /> + )} + + ); +}; + +export default HistoryModal; diff --git a/frontend/src/pages/Workflow/Instance/index.tsx b/frontend/src/pages/Workflow/Instance/index.tsx index ae90041d..4fb3b849 100644 --- a/frontend/src/pages/Workflow/Instance/index.tsx +++ b/frontend/src/pages/Workflow/Instance/index.tsx @@ -5,22 +5,25 @@ import { getWorkflowInstances } from './service'; import { WorkflowTemplateWithInstances } from './types'; import { Page } from '@/types/base'; import DetailModal from './components/DetailModal'; +import HistoryModal from './components/HistoryModal'; const WorkflowInstanceList: React.FC = () => { const [loading, setLoading] = useState(false); - const [data, setData] = useState([]); + const [data, setData] = useState>([]); const [query, setQuery] = useState({ current: 1, pageSize: 10, }); const [detailVisible, setDetailVisible] = useState(false); const [selectedInstance, setSelectedInstance] = useState(); + const [historyVisible, setHistoryVisible] = useState(false); + const [selectedWorkflowDefinitionId, setSelectedWorkflowDefinitionId] = useState(); const loadData = async (params: any) => { setLoading(true); try { const result = await getWorkflowInstances(params); - setData(result?.content || []); + setData(result); } catch (error) { console.error('加载流程实例失败:', error); } finally { @@ -37,6 +40,11 @@ const WorkflowInstanceList: React.FC = () => { setDetailVisible(true); }; + const handleViewHistory = (record: WorkflowTemplateWithInstances) => { + setSelectedWorkflowDefinitionId(record.id); + setHistoryVisible(true); + }; + const columns: ColumnsType = [ { title: '流程名称', @@ -73,14 +81,14 @@ const WorkflowInstanceList: React.FC = () => { title: '操作', key: 'action', fixed: 'right', - width: 230, + width: 200, render: (_, record) => ( handleViewDetail(record)}>查看详情 + handleViewHistory(record)}>历史执行 {record?.lastExecutionStatus === 'RUNNING' && ( console.log('终止流程', record)}>终止流程 )} - handleViewDetail(record)}>历史执行 ), }, @@ -98,7 +106,7 @@ const WorkflowInstanceList: React.FC = () => {
{ onCancel={() => setDetailVisible(false)} instanceData={selectedInstance} /> + setHistoryVisible(false)} + workflowDefinitionId={selectedWorkflowDefinitionId} + /> ); }; diff --git a/frontend/src/pages/Workflow/Instance/service.ts b/frontend/src/pages/Workflow/Instance/service.ts index bee6f074..0de85ddd 100644 --- a/frontend/src/pages/Workflow/Instance/service.ts +++ b/frontend/src/pages/Workflow/Instance/service.ts @@ -1,6 +1,7 @@ import request from '@/utils/request'; import { WorkflowTemplateWithInstances, WorkflowTemplateWithInstancesQuery } from './types'; import { Page } from '@/types/base'; +import { WorkflowHistoricalInstance, WorkflowHistoricalInstanceQuery } from './types'; const INSTANCE_URL = '/api/v1/workflow/instance'; @@ -10,3 +11,10 @@ const INSTANCE_URL = '/api/v1/workflow/instance'; */ export const getWorkflowInstances = (params?: WorkflowTemplateWithInstancesQuery) => request.get>(`${INSTANCE_URL}/templates-with-instances`, { params }); + +/** + * 获取流程实例历史记录 + * @param params 查询参数 + */ +export const getHistoricalInstances = (params?: WorkflowHistoricalInstanceQuery) => + request.post>(`${INSTANCE_URL}/historical-instances`, params); diff --git a/frontend/src/pages/Workflow/Instance/types.ts b/frontend/src/pages/Workflow/Instance/types.ts index b6823e3e..6e06c1e1 100644 --- a/frontend/src/pages/Workflow/Instance/types.ts +++ b/frontend/src/pages/Workflow/Instance/types.ts @@ -1,4 +1,4 @@ -import { Page } from '@/types/base'; +import { Page, BaseQuery } from '@/types/base'; export interface WorkflowTemplateWithInstances { id: number; @@ -11,3 +11,28 @@ export interface WorkflowTemplateWithInstances { export interface WorkflowTemplateWithInstancesQuery { } + +export interface WorkflowInstanceStage { + id: number | null; + nodeId: string; + nodeName: string; + nodeType: string; + status: string; + startTime: string | null; + endTime: string | null; +} + +export interface WorkflowHistoricalInstance { + id: number; + processInstanceId: string; + processDefinitionId: string; + businessKey: string; + status: string; + startTime: string; + endTime: string | null; + stages: WorkflowInstanceStage[]; +} + +export interface WorkflowHistoricalInstanceQuery extends BaseQuery { + workflowDefinitionId?: number; +}