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
29 KiB
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;
}
以上实现方案确保五套主题能够稳定、高效、优雅地在平台中运行,为用户提供丰富的个性化选择。