refactor(frontend): optimize view pages (admin, shared, user)

This commit is contained in:
fawney19
2025-12-13 22:26:47 +08:00
parent 9ca845f9d0
commit d564842c4d
14 changed files with 980 additions and 268 deletions

View File

@@ -6,17 +6,17 @@
class="overflow-hidden"
>
<!-- 标题和操作栏 -->
<div class="px-6 py-3.5 border-b border-border/60">
<div class="flex items-center justify-between gap-4">
<div>
<h3 class="text-base font-semibold">
<div class="px-4 sm:px-6 py-3 sm:py-3.5 border-b border-border/60">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<div class="shrink-0">
<h3 class="text-sm sm:text-base font-semibold">
公告管理
</h3>
<p class="text-xs text-muted-foreground mt-0.5">
{{ isAdmin ? '管理系统公告和通知' : '查看系统公告和通知' }}
</p>
</div>
<div class="flex items-center gap-2">
<div class="flex flex-wrap items-center gap-2">
<Badge
v-if="unreadCount > 0"
variant="default"
@@ -24,7 +24,7 @@
>
{{ unreadCount }} 条未读
</Badge>
<div class="h-4 w-px bg-border" />
<div class="hidden sm:block h-4 w-px bg-border" />
<Button
v-if="isAdmin"
variant="ghost"
@@ -68,7 +68,7 @@
v-else
class="overflow-x-auto"
>
<Table>
<Table class="hidden xl:table">
<TableHeader>
<TableRow class="border-b border-border/60 hover:bg-transparent">
<TableHead class="w-[80px] h-12 font-semibold text-center">
@@ -217,6 +217,91 @@
</TableRow>
</TableBody>
</Table>
<!-- 移动端卡片列表 -->
<div
v-if="announcements.length > 0"
class="xl:hidden divide-y divide-border/40"
>
<div
v-for="announcement in announcements"
:key="announcement.id"
:class="[
'p-4 space-y-2 cursor-pointer transition-colors',
announcement.is_read ? 'hover:bg-muted/30' : 'bg-primary/5 hover:bg-primary/10'
]"
@click="viewAnnouncementDetail(announcement)"
>
<div class="flex items-start justify-between gap-3">
<div class="flex items-center gap-2">
<component
:is="getAnnouncementIcon(announcement.type)"
class="w-4 h-4 shrink-0"
:class="getIconColor(announcement.type)"
/>
<span class="font-medium text-sm">{{ announcement.title }}</span>
<Pin
v-if="announcement.is_pinned"
class="w-3.5 h-3.5 text-muted-foreground shrink-0"
/>
</div>
<Badge
:variant="announcement.is_read ? 'secondary' : 'default'"
class="text-xs shrink-0"
>
{{ announcement.is_read ? '已读' : '未读' }}
</Badge>
</div>
<p class="text-xs text-muted-foreground line-clamp-2">
{{ getPlainText(announcement.content) }}
</p>
<div class="flex items-center gap-2 text-xs text-muted-foreground">
<span>{{ announcement.author.username }}</span>
<span>·</span>
<span>{{ formatDate(announcement.created_at) }}</span>
</div>
<div
v-if="isAdmin"
class="flex items-center gap-4 pt-2"
@click.stop
>
<div class="flex items-center gap-2">
<span class="text-xs text-muted-foreground">置顶</span>
<Switch
:model-value="announcement.is_pinned"
class="data-[state=checked]:bg-emerald-500 scale-75"
@update:model-value="toggleAnnouncementPin(announcement, $event)"
/>
</div>
<div class="flex items-center gap-2">
<span class="text-xs text-muted-foreground">启用</span>
<Switch
:model-value="announcement.is_active"
class="data-[state=checked]:bg-primary scale-75"
@update:model-value="toggleAnnouncementActive(announcement, $event)"
/>
</div>
<div class="flex items-center gap-1 ml-auto">
<Button
variant="ghost"
size="icon"
class="h-7 w-7"
@click="openEditDialog(announcement)"
>
<SquarePen class="w-3.5 h-3.5" />
</Button>
<Button
variant="ghost"
size="icon"
class="h-7 w-7 hover:text-destructive"
@click="confirmDelete(announcement)"
>
<Trash2 class="w-3.5 h-3.5" />
</Button>
</div>
</div>
</div>
</div>
</div>
<!-- 分页 -->

View File

@@ -3,15 +3,15 @@
<!-- 模型列表 -->
<Card class="overflow-hidden">
<!-- 标题和操作栏 -->
<div class="px-6 py-3.5 border-b border-border/60">
<div class="flex items-center justify-between gap-4">
<div class="px-4 sm:px-6 py-3 sm:py-3.5 border-b border-border/60">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<!-- 左侧标题 -->
<h3 class="text-base font-semibold">
<h3 class="text-sm sm:text-base font-semibold shrink-0">
可用模型
</h3>
<!-- 右侧操作区 -->
<div class="flex items-center gap-2">
<div class="flex flex-wrap items-center gap-2">
<!-- 搜索框 -->
<div class="relative">
<Search class="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
@@ -20,11 +20,11 @@
v-model="searchQuery"
type="text"
placeholder="搜索模型名称..."
class="w-44 pl-8 pr-3 h-8 text-sm bg-background/50 border-border/60 focus:border-primary/40 transition-colors"
class="w-32 sm:w-44 pl-8 pr-3 h-8 text-sm bg-background/50 border-border/60 focus:border-primary/40 transition-colors"
/>
</div>
<div class="h-4 w-px bg-border" />
<div class="hidden sm:block h-4 w-px bg-border" />
<!-- 能力筛选 -->
<div class="flex items-center border rounded-md border-border/60 h-8 overflow-hidden">
@@ -56,7 +56,7 @@
</button>
</div>
<div class="h-4 w-px bg-border" />
<div class="hidden sm:block h-4 w-px bg-border" />
<!-- 刷新按钮 -->
<RefreshButton
@@ -68,7 +68,7 @@
</div>
<div class="overflow-x-auto">
<Table class="table-fixed w-full">
<Table class="hidden xl:table table-fixed w-full">
<TableHeader>
<TableRow class="border-b border-border/60 hover:bg-transparent">
<TableHead class="w-[140px] h-12 font-semibold">
@@ -219,6 +219,91 @@
</template>
</TableBody>
</Table>
<!-- 移动端卡片列表 -->
<div
v-if="!loading && filteredModels.length > 0"
class="xl:hidden divide-y divide-border/40"
>
<div
v-for="model in paginatedModels"
:key="model.id"
class="p-4 space-y-3 hover:bg-muted/30 cursor-pointer transition-colors"
@click="selectedModel = model; drawerOpen = true"
>
<!-- 第一行名称 + 状态 -->
<div class="flex items-start justify-between gap-3">
<div class="flex-1 min-w-0">
<span class="font-medium truncate block">{{ model.display_name || model.name }}</span>
<div class="text-xs text-muted-foreground flex items-center gap-1 mt-0.5">
<span class="truncate">{{ model.name }}</span>
<button
class="p-0.5 rounded hover:bg-muted transition-colors shrink-0"
@click.stop="copyToClipboard(model.name)"
>
<Copy class="w-3 h-3" />
</button>
</div>
</div>
<Badge :variant="model.is_active ? 'success' : 'secondary'">
{{ model.is_active ? '可用' : '停用' }}
</Badge>
</div>
<!-- 第二行能力图标 -->
<div class="flex gap-1.5">
<Eye
v-if="model.default_supports_vision"
class="w-4 h-4 text-muted-foreground"
/>
<Wrench
v-if="model.default_supports_function_calling"
class="w-4 h-4 text-muted-foreground"
/>
<Brain
v-if="model.default_supports_extended_thinking"
class="w-4 h-4 text-muted-foreground"
/>
</div>
<!-- 第三行价格 -->
<div
v-if="getFirstTierPrice(model, 'input') || getFirstTierPrice(model, 'output')"
class="text-xs text-muted-foreground font-mono"
>
In: ${{ getFirstTierPrice(model, 'input')?.toFixed(2) || '-' }} / Out: ${{ getFirstTierPrice(model, 'output')?.toFixed(2) || '-' }}
</div>
<!-- 第四行模型偏好按钮 -->
<div
v-if="getModelSupportedCapabilities(model).length > 0"
class="flex gap-1.5 flex-wrap"
@click.stop
>
<button
v-for="cap in getModelSupportedCapabilitiesDetails(model)"
:key="cap.name"
class="inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium transition-all"
:class="[
isCapabilityEnabled(model.name, cap.name)
? 'bg-primary text-primary-foreground'
: 'bg-transparent text-muted-foreground border border-dashed border-muted-foreground/50'
]"
@click="toggleCapability(model.name, cap.name)"
>
<Check
v-if="isCapabilityEnabled(model.name, cap.name)"
class="w-3 h-3"
/>
<Plus
v-else
class="w-3 h-3"
/>
{{ cap.short_name || cap.display_name }}
</button>
</div>
</div>
</div>
</div>
<!-- 分页 -->

View File

@@ -6,9 +6,9 @@
class="overflow-hidden"
>
<!-- 标题和操作栏 -->
<div class="px-6 py-3.5 border-b border-border/60">
<div class="flex items-center justify-between gap-4">
<h3 class="text-base font-semibold">
<div class="px-4 sm:px-6 py-3 sm:py-3.5 border-b border-border/60">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<h3 class="text-sm sm:text-base font-semibold shrink-0">
我的 API Keys
</h3>

View File

@@ -13,12 +13,12 @@
/>
<!-- 抽屉内容 -->
<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 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">
<h3 class="text-xl font-bold truncate">
<h3 class="text-lg sm:text-xl font-bold truncate">
{{ model.display_name || model.name }}
</h3>
<div class="flex items-center gap-2">
@@ -55,13 +55,13 @@
</div>
</div>
<div class="p-6 space-y-6">
<div class="p-4 sm:p-6 space-y-6">
<!-- 模型能力 -->
<div class="space-y-3">
<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">