This commit is contained in:
asp_ly 2024-12-17 21:02:15 +08:00
parent 2515ab3d14
commit 3374885ce2
4 changed files with 102 additions and 110 deletions

View File

@ -1,129 +1,115 @@
import React from 'react';
import { Modal, Steps, Card, Tag, Row, Col } from 'antd';
import { CheckCircleOutlined, LoadingOutlined, CloseCircleOutlined } from '@ant-design/icons';
import './styles.css';
import { Modal, Steps, Card, Descriptions, Tag, Timeline } from 'antd';
import { WorkflowHistoricalInstance, WorkflowInstanceStage } from '../types';
import dayjs from 'dayjs';
interface DetailModalProps {
visible: boolean;
onCancel: () => void;
instanceData?: any;
instanceData?: WorkflowHistoricalInstance;
}
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 DetailModal: React.FC<DetailModalProps> = ({ visible, onCancel, instanceData }) => {
if (!instanceData) return null;
const getStepStatus = (status: string) => {
switch (status) {
const getStatusTag = (status: string) => {
const statusMap: Record<string, { color: string; text: string }> = {
COMPLETED: { color: 'success', text: '已完成' },
RUNNING: { color: 'processing', text: '运行中' },
FAILED: { color: 'error', text: '失败' },
TERMINATED: { color: 'warning', text: '已终止' },
NOT_STARTED: { color: 'default', text: '未执行' }
};
const statusInfo = statusMap[status] || { color: 'default', text: status };
return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
};
const getNodeTypeText = (nodeType: string) => {
const nodeTypeMap: Record<string, string> = {
startEvent: '开始节点',
endEvent: '结束节点',
serviceTask: '服务任务',
userTask: '用户任务',
scriptTask: '脚本任务'
};
return nodeTypeMap[nodeType] || nodeType;
};
const getStepStatus = (stage: WorkflowInstanceStage) => {
switch (stage.status) {
case 'COMPLETED':
return 'finish';
case 'RUNNING':
return 'process';
case 'ERROR':
case 'FAILED':
return 'error';
case 'TERMINATED':
return 'wait';
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="流程实例详情"
title="流程执行详情"
open={visible}
onCancel={onCancel}
width={1200}
footer={null}
width={800}
>
<div className="workflow-steps">
<Card className="mb-4">
<Descriptions title="基本信息" bordered column={2}>
<Descriptions.Item label="业务标识">{instanceData.businessKey}</Descriptions.Item>
<Descriptions.Item label="状态">{getStatusTag(instanceData.status)}</Descriptions.Item>
<Descriptions.Item label="开始时间">{dayjs(instanceData.startTime).format('YYYY-MM-DD HH:mm:ss')}</Descriptions.Item>
<Descriptions.Item label="结束时间">
{instanceData.endTime ? dayjs(instanceData.endTime).format('YYYY-MM-DD HH:mm:ss') : '暂无'}
</Descriptions.Item>
<Descriptions.Item label="流程实例ID">{instanceData.processInstanceId}</Descriptions.Item>
<Descriptions.Item label="流程定义ID">{instanceData.processDefinitionId}</Descriptions.Item>
</Descriptions>
</Card>
<Card title="执行阶段" className="mb-4">
<Steps
current={currentNodeIndex}
items={data.nodes.map(node => ({
title: node.name,
status: getStepStatus(node.status)
progressDot
current={instanceData.stages.length}
items={instanceData.stages.map((stage) => ({
title: stage.nodeName,
description: (
<div className="text-xs">
<div>{getNodeTypeText(stage.nodeType)}</div>
<div>{getStatusTag(stage.status)}</div>
</div>
),
status: getStepStatus(stage)
}))}
/>
</div>
</Card>
<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>
<Card title="详细时间线">
<Timeline
items={instanceData.stages.map((stage) => ({
color: stage.status === 'COMPLETED' ? 'green' :
stage.status === 'FAILED' ? 'red' :
stage.status === 'RUNNING' ? 'blue' : 'gray',
children: (
<div>
<div className="font-medium">{stage.nodeName} ({getNodeTypeText(stage.nodeType)})</div>
<div className="text-gray-500 text-sm">
{dayjs(stage.startTime).format('YYYY-MM-DD HH:mm:ss')}
</div>
</Col>
<Col span={8}>
<div className="info-item">
<div className="info-label"></div>
<div className="info-value">{getStatusTag(currentNode.status)}</div>
{stage.endTime && (
<div className="text-gray-500 text-sm">
{dayjs(stage.endTime).format('YYYY-MM-DD HH:mm:ss')}
</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>
)}
<div>{getStatusTag(stage.status)}</div>
</div>
)
}))}
/>
</Card>
</Modal>
);

View File

@ -17,13 +17,19 @@ const HistoryModal: React.FC<HistoryModalProps> = ({ visible, onCancel, workflow
const [data, setData] = useState<WorkflowHistoricalInstance[]>([]);
const [total, setTotal] = useState(0);
const [query, setQuery] = useState({
pageNum: DEFAULT_CURRENT,
pageNum: DEFAULT_CURRENT - 1,
pageSize: DEFAULT_PAGE_SIZE,
workflowDefinitionId
});
const [detailVisible, setDetailVisible] = useState(false);
const [selectedInstance, setSelectedInstance] = useState<WorkflowHistoricalInstance>();
useEffect(() => {
setQuery(prev => ({
...prev,
workflowDefinitionId
}));
}, [workflowDefinitionId]);
const loadData = async () => {
setLoading(true);
try {
@ -118,11 +124,11 @@ const HistoryModal: React.FC<HistoryModalProps> = ({ visible, onCancel, workflow
current: query.pageNum + 1,
pageSize: query.pageSize,
total: total,
onChange: (page, pageSize) => setQuery({
...query,
onChange: (page, pageSize) => setQuery(prev => ({
...prev,
pageNum: page - 1,
pageSize
}),
})),
showSizeChanger: true,
showQuickJumper: true,
}}

View File

@ -63,7 +63,7 @@ const WorkflowInstanceList: React.FC = () => {
render: (time: string) => time || '暂无'
},
{
title: '状态',
title: '最后执行状态',
dataIndex: 'lastExecutionStatus',
key: 'lastExecutionStatus',
render: (status: string) => {

View File

@ -17,4 +17,4 @@ export const getWorkflowInstances = (params?: WorkflowTemplateWithInstancesQuery
* @param params
*/
export const getHistoricalInstances = (params?: WorkflowHistoricalInstanceQuery) =>
request.post<Page<WorkflowHistoricalInstance>>(`${INSTANCE_URL}/historical-instances`, params);
request.get<Page<WorkflowHistoricalInstance>>(`${INSTANCE_URL}/historical-instances`, {params});