feat: newapi-dashboard 全栈项目初始化
为 new-api (LLM API 网关) 构建独立前端管理面板: - React 18 + TypeScript + Vite + Ant Design 5 前端 - Node.js + Express + better-sqlite3 BFF 后端 - 登录页: 站点选择器 + UserID + AccessToken 认证 - 仪表盘: 用户信息、额度环形图、7天趋势图、模型饼图、令牌概览、日志时间线 - 账单页: 筛选日志表格、模型消耗柱状图、充值记录、CSV/PDF 导出(HMAC签名) - 管理员站点管理: 站点 CRUD - API 代理: 多站点切换,会话管理 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
77
src/components/TokenOverview.tsx
Normal file
77
src/components/TokenOverview.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Card, Table, Tag, Typography } from 'antd'
|
||||
import { KeyOutlined } from '@ant-design/icons'
|
||||
import { quotaToUsd, getTokenStatusTag, formatTimestamp } from '@/utils/quota'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
interface TokenItem {
|
||||
id: number
|
||||
name: string
|
||||
status: number
|
||||
remain_quota: number
|
||||
used_quota: number
|
||||
unlimited_quota: boolean
|
||||
created_time: number
|
||||
expired_time: number
|
||||
}
|
||||
|
||||
interface Props {
|
||||
tokens: TokenItem[]
|
||||
loading: boolean
|
||||
}
|
||||
|
||||
export default function TokenOverview({ tokens, loading }: Props) {
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
render: (status: number) => {
|
||||
const tag = getTokenStatusTag(status)
|
||||
return <Tag color={tag.color}>{tag.text}</Tag>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '剩余额度',
|
||||
key: 'remain',
|
||||
width: 120,
|
||||
render: (_: any, record: TokenItem) =>
|
||||
record.unlimited_quota ? <Text type="success">无限</Text> : `$${quotaToUsd(record.remain_quota)}`,
|
||||
},
|
||||
{
|
||||
title: '已用额度',
|
||||
dataIndex: 'used_quota',
|
||||
key: 'used',
|
||||
width: 120,
|
||||
render: (val: number) => `$${quotaToUsd(val)}`,
|
||||
},
|
||||
{
|
||||
title: '过期时间',
|
||||
dataIndex: 'expired_time',
|
||||
key: 'expired',
|
||||
width: 120,
|
||||
render: (val: number) => val === -1 ? '永不过期' : formatTimestamp(val),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<Card title={<><KeyOutlined /> 令牌概览</>} hoverable>
|
||||
<Table
|
||||
dataSource={tokens}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
pagination={false}
|
||||
size="small"
|
||||
scroll={{ y: 240 }}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user