This commit is contained in:
dengqichen 2024-12-17 16:50:04 +08:00
parent 45e6d70ca4
commit b3241f51fb
5 changed files with 321 additions and 5 deletions

View File

@ -0,0 +1,132 @@
import React from 'react';
import { Modal, Steps, Card, Tag, Row, Col } from 'antd';
import { CheckCircleOutlined, LoadingOutlined, CloseCircleOutlined } from '@ant-design/icons';
import './styles.css';
interface DetailModalProps {
visible: boolean;
onCancel: () => void;
instanceData?: any;
}
const mockInstanceDetail = {
nodes: [
{
id: 'start',
name: '开始',
status: 'COMPLETED',
startTime: '2024-12-17 16:25:00',
endTime: '2024-12-17 16:25:01',
},
{
id: 'script1',
name: '环境检查',
status: 'COMPLETED',
startTime: '2024-12-17 16:25:02',
endTime: '2024-12-17 16:25:10',
output: '环境检查完成,所有依赖已就绪'
},
{
id: 'script2',
name: '部署应用',
status: 'RUNNING',
startTime: '2024-12-17 16:25:11',
output: '正在部署应用...'
},
{
id: 'end',
name: '结束',
status: 'PENDING'
}
]
};
const getStepStatus = (status: string) => {
switch (status) {
case 'COMPLETED':
return 'finish';
case 'RUNNING':
return 'process';
case 'ERROR':
return 'error';
default:
return 'wait';
}
};
const getStatusTag = (status: string) => {
const statusConfig = {
COMPLETED: { color: 'success', icon: <CheckCircleOutlined />, text: '已完成' },
RUNNING: { color: 'processing', icon: <LoadingOutlined />, text: '执行中' },
ERROR: { color: 'error', icon: <CloseCircleOutlined />, text: '错误' },
PENDING: { color: 'default', text: '等待中' }
};
const config = statusConfig[status] || statusConfig.PENDING;
return (
<Tag color={config.color} icon={config.icon}>
{config.text}
</Tag>
);
};
const DetailModal: React.FC<DetailModalProps> = ({ visible, onCancel }) => {
const data = mockInstanceDetail;
const currentNodeIndex = data.nodes.findIndex(node => node.status === 'RUNNING');
const currentNode = data.nodes[currentNodeIndex];
return (
<Modal
title="流程实例详情"
open={visible}
onCancel={onCancel}
footer={null}
width={800}
>
<div className="workflow-steps">
<Steps
current={currentNodeIndex}
items={data.nodes.map(node => ({
title: node.name,
status: getStepStatus(node.status)
}))}
/>
</div>
<Card className="current-node-info" title="当前节点详情">
{currentNode && (
<Row gutter={16}>
<Col span={8}>
<div className="info-item">
<div className="info-label"></div>
<div className="info-value">{currentNode.name}</div>
</div>
</Col>
<Col span={8}>
<div className="info-item">
<div className="info-label"></div>
<div className="info-value">{getStatusTag(currentNode.status)}</div>
</div>
</Col>
<Col span={8}>
<div className="info-item">
<div className="info-label"></div>
<div className="info-value">{currentNode.startTime || '-'}</div>
</div>
</Col>
{currentNode.output && (
<Col span={24}>
<div className="info-item">
<div className="info-label"></div>
<div className="output-block">{currentNode.output}</div>
</div>
</Col>
)}
</Row>
)}
</Card>
</Modal>
);
};
export default DetailModal;

View File

@ -0,0 +1,49 @@
.workflow-steps {
margin: 24px 0;
padding: 0 40px;
}
.workflow-steps .ant-steps-item-tail::after {
background-color: #e8e8e8 !important;
}
.workflow-steps .ant-steps-item-finish .ant-steps-item-tail::after {
background-color: #52c41a !important;
}
.current-node-info {
margin: 24px 0;
}
.info-item {
margin-bottom: 16px;
}
.info-label {
color: #666;
font-size: 14px;
margin-bottom: 8px;
}
.info-value {
font-size: 14px;
}
.output-block {
margin-top: 8px;
padding: 12px;
background: #f5f5f5;
border-radius: 4px;
font-size: 13px;
font-family: monospace;
line-height: 1.5;
}
/* 优化步骤连接线 */
.ant-steps-item-finish .ant-steps-item-tail::after {
background-color: #52c41a !important;
}
.ant-steps-item-process .ant-steps-item-tail::after {
background-color: #1890ff !important;
}

View File

@ -1,12 +1,122 @@
import React from 'react';
import {Card} from 'antd';
import React, { useState, useEffect } from 'react';
import { Card, Table, Tag, Space, Empty } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { getWorkflowInstances } from './service';
import { WorkflowTemplateWithInstances } from './types';
import { Page } from '@/types/base';
import DetailModal from './components/DetailModal';
const WorkflowInstanceList: React.FC = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<WorkflowTemplateWithInstances[]>([]);
const [query, setQuery] = useState({
current: 1,
pageSize: 10,
});
const [detailVisible, setDetailVisible] = useState(false);
const [selectedInstance, setSelectedInstance] = useState<WorkflowTemplateWithInstances>();
const loadData = async (params: any) => {
setLoading(true);
try {
const result = await getWorkflowInstances(params);
setData(result?.content || []);
} catch (error) {
console.error('加载流程实例失败:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
loadData(query);
}, [query]);
const handleViewDetail = (record: WorkflowTemplateWithInstances) => {
setSelectedInstance(record);
setDetailVisible(true);
};
const columns: ColumnsType<WorkflowTemplateWithInstances> = [
{
title: '流程名称',
dataIndex: 'name',
key: 'name',
},
{
title: '业务标识',
dataIndex: 'businessKey',
key: 'businessKey',
},
{
title: '最后执行时间',
dataIndex: 'lastExecutionTime',
key: 'lastExecutionTime',
render: (time: string) => time || '暂无'
},
{
title: '状态',
dataIndex: 'lastExecutionStatus',
key: 'lastExecutionStatus',
render: (status: string) => {
if (!status) return '暂无';
const statusMap: Record<string, { color: string; text: string }> = {
TERMINATED: { color: 'red', text: '已终止' },
COMPLETED: { color: 'green', text: '已完成' },
RUNNING: { color: 'blue', text: '运行中' },
};
const statusInfo = statusMap[status] || { color: 'default', text: status };
return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
},
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<Space size="middle">
<a onClick={() => handleViewDetail(record)}></a>
{record?.lastExecutionStatus === 'RUNNING' && (
<a onClick={() => console.log('终止流程', record)}></a>
)}
</Space>
),
},
];
const handleTableChange = (pagination: any) => {
setQuery({
...query,
current: pagination.current,
pageSize: pagination.pageSize,
});
};
const WorkflowInstance: React.FC = () => {
return (
<Card title="流程实例">
<div></div>
<Table
columns={columns}
dataSource={data}
loading={loading}
rowKey="id"
locale={{
emptyText: <Empty description="暂无数据" />
}}
pagination={{
current: query.current,
pageSize: query.pageSize,
total: data?.length || 0,
showSizeChanger: true,
showQuickJumper: true,
}}
onChange={handleTableChange}
/>
<DetailModal
visible={detailVisible}
onCancel={() => setDetailVisible(false)}
instanceData={selectedInstance}
/>
</Card>
);
};
export default WorkflowInstance;
export default WorkflowInstanceList;

View File

@ -0,0 +1,12 @@
import request from '@/utils/request';
import { WorkflowTemplateWithInstances, WorkflowTemplateWithInstancesQuery } from './types';
import { Page } from '@/types/base';
const INSTANCE_URL = '/api/v1/workflow/instance';
/**
*
* @param params
*/
export const getWorkflowInstances = (params?: WorkflowTemplateWithInstancesQuery) =>
request.get<Page<WorkflowTemplateWithInstances>>(`${INSTANCE_URL}/templates-with-instances`, { params });

View File

@ -0,0 +1,13 @@
import { Page } from '@/types/base';
export interface WorkflowTemplateWithInstances {
id: number;
name: string;
businessKey: string;
lastExecutionTime: string;
lastExecutionStatus: string;
}
export interface WorkflowTemplateWithInstancesQuery {
}