|
@@ -1,5 +1,5 @@
|
|
|
-import React, { useState } from 'react';
|
|
|
|
|
-import { Table, Button, Space, Input, Select, DatePicker, Form } from 'antd';
|
|
|
|
|
|
|
+import React from 'react';
|
|
|
|
|
+import { Table, Button, Space, Input, Form, DatePicker } from 'antd';
|
|
|
import { SearchOutlined, FilterOutlined } from '@ant-design/icons';
|
|
import { SearchOutlined, FilterOutlined } from '@ant-design/icons';
|
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
|
import { logfileClient } from '@/client/api';
|
|
import { logfileClient } from '@/client/api';
|
|
@@ -14,16 +14,19 @@ const { RangePicker } = DatePicker;
|
|
|
|
|
|
|
|
const Logs: React.FC = () => {
|
|
const Logs: React.FC = () => {
|
|
|
const [form] = Form.useForm();
|
|
const [form] = Form.useForm();
|
|
|
- const [searchText, setSearchText] = useState('');
|
|
|
|
|
- const [filters, setFilters] = useState({
|
|
|
|
|
- class: '',
|
|
|
|
|
- action: '',
|
|
|
|
|
- userId: '',
|
|
|
|
|
- dateRange: [] as [dayjs.Dayjs | null, dayjs.Dayjs | null]
|
|
|
|
|
- });
|
|
|
|
|
|
|
|
|
|
// 获取日志列表数据
|
|
// 获取日志列表数据
|
|
|
- const fetchLogs = async ({ page, pageSize }: { page: number; pageSize: number }): Promise<LogfileListResponse> => {
|
|
|
|
|
|
|
+ const fetchLogs = async ({
|
|
|
|
|
+ page = 1,
|
|
|
|
|
+ pageSize = 10,
|
|
|
|
|
+ searchText = '',
|
|
|
|
|
+ filters = {}
|
|
|
|
|
+ }: {
|
|
|
|
|
+ page?: number;
|
|
|
|
|
+ pageSize?: number;
|
|
|
|
|
+ searchText?: string;
|
|
|
|
|
+ filters?: Record<string, any>;
|
|
|
|
|
+ }): Promise<LogfileListResponse> => {
|
|
|
const queryParams: Record<string, any> = { page, pageSize };
|
|
const queryParams: Record<string, any> = { page, pageSize };
|
|
|
|
|
|
|
|
if (searchText) queryParams.keyword = searchText;
|
|
if (searchText) queryParams.keyword = searchText;
|
|
@@ -34,68 +37,10 @@ const Logs: React.FC = () => {
|
|
|
if (filters.dateRange?.[1]) queryParams.endDate = filters.dateRange[1].format('YYYY-MM-DD');
|
|
if (filters.dateRange?.[1]) queryParams.endDate = filters.dateRange[1].format('YYYY-MM-DD');
|
|
|
|
|
|
|
|
const response = await logfileClient.$get({ query: queryParams });
|
|
const response = await logfileClient.$get({ query: queryParams });
|
|
|
- if (response.status !== 200) throw new Error('Failed to fetch logs');
|
|
|
|
|
|
|
+ if (!response.ok) throw new Error('Failed to fetch logs');
|
|
|
return response.json() as Promise<LogfileListResponse>;
|
|
return response.json() as Promise<LogfileListResponse>;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const [pagination, setPagination] = useState({
|
|
|
|
|
- current: 1,
|
|
|
|
|
- pageSize: 10,
|
|
|
|
|
- total: 0,
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const { data, isLoading: loading, refetch } = useQuery(
|
|
|
|
|
- ['logs', pagination.current, pagination.pageSize, searchText, filters],
|
|
|
|
|
- () => fetchLogs({ page: pagination.current, pageSize: pagination.pageSize }),
|
|
|
|
|
- {
|
|
|
|
|
- onSuccess: (result) => {
|
|
|
|
|
- setPagination({
|
|
|
|
|
- ...pagination,
|
|
|
|
|
- total: result.pagination.total,
|
|
|
|
|
- });
|
|
|
|
|
- },
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- // 搜索
|
|
|
|
|
- const handleSearch = () => {
|
|
|
|
|
- setPagination({ ...pagination, current: 1 });
|
|
|
|
|
- refetch();
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 过滤条件变化
|
|
|
|
|
- const handleFilterChange = (values: any) => {
|
|
|
|
|
- setFilters({
|
|
|
|
|
- ...filters,
|
|
|
|
|
- class: values.class || '',
|
|
|
|
|
- action: values.action || '',
|
|
|
|
|
- userId: values.userId || '',
|
|
|
|
|
- dateRange: values.dateRange || []
|
|
|
|
|
- });
|
|
|
|
|
- setPagination({ ...pagination, current: 1 });
|
|
|
|
|
- refetch();
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 分页变化
|
|
|
|
|
- const handleTableChange = (pagination: any) => {
|
|
|
|
|
- setPagination(pagination);
|
|
|
|
|
- refetch();
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 重置过滤条件
|
|
|
|
|
- const handleResetFilters = () => {
|
|
|
|
|
- form.resetFields();
|
|
|
|
|
- setFilters({
|
|
|
|
|
- class: '',
|
|
|
|
|
- action: '',
|
|
|
|
|
- userId: '',
|
|
|
|
|
- dateRange: []
|
|
|
|
|
- });
|
|
|
|
|
- setSearchText('');
|
|
|
|
|
- setPagination({ ...pagination, current: 1 });
|
|
|
|
|
- refetch();
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
// 表格列定义
|
|
// 表格列定义
|
|
|
const columns = [
|
|
const columns = [
|
|
|
{
|
|
{
|
|
@@ -127,7 +72,7 @@ const Logs: React.FC = () => {
|
|
|
title: '操作时间',
|
|
title: '操作时间',
|
|
|
dataIndex: 'logTime',
|
|
dataIndex: 'logTime',
|
|
|
key: 'logTime',
|
|
key: 'logTime',
|
|
|
- render: (time: string) => time ? new Date(time).toLocaleString() : '-',
|
|
|
|
|
|
|
+ render: (time: string) => time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '-',
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
title: '操作原因',
|
|
title: '操作原因',
|
|
@@ -136,6 +81,51 @@ const Logs: React.FC = () => {
|
|
|
},
|
|
},
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
|
|
+ // 使用React Query v5实现数据获取
|
|
|
|
|
+ const { data, isLoading, refetch } = useQuery({
|
|
|
|
|
+ queryKey: ['logs', form.getFieldsValue(), form.getFieldValue('searchText')],
|
|
|
|
|
+ queryFn: ({ signal }) => {
|
|
|
|
|
+ const values = form.getFieldsValue();
|
|
|
|
|
+ const searchText = form.getFieldValue('searchText') || '';
|
|
|
|
|
+
|
|
|
|
|
+ return fetchLogs({
|
|
|
|
|
+ page: values.pagination?.current || 1,
|
|
|
|
|
+ pageSize: values.pagination?.pageSize || 10,
|
|
|
|
|
+ searchText,
|
|
|
|
|
+ filters: {
|
|
|
|
|
+ class: values.class,
|
|
|
|
|
+ action: values.action,
|
|
|
|
|
+ userId: values.userId,
|
|
|
|
|
+ dateRange: values.dateRange
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ staleTime: 1000 * 60 * 5, // 5分钟缓存
|
|
|
|
|
+ gcTime: 1000 * 60 * 30, // 30分钟垃圾回收
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 处理搜索
|
|
|
|
|
+ const handleSearch = () => {
|
|
|
|
|
+ refetch();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 处理过滤
|
|
|
|
|
+ const handleFilterChange = () => {
|
|
|
|
|
+ refetch();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 处理分页变化
|
|
|
|
|
+ const handleTableChange = (pagination: any) => {
|
|
|
|
|
+ form.setFieldsValue({ pagination });
|
|
|
|
|
+ refetch();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 重置过滤条件
|
|
|
|
|
+ const handleResetFilters = () => {
|
|
|
|
|
+ form.resetFields();
|
|
|
|
|
+ refetch();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<div className="p-4">
|
|
<div className="p-4">
|
|
|
<div className="flex justify-between items-center mb-4">
|
|
<div className="flex justify-between items-center mb-4">
|
|
@@ -143,7 +133,17 @@ const Logs: React.FC = () => {
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div className="bg-white p-4 mb-4 rounded-lg shadow-sm">
|
|
<div className="bg-white p-4 mb-4 rounded-lg shadow-sm">
|
|
|
- <Form form={form} layout="inline" onFinish={handleFilterChange}>
|
|
|
|
|
|
|
+ <Form
|
|
|
|
|
+ form={form}
|
|
|
|
|
+ layout="inline"
|
|
|
|
|
+ onValuesChange={handleFilterChange}
|
|
|
|
|
+ initialValues={{
|
|
|
|
|
+ pagination: {
|
|
|
|
|
+ current: 1,
|
|
|
|
|
+ pageSize: 10
|
|
|
|
|
+ }
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
<Form.Item name="class" label="日志类别">
|
|
<Form.Item name="class" label="日志类别">
|
|
|
<Input placeholder="请输入日志类别" style={{ width: 150 }} />
|
|
<Input placeholder="请输入日志类别" style={{ width: 150 }} />
|
|
|
</Form.Item>
|
|
</Form.Item>
|
|
@@ -157,7 +157,7 @@ const Logs: React.FC = () => {
|
|
|
<RangePicker format="YYYY-MM-DD" />
|
|
<RangePicker format="YYYY-MM-DD" />
|
|
|
</Form.Item>
|
|
</Form.Item>
|
|
|
<Form.Item>
|
|
<Form.Item>
|
|
|
- <Button type="primary" htmlType="submit" icon={<FilterOutlined />}>
|
|
|
|
|
|
|
+ <Button type="primary" icon={<FilterOutlined />} onClick={handleFilterChange}>
|
|
|
筛选
|
|
筛选
|
|
|
</Button>
|
|
</Button>
|
|
|
</Form.Item>
|
|
</Form.Item>
|
|
@@ -166,29 +166,36 @@ const Logs: React.FC = () => {
|
|
|
重置
|
|
重置
|
|
|
</Button>
|
|
</Button>
|
|
|
</Form.Item>
|
|
</Form.Item>
|
|
|
|
|
+
|
|
|
|
|
+ <Form.Item name="searchText" noStyle>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ placeholder="搜索日志内容"
|
|
|
|
|
+ prefix={<SearchOutlined />}
|
|
|
|
|
+ onPressEnter={handleSearch}
|
|
|
|
|
+ style={{ width: 300, marginLeft: 16 }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ <Form.Item noStyle>
|
|
|
|
|
+ <Button type="default" onClick={handleSearch} style={{ marginLeft: 8 }}>
|
|
|
|
|
+ 搜索
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </Form.Item>
|
|
|
</Form>
|
|
</Form>
|
|
|
-
|
|
|
|
|
- <div className="mt-4">
|
|
|
|
|
- <Input
|
|
|
|
|
- placeholder="搜索日志内容"
|
|
|
|
|
- prefix={<SearchOutlined />}
|
|
|
|
|
- value={searchText}
|
|
|
|
|
- onChange={(e) => setSearchText(e.target.value)}
|
|
|
|
|
- onPressEnter={handleSearch}
|
|
|
|
|
- style={{ width: 300 }}
|
|
|
|
|
- />
|
|
|
|
|
- <Button type="default" onClick={handleSearch} style={{ marginLeft: 8 }}>
|
|
|
|
|
- 搜索
|
|
|
|
|
- </Button>
|
|
|
|
|
- </div>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<Table
|
|
<Table
|
|
|
columns={columns}
|
|
columns={columns}
|
|
|
dataSource={data?.data || []}
|
|
dataSource={data?.data || []}
|
|
|
rowKey="id"
|
|
rowKey="id"
|
|
|
- loading={loading}
|
|
|
|
|
- pagination={pagination}
|
|
|
|
|
|
|
+ loading={isLoading}
|
|
|
|
|
+ pagination={{
|
|
|
|
|
+ current: data?.pagination.current || 1,
|
|
|
|
|
+ pageSize: data?.pagination.pageSize || 10,
|
|
|
|
|
+ total: data?.pagination.total || 0,
|
|
|
|
|
+ showSizeChanger: true,
|
|
|
|
|
+ showQuickJumper: true,
|
|
|
|
|
+ showTotal: (total) => `共 ${total} 条记录`
|
|
|
|
|
+ }}
|
|
|
onChange={handleTableChange}
|
|
onChange={handleTableChange}
|
|
|
bordered
|
|
bordered
|
|
|
size="middle"
|
|
size="middle"
|