refactor: 收敛单用户模型并优化可访问性与响应式体验

- 后端移除 is_admin 权限字段与相关逻辑,统一为单用户系统模型
- 修复会话过期清理的时间比较方式(改为 RFC3339 参数比较)
- /api/config 聚合配置增加敏感字段脱敏,避免暴露 TURN/RustDesk 密钥与密码
- 配置更新日志改为摘要,避免打印完整配置内容
- 前端修复可点击卡片语义与键盘可达,补齐图标按钮可访问名称
- 调整弹窗与抽屉的响应式尺寸,优化多端显示与交互
This commit is contained in:
mofeng-git
2026-02-10 22:30:52 +08:00
parent 394baca938
commit 261deb1303
13 changed files with 81 additions and 60 deletions

View File

@@ -52,7 +52,7 @@ async function handleLogout() {
</script>
<template>
<div class="h-screen flex flex-col bg-background overflow-hidden">
<div class="h-screen h-dvh flex flex-col bg-background overflow-hidden">
<!-- Header -->
<header class="shrink-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div class="flex h-14 items-center px-4 max-w-full">
@@ -86,14 +86,14 @@ async function handleLogout() {
</span>
<!-- Theme Toggle -->
<Button variant="ghost" size="icon" @click="toggleTheme">
<Button variant="ghost" size="icon" :aria-label="t('common.toggleTheme')" @click="toggleTheme">
<Sun class="h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon class="absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span class="sr-only">{{ t('common.toggleTheme') }}</span>
</Button>
<!-- Language Toggle -->
<Button variant="ghost" size="icon" @click="toggleLanguage">
<Button variant="ghost" size="icon" :aria-label="t('common.toggleLanguage')" @click="toggleLanguage">
<Languages class="h-4 w-4" />
<span class="sr-only">{{ t('common.toggleLanguage') }}</span>
</Button>
@@ -101,7 +101,7 @@ async function handleLogout() {
<!-- Mobile Menu -->
<DropdownMenu>
<DropdownMenuTrigger as-child class="md:hidden">
<Button variant="ghost" size="icon">
<Button variant="ghost" size="icon" :aria-label="t('common.menu')">
<Menu class="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
@@ -119,7 +119,7 @@ async function handleLogout() {
</DropdownMenu>
<!-- Logout Button (Desktop) -->
<Button variant="ghost" size="icon" class="hidden md:flex" @click="handleLogout">
<Button variant="ghost" size="icon" class="hidden md:flex" :aria-label="t('nav.logout')" @click="handleLogout">
<LogOut class="h-4 w-4" />
<span class="sr-only">{{ t('nav.logout') }}</span>
</Button>

View File

@@ -442,7 +442,7 @@ onUnmounted(() => {
<Sheet :open="props.open" @update:open="emit('update:open', $event)">
<SheetContent
side="right"
class="w-[400px] sm:w-[440px] p-0 border-l border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-950"
class="w-[90vw] max-w-[440px] p-0 border-l border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-950"
>
<!-- Header -->
<SheetHeader class="px-6 py-3 border-b border-slate-200 dark:border-slate-800">
@@ -454,7 +454,7 @@ onUnmounted(() => {
</div>
</SheetHeader>
<ScrollArea class="h-[calc(100vh-60px)]">
<ScrollArea class="h-[calc(100dvh-60px)]">
<div class="px-6 py-4 space-y-6">
<!-- Video Section Header -->
<div>

View File

@@ -129,9 +129,11 @@ const statusBadgeText = computed(() => {
<HoverCard v-if="!prefersPopover" :open-delay="200" :close-delay="100">
<HoverCardTrigger as-child>
<!-- New layout: vertical with title on top, status+quickInfo on bottom -->
<div
<button
type="button"
:aria-label="`${title}: ${quickInfo || subtitle || statusText}`"
:class="cn(
'flex flex-col gap-0.5 rounded-md border cursor-pointer transition-colors',
'flex flex-col gap-0.5 rounded-md border cursor-pointer transition-colors text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
compact ? 'px-2 py-1 text-xs min-w-[80px]' : 'px-3 py-1.5 text-sm min-w-[100px]',
'bg-white dark:bg-slate-800 hover:bg-slate-50 dark:hover:bg-slate-700',
'border-slate-200 dark:border-slate-700',
@@ -147,7 +149,7 @@ const statusBadgeText = computed(() => {
{{ quickInfo || subtitle || statusText }}
</span>
</div>
</div>
</button>
</HoverCardTrigger>
<HoverCardContent class="w-80" :align="hoverAlign">
@@ -228,9 +230,11 @@ const statusBadgeText = computed(() => {
<Popover v-else>
<PopoverTrigger as-child>
<!-- New layout: vertical with title on top, status+quickInfo on bottom -->
<div
<button
type="button"
:aria-label="`${title}: ${quickInfo || subtitle || statusText}`"
:class="cn(
'flex flex-col gap-0.5 rounded-md border cursor-pointer transition-colors',
'flex flex-col gap-0.5 rounded-md border cursor-pointer transition-colors text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
compact ? 'px-2 py-1 text-xs min-w-[80px]' : 'px-3 py-1.5 text-sm min-w-[100px]',
'bg-white dark:bg-slate-800 hover:bg-slate-50 dark:hover:bg-slate-700',
'border-slate-200 dark:border-slate-700',
@@ -246,7 +250,7 @@ const statusBadgeText = computed(() => {
{{ quickInfo || subtitle || statusText }}
</span>
</div>
</div>
</button>
</PopoverTrigger>
<PopoverContent class="w-80" :align="hoverAlign">