mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-04 00:32:26 +08:00
refactor(frontend): optimize view pages (admin, shared, user)
This commit is contained in:
@@ -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>
|
||||
|
||||
<!-- 分页 -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
<!-- 分页 -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user