import { useEffect, useState, useCallback } from 'react'; import { adminApi } from '../lib/api'; import type { AuditLog } from '../types'; import { showToast } from '../components/Toast'; import { DatePicker } from '../components/DatePicker'; import { Select } from '../components/Select'; import styles from './AuditLogsPage.module.css'; const ACTION_OPTIONS = [ { label: '全部操作', value: '' }, { label: '创建团队', value: 'team_create' }, { label: '更新团队', value: 'team_update' }, { label: '团队充值', value: 'team_topup' }, { label: '设置额度池', value: 'team_set_pool' }, { label: '创建团队管理员', value: 'team_create_admin' }, { label: '创建用户', value: 'user_create' }, { label: '更新用户额度', value: 'user_quota_update' }, { label: '切换用户状态', value: 'user_status_toggle' }, { label: '更新系统设置', value: 'settings_update' }, { label: '创建团队成员', value: 'member_create' }, { label: '更新成员额度', value: 'member_quota_update' }, { label: '切换成员状态', value: 'member_status_toggle' }, ]; function renderChanges(before: Record | null, after: Record | null) { if (!before && !after) return '-'; const fields = new Set([...Object.keys(before || {}), ...Object.keys(after || {})]); if (fields.size === 0) return '-'; return (
{[...fields].map((field) => { const oldVal = before?.[field]; const newVal = after?.[field]; if (oldVal === undefined && newVal !== undefined) { return (
{field}: {String(newVal)}
); } if (oldVal !== undefined && newVal !== undefined && String(oldVal) !== String(newVal)) { return (
{field}: {String(oldVal)} {String(newVal)}
); } if (oldVal === undefined && newVal === undefined) return null; // Same value, show as-is for create actions if (oldVal === undefined) { return (
{field}: {String(newVal)}
); } return null; })}
); } export function AuditLogsPage() { const [logs, setLogs] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [actionFilter, setActionFilter] = useState(''); const [operatorSearch, setOperatorSearch] = useState(''); const [startDate, setStartDate] = useState(''); const [endDate, setEndDate] = useState(''); const [loading, setLoading] = useState(true); const pageSize = 20; const fetchLogs = useCallback(async () => { setLoading(true); try { const { data } = await adminApi.getAuditLogs({ page, page_size: pageSize, action: actionFilter || undefined, operator: operatorSearch || undefined, start_date: startDate || undefined, end_date: endDate || undefined, }); setLogs(data.results); setTotal(data.total); } catch { showToast('加载审计日志失败'); } finally { setLoading(false); } }, [page, actionFilter, operatorSearch, startDate, endDate]); useEffect(() => { fetchLogs(); }, [fetchLogs]); const handleSearch = () => { setPage(1); fetchLogs(); }; const totalPages = Math.ceil(total / pageSize); return (

操作日志

setOperatorSearch(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} /> ~
{loading ? ( Array.from({ length: 5 }).map((_, i) => ( {Array.from({ length: 6 }).map((_, j) => ( ))} )) ) : logs.length === 0 ? ( ) : ( logs.map((log) => ( )) )}
时间 操作人 操作类型 目标 变更详情 IP
暂无日志记录
{new Date(log.created_at).toLocaleString('zh-CN')} {log.operator_name} {log.action_display} {log.target_name || '-'} {log.target_type && ({log.target_type})} {renderChanges(log.before, log.after)} {log.ip_address || '-'}
{totalPages > 1 && (
共 {total} 条
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { let p: number; if (totalPages <= 5) p = i + 1; else if (page <= 3) p = i + 1; else if (page >= totalPages - 2) p = totalPages - 4 + i; else p = page - 2 + i; return ( ); })}
)}
); }