refactor(frontend): optimize feature components (models, providers, usage)

This commit is contained in:
fawney19
2025-12-13 22:26:36 +08:00
parent cb990b9018
commit 9ca845f9d0
5 changed files with 190 additions and 41 deletions

View File

@@ -14,12 +14,12 @@
/>
<!-- 抽屉内容 -->
<Card class="relative h-full w-[700px] rounded-none shadow-2xl overflow-y-auto">
<div class="sticky top-0 z-10 bg-background border-b p-6">
<div class="flex items-start justify-between gap-4">
<Card class="relative h-full w-full sm:w-[700px] sm:max-w-[90vw] rounded-none shadow-2xl overflow-y-auto">
<div class="sticky top-0 z-10 bg-background border-b p-4 sm:p-6">
<div class="flex items-start justify-between gap-3 sm:gap-4">
<div class="space-y-1 flex-1 min-w-0">
<div class="flex items-center gap-2">
<h3 class="text-xl font-bold truncate">
<h3 class="text-lg sm:text-xl font-bold truncate">
{{ model.display_name }}
</h3>
<Badge
@@ -76,12 +76,12 @@
</div>
</div>
<div class="p-6">
<div class="p-4 sm:p-6">
<!-- 自定义 Tab 切换 -->
<div class="flex gap-1 p-1 bg-muted/40 rounded-lg mb-4">
<button
type="button"
class="flex-1 px-4 py-2 text-sm font-medium rounded-md transition-all duration-200"
class="flex-1 px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium rounded-md transition-all duration-200"
:class="[
detailTab === 'basic'
? 'bg-primary text-primary-foreground shadow-sm'
@@ -93,7 +93,7 @@
</button>
<button
type="button"
class="flex-1 px-4 py-2 text-sm font-medium rounded-md transition-all duration-200"
class="flex-1 px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium rounded-md transition-all duration-200"
:class="[
detailTab === 'providers'
? 'bg-primary text-primary-foreground shadow-sm'
@@ -101,11 +101,12 @@
]"
@click="detailTab = 'providers'"
>
关联提供商
<span class="hidden sm:inline">关联提供商</span>
<span class="sm:hidden">提供商</span>
</button>
<button
type="button"
class="flex-1 px-4 py-2 text-sm font-medium rounded-md transition-all duration-200"
class="flex-1 px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium rounded-md transition-all duration-200"
:class="[
detailTab === 'aliases'
? 'bg-primary text-primary-foreground shadow-sm'
@@ -113,7 +114,8 @@
]"
@click="detailTab = 'aliases'"
>
别名/映射
<span class="hidden sm:inline">别名/映射</span>
<span class="sm:hidden">别名</span>
</button>
</div>
@@ -142,7 +144,7 @@
<h4 class="font-semibold text-sm">
模型能力
</h4>
<div class="grid grid-cols-2 gap-3">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div class="flex items-center gap-2 p-3 rounded-lg border">
<Zap class="w-5 h-5 text-muted-foreground" />
<div class="flex-1">
@@ -262,7 +264,7 @@
v-if="getTierCount(model.default_tiered_pricing) <= 1"
class="space-y-3"
>
<div class="grid grid-cols-2 gap-3">
<div class="grid grid-cols-2 sm:grid-cols-2 gap-3">
<!-- Token 计费 -->
<div class="p-3 rounded-lg border">
<Label class="text-xs text-muted-foreground">输入价格 ($/M)</Label>
@@ -463,7 +465,9 @@
<Loader2 class="w-6 h-6 animate-spin text-primary" />
</div>
<Table v-else-if="providers.length > 0">
<template v-else-if="providers.length > 0">
<!-- 桌面端表格 -->
<Table class="hidden sm:table">
<TableHeader>
<TableRow class="border-b border-border/60 hover:bg-transparent">
<TableHead class="h-10 font-semibold">
@@ -589,13 +593,81 @@
</TableCell>
</TableRow>
</TableBody>
</Table>
</Table>
<!-- 移动端卡片列表 -->
<div class="sm:hidden divide-y divide-border/40">
<div
v-for="provider in providers"
:key="provider.id"
class="p-4 space-y-3"
>
<div class="flex items-start justify-between gap-3">
<div class="flex items-center gap-2 min-w-0">
<span
class="w-2 h-2 rounded-full shrink-0"
:class="provider.is_active ? 'bg-green-500' : 'bg-gray-300'"
/>
<span class="font-medium truncate">{{ provider.display_name }}</span>
</div>
<div class="flex items-center gap-1 shrink-0">
<Button
variant="ghost"
size="icon"
class="h-7 w-7"
@click="$emit('editProvider', provider)"
>
<Edit class="w-3.5 h-3.5" />
</Button>
<Button
variant="ghost"
size="icon"
class="h-7 w-7"
@click="$emit('toggleProviderStatus', provider)"
>
<Power class="w-3.5 h-3.5" />
</Button>
<Button
variant="ghost"
size="icon"
class="h-7 w-7"
@click="$emit('deleteProvider', provider)"
>
<Trash2 class="w-3.5 h-3.5" />
</Button>
</div>
</div>
<div class="flex items-center gap-3 text-xs">
<div class="flex gap-1">
<Zap
v-if="provider.supports_streaming"
class="w-3.5 h-3.5 text-muted-foreground"
/>
<Eye
v-if="provider.supports_vision"
class="w-3.5 h-3.5 text-muted-foreground"
/>
<Wrench
v-if="provider.supports_function_calling"
class="w-3.5 h-3.5 text-muted-foreground"
/>
</div>
<div
v-if="(provider.input_price_per_1m || 0) > 0 || (provider.output_price_per_1m || 0) > 0"
class="text-muted-foreground font-mono"
>
${{ (provider.input_price_per_1m || 0).toFixed(1) }}/${{ (provider.output_price_per_1m || 0).toFixed(1) }}
</div>
</div>
</div>
</div>
</template>
<!-- 空状态 -->
<div
v-else
class="text-center py-12"
>
<!-- 空状态 -->
<Building2 class="w-12 h-12 mx-auto text-muted-foreground/30 mb-3" />
<p class="text-sm text-muted-foreground">
暂无关联提供商
@@ -658,7 +730,9 @@
<Loader2 class="w-6 h-6 animate-spin text-primary" />
</div>
<Table v-else-if="aliases.length > 0">
<template v-else-if="aliases.length > 0">
<!-- 桌面端表格 -->
<Table class="hidden sm:table">
<TableHeader>
<TableRow class="border-b border-border/60 hover:bg-transparent">
<TableHead class="h-10 font-semibold">
@@ -749,13 +823,81 @@
</TableCell>
</TableRow>
</TableBody>
</Table>
</Table>
<!-- 移动端卡片列表 -->
<div class="sm:hidden divide-y divide-border/40">
<div
v-for="alias in aliases"
:key="alias.id"
class="p-4 space-y-2"
>
<div class="flex items-start justify-between gap-3">
<div class="flex items-center gap-2 min-w-0 flex-1">
<span
class="w-2 h-2 rounded-full shrink-0"
:class="alias.is_active ? 'bg-green-500' : 'bg-gray-300'"
/>
<code class="text-sm font-medium bg-muted px-1.5 py-0.5 rounded truncate">{{ alias.alias }}</code>
</div>
<div class="flex items-center gap-1 shrink-0">
<Button
variant="ghost"
size="icon"
class="h-7 w-7"
@click="$emit('editAlias', alias)"
>
<Edit class="w-3.5 h-3.5" />
</Button>
<Button
variant="ghost"
size="icon"
class="h-7 w-7"
@click="$emit('toggleAliasStatus', alias)"
>
<Power class="w-3.5 h-3.5" />
</Button>
<Button
variant="ghost"
size="icon"
class="h-7 w-7"
@click="$emit('deleteAlias', alias)"
>
<Trash2 class="w-3.5 h-3.5" />
</Button>
</div>
</div>
<div class="flex items-center gap-2">
<Badge
variant="secondary"
class="text-xs"
>
{{ alias.mapping_type === 'mapping' ? '映射' : '别名' }}
</Badge>
<Badge
v-if="alias.provider_id"
variant="outline"
class="text-xs truncate max-w-[120px]"
>
{{ alias.provider_name || 'Provider' }}
</Badge>
<Badge
v-else
variant="default"
class="text-xs"
>
全局
</Badge>
</div>
</div>
</div>
</template>
<!-- 空状态 -->
<div
v-else
class="text-center py-12"
>
<!-- 空状态 -->
<Tag class="w-12 h-12 mx-auto text-muted-foreground/30 mb-3" />
<p class="text-sm text-muted-foreground">
暂无别名或映射

View File

@@ -74,12 +74,12 @@
:key="monitor.api_format"
class="border border-border/60 rounded-lg p-4 hover:border-primary/50 transition-colors"
>
<!-- 左右结构布局 -->
<div class="flex gap-6 items-center">
<!-- 左侧信息区域 -->
<div class="w-44 flex-shrink-0 space-y-1.5">
<!-- 响应式布局窄屏上下两行宽屏左右结构 -->
<div class="flex flex-col sm:flex-row sm:gap-6 sm:items-center">
<!-- 第一行/左侧信息区域 -->
<div class="sm:w-44 flex-shrink-0 space-y-1.5 mb-3 sm:mb-0">
<!-- API 格式标签和成功率 -->
<div class="flex items-center gap-2">
<div class="flex items-center gap-2 flex-wrap">
<Badge
variant="outline"
class="font-mono text-xs"
@@ -93,20 +93,27 @@
>
{{ (monitor.success_rate * 100).toFixed(0) }}%
</Badge>
<!-- 提供商信息仅管理员可见- 窄屏时显示在同一行 -->
<span
v-if="showProviderInfo && 'provider_count' in monitor"
class="text-xs text-muted-foreground sm:hidden"
>
{{ monitor.provider_count }} 个提供商 / {{ monitor.key_count }} 个密钥
</span>
</div>
<!-- 提供商信息仅管理员可见 -->
<!-- 提供商信息仅管理员可见- 宽屏时显示在下方 -->
<div
v-if="showProviderInfo && 'provider_count' in monitor"
class="text-xs text-muted-foreground"
class="text-xs text-muted-foreground hidden sm:block"
>
{{ monitor.provider_count }} 个提供商 / {{ monitor.key_count }} 个密钥
</div>
</div>
<!-- 右侧时间线区域 -->
<div class="flex-1 min-w-0 flex justify-end">
<div class="w-full max-w-5xl">
<!-- 第二行/右侧时间线区域 -->
<div class="flex-1 min-w-0 sm:flex sm:justify-end">
<div class="w-full sm:max-w-5xl">
<EndpointHealthTimeline
:monitor="monitor"
:lookback-hours="parseInt(lookbackHours)"

View File

@@ -14,7 +14,7 @@
/>
<!-- 抽屉内容 -->
<Card class="relative h-full w-[700px] rounded-none shadow-2xl overflow-y-auto">
<Card class="relative h-full w-full sm:w-[700px] sm:max-w-[90vw] rounded-none shadow-2xl overflow-y-auto">
<!-- 加载状态 -->
<div
v-if="loading"
@@ -25,11 +25,11 @@
<template v-else-if="provider">
<!-- 头部:名称 + 快捷操作 -->
<div class="sticky top-0 z-10 bg-background border-b p-6">
<div class="flex items-start justify-between gap-4">
<div class="sticky top-0 z-10 bg-background border-b p-4 sm:p-6">
<div class="flex items-start justify-between gap-3 sm:gap-4">
<div class="space-y-1 flex-1 min-w-0">
<div class="flex items-center gap-2">
<h2 class="text-xl font-bold truncate">
<h2 class="text-lg sm:text-xl font-bold truncate">
{{ provider.display_name }}
</h2>
<Badge
@@ -84,7 +84,7 @@
</div>
</div>
<div class="space-y-6 p-6">
<div class="space-y-6 p-4 sm:p-6">
<!-- 配额使用情况 -->
<Card
v-if="provider.billing_type === 'monthly_quota' && provider.monthly_quota_usd"

View File

@@ -1,5 +1,5 @@
<template>
<Card class="p-4 !overflow-visible">
<Card class="p-4 overflow-hidden">
<div class="flex items-center justify-between mb-3">
<p class="text-sm font-semibold">
{{ title }}

View File

@@ -7,7 +7,7 @@
:model-value="selectedPeriod"
@update:model-value="$emit('update:selectedPeriod', $event)"
>
<SelectTrigger class="w-32 h-8 text-xs border-border/60">
<SelectTrigger class="w-24 sm:w-32 h-8 text-xs border-border/60">
<SelectValue placeholder="选择时间段" />
</SelectTrigger>
<SelectContent>
@@ -30,7 +30,7 @@
</Select>
<!-- 分隔线 -->
<div class="h-4 w-px bg-border" />
<div class="hidden sm:block h-4 w-px bg-border" />
<!-- 用户筛选仅管理员可见 -->
<Select
@@ -39,7 +39,7 @@
:model-value="filterUser"
@update:model-value="$emit('update:filterUser', $event)"
>
<SelectTrigger class="w-36 h-8 text-xs border-border/60">
<SelectTrigger class="w-24 sm:w-36 h-8 text-xs border-border/60">
<SelectValue placeholder="全部用户" />
</SelectTrigger>
<SelectContent>
@@ -62,7 +62,7 @@
:model-value="filterModel"
@update:model-value="$emit('update:filterModel', $event)"
>
<SelectTrigger class="w-40 h-8 text-xs border-border/60">
<SelectTrigger class="w-24 sm:w-40 h-8 text-xs border-border/60">
<SelectValue placeholder="全部模型" />
</SelectTrigger>
<SelectContent>
@@ -85,7 +85,7 @@
:model-value="filterProvider"
@update:model-value="$emit('update:filterProvider', $event)"
>
<SelectTrigger class="w-32 h-8 text-xs border-border/60">
<SelectTrigger class="w-24 sm:w-32 h-8 text-xs border-border/60">
<SelectValue placeholder="全部提供商" />
</SelectTrigger>
<SelectContent>
@@ -108,7 +108,7 @@
:model-value="filterStatus"
@update:model-value="$emit('update:filterStatus', $event)"
>
<SelectTrigger class="w-28 h-8 text-xs border-border/60">
<SelectTrigger class="w-20 sm:w-28 h-8 text-xs border-border/60">
<SelectValue placeholder="全部状态" />
</SelectTrigger>
<SelectContent>
@@ -134,7 +134,7 @@
</Select>
<!-- 分隔线 -->
<div class="h-4 w-px bg-border" />
<div class="hidden sm:block h-4 w-px bg-border" />
<!-- 刷新按钮 -->
<RefreshButton