mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 00:35:32 +08:00
c0122d717c
Add database tables for proxy request logs and model pricing. Extend Provider and error types to support usage statistics.
179 lines
7.4 KiB
TypeScript
179 lines
7.4 KiB
TypeScript
import { useTranslation } from 'react-i18next';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/components/ui/dialog';
|
|
import { useRequestDetail } from '@/lib/query/usage';
|
|
|
|
interface RequestDetailPanelProps {
|
|
requestId: string;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export function RequestDetailPanel({ requestId, onClose }: RequestDetailPanelProps) {
|
|
const { t } = useTranslation();
|
|
const { data: request, isLoading } = useRequestDetail(requestId);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Dialog open onOpenChange={onClose}>
|
|
<DialogContent className="max-w-2xl">
|
|
<div className="h-[400px] animate-pulse rounded bg-gray-100" />
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
if (!request) {
|
|
return (
|
|
<Dialog open onOpenChange={onClose}>
|
|
<DialogContent className="max-w-2xl">
|
|
<DialogHeader>
|
|
<DialogTitle>{t('usage.requestDetail', '请求详情')}</DialogTitle>
|
|
</DialogHeader>
|
|
<div className="text-center text-muted-foreground">
|
|
{t('usage.requestNotFound', '请求未找到')}
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Dialog open onOpenChange={onClose}>
|
|
<DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle>{t('usage.requestDetail', '请求详情')}</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-4">
|
|
{/* 基本信息 */}
|
|
<div className="rounded-lg border p-4">
|
|
<h3 className="mb-3 font-semibold">{t('usage.basicInfo', '基本信息')}</h3>
|
|
<dl className="grid grid-cols-2 gap-3 text-sm">
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.requestId', '请求ID')}</dt>
|
|
<dd className="font-mono">{request.requestId}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.time', '时间')}</dt>
|
|
<dd>{new Date(request.createdAt * 1000).toLocaleString('zh-CN')}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.provider', '供应商')}</dt>
|
|
<dd className="text-sm">
|
|
<span className="font-medium">{request.providerName || t('usage.unknownProvider', '未知')}</span>
|
|
<span className="ml-2 font-mono text-xs text-muted-foreground">
|
|
{request.providerId}
|
|
</span>
|
|
</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.appType', '应用类型')}</dt>
|
|
<dd>{request.appType}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.model', '模型')}</dt>
|
|
<dd className="font-mono">{request.model}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.status', '状态')}</dt>
|
|
<dd>
|
|
<span
|
|
className={`inline-flex rounded-full px-2 py-1 text-xs ${
|
|
request.statusCode >= 200 && request.statusCode < 300
|
|
? 'bg-green-100 text-green-800'
|
|
: 'bg-red-100 text-red-800'
|
|
}`}
|
|
>
|
|
{request.statusCode}
|
|
</span>
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
{/* Token 使用量 */}
|
|
<div className="rounded-lg border p-4">
|
|
<h3 className="mb-3 font-semibold">{t('usage.tokenUsage', 'Token 使用量')}</h3>
|
|
<dl className="grid grid-cols-2 gap-3 text-sm">
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.inputTokens', '输入 Tokens')}</dt>
|
|
<dd className="font-mono">{request.inputTokens.toLocaleString()}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.outputTokens', '输出 Tokens')}</dt>
|
|
<dd className="font-mono">{request.outputTokens.toLocaleString()}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.cacheReadTokens', '缓存读取')}</dt>
|
|
<dd className="font-mono">{request.cacheReadTokens.toLocaleString()}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.cacheCreationTokens', '缓存写入')}</dt>
|
|
<dd className="font-mono">{request.cacheCreationTokens.toLocaleString()}</dd>
|
|
</div>
|
|
<div className="col-span-2">
|
|
<dt className="text-muted-foreground">{t('usage.totalTokens', '总计')}</dt>
|
|
<dd className="text-lg font-semibold">
|
|
{(request.inputTokens + request.outputTokens).toLocaleString()}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
{/* 成本明细 */}
|
|
<div className="rounded-lg border p-4">
|
|
<h3 className="mb-3 font-semibold">{t('usage.costBreakdown', '成本明细')}</h3>
|
|
<dl className="grid grid-cols-2 gap-3 text-sm">
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.inputCost', '输入成本')}</dt>
|
|
<dd className="font-mono">${parseFloat(request.inputCostUsd).toFixed(6)}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.outputCost', '输出成本')}</dt>
|
|
<dd className="font-mono">${parseFloat(request.outputCostUsd).toFixed(6)}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.cacheReadCost', '缓存读取成本')}</dt>
|
|
<dd className="font-mono">${parseFloat(request.cacheReadCostUsd).toFixed(6)}</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.cacheCreationCost', '缓存写入成本')}</dt>
|
|
<dd className="font-mono">${parseFloat(request.cacheCreationCostUsd).toFixed(6)}</dd>
|
|
</div>
|
|
<div className="col-span-2 border-t pt-3">
|
|
<dt className="text-muted-foreground">{t('usage.totalCost', '总成本')}</dt>
|
|
<dd className="text-lg font-semibold text-primary">
|
|
${parseFloat(request.totalCostUsd).toFixed(6)}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
{/* 性能信息 */}
|
|
<div className="rounded-lg border p-4">
|
|
<h3 className="mb-3 font-semibold">{t('usage.performance', '性能信息')}</h3>
|
|
<dl className="grid grid-cols-2 gap-3 text-sm">
|
|
<div>
|
|
<dt className="text-muted-foreground">{t('usage.latency', '延迟')}</dt>
|
|
<dd className="font-mono">{request.latencyMs}ms</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
{/* 错误信息 */}
|
|
{request.errorMessage && (
|
|
<div className="rounded-lg border border-red-200 bg-red-50 p-4">
|
|
<h3 className="mb-2 font-semibold text-red-800">{t('usage.errorMessage', '错误信息')}</h3>
|
|
<p className="text-sm text-red-700">{request.errorMessage}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|