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:
64
src/components/RecentLogs.tsx
Normal file
64
src/components/RecentLogs.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Card, Timeline, Typography, Tag, Empty } from 'antd'
|
||||
import { HistoryOutlined } from '@ant-design/icons'
|
||||
import { formatTimestamp, quotaToUsd } from '@/utils/quota'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
interface LogItem {
|
||||
id: number
|
||||
created_at: number
|
||||
model_name: string
|
||||
token_name: string
|
||||
quota: number
|
||||
type: number
|
||||
}
|
||||
|
||||
interface Props {
|
||||
logs: LogItem[]
|
||||
loading: boolean
|
||||
}
|
||||
|
||||
const logTypeMap: Record<number, { color: string; label: string }> = {
|
||||
1: { color: 'green', label: '充值' },
|
||||
2: { color: 'blue', label: '消费' },
|
||||
3: { color: 'orange', label: '管理' },
|
||||
4: { color: 'purple', label: '系统' },
|
||||
5: { color: 'red', label: '错误' },
|
||||
6: { color: 'cyan', label: '退款' },
|
||||
}
|
||||
|
||||
export default function RecentLogs({ logs, loading }: Props) {
|
||||
if (!loading && logs.length === 0) {
|
||||
return <Card title={<><HistoryOutlined /> 最近操作日志</>}><Empty description="暂无日志" /></Card>
|
||||
}
|
||||
|
||||
return (
|
||||
<Card title={<><HistoryOutlined /> 最近操作日志</>} loading={loading} hoverable>
|
||||
<Timeline
|
||||
items={logs.slice(0, 10).map((log) => {
|
||||
const typeInfo = logTypeMap[log.type] || { color: 'default', label: '未知' }
|
||||
return {
|
||||
color: typeInfo.color,
|
||||
children: (
|
||||
<div>
|
||||
<div>
|
||||
<Tag color={typeInfo.color} style={{ marginRight: 8 }}>{typeInfo.label}</Tag>
|
||||
<Text strong>{log.model_name || '-'}</Text>
|
||||
{log.quota > 0 && (
|
||||
<Text type="secondary" style={{ marginLeft: 8 }}>
|
||||
${quotaToUsd(log.quota)}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{formatTimestamp(log.created_at)}
|
||||
{log.token_name && ` · ${log.token_name}`}
|
||||
</Text>
|
||||
</div>
|
||||
),
|
||||
}
|
||||
})}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user