"use client"; import { useState, useMemo } from "react"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { DollarSign, TrendingUp, AlertTriangle, CheckCircle, Loader2, RefreshCw, } from "lucide-react"; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell, Legend, } from "recharts"; import { useApi } from "@/lib/hooks/use-api"; type TimeRange = "7d" | "30d" | "month"; const TIME_RANGE_LABELS: Record = { "7d": "最近7天", "30d": "最近30天", month: "本月", }; const PIE_COLORS = [ "#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#6366f1", "#ef4444", "#14b8a6", "#f97316", ]; interface QuotaData { used: number; limit: number; percentage: number; status: "normal" | "warning" | "exceeded"; } interface TrendItem { date: string; cost: number; } interface EngineUsageItem { engine: string; label: string; queries: number; inputTokens: number; outputTokens: number; cost: number; } interface UsageData { quota: QuotaData; trends: TrendItem[]; engineDistribution: { engine: string; label: string; cost: number }[]; engineUsage: EngineUsageItem[]; } const MOCK_USAGE_DATA: UsageData = { quota: { used: 45.6, limit: 100, percentage: 45.6, status: "normal" }, trends: [ { date: "05-19", cost: 5.2 }, { date: "05-20", cost: 6.8 }, { date: "05-21", cost: 4.5 }, { date: "05-22", cost: 7.3 }, { date: "05-23", cost: 8.1 }, { date: "05-24", cost: 6.9 }, { date: "05-25", cost: 7.0 }, ], engineDistribution: [ { engine: "deepseek", label: "DeepSeek", cost: 15.2 }, { engine: "chatgpt", label: "ChatGPT", cost: 12.8 }, { engine: "qwen", label: "通义千问", cost: 8.5 }, { engine: "gemini", label: "Google Gemini", cost: 5.3 }, { engine: "kimi", label: "Kimi", cost: 3.8 }, ], engineUsage: [ { engine: "deepseek", label: "DeepSeek", queries: 1250, inputTokens: 3200000, outputTokens: 1800000, cost: 15.2 }, { engine: "chatgpt", label: "ChatGPT", queries: 890, inputTokens: 2100000, outputTokens: 1500000, cost: 12.8 }, { engine: "qwen", label: "通义千问", queries: 720, inputTokens: 1800000, outputTokens: 900000, cost: 8.5 }, { engine: "gemini", label: "Google Gemini", queries: 450, inputTokens: 1100000, outputTokens: 600000, cost: 5.3 }, { engine: "kimi", label: "Kimi", queries: 320, inputTokens: 800000, outputTokens: 400000, cost: 3.8 }, { engine: "wenxin", label: "文心一言", queries: 280, inputTokens: 700000, outputTokens: 350000, cost: 0.02 }, { engine: "doubao", label: "豆包", queries: 210, inputTokens: 520000, outputTokens: 280000, cost: 0.5 }, { engine: "yuanbao", label: "腾讯元宝", queries: 150, inputTokens: 380000, outputTokens: 200000, cost: 0.7 }, { engine: "perplexity", label: "Perplexity", queries: 30, inputTokens: 75000, outputTokens: 40000, cost: 2.6 }, ], }; function formatTokenCount(count: number): string { if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`; if (count >= 1000) return `${(count / 1000).toFixed(0)}K`; return String(count); } function formatCurrency(value: number): string { return `¥${value.toFixed(2)}`; } function CircularProgress({ percentage, size = 120, strokeWidth = 10 }: { percentage: number; size?: number; strokeWidth?: number }) { const radius = (size - strokeWidth) / 2; const circumference = 2 * Math.PI * radius; const offset = circumference - (percentage / 100) * circumference; const color = percentage >= 90 ? "#ef4444" : percentage >= 70 ? "#f59e0b" : "#10b981"; return (
{percentage.toFixed(1)}% 已使用
); } function QuotaStatusBadge({ status }: { status: "normal" | "warning" | "exceeded" }) { const config = { normal: { label: "正常", className: "bg-emerald-100 text-emerald-700 hover:bg-emerald-100", icon: CheckCircle }, warning: { label: "预警", className: "bg-amber-100 text-amber-700 hover:bg-amber-100", icon: AlertTriangle }, exceeded: { label: "超限", className: "bg-red-100 text-red-700 hover:bg-red-100", icon: AlertTriangle }, }; const c = config[status]; const Icon = c.icon; return ( {c.label} ); } export default function UsagePage() { const [timeRange, setTimeRange] = useState("7d"); const { data: usageData, isLoading, refresh } = useApi( `/api/v1/usage/summary?period=${timeRange === "7d" ? "week" : timeRange === "30d" ? "month" : "month"}` ); const data = usageData || MOCK_USAGE_DATA; const totalCost = useMemo(() => { return data.engineUsage.reduce((sum, item) => sum + item.cost, 0); }, [data.engineUsage]); const totalQueries = useMemo(() => { return data.engineUsage.reduce((sum, item) => sum + item.queries, 0); }, [data.engineUsage]); return (

用量统计

监控AI引擎调用成本和配额使用情况

{(["7d", "30d", "month"] as TimeRange[]).map((range) => ( ))}
{isLoading ? (

正在加载用量数据...

) : ( <>

本月已用金额

{formatCurrency(data.quota.used)}

配额上限

{formatCurrency(data.quota.limit)}

配额状态

用量趋势 每日成本变化趋势
`¥${v}`} /> [formatCurrency(value), "成本"]} labelFormatter={(label) => `日期: ${label}`} />
引擎用量分布 各引擎成本占比
{data.engineDistribution.map((_, index) => ( ))} [formatCurrency(value), "成本"]} /> ( {value} )} />
引擎用量明细 共 {totalQueries} 次查询,总成本 {formatCurrency(totalCost)} 引擎名称 查询次数 输入Token 输出Token 成本 {data.engineUsage.map((item) => ( {item.label} {item.queries.toLocaleString()} {formatTokenCount(item.inputTokens)} {formatTokenCount(item.outputTokens)} {formatCurrency(item.cost)} ))}
)}
); }