/** * Admin 后台 - 评估汇总页(报刊风) * * 功能: * - 按 provider / model / prompt_version / mode / league_code 筛选 * - 展示汇总统计卡片(已结算/已评估/跳过数) * - 准确率对比表格 * - 空态与加载态 */ import { useCallback, useEffect, useState } from 'react' import { fetchEvalSummary, fetchLeagues } from '../dal' import type { EvalSummary } from '../types' import { Card, CardBody, CardHeader, StatCard, Badge, DataTable, Alert, Spinner, EmptyState } from '../components' interface Filters { provider: string model: string prompt_version: string mode: string league_code: string } const EMPTY_FILTERS: Filters = { provider: '', model: '', prompt_version: '', mode: '', league_code: '' } export default function EvalPage() { const [filters, setFilters] = useState(EMPTY_FILTERS) const [leagues, setLeagues] = useState>([]) const [data, setData] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) useEffect(() => { fetchLeagues() .then(l => setLeagues(l.map(x => ({ code: x.code, name: x.name })))) .catch(() => setLeagues([])) }, []) const load = useCallback(async () => { setLoading(true) setError(null) try { const params: Record = {} if (filters.provider) params.provider = filters.provider if (filters.model) params.model = filters.model if (filters.prompt_version) params.prompt_version = filters.prompt_version if (filters.mode) params.mode = filters.mode if (filters.league_code) params.league_code = filters.league_code const result = await fetchEvalSummary(params) setData(result) } catch (err: unknown) { setError(err instanceof Error ? err.message : '加载失败') } finally { setLoading(false) } }, [filters]) useEffect(() => { load() }, [load]) const handleChange = (key: keyof Filters) => (e: React.ChangeEvent) => { setFilters(f => ({ ...f, [key]: e.target.value })) } const handleReset = () => setFilters(EMPTY_FILTERS) const summary = data?.summary ?? [] return (
{/* 筛选控件 */}
{/* 错误态 */} {error && } {/* 汇总统计 */} {data && (
)} {/* 准确率表格 */} 0 ? `共 ${summary.length} 组` : "按 provider × 模型 × prompt_version 聚合,仅统计有效预测"} /> {loading ? (
) : summary.length === 0 ? ( ) : (
( {row.prompt_version ?? '—'} ) }, { key: 'total', label: '评估条数' }, { key: 'accuracy_1x2', label: '1X2 准确率', render: (row: any) => ( {row.accuracy_1x2 != null ? `${row.accuracy_1x2}%` : '—'} ) }, { key: 'avg_score_rmse', label: '比分 RMSE', render: (row: any) => ( {row.avg_score_rmse != null ? row.avg_score_rmse.toFixed(2) : '—'} ) }, { key: 'avg_subjective_confidence', label: '平均置信度', render: (row: any) => ( {row.avg_subjective_confidence != null ? row.avg_subjective_confidence.toFixed(2) : '—'} ) }, { key: 'calibration', label: '置信度校准(桶命中率)', render: (row: any) => ( row.calibration ? (
{Object.entries(row.calibration).map(([name, b]: [string, any]) => ( {name}: {b.hit_rate != null ? `${b.hit_rate}%` : '—'} ({b.total}) ))}
) : '—' ) }, ]} data={summary} rowKey={(row: any) => `${row.provider}-${row.model}-${row.prompt_version ?? ''}`} emptyText="暂无评估数据" />
)}
) }