feat: 实现多主题系统,支持6套主题切换和设置页面
重构 ThemeContext 为多主题模型(themeId + followSystem + systemIsDark), 新增设置页面(主题下拉栏 + 跟随系统开关),移除旧 ThemeToggle 按钮, 引入 antd-style 和 clsx 依赖支持 MUI/shadcn/Bootstrap/玻璃主题。
This commit is contained in:
151
openspec/specs/theme-system/spec.md
Normal file
151
openspec/specs/theme-system/spec.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# 主题系统
|
||||
|
||||
## Purpose
|
||||
|
||||
提供多主题选择功能,支持 6 套主题切换、跟随系统暗色模式、主题注册表管理和设置页面。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 提供多主题选择功能
|
||||
|
||||
前端 SHALL 提供 6 套主题供用户选择:默认、暗黑、MUI、shadcn、Bootstrap、玻璃。
|
||||
|
||||
#### Scenario: 默认主题
|
||||
|
||||
- **WHEN** 用户首次访问应用且无持久化偏好
|
||||
- **THEN** 前端 SHALL 使用"默认"主题
|
||||
- **THEN** ConfigProvider SHALL 应用 `theme.defaultAlgorithm`
|
||||
|
||||
#### Scenario: 切换到暗黑主题
|
||||
|
||||
- **WHEN** 用户在设置页面选择"暗黑"主题
|
||||
- **THEN** 前端 SHALL 应用"暗黑"主题
|
||||
- **THEN** ConfigProvider SHALL 应用 `theme.darkAlgorithm`
|
||||
|
||||
#### Scenario: 切换到 MUI 主题
|
||||
|
||||
- **WHEN** 用户在设置页面选择"MUI"主题
|
||||
- **THEN** 前端 SHALL 应用 MUI 主题的完整配置(algorithm + token + 组件样式)
|
||||
|
||||
#### Scenario: 切换到 shadcn 主题
|
||||
|
||||
- **WHEN** 用户在设置页面选择"shadcn"主题
|
||||
- **THEN** 前端 SHALL 应用 shadcn 主题的完整配置
|
||||
|
||||
#### Scenario: 切换到 Bootstrap 主题
|
||||
|
||||
- **WHEN** 用户在设置页面选择"Bootstrap"主题
|
||||
- **THEN** 前端 SHALL 应用 Bootstrap 主题的完整配置
|
||||
|
||||
#### Scenario: 切换到玻璃主题
|
||||
|
||||
- **WHEN** 用户在设置页面选择"玻璃"主题
|
||||
- **THEN** 前端 SHALL 应用玻璃主题的完整配置
|
||||
|
||||
### Requirement: 提供跟随系统功能
|
||||
|
||||
前端 SHALL 提供跟随系统开关,开启后自动检测 OS 暗色模式状态。
|
||||
|
||||
#### Scenario: 开启跟随系统且 OS 处于暗色模式
|
||||
|
||||
- **WHEN** 用户开启"跟随系统"开关
|
||||
- **AND** OS 当前处于暗色模式
|
||||
- **THEN** 前端 SHALL 强制使用"暗黑"主题
|
||||
- **AND** 忽略用户在下拉栏选择的主题
|
||||
|
||||
#### Scenario: 开启跟随系统且 OS 处于亮色模式
|
||||
|
||||
- **WHEN** 用户开启"跟随系统"开关
|
||||
- **AND** OS 当前处于亮色模式
|
||||
- **THEN** 前端 SHALL 使用用户在下拉栏选择的主题
|
||||
|
||||
#### Scenario: 关闭跟随系统
|
||||
|
||||
- **WHEN** 用户关闭"跟随系统"开关
|
||||
- **THEN** 前端 SHALL 始终使用用户在下拉栏选择的主题
|
||||
- **AND** 忽略 OS 暗色模式状态
|
||||
|
||||
#### Scenario: OS 暗色模式实时变化
|
||||
|
||||
- **WHEN** "跟随系统"开关处于开启状态
|
||||
- **AND** OS 从亮色模式切换到暗色模式
|
||||
- **THEN** 前端 SHALL 自动切换到"暗黑"主题
|
||||
- **WHEN** OS 从暗色模式切换到亮色模式
|
||||
- **THEN** 前端 SHALL 自动恢复为用户选择的主题
|
||||
|
||||
### Requirement: 持久化主题偏好
|
||||
|
||||
前端 SHALL 使用 localStorage 持久化用户的主题偏好。
|
||||
|
||||
#### Scenario: 保存主题选择
|
||||
|
||||
- **WHEN** 用户选择主题
|
||||
- **THEN** 前端 SHALL 将主题 ID 保存到 localStorage 键 `nex-theme-id`
|
||||
- **THEN** 值 SHALL 为有效的主题标识符(`default`、`dark`、`mui`、`shadcn`、`bootstrap`、`glass`)
|
||||
|
||||
#### Scenario: 保存跟随系统偏好
|
||||
|
||||
- **WHEN** 用户切换跟随系统开关
|
||||
- **THEN** 前端 SHALL 将偏好保存到 localStorage 键 `nex-follow-system`
|
||||
- **THEN** 值 SHALL 为 `'true'` 或 `'false'`
|
||||
|
||||
#### Scenario: 恢复偏好
|
||||
|
||||
- **WHEN** 用户重新访问应用
|
||||
- **THEN** 前端 SHALL 从 localStorage 读取 `nex-theme-id` 和 `nex-follow-system`
|
||||
- **THEN** 如果 `nex-theme-id` 无效或不存在,SHALL 使用 `'default'`
|
||||
- **THEN** 如果 `nex-follow-system` 不存在,SHALL 使用 `false`
|
||||
|
||||
#### Scenario: 忽略旧键名
|
||||
|
||||
- **WHEN** localStorage 中存在旧键 `theme-mode`
|
||||
- **THEN** 前端 SHALL 忽略该值
|
||||
- **THEN** 前端 SHALL NOT 读取或迁移该键
|
||||
|
||||
### Requirement: 提供设置页面
|
||||
|
||||
前端 SHALL 提供设置页面,使用卡片布局组织设置内容。
|
||||
|
||||
#### Scenario: 主题卡片
|
||||
|
||||
- **WHEN** 用户访问设置页面
|
||||
- **THEN** 前端 SHALL 显示主题设置卡片
|
||||
- **THEN** 卡片内 SHALL 包含主题下拉栏和跟随系统开关
|
||||
|
||||
#### Scenario: 主题下拉栏
|
||||
|
||||
- **WHEN** 用户展开主题下拉栏
|
||||
- **THEN** 下拉栏 SHALL 列出所有 6 套主题:默认、暗黑、MUI、shadcn、Bootstrap、玻璃
|
||||
- **THEN** 当前选中的主题 SHALL 被高亮显示
|
||||
- **WHEN** 用户选择一个主题
|
||||
- **THEN** 前端 SHALL 立即切换到该主题
|
||||
|
||||
#### Scenario: 跟随系统开关
|
||||
|
||||
- **WHEN** 用户切换跟随系统开关
|
||||
- **THEN** 前端 SHALL 立即应用新的跟随系统设置
|
||||
- **THEN** 开关状态 SHALL 反映当前设置
|
||||
|
||||
#### Scenario: 侧边栏设置入口
|
||||
|
||||
- **WHEN** 渲染侧边栏菜单
|
||||
- **THEN** 菜单 SHALL 包含"设置"菜单项
|
||||
- **THEN** 菜单项 SHALL 使用 `SettingOutlined` 图标
|
||||
- **WHEN** 用户点击"设置"菜单项
|
||||
- **THEN** 前端 SHALL 导航到 `/settings` 路由
|
||||
|
||||
### Requirement: 使用主题注册表管理主题
|
||||
|
||||
前端 SHALL 使用主题注册表统一管理所有主题配置。
|
||||
|
||||
#### Scenario: 主题注册表结构
|
||||
|
||||
- **WHEN** 应用初始化
|
||||
- **THEN** 主题注册表 SHALL 包含 6 套主题的完整配置
|
||||
- **THEN** 每套主题 SHALL 通过 `useConfig()` hook 返回 `ConfigProviderProps`
|
||||
|
||||
#### Scenario: 主题文件组织
|
||||
|
||||
- **THEN** 主题文件 SHALL 位于 `src/themes/` 目录
|
||||
- **THEN** 每套主题 SHALL 有独立的配置文件
|
||||
- **THEN** `src/themes/index.ts` SHALL 导出注册表和类型定义
|
||||
@@ -2,85 +2,32 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
提供亮色/暗色模式切换功能,允许用户自定义界面主题并持久化偏好。
|
||||
管理主题状态与应用,使用 React Context 提供全局主题配置,支持多主题切换和持久化。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 提供主题切换功能
|
||||
|
||||
前端 SHALL 提供亮色/暗色模式切换功能,允许用户自定义界面主题。
|
||||
|
||||
#### Scenario: 初始加载默认主题
|
||||
|
||||
- **WHEN** 用户首次访问前端应用
|
||||
- **THEN** 前端 SHALL 使用亮色模式作为默认主题
|
||||
- **THEN** 前端 SHALL 应用 Ant Design defaultAlgorithm
|
||||
|
||||
#### Scenario: 切换到暗色模式
|
||||
|
||||
- **WHEN** 用户点击主题切换按钮(当前为亮色模式)
|
||||
- **THEN** 前端 SHALL 切换到暗色模式
|
||||
- **THEN** 前端 SHALL 应用 Ant Design darkAlgorithm
|
||||
- **THEN** 所有 Ant Design 组件 SHALL 自动应用暗色主题
|
||||
- **THEN** 主题切换按钮图标 SHALL 从月亮图标变为太阳图标
|
||||
|
||||
#### Scenario: 切换到亮色模式
|
||||
|
||||
- **WHEN** 用户点击主题切换按钮(当前为暗色模式)
|
||||
- **THEN** 前端 SHALL 切换到亮色模式
|
||||
- **THEN** 前端 SHALL 应用 Ant Design defaultAlgorithm
|
||||
- **THEN** 所有 Ant Design 组件 SHALL 自动应用亮色主题
|
||||
- **THEN** 主题切换按钮图标 SHALL 从太阳图标变为月亮图标
|
||||
|
||||
### Requirement: 持久化主题偏好
|
||||
|
||||
前端 SHALL 使用 localStorage 持久化用户的主题偏好。
|
||||
|
||||
#### Scenario: 保存主题偏好
|
||||
|
||||
- **WHEN** 用户切换主题
|
||||
- **THEN** 前端 SHALL 将主题模式保存到 localStorage
|
||||
- **THEN** localStorage 键名 SHALL 为 'theme-mode'
|
||||
- **THEN** 值 SHALL 为 'light' 或 'dark'
|
||||
- **WHEN** 用户选择主题或切换跟随系统开关
|
||||
- **THEN** 前端 SHALL 将主题 ID 保存到 localStorage 键 `nex-theme-id`
|
||||
- **THEN** 前端 SHALL 将跟随系统偏好保存到 localStorage 键 `nex-follow-system`
|
||||
|
||||
#### Scenario: 恢复主题偏好
|
||||
|
||||
- **WHEN** 用户重新访问前端应用
|
||||
- **THEN** 前端 SHALL 从 localStorage 读取 'theme-mode'
|
||||
- **THEN** 如果存在有效值,前端 SHALL 应用保存的主题
|
||||
- **THEN** 如果不存在或值无效,前端 SHALL 使用默认亮色模式
|
||||
- **THEN** 前端 SHALL 从 localStorage 读取 `nex-theme-id`
|
||||
- **THEN** 前端 SHALL 从 localStorage 读取 `nex-follow-system`
|
||||
- **THEN** 如果 `nex-theme-id` 不存在或无效,SHALL 使用 `'default'`
|
||||
- **THEN** 如果 `nex-follow-system` 不存在,SHALL 使用 `false`
|
||||
|
||||
#### Scenario: 刷新页面保持主题
|
||||
#### Scenario: 刷新页面保持设置
|
||||
|
||||
- **WHEN** 用户在暗色模式下刷新浏览器
|
||||
- **THEN** 前端 SHALL 保持暗色模式
|
||||
- **WHEN** 用户在亮色模式下刷新浏览器
|
||||
- **THEN** 前端 SHALL 保持亮色模式
|
||||
|
||||
### Requirement: 提供主题切换 UI
|
||||
|
||||
前端 SHALL 在侧边栏底部提供主题切换按钮。
|
||||
|
||||
#### Scenario: 显示主题切换按钮
|
||||
|
||||
- **WHEN** 渲染 AppLayout Sider
|
||||
- **THEN** 前端 SHALL 在 Sider 底部显示主题切换按钮
|
||||
- **THEN** 按钮 SHALL 使用 Ant Design Button 组件(type="text")
|
||||
- **THEN** 按钮图标 SHALL 根据当前主题显示(亮色模式显示月亮图标,暗色模式显示太阳图标)
|
||||
- **THEN** 按钮颜色 SHALL 在暗色侧边栏中保持可见(使用 `rgba(255, 255, 255, 0.85)` 或等效浅色)
|
||||
|
||||
#### Scenario: 主题切换按钮交互
|
||||
|
||||
- **WHEN** 用户点击主题切换按钮
|
||||
- **THEN** 前端 SHALL 切换主题模式
|
||||
- **THEN** 按钮 SHALL 显示 Tooltip 提示("切换到暗色模式"或"切换到亮色模式")
|
||||
|
||||
#### Scenario: 侧边栏折叠时的主题切换
|
||||
|
||||
- **WHEN** 侧边栏处于折叠状态
|
||||
- **THEN** 主题切换按钮 SHALL 保持可见且可点击
|
||||
- **THEN** 按钮 SHALL 仅显示图标(Tooltip 保持可用)
|
||||
- **THEN** 按钮颜色 SHALL 保持可见
|
||||
- **WHEN** 用户在任意主题和跟随系统设置下刷新浏览器
|
||||
- **THEN** 前端 SHALL 保持用户的选择
|
||||
|
||||
### Requirement: 使用 React Context 管理主题状态
|
||||
|
||||
@@ -89,17 +36,28 @@
|
||||
#### Scenario: 主题 Context 提供
|
||||
|
||||
- **WHEN** 应用初始化
|
||||
- **THEN** 前端 SHALL 创建 ThemeContext
|
||||
- **THEN** ThemeContext SHALL 提供 mode(当前主题模式)
|
||||
- **THEN** ThemeContext SHALL 提供 toggleTheme(切换主题方法)
|
||||
- **THEN** ThemeContext SHALL 提供 setTheme(设置主题方法)
|
||||
- **THEN** ThemeContext SHALL 提供 `themeId`(当前选择的主题 ID)
|
||||
- **THEN** ThemeContext SHALL 提供 `followSystem`(跟随系统开关状态)
|
||||
- **THEN** ThemeContext SHALL 提供 `systemIsDark`(OS 暗色模式状态)
|
||||
- **THEN** ThemeContext SHALL 提供 `effectiveThemeId`(计算后的实际主题 ID)
|
||||
- **THEN** ThemeContext SHALL 提供 `setThemeId`(设置主题方法)
|
||||
- **THEN** ThemeContext SHALL 提供 `setFollowSystem`(设置跟随系统方法)
|
||||
|
||||
#### Scenario: 主题 Context 使用
|
||||
|
||||
- **WHEN** 组件需要访问或修改主题
|
||||
- **THEN** 组件 SHALL 使用 useTheme hook
|
||||
- **THEN** useTheme SHALL 返回当前主题状态和操作方法
|
||||
- **THEN** 在 ThemeProvider 外使用 useTheme SHALL 抛出错误
|
||||
- **THEN** 组件 SHALL 使用 `useTheme` hook
|
||||
- **THEN** `useTheme` SHALL 返回当前主题状态和操作方法
|
||||
- **THEN** 在 ThemeProvider 外使用 `useTheme` SHALL 抛出错误
|
||||
|
||||
#### Scenario: 计算有效主题
|
||||
|
||||
- **WHEN** `followSystem` 为 `true` 且 `systemIsDark` 为 `true`
|
||||
- **THEN** `effectiveThemeId` SHALL 为 `'dark'`
|
||||
- **WHEN** `followSystem` 为 `true` 且 `systemIsDark` 为 `false`
|
||||
- **THEN** `effectiveThemeId` SHALL 为 `themeId`
|
||||
- **WHEN** `followSystem` 为 `false`
|
||||
- **THEN** `effectiveThemeId` SHALL 为 `themeId`
|
||||
|
||||
### Requirement: 集成 Ant Design ConfigProvider
|
||||
|
||||
@@ -109,11 +67,11 @@
|
||||
|
||||
- **WHEN** 渲染应用
|
||||
- **THEN** 前端 SHALL 使用 ConfigProvider 包裹应用
|
||||
- **THEN** ConfigProvider theme.algorithm SHALL 根据当前主题设置(defaultAlgorithm 或 darkAlgorithm)
|
||||
- **THEN** ConfigProvider 的 theme 属性 SHALL 由当前 `effectiveThemeId` 对应的主题配置决定
|
||||
- **THEN** 配置 SHALL 包含完整的 `algorithm`、`token` 和组件样式覆盖
|
||||
|
||||
#### Scenario: 组件自动应用主题
|
||||
|
||||
- **WHEN** 主题切换
|
||||
- **THEN** 所有 Ant Design 组件 SHALL 自动应用新主题
|
||||
- **THEN** 组件 SHALL 无需手动样式调整
|
||||
- **THEN** 主题切换 SHALL 平滑过渡,无闪烁
|
||||
|
||||
Reference in New Issue
Block a user