import { useEffect, useMemo, useState } from "react"; import { isApiError } from "../api/types"; import { exportAuditLogs } from "../api/exports"; import { fetchAuditLogs, type AuditLogListResponse } from "../api/audit"; import { PageHeader } from "../components/common/PageHeader"; import { Button } from "../components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../components/ui/dropdown-menu"; import { downloadBlob } from "../lib/download"; import { TableSkeleton } from "../components/common/TableSkeleton"; const DEFAULT_PAGE_SIZE = 20; export function AuditLogsPage() { const [page, setPage] = useState(1); const [actionFilter, setActionFilter] = useState(""); const [targetTypeFilter, setTargetTypeFilter] = useState(""); const [loading, setLoading] = useState(false); const [exporting, setExporting] = useState(false); const [error, setError] = useState(""); const [data, setData] = useState(null); const totalPages = useMemo(() => { if (!data) { return 1; } return Math.max(1, Math.ceil(data.total / data.pageSize)); }, [data]); useEffect(() => { let cancelled = false; async function load() { setLoading(true); setError(""); try { const response = await fetchAuditLogs({ page, pageSize: DEFAULT_PAGE_SIZE, action: actionFilter || undefined, targetType: targetTypeFilter || undefined, }); if (!cancelled) { setData(response); } } catch (err) { if (!cancelled) { if (isApiError(err)) { setError(`${err.code}: ${err.message}`); } else { setError("加载审计日志失败,请稍后重试"); } } } finally { if (!cancelled) { setLoading(false); } } } void load(); return () => { cancelled = true; }; }, [actionFilter, page, targetTypeFilter]); return (
{ setExporting(true); try { const response = await exportAuditLogs({ format: "csv", action: actionFilter || undefined, targetType: targetTypeFilter || undefined, }); downloadBlob(response.blob, response.filename ?? "audit-logs.csv"); setError(""); } catch (err) { if (isApiError(err)) { setError(`${err.code}: ${err.message}`); } else { setError("导出审计日志失败,请稍后重试"); } } finally { setExporting(false); } }} > 导出 CSV { setExporting(true); try { const response = await exportAuditLogs({ format: "xlsx", action: actionFilter || undefined, targetType: targetTypeFilter || undefined, }); downloadBlob(response.blob, response.filename ?? "audit-logs.xlsx"); setError(""); } catch (err) { if (isApiError(err)) { setError(`${err.code}: ${err.message}`); } else { setError("导出审计日志失败,请稍后重试"); } } finally { setExporting(false); } }} > 导出 Excel } />
结果统计
共 {data?.total ?? 0} 条
{error ? (

{error}

) : null}
{loading ? ( ) : null} {!loading && data?.items?.length ? ( data.items.map((item) => ( )) ) : null} {!loading && !(data?.items?.length ?? 0) ? ( ) : null}
时间 Action Actor Target Request ID
{new Date(item.createdAt).toLocaleString()} {item.action}
{item.actorName || item.actorId}
{item.actorEmail}
{item.targetType} {item.targetId ? `/${item.targetId}` : ""} {item.requestId}
暂无数据
第 {page} / {totalPages} 页
); }