- ConfigProvider 注入全局配置(动画、表格尺寸) - CSS Variables 主题微调(页面背景、圆角、字体栈) - AppLayout Menu 支持 logo/operations/collapsed - Statistic 组件增加 color/prefix/suffix/animation - Card 组件启用 hoverShadow/headerBordered - Table 组件启用 stripe 斑马纹 - Tag 组件使用 variant="light" + shape="round" - Dialog 居中显示并设置固定宽度 - 布局样式硬编码颜色替换为 TDesign Token - UsageChart 改用 AreaChart + 渐变填充 - 更新 frontend spec 同步样式体系要求
117 lines
3.2 KiB
TypeScript
117 lines
3.2 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { Table, Select, Input, DateRangePicker, Space, Card } from 'tdesign-react';
|
|
import type { PrimaryTableCol } from 'tdesign-react/es/table/type';
|
|
import type { UsageStats, Provider } from '@/types';
|
|
|
|
interface StatsTableProps {
|
|
providers: Provider[];
|
|
stats: UsageStats[];
|
|
loading: boolean;
|
|
providerId?: string;
|
|
modelName?: string;
|
|
dateRange: [Date | null, Date | null] | null;
|
|
onProviderIdChange: (value: string | undefined) => void;
|
|
onModelNameChange: (value: string | undefined) => void;
|
|
onDateRangeChange: (dates: [Date | null, Date | null] | null) => void;
|
|
}
|
|
|
|
export function StatsTable({
|
|
providers,
|
|
stats,
|
|
loading,
|
|
providerId,
|
|
modelName,
|
|
dateRange,
|
|
onProviderIdChange,
|
|
onModelNameChange,
|
|
onDateRangeChange,
|
|
}: StatsTableProps) {
|
|
const providerMap = useMemo(() => {
|
|
const map = new Map<string, string>();
|
|
for (const p of providers) {
|
|
map.set(p.id, p.name);
|
|
}
|
|
return map;
|
|
}, [providers]);
|
|
|
|
const columns: PrimaryTableCol<UsageStats>[] = [
|
|
{
|
|
title: '供应商',
|
|
colKey: 'providerId',
|
|
width: 180,
|
|
ellipsis: true,
|
|
cell: ({ row }) => providerMap.get(row.providerId) ?? row.providerId,
|
|
},
|
|
{
|
|
title: '模型',
|
|
colKey: 'modelName',
|
|
width: 250,
|
|
ellipsis: true,
|
|
cell: ({ row }) => {
|
|
// 如果后端返回统一 ID 格式(包含 /),直接显示
|
|
// 否则显示原始 model_name
|
|
return row.modelName;
|
|
},
|
|
},
|
|
{
|
|
title: '日期',
|
|
colKey: 'date',
|
|
width: 120,
|
|
},
|
|
{
|
|
title: '请求数',
|
|
colKey: 'requestCount',
|
|
width: 100,
|
|
align: 'right',
|
|
},
|
|
];
|
|
|
|
const handleDateChange = (value: unknown) => {
|
|
if (Array.isArray(value) && value.length === 2) {
|
|
// 将值转换为Date对象
|
|
const startDate = value[0] ? new Date(value[0] as string | number | Date) : null;
|
|
const endDate = value[1] ? new Date(value[1] as string | number | Date) : null;
|
|
onDateRangeChange([startDate, endDate]);
|
|
} else {
|
|
onDateRangeChange(null);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card title="统计数据" headerBordered hoverShadow>
|
|
<Space style={{ marginBottom: 16 }} size="medium" breakLine>
|
|
<Select
|
|
clearable
|
|
placeholder="所有供应商"
|
|
style={{ width: 200 }}
|
|
value={providerId}
|
|
onChange={(value) => onProviderIdChange(value as string | undefined)}
|
|
options={providers.map((p) => ({ label: p.name, value: p.id }))}
|
|
/>
|
|
<Input
|
|
clearable
|
|
placeholder="模型名称"
|
|
style={{ width: 200 }}
|
|
value={modelName ?? ''}
|
|
onChange={(value) => onModelNameChange((value as string) || undefined)}
|
|
/>
|
|
<DateRangePicker
|
|
mode="date"
|
|
value={dateRange && dateRange[0] && dateRange[1] ? [dateRange[0], dateRange[1]] : []}
|
|
onChange={handleDateChange}
|
|
/>
|
|
</Space>
|
|
|
|
<Table<UsageStats>
|
|
columns={columns}
|
|
data={stats}
|
|
rowKey="id"
|
|
loading={loading}
|
|
stripe
|
|
pagination={{ pageSize: 20 }}
|
|
empty="暂无统计数据"
|
|
/>
|
|
</Card>
|
|
);
|
|
}
|