import { useState, useEffect, useCallback, useRef } from 'react'; import { Table, Select, Input, Tag, message } 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'; 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: 'patient', label: '患者' }, { value: 'patient_tag', label: '患者标签' }, { value: 'patient_family_member', label: '家庭成员' }, { value: 'patient_doctor_relation', label: '医患关系' }, { value: 'points_transaction', label: '积分流水' }, { value: 'points_product', label: '积分商品' }, { value: 'points_order', label: '积分订单' }, { value: 'points_rule', label: '积分规则' }, { value: 'offline_event', label: '线下活动' }, { value: 'offline_event_registration', 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 [logs, setLogs] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [query, setQuery] = useState({ page: 1, page_size: 20 }); const isDark = useThemeMode(); const userNameCache = useRef>({}); const cacheLoaded = useRef(false); const fetchLogs = useCallback(async (params: AuditLogQuery) => { setLoading(true); try { const result = await listAuditLogs(params); setLogs(result.data); setTotal(result.total); } catch { message.error('加载审计日志失败'); } setLoading(false); }, []); // 加载用户名称缓存(分页遍历所有用户) 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 { // 静默失败,将显示 UUID } }; loadAllUsers(); return () => { cancelled = true; }; }, []); useEffect(() => { fetchLogs(query); }, [query, fetchLogs]); const handleFilterChange = (field: keyof AuditLogQuery, value: string | undefined) => { setQuery((prev) => ({ ...prev, [field]: value || undefined, page: 1, })); }; const handleTableChange = (pagination: TablePaginationConfig) => { setQuery((prev) => ({ ...prev, page: pagination.current, page_size: pagination.pageSize, })); }; 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} 条日志
{/* 表格 */}
`共 ${t} 条`, }} scroll={{ x: 900 }} /> ); }