'use client' import { useEffect, useState } from 'react' import { Users, Server, ArrowLeftRight, Zap, Loader2, TrendingUp, } from 'lucide-react' import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar, Legend, } from 'recharts' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { api } from '@/lib/api-client' import { formatNumber, formatDate } from '@/lib/utils' import type { DashboardStats, UsageRecord, OperationLog, } from '@/lib/types' interface StatCardProps { title: string value: string | number icon: React.ReactNode color: string subtitle?: string } function StatCard({ title, value, icon, color, subtitle }: StatCardProps) { return (

{title}

{value}

{subtitle && (

{subtitle}

)}
{icon}
) } function StatusBadge({ status }: { status: string }) { const variantMap: Record = { active: 'success', completed: 'success', disabled: 'destructive', failed: 'destructive', processing: 'info', queued: 'warning', suspended: 'destructive', } return ( {status} ) } export default function DashboardPage() { const [stats, setStats] = useState(null) const [usageData, setUsageData] = useState([]) const [recentLogs, setRecentLogs] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState('') useEffect(() => { async function fetchData() { try { const [statsRes, usageRes, logsRes] = await Promise.allSettled([ api.stats.dashboard(), api.usage.daily({ days: 30 }), api.logs.list({ page: 1, page_size: 5 }), ]) if (statsRes.status === 'fulfilled') setStats(statsRes.value) if (usageRes.status === 'fulfilled') setUsageData(usageRes.value) if (logsRes.status === 'fulfilled') setRecentLogs(logsRes.value.items) } catch (err) { setError('加载数据失败,请检查后端服务是否启动') } finally { setLoading(false) } } fetchData() }, []) if (loading) { return (

加载中...

) } if (error) { return (

{error}

) } const chartData = usageData.map((r) => ({ day: r.day.slice(5), // MM-DD 请求量: r.count, Input: r.input_tokens, Output: r.output_tokens, })) return (
{/* 统计卡片 */}
} color="bg-blue-500/10" subtitle={`活跃 ${stats?.active_accounts ?? 0}`} /> } color="bg-green-500/10" subtitle={`模型 ${stats?.active_models ?? 0}`} /> } color="bg-purple-500/10" subtitle="中转任务" /> } color="bg-orange-500/10" subtitle={`In: ${formatNumber(stats?.tokens_today_input ?? 0)} / Out: ${formatNumber(stats?.tokens_today_output ?? 0)}`} />
{/* 图表 */}
{/* 请求趋势 */} 请求趋势 (30 天) {chartData.length > 0 ? ( ) : (
暂无数据
)}
{/* Token 用量 */} Token 用量 (30 天) {chartData.length > 0 ? ( ) : (
暂无数据
)}
{/* 最近操作日志 */} 最近操作 {recentLogs.length > 0 ? ( 时间 账号 ID 操作 目标类型 目标 ID {recentLogs.map((log) => ( {formatDate(log.created_at)} {log.account_id.slice(0, 8)}... {log.action} {log.target_type} {log.target_id.slice(0, 8)}... ))}
) : (
暂无操作日志
)}
) }