// ============================================================ // 仪表盘页面 // ============================================================ import { useQuery } from '@tanstack/react-query' import { Card, Col, Row, Statistic, Table, Tag, Spin } from 'antd' import { TeamOutlined, CloudServerOutlined, ApiOutlined, ThunderboltOutlined, ColumnWidthOutlined, } from '@ant-design/icons' import { statsService } from '@/services/stats' import { logService } from '@/services/logs' import { PageHeader } from '@/components/PageHeader' import { ErrorState } from '@/components/ErrorState' import { actionLabels, actionColors } from '@/constants/status' import type { OperationLog } from '@/types' export default function Dashboard() { const { data: stats, isLoading: statsLoading, error: statsError, refetch: refetchStats, } = useQuery({ queryKey: ['dashboard-stats'], queryFn: ({ signal }) => statsService.dashboard(signal), }) const { data: logsData, isLoading: logsLoading } = useQuery({ queryKey: ['recent-logs'], queryFn: ({ signal }) => logService.list({ page: 1, page_size: 10 }, signal), }) if (statsError) { return ( <> refetchStats()} /> ) } const statCards = [ { title: '总账号', value: stats?.total_accounts ?? 0, icon: , color: '#863bff' }, { title: '活跃服务商', value: stats?.active_providers ?? 0, icon: , color: '#47bfff' }, { title: '活跃模型', value: stats?.active_models ?? 0, icon: , color: '#22c55e' }, { title: '今日请求', value: stats?.tasks_today ?? 0, icon: , color: '#f59e0b' }, { title: '今日 Token', value: (stats?.tokens_today_input ?? 0) + (stats?.tokens_today_output ?? 0), icon: , color: '#ef4444', }, ] const logColumns = [ { title: '操作类型', dataIndex: 'action', key: 'action', width: 140, render: (action: string) => ( {actionLabels[action] || action} ), }, { title: '目标类型', dataIndex: 'target_type', key: 'target_type', width: 100, render: (v: string | null) => v || '-', }, { title: '时间', dataIndex: 'created_at', key: 'created_at', width: 180, render: (v: string) => new Date(v).toLocaleString('zh-CN'), }, ] return (
{/* Stat Cards */} {statsLoading ? (
) : ( statCards.map((card) => ( {card.title} } value={card.value} valueStyle={{ fontSize: 28, fontWeight: 600, color: card.color }} prefix={ {card.icon} } /> )) )}
{/* Recent Logs */} 最近操作日志 } size="small" styles={{ body: { padding: 0 } }} > columns={logColumns} dataSource={logsData?.items ?? []} loading={logsLoading} rowKey="id" pagination={false} size="small" />
) }