Files
opsxsql/SQL平台评估与优化/sql_platform_optimization_sec04.md
T
logan 746a000e8a feat: 全面UI优化 — 5套主题系统 + 设计体系 + 菜单重组 + 页面优化
1. 主题系统扩展 (5套主题)
   - ocean-deep(深海蓝) / light-professional(明亮白) / midnight-purple(午夜紫)
   - pine-green(松林绿) / orange-vitality(活力橙)
   - useTheme hook 重写支持5主题 + 向后兼容
   - AppearanceSettings 5主题预览卡片

2. 设计体系 (index.css)
   - 按钮系统: dg-btn-primary/secondary/ghost/danger + 尺寸变体
   - 徽章系统: dg-badge-* (8色)
   - 卡片/表格: dg-card-section, dg-table-row, dg-table-header
   - 状态点: dg-status-dot-* (4色)
   - 动效关键帧: slideInLeft, scaleIn

3. 菜单重组 (menu.ts)
   - 5分组: 概览/数据源/工单/权限/系统
   - 6个图标更新: Code2/FileEdit/ShieldCheck/FolderLock/ScrollText/Timer
   - 新增 permission 权限码字段

4. Layout 侧边栏增强
   - 左侧渐变指示条 + 发光效果
   - hover 微动画 + 图标变色
   - 面包屑导航头部
   - 主题切换循环5主题

5. 核心页面优化
   - TicketsPage: 设计系统类 + 颜色对齐
   - QueriesPage: 去Card依赖 + dg-table/dg-btn统一
   - Breadcrumb组件 + useBreadcrumb hook
2026-05-27 01:59:08 +08:00

29 KiB
Raw Blame History

4. 五套登录后主题设计方案

为满足不同用户群体在不同使用场景下的视觉偏好,本章为 SQL-Platform 设计五套完整的登录后主题方案。每套主题均包含完整的 CSS 变量规范、色彩心理学依据及典型应用场景说明。五套主题涵盖从专业沉稳到活力温暖的全方位视觉谱系,确保平台在各类企业环境中都能提供最佳的用户体验。


4.1 主题一「深蓝专业」— 默认深色增强版

设计定位:深蓝专业主题是在现有深色主题基础上的全面增强版本,定位为平台的默认主题。设计目标是在保持深色模式优势(降低眼部疲劳、突出内容)的同时,通过更纯正深邃的蓝色调提升整体品质感,并强化视觉层次与可读性。

色彩设计理念:该主题摒弃了原主题中偏灰的杂色倾向,将背景色收敛到更纯正的深蓝谱系。主背景色 #070d19 接近纯黑但带有微弱的蓝色相,营造出深邃而稳定的基底。次背景色 #0f172a 参考了 Slate 色板中最深沉的蓝色调,既保证了与主背景的层次区分,又维持了蓝色谱系的纯粹性。文字色彩采用 #f8fafc(极亮蓝白)作为主文字色,相比纯白色更具温润感,长时间阅读不易产生刺眼感。次文字色 #94a3b8 提供了充分的对比度,确保辅助信息的可读性。

强调色体系:蓝色系强调色选用 #3b82f6(Tailwind Blue 500),这是一个饱和度适中、辨识度高的蓝色,在深色背景上呈现明亮但不刺眼的视觉效果。状态色(成功、错误、警告)均经过深色背景适配,确保各色彩在深蓝底上都有足够的明度对比。

适用场景:作为默认主题,适用于绝大多数使用场景,特别适合技术团队日常操作、数据库管理、权限配置等需要长时间专注的工作界面。

/* === 主题一:深蓝专业 ocean-deep === */
[data-theme="ocean-deep"] {
  /* shadcn/ui 基础变量 */
  --background: 222 47% 4%;
  --foreground: 210 40% 98%;
  --card: 222 47% 7%;
  --card-foreground: 210 40% 98%;
  --popover: 222 47% 7%;
  --popover-foreground: 210 40% 98%;
  --primary: 217 91% 60%;
  --primary-foreground: 0 0% 100%;
  --secondary: 217 33% 17%;
  --secondary-foreground: 210 40% 98%;
  --muted: 217 33% 17%;
  --muted-foreground: 215 20% 65%;
  --accent: 217 33% 17%;
  --accent-foreground: 210 40% 98%;
  --destructive: 0 84% 60%;
  --destructive-foreground: 0 0% 100%;
  --border: 217 33% 17%;
  --input: 217 33% 17%;
  --ring: 217 91% 60%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #070d19;
  --dg-bg-secondary: #0f172a;
  --dg-bg-tertiary: #172033;
  --dg-bg-hover: #233049;
  --dg-border: #1e293b;
  --dg-border-light: #172033;
  --dg-text-primary: #f8fafc;
  --dg-text-secondary: #94a3b8;
  --dg-text-muted: #64748b;
  --dg-blue: #3b82f6;
  --dg-green: #22c55e;
  --dg-red: #ef4444;
  --dg-orange: #f97316;
  --dg-purple: #8b5cf6;
  --dg-cyan: #06b6d4;
  --dg-yellow: #eab308;
  --dg-gradient-start: #3b82f6;
  --dg-gradient-end: #2563eb;
}

4.2 主题二「浅色商务」— 企业办公风

设计定位:浅色商务主题面向白天办公环境优化,采用温暖的灰白色调替代常见的冷白背景,营造舒适、专业且不刺眼的工作界面。该主题适合不习惯深色模式的用户,或在明亮办公室环境中使用。

色彩设计理念:背景色摒弃了纯白的冷峻感,主背景色 #fafaf9 选用带有微弱暖色相的" zinc white",在显示器上呈现柔和的纸质感。次背景色 #f5f5f4 和三级背景色 #e7e5e4 构成自然的层次递进,避免了传统浅色主题中背景层次过于平淡的问题。边框色 #d6d3d1 同样带有暖色调,使界面分割更加自然和谐。文字色彩方面,主文字色 #1c1917 是带有微弱棕色相的深黑色,相比纯黑在暖白背景上对比度更舒适,避免了"黑字白纸"的刺眼感。

强调色体系:强调色选用稳重的 #2563eb(Tailwind Blue 600),这是一个偏深的标准蓝色,传达专业、可信赖的企业形象。状态色均调整为适合浅色背景的版本,成功绿 #16a34a、错误红 #dc2626、警告橙 #ca8a04 在浅色背景上均保持良好的可读性。

适用场景:适合白天办公环境、管理人员日常审批查看、客户演示投屏等场景。浅色界面在投影设备上的显示效果通常优于深色界面,因此该主题也是会议室演示的首选。

/* === 主题二:浅色商务 light-professional === */
[data-theme="light-professional"] {
  /* shadcn/ui 基础变量 */
  --background: 40 9% 98%;
  --foreground: 24 10% 10%;
  --card: 40 9% 96%;
  --card-foreground: 24 10% 10%;
  --popover: 40 9% 96%;
  --popover-foreground: 24 10% 10%;
  --primary: 221 83% 53%;
  --primary-foreground: 0 0% 100%;
  --secondary: 40 6% 90%;
  --secondary-foreground: 24 10% 10%;
  --muted: 40 6% 90%;
  --muted-foreground: 25 5% 45%;
  --accent: 40 6% 90%;
  --accent-foreground: 24 10% 10%;
  --destructive: 0 72% 51%;
  --destructive-foreground: 0 0% 100%;
  --border: 24 6% 83%;
  --input: 24 6% 83%;
  --ring: 221 83% 53%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #fafaf9;
  --dg-bg-secondary: #f5f5f4;
  --dg-bg-tertiary: #e7e5e4;
  --dg-bg-hover: #d6d3d1;
  --dg-border: #d6d3d1;
  --dg-border-light: #e7e5e4;
  --dg-text-primary: #1c1917;
  --dg-text-secondary: #78716c;
  --dg-text-muted: #a8a29e;
  --dg-blue: #2563eb;
  --dg-green: #16a34a;
  --dg-red: #dc2626;
  --dg-orange: #ea580c;
  --dg-purple: #7c3aed;
  --dg-cyan: #0891b2;
  --dg-yellow: #ca8a04;
  --dg-gradient-start: #2563eb;
  --dg-gradient-end: #4f46e5;
}

4.3 主题三「午夜紫韵」— 高端紫色调

设计定位:午夜紫韵主题以深紫黑色为基底,搭配明亮的紫色渐变强调色,营造高端、神秘且富有科技感的视觉氛围。该主题适合作为给客户演示时的展示主题,能够传达产品的技术领先性和品质感。

色彩设计理念:主背景色 #0a0612 是带有明显紫色相的极深色,接近"午夜黑"的概念,为整个界面奠定了神秘而高贵的基调。次背景色 #130c21 和三级背景色 #1c1330 沿紫色谱系逐步提亮,形成清晰的层次结构。悬停背景色 #2d1f4a 是一个中等深度的紫色,在交互时能够产生明显的视觉反馈,同时保持整体色调的一致性。文字色彩方面,主文字色 #faf5ff 带有微弱的紫色调,使文字在深色背景上更加柔和雅致。

强调色体系:该主题的核心亮点在于紫色渐变强调体系。渐变起点 #9333ea(Tailwind Purple 600)到渐变终点 #c084fc(Tailwind Purple 400)构成了一段明亮且富有活力的紫色渐变,用于品牌标识、主要按钮和重要数据高亮。蓝色调整为偏紫的靛蓝色 #818cf8,绿色调整为翠绿色 #34d399,红色调整为玫红色 #fb7185,所有状态色都经过紫色调环境的协调适配。

适用场景:产品发布会演示、客户汇报展示、高端企业部署、技术峰会展台等需要彰显产品品质的正式场合。

/* === 主题三:午夜紫韵 midnight-purple === */
[data-theme="midnight-purple"] {
  /* shadcn/ui 基础变量 */
  --background: 264 56% 4%;
  --foreground: 280 100% 98%;
  --card: 264 48% 8%;
  --card-foreground: 280 100% 98%;
  --popover: 264 48% 8%;
  --popover-foreground: 280 100% 98%;
  --primary: 270 91% 65%;
  --primary-foreground: 0 0% 100%;
  --secondary: 264 40% 20%;
  --secondary-foreground: 280 100% 98%;
  --muted: 264 40% 20%;
  --muted-foreground: 280 30% 60%;
  --accent: 264 40% 20%;
  --accent-foreground: 280 100% 98%;
  --destructive: 348 100% 72%;
  --destructive-foreground: 264 56% 4%;
  --border: 264 40% 20%;
  --input: 264 40% 20%;
  --ring: 270 91% 65%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #0a0612;
  --dg-bg-secondary: #130c21;
  --dg-bg-tertiary: #1c1330;
  --dg-bg-hover: #2d1f4a;
  --dg-border: #2d1f4a;
  --dg-border-light: #1c1330;
  --dg-text-primary: #faf5ff;
  --dg-text-secondary: #b794c7;
  --dg-text-muted: #7c5a8a;
  --dg-blue: #818cf8;
  --dg-green: #34d399;
  --dg-red: #fb7185;
  --dg-orange: #fb923c;
  --dg-purple: #c084fc;
  --dg-cyan: #22d3ee;
  --dg-yellow: #facc15;
  --dg-gradient-start: #9333ea;
  --dg-gradient-end: #c084fc;
}

4.4 主题四「松绿自然」— 护眼绿调

设计定位:松绿自然主题面向长时间使用平台的开发团队设计,以墨绿色为基调,利用绿色光谱对人眼最为友好的特性,最大程度降低长时间注视屏幕带来的视觉疲劳。

色彩设计理念:该主题的核心设计原则是"护眼优先"。主背景色 #0a1210 是带有明显绿色相的极深色,类似深夜松林深处的颜色,极低明度确保了基础视觉负荷的最小化。次背景色 #111f1a(深墨绿)和三级背景色 #192c24(墨绿灰)沿绿色谱系递进,在保持低明度的同时构建清晰的视觉层次。悬停背景色 #253d32 选用了中等深度的松绿色,交互时呈现自然、柔和的状态变化。边框色 #253d32 与悬停色共用,确保交互反馈与界面分割的视觉统一。主文字色 #f0fdf4 是极淡的薄荷白,在墨绿背景上明亮而不刺眼,具有极佳的阅读舒适度。

强调色体系:绿色系强调色选用 #4ade80(Tailwind Green 400),这是一个明亮、清新的绿色,在墨绿暗底上如同松针上的晨露般醒目。信息色 #2dd4bf 偏向青绿色,与主色调完美协调。蓝色调整为天蓝色 #38bdf8,在绿调环境中起到色彩平衡的作用,避免界面过于单调。

适用场景:开发团队日常长时间使用、DBA 持续监控数据库状态、运维人员驻屏值班等需要长时间面对屏幕的工作场景。

/* === 主题四:松绿自然 pine-green === */
[data-theme="pine-green"] {
  /* shadcn/ui 基础变量 */
  --background: 150 25% 5%;
  --foreground: 140 60% 97%;
  --card: 150 25% 9%;
  --card-foreground: 140 60% 97%;
  --popover: 150 25% 9%;
  --popover-foreground: 140 60% 97%;
  --primary: 142 71% 45%;
  --primary-foreground: 150 25% 5%;
  --secondary: 150 20% 18%;
  --secondary-foreground: 140 60% 97%;
  --muted: 150 20% 18%;
  --muted-foreground: 150 20% 55%;
  --accent: 150 20% 18%;
  --accent-foreground: 140 60% 97%;
  --destructive: 0 90% 72%;
  --destructive-foreground: 150 25% 5%;
  --border: 150 20% 18%;
  --input: 150 20% 18%;
  --ring: 142 71% 45%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #0a1210;
  --dg-bg-secondary: #111f1a;
  --dg-bg-tertiary: #192c24;
  --dg-bg-hover: #253d32;
  --dg-border: #253d32;
  --dg-border-light: #192c24;
  --dg-text-primary: #f0fdf4;
  --dg-text-secondary: #86b8a0;
  --dg-text-muted: #5a7c6b;
  --dg-blue: #38bdf8;
  --dg-green: #4ade80;
  --dg-red: #f87171;
  --dg-orange: #fb923c;
  --dg-purple: #c084fc;
  --dg-cyan: #2dd4bf;
  --dg-yellow: #facc15;
  --dg-gradient-start: #059669;
  --dg-gradient-end: #10b981;
}

4.5 主题五「橙光活力」— 活力暖色调

设计定位:橙光活力主题以深棕暗橙为基底,搭配明亮的橙色渐变,传递温暖、积极、富有活力的品牌情绪。该主题面向年轻化团队和创新型企业,打破传统数据安全平台冰冷刻板的印象。

色彩设计理念:主背景色 #110d08 是带有明显棕色相的极深色,类似深烘焙咖啡豆的颜色,温暖而沉稳。次背景色 #1d1610 和三级背景色 #2b1f16 沿棕色谱系递进,营造出如同暖色调咖啡馆般的舒适感。悬停背景色 #3d2d20 是一个中等深度的暖棕色,交互反馈如同木材被温暖光线照亮。文字色彩方面,主文字色 #fef7ed 是极淡的暖白色,如同烛光下的纸张,在暗棕底上既明亮又温暖。

强调色体系:核心渐变从 #ea580c(Tailwind Orange 600)到 #f59e0b(Tailwind Amber 500),这是一段充满能量感的橙黄渐变,用于重要按钮、品牌标识和数据高亮时极具视觉冲击力。蓝色调整为柔和的 #60a5fa,在暖色调环境中起到中和平衡的作用。紫色调整为偏暖的 #a78bfa,与整体暖调和谐共存。

适用场景:互联网创业公司、年轻化的技术团队、创意型企业、产品内部分享会等追求活力与创新氛围的使用场景。

/* === 主题五:橙光活力 orange-vitality === */
[data-theme="orange-vitality"] {
  /* shadcn/ui 基础变量 */
  --background: 30 35% 5%;
  --foreground: 30 100% 97%;
  --card: 30 30% 9%;
  --card-foreground: 30 100% 97%;
  --popover: 30 30% 9%;
  --popover-foreground: 30 100% 97%;
  --primary: 25 95% 53%;
  --primary-foreground: 0 0% 100%;
  --secondary: 30 25% 18%;
  --secondary-foreground: 30 100% 97%;
  --muted: 30 25% 18%;
  --muted-foreground: 30 25% 55%;
  --accent: 30 25% 18%;
  --accent-foreground: 30 100% 97%;
  --destructive: 0 80% 70%;
  --destructive-foreground: 30 35% 5%;
  --border: 30 25% 18%;
  --input: 30 25% 18%;
  --ring: 25 95% 53%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #110d08;
  --dg-bg-secondary: #1d1610;
  --dg-bg-tertiary: #2b1f16;
  --dg-bg-hover: #3d2d20;
  --dg-border: #3d2d20;
  --dg-border-light: #2b1f16;
  --dg-text-primary: #fef7ed;
  --dg-text-secondary: #d4a574;
  --dg-text-muted: #9c7a54;
  --dg-blue: #60a5fa;
  --dg-green: #4ade80;
  --dg-red: #f87171;
  --dg-orange: #fb923c;
  --dg-purple: #a78bfa;
  --dg-cyan: #2dd4bf;
  --dg-yellow: #fcd34d;
  --dg-gradient-start: #ea580c;
  --dg-gradient-end: #f59e0b;
}

4.6 各主题 CSS 变量规范对比表

下表汇总了五套主题的全部 DataGuard 自定义 CSS 变量值,便于开发实现时快速查阅与对比。

CSS 变量 深蓝专业 浅色商务 午夜紫韵 松绿自然 橙光活力
--dg-bg-primary #070d19 #fafaf9 #0a0612 #0a1210 #110d08
--dg-bg-secondary #0f172a #f5f5f4 #130c21 #111f1a #1d1610
--dg-bg-tertiary #172033 #e7e5e4 #1c1330 #192c24 #2b1f16
--dg-bg-hover #233049 #d6d3d1 #2d1f4a #253d32 #3d2d20
--dg-border #1e293b #d6d3d1 #2d1f4a #253d32 #3d2d20
--dg-border-light #172033 #e7e5e4 #1c1330 #192c24 #2b1f16
--dg-text-primary #f8fafc #1c1917 #faf5ff #f0fdf4 #fef7ed
--dg-text-secondary #94a3b8 #78716c #b794c7 #86b8a0 #d4a574
--dg-text-muted #64748b #a8a29e #7c5a8a #5a7c6b #9c7a54
--dg-blue #3b82f6 #2563eb #818cf8 #38bdf8 #60a5fa
--dg-green #22c55e #16a34a #34d399 #4ade80 #4ade80
--dg-red #ef4444 #dc2626 #fb7185 #f87171 #f87171
--dg-orange #f97316 #ea580c #fb923c #fb923c #fb923c
--dg-purple #8b5cf6 #7c3aed #c084fc #c084fc #a78bfa
--dg-cyan #06b6d4 #0891b2 #22d3ee #2dd4bf #2dd4bf
--dg-yellow #eab308 #ca8a04 #facc15 #facc15 #fcd34d
--dg-gradient-start #3b82f6 #2563eb #9333ea #059669 #ea580c
--dg-gradient-end #2563eb #4f46e5 #c084fc #10b981 #f59e0b

shadcn/ui 基础变量对比表

HSL 变量 深蓝专业 浅色商务 午夜紫韵 松绿自然 橙光活力
--background 222 47% 4% 40 9% 98% 264 56% 4% 150 25% 5% 30 35% 5%
--foreground 210 40% 98% 24 10% 10% 280 100% 98% 140 60% 97% 30 100% 97%
--card 222 47% 7% 40 9% 96% 264 48% 8% 150 25% 9% 30 30% 9%
--primary 217 91% 60% 221 83% 53% 270 91% 65% 142 71% 45% 25 95% 53%
--secondary 217 33% 17% 40 6% 90% 264 40% 20% 150 20% 18% 30 25% 18%
--muted 217 33% 17% 40 6% 90% 264 40% 20% 150 20% 18% 30 25% 18%
--muted-foreground 215 20% 65% 25 5% 45% 280 30% 60% 150 20% 55% 30 25% 55%
--destructive 0 84% 60% 0 72% 51% 348 100% 72% 0 90% 72% 0 80% 70%
--border 217 33% 17% 24 6% 83% 264 40% 20% 150 20% 18% 30 25% 18%
--ring 217 91% 60% 221 83% 53% 270 91% 65% 142 71% 45% 25 95% 53%

4.7 主题切换实现方案

4.7.1 CSS 变量组织方式

五套主题的 CSS 变量采用 "分散定义、统一命名空间" 的组织策略。每套主题的所有变量定义在一个独立的 data-theme 属性选择器下,避免变量冲突和覆盖混乱。

文件结构建议:

frontend/src/styles/
├── themes/
│   ├── index.css          /* 主题入口文件,导入所有主题 */
│   ├── ocean-deep.css     /* 主题一:深蓝专业 */
│   ├── light-professional.css  /* 主题二:浅色商务 */
│   ├── midnight-purple.css     /* 主题三:午夜紫韵 */
│   ├── pine-green.css          /* 主题四:松绿自然 */
│   └── orange-vitality.css     /* 主题五:橙光活力 */
└── index.css              /* 现有主样式文件 */

入口文件 themes/index.css 内容:

/* ===== SQL-Platform 主题系统入口 ===== */
/* 按加载顺序:默认主题(深蓝专业)无需 data-theme 属性 */

@import "./ocean-deep.css";
@import "./light-professional.css";
@import "./midnight-purple.css";
@import "./pine-green.css";
@import "./orange-vitality.css";

实现说明:ocean-deep.css 同时提供 :root 级别的默认变量(确保无 data-theme 属性时回退到默认主题)和 [data-theme="ocean-deep"] 选择器下的显式定义,保证其他主题切换回来时变量能正确覆盖。

4.7.2 useTheme Hook 增强实现

现有的 useTheme hook 需要从双值切换(dark/light)扩展为多主题选择。以下是增强后的实现方案:

// frontend/src/hooks/useTheme.ts
import { useState, useEffect, useCallback } from 'react';

/** 支持的主题标识列表 */
export const THEMES = [
  { id: 'ocean-deep', label: '深蓝专业', description: '默认深色增强版' },
  { id: 'light-professional', label: '浅色商务', description: '企业办公风' },
  { id: 'midnight-purple', label: '午夜紫韵', description: '高端紫色调' },
  { id: 'pine-green', label: '松绿自然', description: '护眼绿调' },
  { id: 'orange-vitality', label: '橙光活力', description: '活力暖色调' },
] as const;

export type ThemeId = typeof THEMES[number]['id'];

const STORAGE_KEY = 'sqlp-theme-id';
const DEFAULT_THEME: ThemeId = 'ocean-deep';

/**
 * 从 localStorage 读取保存的主题
 */
function getSavedTheme(): ThemeId {
  try {
    const saved = localStorage.getItem(STORAGE_KEY);
    if (saved && THEMES.some(t => t.id === saved)) {
      return saved as ThemeId;
    }
  } catch {
    // localStorage 不可用时忽略
  }
  return DEFAULT_THEME;
}

/**
 * 检测系统色彩偏好
 */
function getSystemPreference(): 'dark' | 'light' {
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}

/**
 * 根据系统偏好映射默认主题
 */
function getDefaultBySystem(): ThemeId {
  return getSystemPreference() === 'dark' ? 'ocean-deep' : 'light-professional';
}

export function useTheme() {
  const [theme, setThemeState] = useState<ThemeId>(() => {
    const saved = getSavedTheme();
    return saved || getDefaultBySystem();
  });

  /**
   * 切换主题的核心方法
   */
  const setTheme = useCallback((newTheme: ThemeId) => {
    setThemeState(newTheme);
    document.documentElement.setAttribute('data-theme', newTheme);
    try {
      localStorage.setItem(STORAGE_KEY, newTheme);
    } catch {
      // localStorage 不可用时静默处理
    }
  }, []);

  /**
   * 初始化主题:首次加载时应用
   */
  useEffect(() => {
    const saved = getSavedTheme();
    const initial = saved || getDefaultBySystem();
    document.documentElement.setAttribute('data-theme', initial);
    setThemeState(initial);
  }, []);

  /**
   * 监听系统主题变化,仅在用户未手动选择主题时自动切换
   */
  useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
    const handler = (e: MediaQueryListEvent) => {
      // 检查用户是否已手动设置过主题
      const hasUserPreference = localStorage.getItem(STORAGE_KEY) !== null;
      if (!hasUserPreference) {
        const newTheme = e.matches ? 'ocean-deep' : 'light-professional';
        setTheme(newTheme);
      }
    };
    mediaQuery.addEventListener('change', handler);
    return () => mediaQuery.removeEventListener('change', handler);
  }, [setTheme]);

  return { theme, setTheme, themes: THEMES };
}

4.7.3 主题预览组件设计

AppearanceSettings 页面需新增主题选择区域,包含五张主题预览卡片。每张卡片展示主题的核心色彩样本和简要描述。

// frontend/src/components/settings/ThemePreviewCard.tsx
import { Check } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { ThemeId } from '@/hooks/useTheme';

interface ThemePreviewCardProps {
  themeId: ThemeId;
  label: string;
  description: string;
  colors: {
    bg: string;
    secondary: string;
    accent: string;
    text: string;
  };
  isActive: boolean;
  onClick: () => void;
}

export function ThemePreviewCard({
  label,
  description,
  colors,
  isActive,
  onClick,
}: ThemePreviewCardProps) {
  return (
    <button
      onClick={onClick}
      className={cn(
        "relative flex flex-col gap-2 rounded-lg border-2 p-3 transition-all duration-200",
        "hover:shadow-md hover:-translate-y-0.5",
        isActive
          ? "border-dg-blue ring-2 ring-dg-blue/20"
          : "border-dg-border hover:border-dg-border-light"
      )}
      aria-pressed={isActive}
    >
      {/* 预览色块区域 */}
      <div
        className="h-16 w-full rounded-md flex items-end justify-end p-1.5 gap-1"
        style={{ backgroundColor: colors.bg }}
      >
        <div
          className="w-4 h-4 rounded-sm"
          style={{ backgroundColor: colors.secondary }}
        />
        <div
          className="w-4 h-4 rounded-sm"
          style={{ backgroundColor: colors.accent }}
        />
        <div
          className="w-4 h-4 rounded-sm"
          style={{ backgroundColor: colors.text }}
        />
      </div>

      {/* 文字信息 */}
      <div className="text-left">
        <p className="text-sm font-medium text-dg-text-primary">{label}</p>
        <p className="text-xs text-dg-text-muted">{description}</p>
      </div>

      {/* 选中标记 */}
      {isActive && (
        <div className="absolute -top-1.5 -right-1.5 w-5 h-5 bg-dg-blue rounded-full flex items-center justify-center">
          <Check className="w-3 h-3 text-white" />
        </div>
      )}
    </button>
  );
}

AppearanceSettings 页面中的主题选择区域布局:

// 在 AppearanceSettings 页面中使用
import { useTheme, THEMES } from '@/hooks/useTheme';
import { ThemePreviewCard } from '@/components/settings/ThemePreviewCard';

// 各主题预览色值配置
const THEME_PREVIEW_COLORS: Record<string, { bg: string; secondary: string; accent: string; text: string }> = {
  'ocean-deep':       { bg: '#070d19', secondary: '#0f172a', accent: '#3b82f6', text: '#f8fafc' },
  'light-professional': { bg: '#fafaf9', secondary: '#f5f5f4', accent: '#2563eb', text: '#1c1917' },
  'midnight-purple':  { bg: '#0a0612', secondary: '#130c21', accent: '#c084fc', text: '#faf5ff' },
  'pine-green':       { bg: '#0a1210', secondary: '#111f1a', accent: '#4ade80', text: '#f0fdf4' },
  'orange-vitality':  { bg: '#110d08', secondary: '#1d1610', accent: '#fb923c', text: '#fef7ed' },
};

function ThemeSelector() {
  const { theme: activeTheme, setTheme } = useTheme();

  return (
    <div className="space-y-3">
      <h3 className="text-sm font-medium text-dg-text-primary">界面主题</h3>
      <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3">
        {THEMES.map((t) => (
          <ThemePreviewCard
            key={t.id}
            themeId={t.id}
            label={t.label}
            description={t.description}
            colors={THEME_PREVIEW_COLORS[t.id]}
            isActive={activeTheme === t.id}
            onClick={() => setTheme(t.id)}
          />
        ))}
      </div>
    </div>
  );
}

4.7.4 localStorage 持久化方案

主题持久化遵循以下策略:

策略项 说明
存储键名 sqlp-theme-id,与现有 theme 键分离,避免升级冲突
存储值 主题标识字符串,如 ocean-deep、light-professional
回退逻辑 读取不到有效值时,检测 prefers-color-scheme 系统偏好,暗色默认 ocean-deep,亮色默认 light-professional
版本兼容 首次升级时,若检测到旧版 theme: "dark" 键,自动迁移至 ocean-deep;theme: "light" 迁移至 light-professional,随后删除旧键
异常处理 localStorage 不可用时(隐私模式、存储已满),降级为仅内存状态,不抛出错误
跨设备同步 通过 storage 事件监听同源其他标签页的主题变更,实现多标签页同步:
/**
 * 跨标签页主题同步
 */
useEffect(() => {
  const handler = (e: StorageEvent) => {
    if (e.key === STORAGE_KEY && e.newValue) {
      const newTheme = e.newValue as ThemeId;
      if (THEMES.some(t => t.id === newTheme)) {
        setThemeState(newTheme);
        document.documentElement.setAttribute('data-theme', newTheme);
      }
    }
  };
  window.addEventListener('storage', handler);
  return () => window.removeEventListener('storage', handler);
}, []);

4.7.5 系统主题偏好检测

平台需响应操作系统的 prefers-color-scheme 媒体查询,提供智能化的默认主题推荐。

检测逻辑:

// 检测系统暗色/浅色偏好
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const prefersLight = window.matchMedia('(prefers-color-scheme: light)').matches;

偏好映射规则:

系统偏好 默认推荐主题 推荐逻辑
prefers-color-scheme: dark 深蓝专业 (ocean-deep) 暗色系统偏好用户通常偏好深色界面
prefers-color-scheme: light 浅色商务 (light-professional) 亮色系统偏好用户通常偏好浅色界面
未指定(no-preference) 深蓝专业 (ocean-deep) 默认采用平台默认主题

动态响应机制:

/**
 * 系统主题偏好动态响应
 * 仅在用户未手动设置主题时自动跟随系统
 */
useEffect(() => {
  const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
  const lightQuery = window.matchMedia('(prefers-color-scheme: light)');

  const handleChange = () => {
    const hasUserPreference = localStorage.getItem(STORAGE_KEY) !== null;
    if (hasUserPreference) return; // 用户已手动选择,不自动切换

    if (darkQuery.matches) {
      setTheme('ocean-deep');
    } else if (lightQuery.matches) {
      setTheme('light-professional');
    }
  };

  darkQuery.addEventListener('change', handleChange);
  lightQuery.addEventListener('change', handleChange);

  return () => {
    darkQuery.removeEventListener('change', handleChange);
    lightQuery.removeEventListener('change', handleChange);
  };
}, [setTheme]);

重要原则:系统偏好仅影响首次访问或清除 localStorage 后的默认主题选择。一旦用户手动选择了主题,该选择将被持久化并优先于系统偏好,避免在用户主动选择后因系统时间变化(如 macOS 自动切换暗色模式)导致界面意外变更。

4.7.6 切换过渡动效

主题切换时应提供平滑的视觉过渡,避免色彩突变带来的不适感。

/* 全局主题过渡效果 —— 添加到 index.css */
html {
  /* 对所有 CSS 变量变化应用 200ms 缓动过渡 */
  transition: background-color 200ms ease, color 200ms ease;
}

html *, html *::before, html *::after {
  /* 谨慎使用:仅对关键属性过渡,避免性能问题 */
  transition: background-color 200ms ease,
              border-color 200ms ease,
              color 150ms ease;
}

/* 可选:切换瞬间的淡入效果 */
[data-theme] {
  animation: themeFadeIn 200ms ease;
}

@keyframes themeFadeIn {
  from { opacity: 0.92; }
  to   { opacity: 1; }
}

性能优化建议:全局 * 过渡规则可能影响大型列表渲染性能。生产环境中建议仅对需要过渡的元素添加过渡规则,或通过 JavaScript 在切换主题时临时添加过渡 class,切换完成后再移除:

/**
 * 带过渡动画的主题切换
 */
const setThemeWithTransition = useCallback((newTheme: ThemeId) => {
  const root = document.documentElement;
  root.classList.add('theme-transitioning');
  setTheme(newTheme);
  // 300ms 后移除过渡 class
  setTimeout(() => {
    root.classList.remove('theme-transitioning');
  }, 300);
}, [setTheme]);
/* 仅在切换时启用过渡 */
html.theme-transitioning,
html.theme-transitioning * {
  transition: background-color 200ms ease,
              border-color 200ms ease,
              color 150ms ease !important;
}

以上实现方案确保五套主题能够稳定、高效、优雅地在平台中运行,为用户提供丰富的个性化选择。