feat(task1): restructure directories for turborepo monorepo
- Move backend/ to apps/server/ via git mv - Move frontend/ to apps/admin/ via git mv - Create packages/typescript-config/ with base, nestjs, and react-vite presets
This commit is contained in:
97
apps/admin/src/pages/OperationLogs/index.tsx
Normal file
97
apps/admin/src/pages/OperationLogs/index.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Table, Select, DatePicker, Space, Tag, Tooltip } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import api from '../../api';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const moduleColorMap: Record<string, string> = {
|
||||
'学生': 'blue', '宿舍': 'green', '入住': 'cyan', '费用': 'orange', '账单': 'red', '账号': 'purple', '认证': 'magenta',
|
||||
};
|
||||
|
||||
const statusMap: Record<string, { text: string; color: string }> = {
|
||||
success: { text: '成功', color: 'green' },
|
||||
fail: { text: '失败', color: 'red' },
|
||||
};
|
||||
|
||||
const OperationLogsPage: React.FC = () => {
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [filterModule, setFilterModule] = useState<string | undefined>();
|
||||
const [dateRange, setDateRange] = useState<[string, string] | null>(null);
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params: any = { page, pageSize: 20 };
|
||||
if (filterModule) params.module = filterModule;
|
||||
if (dateRange) { params.startDate = dateRange[0]; params.endDate = dateRange[1]; }
|
||||
const res: any = await api.get('/operation-logs', { params });
|
||||
setData(res.data);
|
||||
setTotal(res.total);
|
||||
} catch (e) { console.error(e); }
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => { fetchData(); }, [page, filterModule, dateRange]);
|
||||
|
||||
const columns = [
|
||||
{ title: '时间', dataIndex: 'createdAt', width: 170, render: (v: string) => dayjs(v).format('YYYY-MM-DD HH:mm:ss') },
|
||||
{ title: '操作人', dataIndex: 'username', width: 100 },
|
||||
{ title: '模块', dataIndex: 'module', width: 80, render: (v: string) => <Tag color={moduleColorMap[v] || 'default'}>{v}</Tag> },
|
||||
{ title: '操作', dataIndex: 'action', width: 150 },
|
||||
{ title: '状态', dataIndex: 'status', width: 70, render: (v: string) => {
|
||||
const s = statusMap[v] || statusMap['success'];
|
||||
return <Tag color={s.color}>{s.text}</Tag>;
|
||||
}},
|
||||
{ title: '详情', dataIndex: 'detail', ellipsis: true, render: (v: string) => v ? <Tooltip title={v}><span>{v}</span></Tooltip> : '-' },
|
||||
{ title: 'IP地址', dataIndex: 'ipAddress', width: 130, render: (v: string) => v || '-' },
|
||||
{ title: '终端', dataIndex: 'userAgent', width: 100, ellipsis: true, render: (v: string) => {
|
||||
if (!v) return '-';
|
||||
if (v.includes('Mobile')) return <Tag color="blue">手机</Tag>;
|
||||
if (v.includes('Windows')) return <Tag>Windows</Tag>;
|
||||
if (v.includes('Mac')) return <Tag>Mac</Tag>;
|
||||
if (v.includes('Linux')) return <Tag>Linux</Tag>;
|
||||
return <Tooltip title={v}><Tag>其他</Tag></Tooltip>;
|
||||
}},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 12 }}>
|
||||
<h2 style={{ margin: 0 }}>操作日志</h2>
|
||||
<Space wrap>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="筛选模块"
|
||||
style={{ width: 140 }}
|
||||
value={filterModule}
|
||||
onChange={(v) => { setFilterModule(v); setPage(1); }}
|
||||
options={['认证', '学生', '宿舍', '入住', '费用', '账单', '账号'].map((m) => ({ value: m, label: m }))}
|
||||
/>
|
||||
<RangePicker
|
||||
placeholder={['开始日期', '结束日期']}
|
||||
format="YYYY-MM-DD"
|
||||
onChange={(dates) => {
|
||||
if (dates) setDateRange([dates[0]!.format('YYYY-MM-DD'), dates[1]!.format('YYYY-MM-DD')]);
|
||||
else setDateRange(null);
|
||||
setPage(1);
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
scroll={{ x: 1000 }}
|
||||
pagination={{ current: page, total, pageSize: 20, onChange: setPage, showTotal: (t) => `共 ${t} 条` }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OperationLogsPage;
|
||||
Reference in New Issue
Block a user