- 扩展 Icon.vue 组件,新增 60+ 图标路径 - 导航类: arrowRight, arrowLeft, arrowUp, arrowDown, chevronUp, externalLink - 状态类: checkCircle, xCircle, exclamationCircle, exclamationTriangle, infoCircle - 用户类: user, userCircle, userPlus, users - 文档类: document, clipboard, copy, inbox - 操作类: download, upload, filter, sort - 安全类: key, lock, shield - UI类: menu, calendar, home, terminal, gift, creditCard, mail - 数据类: chartBar, trendingUp, database, cube - 其他: bolt, sparkles, cloud, server, sun, moon, book 等 - 重构 56 个 Vue 组件,用 Icon 组件替换内联 SVG - 净减少约 2200 行代码 - 提升代码可维护性和一致性 - 统一图标样式和尺寸管理
58 lines
2.7 KiB
Vue
58 lines
2.7 KiB
Vue
<template>
|
|
<div class="card">
|
|
<div class="flex items-center justify-between border-b border-gray-100 px-6 py-4 dark:border-dark-700">
|
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">{{ t('dashboard.recentUsage') }}</h2>
|
|
<span class="badge badge-gray">{{ t('dashboard.last7Days') }}</span>
|
|
</div>
|
|
<div class="p-6">
|
|
<div v-if="loading" class="flex items-center justify-center py-12">
|
|
<LoadingSpinner size="lg" />
|
|
</div>
|
|
<div v-else-if="data.length === 0" class="py-8">
|
|
<EmptyState :title="t('dashboard.noUsageRecords')" :description="t('dashboard.startUsingApi')" />
|
|
</div>
|
|
<div v-else class="space-y-3">
|
|
<div v-for="log in data" :key="log.id" class="flex items-center justify-between rounded-xl bg-gray-50 p-4 transition-colors hover:bg-gray-100 dark:bg-dark-800/50 dark:hover:bg-dark-800">
|
|
<div class="flex items-center gap-4">
|
|
<div class="flex h-10 w-10 items-center justify-center rounded-xl bg-primary-100 dark:bg-primary-900/30">
|
|
<Icon name="beaker" size="md" class="text-primary-600 dark:text-primary-400" />
|
|
</div>
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-900 dark:text-white">{{ log.model }}</p>
|
|
<p class="text-xs text-gray-500 dark:text-dark-400">{{ formatDateTime(log.created_at) }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="text-right">
|
|
<p class="text-sm font-semibold">
|
|
<span class="text-green-600 dark:text-green-400" :title="t('dashboard.actual')">${{ formatCost(log.actual_cost) }}</span>
|
|
<span class="font-normal text-gray-400 dark:text-gray-500" :title="t('dashboard.standard')"> / ${{ formatCost(log.total_cost) }}</span>
|
|
</p>
|
|
<p class="text-xs text-gray-500 dark:text-dark-400">{{ (log.input_tokens + log.output_tokens).toLocaleString() }} tokens</p>
|
|
</div>
|
|
</div>
|
|
|
|
<router-link to="/usage" class="flex items-center justify-center gap-2 py-3 text-sm font-medium text-primary-600 transition-colors hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300">
|
|
{{ t('dashboard.viewAllUsage') }}
|
|
<Icon name="arrowRight" size="sm" />
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useI18n } from 'vue-i18n'
|
|
import LoadingSpinner from '@/components/common/LoadingSpinner.vue'
|
|
import EmptyState from '@/components/common/EmptyState.vue'
|
|
import Icon from '@/components/icons/Icon.vue'
|
|
import { formatDateTime } from '@/utils/format'
|
|
import type { UsageLog } from '@/types'
|
|
|
|
defineProps<{
|
|
data: UsageLog[]
|
|
loading: boolean
|
|
}>()
|
|
const { t } = useI18n()
|
|
const formatCost = (c: number) => c.toFixed(4)
|
|
</script>
|