2026-02-04 02:04:54 +08:00
|
|
|
<template>
|
|
|
|
|
<TableCard :title="title">
|
|
|
|
|
<template #actions>
|
2026-05-19 17:38:42 +08:00
|
|
|
<slot name="actions">
|
|
|
|
|
<Select
|
|
|
|
|
v-if="showMetricSelect"
|
|
|
|
|
:model-value="metric"
|
|
|
|
|
@update:model-value="emitMetric"
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger class="h-8 text-xs w-28">
|
|
|
|
|
<SelectValue placeholder="指标" />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="requests">
|
|
|
|
|
请求数
|
|
|
|
|
</SelectItem>
|
|
|
|
|
<SelectItem value="tokens">
|
|
|
|
|
Tokens
|
|
|
|
|
</SelectItem>
|
|
|
|
|
<SelectItem value="cost">
|
|
|
|
|
成本
|
|
|
|
|
</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</slot>
|
2026-02-04 02:04:54 +08:00
|
|
|
</template>
|
|
|
|
|
|
2026-02-04 23:59:45 +08:00
|
|
|
<div
|
|
|
|
|
v-if="loading"
|
|
|
|
|
class="p-6"
|
|
|
|
|
>
|
2026-02-04 02:04:54 +08:00
|
|
|
<LoadingState />
|
|
|
|
|
</div>
|
2026-02-04 23:59:45 +08:00
|
|
|
<div
|
|
|
|
|
v-else-if="items.length === 0"
|
|
|
|
|
class="p-6"
|
|
|
|
|
>
|
|
|
|
|
<EmptyState
|
|
|
|
|
title="暂无数据"
|
|
|
|
|
description="当前时间范围内没有统计结果"
|
|
|
|
|
/>
|
2026-02-04 02:04:54 +08:00
|
|
|
</div>
|
|
|
|
|
<Table v-else>
|
|
|
|
|
<TableHeader>
|
|
|
|
|
<TableRow>
|
2026-02-04 23:59:45 +08:00
|
|
|
<TableHead class="w-16">
|
|
|
|
|
排名
|
|
|
|
|
</TableHead>
|
2026-02-04 02:04:54 +08:00
|
|
|
<TableHead>名称</TableHead>
|
2026-02-04 23:59:45 +08:00
|
|
|
<TableHead class="text-right">
|
|
|
|
|
请求数
|
|
|
|
|
</TableHead>
|
|
|
|
|
<TableHead class="text-right">
|
|
|
|
|
Tokens
|
|
|
|
|
</TableHead>
|
|
|
|
|
<TableHead class="text-right">
|
|
|
|
|
成本
|
|
|
|
|
</TableHead>
|
2026-02-04 02:04:54 +08:00
|
|
|
</TableRow>
|
|
|
|
|
</TableHeader>
|
|
|
|
|
<TableBody>
|
2026-02-04 23:59:45 +08:00
|
|
|
<TableRow
|
|
|
|
|
v-for="item in items"
|
|
|
|
|
:key="item.id"
|
|
|
|
|
>
|
|
|
|
|
<TableCell class="font-medium">
|
|
|
|
|
{{ item.rank }}
|
|
|
|
|
</TableCell>
|
2026-02-04 02:04:54 +08:00
|
|
|
<TableCell>{{ item.name }}</TableCell>
|
2026-02-04 23:59:45 +08:00
|
|
|
<TableCell class="text-right">
|
|
|
|
|
{{ item.requests }}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell class="text-right">
|
|
|
|
|
{{ formatTokens(item.tokens) }}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell class="text-right">
|
|
|
|
|
{{ formatCurrency(item.cost) }}
|
|
|
|
|
</TableCell>
|
2026-02-04 02:04:54 +08:00
|
|
|
</TableRow>
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
2026-05-19 17:38:42 +08:00
|
|
|
|
|
|
|
|
<slot name="pagination" />
|
2026-02-04 02:04:54 +08:00
|
|
|
</TableCard>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-02-09 01:05:48 +08:00
|
|
|
import { computed } from 'vue'
|
2026-02-04 02:04:54 +08:00
|
|
|
import { EmptyState, LoadingState } from '@/components/common'
|
|
|
|
|
import { TableCard } from '@/components/ui'
|
|
|
|
|
import {
|
|
|
|
|
Select,
|
|
|
|
|
SelectContent,
|
|
|
|
|
SelectItem,
|
|
|
|
|
SelectTrigger,
|
|
|
|
|
SelectValue,
|
|
|
|
|
Table,
|
|
|
|
|
TableBody,
|
|
|
|
|
TableCell,
|
|
|
|
|
TableHead,
|
|
|
|
|
TableHeader,
|
|
|
|
|
TableRow
|
|
|
|
|
} from '@/components/ui'
|
|
|
|
|
import { formatCurrency, formatTokens } from '@/utils/format'
|
|
|
|
|
import type { LeaderboardItem } from '@/api/admin'
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
title: string
|
|
|
|
|
items: LeaderboardItem[]
|
|
|
|
|
metric: 'requests' | 'tokens' | 'cost'
|
|
|
|
|
loading?: boolean
|
|
|
|
|
showMetricSelect?: boolean
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
|
|
loading: false,
|
|
|
|
|
showMetricSelect: true
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
(e: 'update:metric', value: 'requests' | 'tokens' | 'cost'): void
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
const metric = computed(() => props.metric)
|
|
|
|
|
|
|
|
|
|
function emitMetric(value: string) {
|
|
|
|
|
if (value === 'requests' || value === 'tokens' || value === 'cost') {
|
|
|
|
|
emit('update:metric', value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|