feat: optimize UI queries and seed number library

This commit is contained in:
hectorzhao
2026-08-27 20:24:18 +08:00
parent 138297d191
commit 564234b769
22 changed files with 631 additions and 84 deletions
+26 -7
View File
@@ -1,7 +1,10 @@
import { useMemo, useState } from 'react';
import { Badge, Button, Field, Input, Select } from '../components/ui.jsx';
import { Icon, PageTitle, Toolbar, Panel, ApiNotice, Drawer, SimpleTable, KeyValue } from '../components/layout.jsx';
import { Icon, PageTitle, Toolbar, Panel, ApiNotice, PageLoadingDialog, Drawer, SimpleTable, KeyValue, DateRangeFields } from '../components/layout.jsx';
import { operationLogRows } from '../fixtures/devFixtures.js';
import { dateRangeParams, lastSevenDays } from '../utils/dateRange.js';
const defaultRange = lastSevenDays();
function toneForStatus(status) {
return status === '成功' || status === 'SUCCESS' ? 'success' : status === '失败' || status === 'FAILURE' ? 'danger' : 'neutral';
@@ -11,8 +14,8 @@ export function OperationLogsPage({ logRows = operationLogRows, apiLoading, apiE
const [keyword, setKeyword] = useState('');
const [moduleFilter, setModuleFilter] = useState('all');
const [resultFilter, setResultFilter] = useState('all');
const [startDate, setStartDate] = useState('');
const [endDate, setEndDate] = useState('');
const [startDate, setStartDate] = useState(defaultRange.startDate);
const [endDate, setEndDate] = useState(defaultRange.endDate);
const [detailLog, setDetailLog] = useState(null);
const modules = useMemo(() => Array.from(new Set(logRows.map((log) => log.module))), [logRows]);
const visibleLogs = useMemo(() => {
@@ -23,18 +26,34 @@ export function OperationLogsPage({ logRows = operationLogRows, apiLoading, apiE
return matchesKeyword && (moduleFilter === 'all' || log.module === moduleFilter) && (resultFilter === 'all' || log.result === resultFilter) && (!startDate || logDate >= startDate) && (!endDate || logDate <= endDate);
});
}, [endDate, keyword, logRows, moduleFilter, resultFilter, startDate]);
const resetFilters = () => { setKeyword(''); setModuleFilter('all'); setResultFilter('all'); setStartDate(''); setEndDate(''); };
const queryLogs = (overrides = {}) => {
const next = { moduleFilter, resultFilter, startDate, endDate, ...overrides };
return refreshApi({
module: next.moduleFilter,
result: next.resultFilter === '成功' ? 'SUCCESS' : next.resultFilter === '失败' ? 'FAILURE' : 'all',
...dateRangeParams(next.startDate, next.endDate, 'createdFrom', 'createdTo'),
});
};
const resetFilters = () => {
setKeyword('');
setModuleFilter('all');
setResultFilter('all');
setStartDate(defaultRange.startDate);
setEndDate(defaultRange.endDate);
void queryLogs({ moduleFilter: 'all', resultFilter: 'all', ...defaultRange });
};
return (
<>
<PageTitle title="操作日志" desc="审计登录、配置变更、敏感操作及其执行结果。" actions={<Button variant="secondary" icon={<Icon type="export" />}>导出日志</Button>} />
<ApiNotice loading={apiLoading} error={apiError} onRetry={refreshApi} />
<ApiNotice loading={apiLoading} error={apiError} onRetry={() => void queryLogs()} />
<PageLoadingDialog loading={apiLoading} />
<Toolbar>
<Field label="关键词"><Input value={keyword} onChange={(event) => setKeyword(event.target.value)} placeholder="用户、操作对象或 IP" /></Field>
<Field label="功能模块"><Select value={moduleFilter} onChange={(event) => setModuleFilter(event.target.value)}><option value="all">全部模块</option>{modules.map((module) => <option key={module} value={module}>{module}</option>)}</Select></Field>
<Field label="执行结果"><Select value={resultFilter} onChange={(event) => setResultFilter(event.target.value)}><option value="all">全部结果</option><option value="成功">成功</option><option value="失败">失败</option></Select></Field>
<Field label="开始日期"><Input type="date" value={startDate} onChange={(event) => setStartDate(event.target.value)} /></Field>
<Field label="结束日期"><Input type="date" value={endDate} onChange={(event) => setEndDate(event.target.value)} /></Field>
<DateRangeFields startDate={startDate} endDate={endDate} onStartChange={setStartDate} onEndChange={setEndDate} />
<Button icon={<Icon type="search" />} disabled={apiLoading} onClick={() => void queryLogs()}>查询</Button>
<Button variant="outline" onClick={resetFilters}>重置</Button>
</Toolbar>
<Panel title="日志列表" aside={<Badge tone="neutral"> {visibleLogs.length} </Badge>} className="wide-panel">