# 暗黑模式 Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans to implement this plan task-by-task. **Goal:** 为企迹系统新增双选暗黑模式:用户在设置页分别选择亮色/暗色偏好主题,顶部栏一键切换亮↔暗。 **Architecture:** 扩展 theme store 支持 mode(light/dark) + lightTheme + darkTheme 三字段,新增 minimal-dark/tech-dark 两套 CSS 变量,Element Plus 通过 `html.dark` class 适配暗色。 **Tech Stack:** Vue 3 + Pinia + TypeScript + Element Plus + CSS Custom Properties ## Global Constraints - 四大主题: editorial(已有), light(已有), minimal-dark(新增), tech-dark(新增) - localStorage 持久化: mode, lightTheme, darkTheme 三项 - 暗色模式时 `` 同时设置 `data-theme` 和 `class="dark"` - 移动端暂不加主题切换(仅 PC 端顶部栏按钮) - 保持现有 editorial/light 的 CSS 变量不变 --- ### Task 1: 改造 Theme Store **Files:** - Modify: `frontend/src/stores/theme.ts` **Interfaces:** - Produces: `useThemeStore()` with `mode`, `lightTheme`, `darkTheme`, `effectiveTheme`, `toggleMode()`, `setLightTheme(t)`, `setDarkTheme(t)`, `applyTheme(t)` - Theme type: `'editorial' | 'light' | 'minimal-dark' | 'tech-dark'` - [ ] **Step 1: 重写 theme store** ```typescript import { defineStore } from 'pinia' import { ref, computed } from 'vue' export type Theme = 'editorial' | 'light' | 'minimal-dark' | 'tech-dark' type Mode = 'light' | 'dark' export const themeLabels: Record = { editorial: '编辑风', light: '极简亮色', 'minimal-dark': '极简深色', 'tech-dark': '科技深色', } export const lightThemes: Theme[] = ['editorial', 'light'] export const darkThemes: Theme[] = ['minimal-dark', 'tech-dark'] export const useThemeStore = defineStore('theme', () => { const mode = ref((localStorage.getItem('theme-mode') as Mode) || 'light') const lightTheme = ref((localStorage.getItem('theme-light') as Theme) || 'editorial') const darkTheme = ref((localStorage.getItem('theme-dark') as Theme) || 'tech-dark') const effectiveTheme = computed(() => { return mode.value === 'light' ? lightTheme.value : darkTheme.value }) function applyTheme(theme: Theme, isDark: boolean) { document.documentElement.setAttribute('data-theme', theme) if (isDark) { document.documentElement.classList.add('dark') } else { document.documentElement.classList.remove('dark') } } function apply() { applyTheme(effectiveTheme.value, mode.value === 'dark') } function setLightTheme(theme: Theme) { lightTheme.value = theme localStorage.setItem('theme-light', theme) if (mode.value === 'light') apply() } function setDarkTheme(theme: Theme) { darkTheme.value = theme localStorage.setItem('theme-dark', theme) if (mode.value === 'dark') apply() } function toggleMode() { mode.value = mode.value === 'light' ? 'dark' : 'light' localStorage.setItem('theme-mode', mode.value) apply() } // Apply on init apply() return { mode, lightTheme, darkTheme, effectiveTheme, setLightTheme, setDarkTheme, toggleMode } }) ``` - [ ] **Step 2: Commit** ```bash git add frontend/src/stores/theme.ts git commit -m "feat: 改造 theme store 支持暗黑模式双选架构" ``` --- ### Task 2: 新增两套暗色 CSS 变量 **Files:** - Modify: `frontend/src/App.vue` (在 `:root[data-theme="light"]` 块之后添加两个新块) **Interfaces:** - Consumes: Theme type `'minimal-dark'`, `'tech-dark'` from store - Produces: CSS variables for both dark themes + Element Plus dark overrides - [ ] **Step 1: 添加 minimal-dark 主题 CSS** 在 `:root[data-theme="light"]` 块结束后(第178行 `}` 之后),插入: ```css /* ═══════════════════════════════════════════════════════════ THEME: Minimal Dark — 极简深色 ═══════════════════════════════════════════════════════════ */ :root[data-theme="minimal-dark"] { /* ── Core palette ── */ --ink: #E5E5E5; --ink-light: #CCCCCC; --ink-dark: #FFFFFF; --vermilion: #F87171; --vermilion-light: #FCA5A5; --vermilion-dark: #EF4444; --gold: #A78BFA; --gold-light: #C4B5FD; --gold-dark: #8B5CF6; --paper: #1A1A1A; --paper-dark: #141414; --surface: #2D2D2D; --sage: #4ADE80; --amber: #FBBF24; --warm-gray: #999999; --warm-border: #3A3A3A; /* ── Semantic tokens ── */ --c-primary: var(--ink); --c-primary-light: var(--ink-light); --c-primary-bg: #262626; --c-accent: var(--vermilion); --c-accent-light: var(--vermilion-light); --c-gold: var(--gold); --c-success: var(--sage); --c-warning: var(--amber); --c-danger: var(--vermilion); --c-info: #60A5FA; --c-text: #E5E5E5; --c-text-secondary: #999999; --c-text-muted: #666666; --c-bg: var(--paper); --c-bg-card: var(--surface); --c-border: var(--warm-border); /* ── Effects ── */ --shadow-sm: 0 1px 2px rgba(0,0,0,0.20); --shadow: 0 1px 3px rgba(0,0,0,0.30); --shadow-md: 0 2px 6px rgba(0,0,0,0.40); --shadow-lg: 0 4px 12px rgba(0,0,0,0.50); --radius: 0px; --radius-sm: 0px; --radius-xs: 0px; --transition: 0.18s cubic-bezier(0.4, 0, 0.2, 1); /* ── Sidebar — dark minimal ── */ --sidebar-bg: #141414; --sidebar-color: #E5E5E5; --sidebar-accent: var(--gold); --sidebar-nav-text: #777777; --sidebar-nav-text-hover: #CCCCCC; --sidebar-nav-text-active: #FFFFFF; --sidebar-nav-bg-hover: rgba(255,255,255,0.04); --sidebar-nav-bg-active: rgba(255,255,255,0.06); --sidebar-nav-border-active: var(--gold); --sidebar-divider: #2A2A2A; --sidebar-brand-color: #FFFFFF; --sidebar-brand-sub-color: #666666; --sidebar-group-label-color: #555555; --sidebar-footer-role-color: var(--gold); --sidebar-footer-name-color: #999999; /* ── Mobile page accent ── */ --page-accent-color: rgba(167,139,250,0.06); /* ── Manager chip tags ── */ --mgr-chip-text: #E5E5E5; /* ── Fonts — sans-serif minimal ── */ --font-body: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif; --font-heading: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif; --font-mono: 'JetBrains Mono', monospace; } ``` - [ ] **Step 2: 添加 tech-dark 主题 CSS** 在 minimal-dark 块之后插入: ```css /* ═══════════════════════════════════════════════════════════ THEME: Tech Dark — 科技深色 (编辑风暗色版) ═══════════════════════════════════════════════════════════ */ :root[data-theme="tech-dark"] { /* ── Core palette ── */ --ink: #C8D6E5; --ink-light: #A0B4CC; --ink-dark: #E8EEF4; --vermilion: #F26B6B; --vermilion-light: #F59898; --vermilion-dark: #E55555; --gold: #5B8DEF; --gold-light: #7BA5F5; --gold-dark: #4070D0; --paper: #0B1120; --paper-dark: #080C18; --surface: #111B2E; --sage: #4EC9B0; --amber: #E5C07B; --warm-gray: #6B7A8D; --warm-border: #1E2D45; /* ── Semantic tokens ── */ --c-primary: var(--ink); --c-primary-light: var(--ink-light); --c-primary-bg: #131D30; --c-accent: var(--vermilion); --c-accent-light: var(--vermilion-light); --c-gold: var(--gold); --c-success: var(--sage); --c-warning: var(--amber); --c-danger: var(--vermilion); --c-info: #5B8DEF; --c-text: #C8D6E5; --c-text-secondary: #6B7A8D; --c-text-muted: #455570; --c-bg: var(--paper); --c-bg-card: var(--surface); --c-border: var(--warm-border); /* ── Effects ── */ --shadow-sm: 0 1px 2px rgba(0,0,0,0.25); --shadow: 0 2px 6px rgba(0,0,0,0.35); --shadow-md: 0 6px 18px rgba(0,0,0,0.45); --shadow-lg: 0 12px 36px rgba(0,0,0,0.55); --radius: 2px; --radius-sm: 2px; --radius-xs: 2px; --transition: 0.22s cubic-bezier(0.4, 0, 0.2, 1); /* ── Sidebar — dark tech ── */ --sidebar-bg: #070D1A; --sidebar-color: #C8D6E5; --sidebar-accent: var(--gold); --sidebar-nav-text: rgba(200,214,229,0.45); --sidebar-nav-text-hover: rgba(200,214,229,0.80); --sidebar-nav-text-active: #FFFFFF; --sidebar-nav-bg-hover: rgba(91,141,239,0.06); --sidebar-nav-bg-active: rgba(91,141,239,0.10); --sidebar-nav-border-active: var(--gold); --sidebar-divider: rgba(200,214,229,0.06); --sidebar-brand-color: #FFFFFF; --sidebar-brand-sub-color: rgba(200,214,229,0.35); --sidebar-group-label-color: rgba(200,214,229,0.20); --sidebar-footer-role-color: var(--gold); --sidebar-footer-name-color: rgba(200,214,229,0.65); /* ── Mobile page accent ── */ --page-accent-color: rgba(91,141,239,0.06); /* ── Manager chip tags ── */ --mgr-chip-text: #C8D6E5; /* ── Fonts — editorial Chinese ── */ --font-body: 'Noto Serif SC', STSong, Songti SC, '宋体', serif; --font-heading: 'ZCOOL XiaoWei', STSong, Songti SC, serif; --font-mono: 'JetBrains Mono', 'SF Mono', monospace; } ``` - [ ] **Step 3: Commit** ```bash git add frontend/src/App.vue git commit -m "feat: 新增 minimal-dark 和 tech-dark 两套暗色 CSS 变量" ``` --- ### Task 3: 改造 DesktopLayout 顶部栏按钮 **Files:** - Modify: `frontend/src/components/DesktopLayout.vue` (script 和 template 中的主题切换按钮) **Interfaces:** - Consumes: `useThemeStore()` — `mode`, `toggleMode()` - [ ] **Step 1: 替换主题按钮为亮/暗切换** 将 `cycleTheme` 函数替换为 `toggleMode`,按钮改为 sun/moon 图标切换: ```typescript // 替换 cycleTheme 函数: function toggleDarkMode() { themeStore.toggleMode() } ``` Template 中替换按钮: ```html ``` - [ ] **Step 2: 触发构建验证** ```bash cd frontend && npm run build -- --emptyOutDir ``` - [ ] **Step 3: Commit** ```bash git add frontend/src/components/DesktopLayout.vue git commit -m "feat: 桌面端顶部栏改为亮/暗模式切换按钮" ``` --- ### Task 4: 改造设置页面主题面板 **Files:** - Modify: `frontend/src/views/desktop/Settings.vue` (替换 Theme 卡片为双选下拉) **Interfaces:** - Consumes: `useThemeStore()` — `lightTheme`, `darkTheme`, `mode`, `setLightTheme()`, `setDarkTheme()` - [ ] **Step 1: 替换主题选择区域** Replace the el-card template section (lines 158-173) and the `themeLabels` import: **Script 部分修改 import:** ```typescript import { useThemeStore, themeLabels, lightThemes, darkThemes, type Theme } from '@/stores/theme' ``` **Template 部分替换主题卡片:** ```html
☀️ 亮色模式主题 当前
🌙 暗色模式主题 当前
``` **Style 部分替换 `.theme-options` 样式:** ```css /* ── Theme Config ── */ .theme-config { display: flex; flex-direction: column; gap: 12px; } .theme-row { display: flex; align-items: center; gap: 12px; } .theme-label { font-family: var(--font-body); font-size: 14px; color: var(--c-text); min-width: 140px; } .theme-badge { font-size: 11px; padding: 2px 8px; border-radius: 2px; font-family: var(--font-mono); } .theme-badge--active { background: var(--c-primary); color: #fff; } ``` - [ ] **Step 2: 触发构建验证** ```bash cd frontend && npm run build -- --emptyOutDir ``` - [ ] **Step 3: Commit** ```bash git add frontend/src/views/desktop/Settings.vue git commit -m "feat: 设置页改为亮/暗双选主题配置面板" ``` --- ### Task 5: 验证与收尾 - [ ] **Step 1: 完整构建** ```bash cd frontend && npm run build ``` - [ ] **Step 2: 检查 localStorage 兼容性** 验证旧 `localStorage.setItem('theme', ...)` 数据不会导致新系统崩溃。在 theme store 的 `apply()` 中已有兜底逻辑。 - [ ] **Step 3: 快速手动测试检查清单** - [ ] 默认加载为 editorial 亮色 - [ ] 点击顶部栏 ☀️→🌙 切换到 tech-dark - [ ] 设置页切换亮色主题为 light,暗色主题为 minimal-dark - [ ] 点击顶部栏按钮在两主题间切换 - [ ] 刷新页面偏好保持 - [ ] Element Plus 组件(表格/对话框/按钮)在暗色下正常 - [ ] **Step 4: Commit** ```bash git add -A git commit -m "chore: 暗黑模式最终验证通过" ```