From cab92155e5a0b9aa457fa24d31d0267d607f28f7 Mon Sep 17 00:00:00 2001 From: WangDL Date: Fri, 22 May 2026 15:21:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20API=20billing=20dashboard=20=E2=80=94?= =?UTF-8?q?=20DeepSeek=20+=20SiliconFlow=20+=20MiniMax=20+=20Baidu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 9 ++++++ src/config/menu.tsx | 3 +- src/layouts/AdminLayout.tsx | 1 + src/pages/Billing.tsx | 61 +++++++++++++++++++++++++++++++++++++ src/services/billing-api.ts | 15 +++++++++ 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 src/pages/Billing.tsx create mode 100644 src/services/billing-api.ts diff --git a/src/App.tsx b/src/App.tsx index 3ea35ed..ab4265b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,7 @@ import PageLoading from './components/PageLoading' import AdminLayout from './layouts/AdminLayout' const Login = lazy(() => import('./pages/Login')) +const BillingPage = lazy(() => import('./pages/Billing')) const GiteaEmbed = lazy(() => import('./pages/GiteaEmbed')) const ServersPage = lazy(() => import("./pages/Servers")) const AuditLogPage = lazy(() => import("./pages/AuditLog")) @@ -82,6 +83,14 @@ function App() { } /> + + }> + + } + /> }, { path: '/files', name: '文件与 COS', icon: }, { path: '/settings', name: '系统配置', icon: , requiredRole: 'ADMIN' }, + { path: '/billing', name: 'API 用量', icon: , requiredRole: 'SUPER_ADMIN' }, { path: '/git', name: '代码仓库', icon: }, { path: '/servers', name: '服务器运维', icon: , requiredRole: 'SUPER_ADMIN' }, { path: '/audit', name: '审计日志', icon: , requiredRole: 'ADMIN' }, diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx index f39cab8..4a251c2 100644 --- a/src/layouts/AdminLayout.tsx +++ b/src/layouts/AdminLayout.tsx @@ -21,6 +21,7 @@ const breadcrumbMap: Record = { '/ai-costs': 'AI 调用与成本', '/files': '文件与 COS', '/settings': '系统配置', + '/billing': 'API 用量', '/git': '代码仓库', '/servers': '服务器运维', '/audit': '审计日志', diff --git a/src/pages/Billing.tsx b/src/pages/Billing.tsx new file mode 100644 index 0000000..6542338 --- /dev/null +++ b/src/pages/Billing.tsx @@ -0,0 +1,61 @@ +import { useState } from 'react' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { Card, Row, Col, Statistic, Button, Tag, Space, Typography, App } from 'antd' +import { DollarOutlined, ReloadOutlined, LinkOutlined } from '@ant-design/icons' +import { getBilling, type BillingInfo } from '@/services/billing-api' + +const { Text } = Typography + +function BillingCard({ p }: { p: BillingInfo }) { + const color = p.status === 'ok' ? (p.currency === 'CNY' && parseFloat(p.balance) < 10 ? '#faad14' : '#52c41a') : '#999' + return ( + {p.name}{p.status === 'ok' ? '正常' : '未知'}} + extra={} + > + {p.currency}} /> +
+ 模型: {p.model} +
+ {p.note} +
+
+ ) +} + +function BillingContent() { + const qc = useQueryClient() + const [refreshing, setRefreshing] = useState(false) + + const { data } = useQuery({ + queryKey: ['billing'], + queryFn: getBilling, + staleTime: 60_000, + }) + + const refresh = async () => { + setRefreshing(true) + await qc.invalidateQueries({ queryKey: ['billing'] }) + setTimeout(() => setRefreshing(false), 800) + } + + return ( +
+
+ API 用量 + +
+ + {(data?.providers || []).map(p => ( + + + + ))} + +
+ ) +} + +export default function BillingPage() { + return +} diff --git a/src/services/billing-api.ts b/src/services/billing-api.ts new file mode 100644 index 0000000..f6115a0 --- /dev/null +++ b/src/services/billing-api.ts @@ -0,0 +1,15 @@ +import { api } from './http-client' + +export interface BillingInfo { + name: string + model: string + balance: string + currency: string + status: 'ok' | 'unknown' + consoleUrl: string + note: string +} + +export function getBilling(): Promise<{ providers: BillingInfo[] }> { + return api.get('/admin-api/billing') +}