kra/web/src/pages/systemTools/sysError/index.tsx

422 lines
11 KiB
TypeScript

/**
* KRA - System Error Log Page
* 系统错误日志页面 - 查看、删除错误日志
*/
import React, { useState, useEffect } from 'react';
import {
Card,
Table,
Button,
Space,
Form,
Input,
DatePicker,
Drawer,
Descriptions,
Tag,
message,
Tooltip,
Popconfirm,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import {
SearchOutlined,
ReloadOutlined,
DeleteOutlined,
InfoCircleOutlined,
QuestionCircleOutlined,
DownOutlined,
UpOutlined,
RobotOutlined,
} from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import {
getSysErrorList,
getSysErrorById,
deleteSysError,
deleteSysErrorByIds,
} from '@/services/kratos/sysError';
import { formatDate } from '@/utils/date';
import styles from './index.less';
const { RangePicker } = DatePicker;
interface SysError {
ID: number;
CreatedAt: string;
form: string;
level: string;
status: string;
info: string;
solution?: string;
}
// Level tag mapping
const levelTagMap: Record<string, string> = {
fatal: 'error',
error: 'warning',
};
const levelLabelMap: Record<string, string> = {
fatal: '致命错误',
error: '一般错误',
};
// Status tag mapping
const statusTagMap: Record<string, string> = {
'未处理': 'default',
'处理中': 'processing',
'处理完成': 'success',
'处理失败': 'error',
};
const statusLabelMap: Record<string, string> = {
'未处理': '未处理',
'处理中': '处理中',
'处理完成': '处理完成',
'处理失败': '处理失败',
};
const SysErrorPage: React.FC = () => {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [dataSource, setDataSource] = useState<SysError[]>([]);
const [total, setTotal] = useState(0);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [showAllQuery, setShowAllQuery] = useState(false);
// Detail drawer
const [detailVisible, setDetailVisible] = useState(false);
const [detailData, setDetailData] = useState<SysError | null>(null);
// Fetch table data
const fetchData = async () => {
setLoading(true);
try {
const values = form.getFieldsValue();
const params: any = {
page: current,
pageSize,
...values,
};
if (values.createdAtRange) {
params.startCreatedAt = values.createdAtRange[0]?.format('YYYY-MM-DD HH:mm:ss');
params.endCreatedAt = values.createdAtRange[1]?.format('YYYY-MM-DD HH:mm:ss');
delete params.createdAtRange;
}
const res = await getSysErrorList(params);
if (res.code === 0) {
setDataSource(res.data?.list || []);
setTotal(res.data?.total || 0);
}
} catch (error) {
console.error('获取错误日志列表失败:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [current, pageSize]);
// Search
const handleSearch = () => {
setCurrent(1);
fetchData();
};
// Reset
const handleReset = () => {
form.resetFields();
setCurrent(1);
fetchData();
};
// Delete single row
const handleDelete = async (record: SysError) => {
try {
const res = await deleteSysError({ ID: record.ID });
if (res.code === 0) {
message.success('删除成功');
fetchData();
}
} catch (error) {
message.error('删除失败');
}
};
// Batch delete
const handleBatchDelete = async () => {
if (selectedRowKeys.length === 0) {
message.warning('请选择要删除的数据');
return;
}
try {
const res = await deleteSysErrorByIds({ IDs: selectedRowKeys as number[] });
if (res.code === 0) {
message.success('删除成功');
setSelectedRowKeys([]);
fetchData();
}
} catch (error) {
message.error('删除失败');
}
};
// View details
const handleViewDetail = async (record: SysError) => {
try {
const res = await getSysErrorById({ ID: record.ID });
if (res.code === 0) {
setDetailData(res.data?.reSysError || res.data);
setDetailVisible(true);
}
} catch (error) {
message.error('获取详情失败');
}
};
// Get AI solution (placeholder - would need backend support)
const handleGetSolution = async (id: number) => {
message.info('AI方案功能需要后端支持');
};
const columns: ColumnsType<SysError> = [
{
title: '日期',
dataIndex: 'CreatedAt',
key: 'CreatedAt',
width: 180,
sorter: true,
render: (text) => formatDate(text),
},
{
title: '错误来源',
dataIndex: 'form',
key: 'form',
width: 120,
},
{
title: '错误等级',
dataIndex: 'level',
key: 'level',
width: 120,
render: (level) => (
<Tag color={levelTagMap[level] || 'default'}>
{levelLabelMap[level] || '一般错误'}
</Tag>
),
},
{
title: '处理状态',
dataIndex: 'status',
key: 'status',
width: 140,
render: (status) => (
<Tag color={statusTagMap[status] || 'default'}>
{statusLabelMap[status] || '未处理'}
</Tag>
),
},
{
title: '错误内容',
dataIndex: 'info',
key: 'info',
width: 240,
ellipsis: {
showTitle: false,
},
render: (text) => (
<Tooltip placement="topLeft" title={text}>
{text}
</Tooltip>
),
},
{
title: '解决方案',
dataIndex: 'solution',
key: 'solution',
width: 120,
ellipsis: {
showTitle: false,
},
render: (text) => (
<Tooltip placement="topLeft" title={text}>
{text || '-'}
</Tooltip>
),
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 280,
render: (_, record) => (
<Space>
{record.status !== '处理中' && (
<Button
type="link"
icon={<RobotOutlined />}
onClick={() => handleGetSolution(record.ID)}
>
</Button>
)}
<Button
type="link"
icon={<InfoCircleOutlined />}
onClick={() => handleViewDetail(record)}
>
</Button>
<Popconfirm
title="确定要删除吗?"
onConfirm={() => handleDelete(record)}
okText="确定"
cancelText="取消"
>
<Button type="link" danger icon={<DeleteOutlined />}>
</Button>
</Popconfirm>
</Space>
),
},
];
return (
<PageContainer>
{/* Search Form */}
<Card className={styles.searchCard}>
<Form form={form} layout="inline" onFinish={handleSearch}>
<Form.Item
name="createdAtRange"
label={
<span>
<Tooltip title="搜索范围是开始日期(包含)至结束日期(不包含)">
<QuestionCircleOutlined style={{ marginLeft: 4 }} />
</Tooltip>
</span>
}
>
<RangePicker showTime style={{ width: 380 }} />
</Form.Item>
<Form.Item name="form" label="错误来源">
<Input placeholder="搜索条件" />
</Form.Item>
<Form.Item name="info" label="错误内容">
<Input placeholder="搜索条件" />
</Form.Item>
{showAllQuery && (
<>
{/* Additional query fields can be added here */}
</>
)}
<Form.Item>
<Space>
<Button type="primary" icon={<SearchOutlined />} htmlType="submit">
</Button>
<Button icon={<ReloadOutlined />} onClick={handleReset}>
</Button>
<Button
type="link"
icon={showAllQuery ? <UpOutlined /> : <DownOutlined />}
onClick={() => setShowAllQuery(!showAllQuery)}
>
{showAllQuery ? '收起' : '展开'}
</Button>
</Space>
</Form.Item>
</Form>
</Card>
{/* Table */}
<Card>
<div className={styles.toolbar}>
<Space>
<Popconfirm
title="确定要删除选中的数据吗?"
onConfirm={handleBatchDelete}
okText="确定"
cancelText="取消"
disabled={selectedRowKeys.length === 0}
>
<Button
icon={<DeleteOutlined />}
disabled={selectedRowKeys.length === 0}
>
</Button>
</Popconfirm>
</Space>
</div>
<Table
rowKey="ID"
columns={columns}
dataSource={dataSource}
loading={loading}
rowSelection={{
selectedRowKeys,
onChange: setSelectedRowKeys,
}}
pagination={{
current,
pageSize,
total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (t) => `${t}`,
onChange: (page, size) => {
setCurrent(page);
setPageSize(size);
},
}}
scroll={{ x: 1200 }}
/>
</Card>
{/* Detail Drawer */}
<Drawer
title="查看"
open={detailVisible}
onClose={() => setDetailVisible(false)}
width={600}
destroyOnClose
>
{detailData && (
<Descriptions column={2} bordered layout="vertical">
<Descriptions.Item label="错误来源">
{detailData.form}
</Descriptions.Item>
<Descriptions.Item label="错误等级">
<Tag color={levelTagMap[detailData.level] || 'default'}>
{levelLabelMap[detailData.level] || '一般错误'}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="处理状态">
<Tag color={statusTagMap[detailData.status] || 'default'}>
{statusLabelMap[detailData.status] || '未处理'}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="错误内容" span={2}>
<pre className={styles.preContent}>{detailData.info}</pre>
</Descriptions.Item>
<Descriptions.Item label="解决方案" span={2}>
<pre className={styles.preContent}>{detailData.solution || '-'}</pre>
</Descriptions.Item>
</Descriptions>
)}
</Drawer>
</PageContainer>
);
};
export default SysErrorPage;