kra/web/src/pages/system/operation/index.tsx

199 lines
5.5 KiB
TypeScript

/**
* 操作记录页面
* 与 GVA sysOperationRecord.vue 保持一致的功能
*/
import { DeleteOutlined, SearchOutlined, ReloadOutlined } from '@ant-design/icons';
import { PageContainer, ProTable } from '@ant-design/pro-components';
import type { ProColumns, ActionType } from '@ant-design/pro-components';
import { Button, message, Popconfirm, Space, Tag, Popover, Typography } from 'antd';
import { useRef, useState } from 'react';
import {
getSysOperationRecordList,
deleteSysOperationRecord,
deleteSysOperationRecordByIds,
} from '@/services/system/operationRecord';
import dayjs from 'dayjs';
const { Text } = Typography;
const OperationRecordPage: React.FC = () => {
const actionRef = useRef<ActionType>();
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);
// 格式化JSON显示
const formatBody = (value: string) => {
try {
return JSON.stringify(JSON.parse(value), null, 2);
} catch {
return value;
}
};
// 删除单条记录
const handleDelete = async (id: number) => {
const res = await deleteSysOperationRecord({ ID: id });
if (res.code === 0) {
message.success('删除成功');
actionRef.current?.reload();
}
};
// 批量删除
const handleBatchDelete = async () => {
if (selectedRowKeys.length === 0) {
message.warning('请选择要删除的记录');
return;
}
const res = await deleteSysOperationRecordByIds({ ids: selectedRowKeys });
if (res.code === 0) {
message.success('删除成功');
setSelectedRowKeys([]);
actionRef.current?.reload();
}
};
const columns: ProColumns<API.OperationRecord>[] = [
{
title: '操作人',
dataIndex: ['user', 'userName'],
width: 140,
search: false,
render: (_, record) => (
<span>{record.user?.userName}({record.user?.nickName})</span>
),
},
{
title: '日期',
dataIndex: 'CreatedAt',
width: 180,
search: false,
render: (_, record) => dayjs(record.CreatedAt).format('YYYY-MM-DD HH:mm:ss'),
},
{
title: '状态码',
dataIndex: 'status',
width: 100,
render: (_, record) => <Tag color="success">{record.status}</Tag>,
},
{
title: '请求IP',
dataIndex: 'ip',
width: 120,
search: false,
},
{
title: '请求方法',
dataIndex: 'method',
width: 100,
},
{
title: '请求路径',
dataIndex: 'path',
width: 240,
ellipsis: true,
},
{
title: '请求',
dataIndex: 'body',
width: 80,
search: false,
render: (_, record) => (
record.body ? (
<Popover
content={
<div style={{ maxHeight: 400, maxWidth: 400, overflow: 'auto', background: '#112435', padding: 12, borderRadius: 4 }}>
<pre style={{ color: '#f08047', margin: 0, fontSize: 12 }}>{formatBody(record.body)}</pre>
</div>
}
trigger="click"
>
<Button type="link" size="small"></Button>
</Popover>
) : <Text type="secondary"></Text>
),
},
{
title: '响应',
dataIndex: 'resp',
width: 80,
search: false,
render: (_, record) => (
record.resp ? (
<Popover
content={
<div style={{ maxHeight: 400, maxWidth: 400, overflow: 'auto', background: '#112435', padding: 12, borderRadius: 4 }}>
<pre style={{ color: '#f08047', margin: 0, fontSize: 12 }}>{formatBody(record.resp)}</pre>
</div>
}
trigger="click"
>
<Button type="link" size="small"></Button>
</Popover>
) : <Text type="secondary"></Text>
),
},
{
title: '操作',
valueType: 'option',
width: 100,
render: (_, record) => (
<Popconfirm title="确定删除?" onConfirm={() => handleDelete(record.ID!)}>
<Button type="link" danger icon={<DeleteOutlined />}></Button>
</Popconfirm>
),
},
];
return (
<PageContainer>
<ProTable<API.OperationRecord>
headerTitle="操作记录"
actionRef={actionRef}
rowKey="ID"
columns={columns}
rowSelection={{
selectedRowKeys,
onChange: (keys) => setSelectedRowKeys(keys as number[]),
}}
toolBarRender={() => [
<Popconfirm
key="batchDelete"
title="确定删除选中的记录?"
onConfirm={handleBatchDelete}
disabled={selectedRowKeys.length === 0}
>
<Button
danger
icon={<DeleteOutlined />}
disabled={selectedRowKeys.length === 0}
>
</Button>
</Popconfirm>,
]}
request={async (params) => {
const res = await getSysOperationRecordList({
page: params.current,
pageSize: params.pageSize,
method: params.method,
path: params.path,
status: params.status ? Number(params.status) : undefined,
});
return {
data: res.data?.list || [],
total: res.data?.total || 0,
success: res.code === 0,
};
}}
pagination={{
defaultPageSize: 10,
showSizeChanger: true,
pageSizeOptions: ['10', '30', '50', '100'],
}}
/>
</PageContainer>
);
};
export default OperationRecordPage;