83 lines
2.9 KiB
TypeScript
83 lines
2.9 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Download, RotateCcw } from 'lucide-react';
|
|
import type { SystemLogExportResult } from '@/api/adminApi';
|
|
import { Button } from './Button';
|
|
|
|
type Filters = { keyword?: string; level?: string; module?: string; range?: string };
|
|
|
|
export function SystemLogExport({
|
|
portal,
|
|
filters,
|
|
exportLogs,
|
|
}: {
|
|
portal: 'admin' | 'client';
|
|
filters: Filters;
|
|
exportLogs: (filters: Filters) => Promise<SystemLogExportResult>;
|
|
}) {
|
|
const storageKey = `cmpp:${portal}:system-log-export-recovery`;
|
|
const [exporting, setExporting] = useState(false);
|
|
const [result, setResult] = useState<SystemLogExportResult | null>(null);
|
|
const [error, setError] = useState('');
|
|
const [retryFilters, setRetryFilters] = useState<Filters | null>(null);
|
|
|
|
useEffect(() => {
|
|
try {
|
|
const saved = sessionStorage.getItem(storageKey);
|
|
if (saved) {
|
|
setRetryFilters(JSON.parse(saved) as Filters);
|
|
setError('上次导出未完成,筛选条件已保留,可直接重试。');
|
|
}
|
|
} catch {
|
|
sessionStorage.removeItem(storageKey);
|
|
}
|
|
}, [storageKey]);
|
|
|
|
async function run(nextFilters: Filters) {
|
|
setExporting(true);
|
|
setError('');
|
|
setResult(null);
|
|
setRetryFilters(nextFilters);
|
|
sessionStorage.setItem(storageKey, JSON.stringify(nextFilters));
|
|
try {
|
|
const exported = await exportLogs(nextFilters);
|
|
setResult(exported);
|
|
setRetryFilters(null);
|
|
sessionStorage.removeItem(storageKey);
|
|
} catch (reason) {
|
|
setError(reason instanceof Error ? reason.message : '日志导出失败,请重试');
|
|
} finally {
|
|
setExporting(false);
|
|
}
|
|
}
|
|
|
|
function download() {
|
|
if (!result) return;
|
|
const url = URL.createObjectURL(new Blob([`\uFEFF${result.content}`], { type: 'text/csv;charset=utf-8' }));
|
|
const anchor = document.createElement('a');
|
|
anchor.href = url;
|
|
anchor.download = result.fileName;
|
|
anchor.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
return (
|
|
<div className="system-log-export">
|
|
<Button disabled={exporting} icon={<Download size={17} />} onClick={() => run(filters)} variant="secondary">
|
|
{exporting ? '导出中…' : '导出日志'}
|
|
</Button>
|
|
{result ? (
|
|
<div className="system-log-export__result" role="status">
|
|
<span>导出完成,共 {result.recordCount} 条{result.truncated ? '(已截取前 10000 条)' : ''},操作单号 {result.operationId}</span>
|
|
<Button icon={<Download size={15} />} onClick={download} size="sm" variant="ghost">下载文件</Button>
|
|
</div>
|
|
) : null}
|
|
{error ? (
|
|
<div className="system-log-export__error" role="alert">
|
|
<span>{error}</span>
|
|
<Button disabled={exporting} icon={<RotateCcw size={15} />} onClick={() => run(retryFilters ?? filters)} size="sm" variant="ghost">重试</Button>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|