import { useEffect, useRef } from 'react'; import { Table, Select, Input, Tag } from 'antd'; import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; import { listAuditLogs, type AuditLogItem, type AuditLogQuery } from '../../api/auditLogs'; import { listUsers } from '../../api/users'; import { useThemeMode } from '../../hooks/useThemeMode'; import { usePaginatedData } from '../../hooks/usePaginatedData'; const RESOURCE_TYPE_OPTIONS = [ { value: 'user', label: '用户' }, { value: 'role', label: '角色' }, { value: 'position', label: '岗位' }, { value: 'organization', label: '组织' }, { value: 'department', label: '部门' }, { value: 'process_instance', label: '流程实例' }, { value: 'process_definition', label: '流程定义' }, { value: 'task', label: '流程任务' }, { value: 'dictionary', label: '字典' }, { value: 'menu', label: '菜单' }, { value: 'setting', label: '设置' }, { value: 'numbering_rule', label: '编号规则' }, { value: 'journal_entry', label: '日记' }, { value: 'school_class', label: '班级' }, { value: 'class_member', label: '班级成员' }, { value: 'topic_assignment', label: '主题布置' }, { value: 'comment', label: '评论' }, { value: 'sticker_pack', label: '贴纸包' }, ]; const ACTION_STYLES: Record = { create: { bg: '#ECFDF5', color: '#059669', text: '创建' }, update: { bg: '#eff6ff', color: '#2563eb', text: '更新' }, delete: { bg: '#FEF2F2', color: '#dc2626', text: '删除' }, }; function formatDateTime(value: string): string { return new Date(value).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', }); } export default function AuditLogViewer() { const isDark = useThemeMode(); const userNameCache = useRef>({}); const cacheLoaded = useRef(false); const { data: logs, total, page, loading, filters, setFilters, refresh } = usePaginatedData( async (p, pageSize, query) => { const result = await listAuditLogs({ ...query, page: p, page_size: pageSize }); return { data: result.data, total: result.total }; }, { pageSize: 20, defaultFilters: { page: 1, page_size: 20 } as unknown as AuditLogQuery, autoFetch: false }, ); // Load user name cache useEffect(() => { if (cacheLoaded.current) return; let cancelled = false; const loadAllUsers = async () => { try { let currentPage = 1; const pageSize = 100; let hasMore = true; while (hasMore && !cancelled) { const result = await listUsers(currentPage, pageSize); for (const user of result.data) { userNameCache.current[user.id] = user.display_name || user.username; } hasMore = result.data.length >= pageSize; currentPage += 1; } if (!cancelled) cacheLoaded.current = true; } catch { /* silent */ } }; loadAllUsers(); return () => { cancelled = true; }; }, []); useEffect(() => { refresh(1); }, []); const handleFilterChange = (field: keyof AuditLogQuery, value: string | undefined) => { setFilters((prev) => ({ ...prev, [field]: value || undefined, page: 1 })); }; const columns: ColumnsType = [ { title: '操作', dataIndex: 'action', key: 'action', width: 100, render: (action: string) => { const info = ACTION_STYLES[action] || { bg: '#f8fafc', color: '#475569', text: action }; return {info.text}; }, }, { title: '资源类型', dataIndex: 'resource_type', key: 'resource_type', width: 120, render: (v: string) => {v}, }, { title: '资源 ID', dataIndex: 'resource_id', key: 'resource_id', width: 200, ellipsis: true, render: (v: string) => {v}, }, { title: '操作用户', dataIndex: 'user_id', key: 'user_id', width: 200, ellipsis: true, render: (v: string) => { const name = userNameCache.current[v]; return {name || v}; }, }, { title: '时间', dataIndex: 'created_at', key: 'created_at', width: 180, render: (value: string) => {formatDateTime(value)}, }, ]; return (
{/* 筛选工具栏 */}
handleFilterChange('user_id', e.target.value)} /> 共 {total} 条日志
{/* 表格 */}
refresh(pagination.current)} pagination={{ current: page, pageSize: 20, total, showSizeChanger: true, showTotal: (t) => `共 ${t} 条`, }} scroll={{ x: 900 }} /> ); }