feat: Dashboard 主题模式切换 — 系统跟随/明亮/黑暗,localStorage 持久化,TDesign theme-mode 驱动
新增 useThemePreference hook 和纯工具函数,支持系统/明亮/黑暗三态主题选择、 matchMedia 系统主题跟随、localStorage 持久化和启动期主题预应用,通过 <html theme-mode> 驱动 TDesign 主题变量切换。 Header 右侧控件重新组织为 .dashboard-header-controls 单行桌面布局,主题 RadioGroup 位于刷新频率 RadioGroup 前。 附带:build.ts import specifier 改为跨平台 sep 转换;config-loader 测试适配 Windows PATH 和 YAML 路径转义;test-utils 类型窄化修复。
This commit is contained in:
@@ -73,9 +73,13 @@ styles.css SHALL 定义前端组件复用的工具类,包含页面布局相关
|
||||
- **WHEN** HeadMenu logo 区域渲染品牌名和副标题
|
||||
- **THEN** 品牌 SHALL 使用 `.dashboard-brand` 类(display: inline-flex; align-items: baseline; gap: var(--td-comp-margin-s)),品牌名 SHALL 使用 `.dashboard-logo` 类(font-size: calc(var(--td-font-size-title-large) + 6px); font-weight: 700),副标题 SHALL 使用 `.dashboard-subtitle` 类(font-size: var(--td-font-size-body-medium); color: var(--td-text-color-secondary))
|
||||
|
||||
#### Scenario: 刷新控制区域类
|
||||
- **WHEN** HeadMenu operations 区域渲染刷新频率选择器和倒计时/按钮
|
||||
- **THEN** 容器 SHALL 使用 `.dashboard-refresh-control` 类(display: inline-flex; align-items: center; gap: var(--td-comp-margin-s); margin-right: var(--td-comp-margin-xxl))
|
||||
#### Scenario: Header 右侧操作区类
|
||||
- **WHEN** HeadMenu operations 区域渲染主题模式选择器、刷新频率选择器和倒计时/按钮
|
||||
- **THEN** 容器 SHALL 使用 `.dashboard-header-controls` 类(display: inline-flex; align-items: center; gap: var(--td-comp-margin-s); margin-right: var(--td-comp-margin-xxl))
|
||||
|
||||
#### Scenario: Header 右侧操作区单行布局
|
||||
- **WHEN** Header 右侧操作区渲染
|
||||
- **THEN** `.dashboard-header-controls` SHALL 保持桌面单行水平布局,不为该区域新增窄屏换行或收纳规则
|
||||
|
||||
#### Scenario: 倒计时文本类
|
||||
- **WHEN** 倒计时文本或刷新按钮渲染
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Purpose
|
||||
|
||||
定义 Dashboard 页面骨架布局:顶部导航栏(含品牌标识和刷新频率选择器/倒计时控件)、内容区域居中与最大宽度、页面背景色。
|
||||
定义 Dashboard 页面骨架布局:顶部导航栏(含品牌标识、主题模式选择器、刷新频率选择器和倒计时控件)、内容区域居中与最大宽度、页面背景色。
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -13,13 +13,17 @@ Dashboard SHALL 使用 TDesign Layout 组件体系构建页面骨架,包含顶
|
||||
|
||||
#### Scenario: 顶部导航栏
|
||||
- **WHEN** Dashboard 页面渲染
|
||||
- **THEN** `Layout.Header` SHALL 内嵌 TDesign `HeadMenu` 组件,`logo` prop 渲染品牌名 "DiAL" 和副标题 "统一拨测平台"(水平排列),`operations` prop 渲染刷新频率选择器和倒计时/刷新按钮组合控件
|
||||
- **THEN** `Layout.Header` SHALL 内嵌 TDesign `HeadMenu` 组件,`logo` prop 渲染品牌名 "DiAL" 和副标题 "统一拨测平台"(水平排列),`operations` prop 渲染主题模式选择器、刷新频率选择器和倒计时/刷新按钮组合控件
|
||||
|
||||
#### Scenario: 刷新控制区域
|
||||
#### Scenario: Header 右侧操作区
|
||||
- **WHEN** Dashboard 页面渲染
|
||||
- **THEN** HeadMenu operations 区域 SHALL 包含 RadioGroup 刷新频率选择器和倒计时文本(或手动刷新按钮),两者水平排列并垂直居中
|
||||
- **THEN** HeadMenu operations 区域 SHALL 包含主题模式 RadioGroup、刷新频率 RadioGroup 和倒计时文本(或手动刷新按钮),三者水平排列并垂直居中
|
||||
|
||||
#### Scenario: 刷新控制区域位置
|
||||
#### Scenario: 主题选择器位置
|
||||
- **WHEN** HeadMenu operations 区域渲染
|
||||
- **THEN** 主题模式 RadioGroup SHALL 位于刷新频率 RadioGroup 前面
|
||||
|
||||
#### Scenario: Header 右侧操作区位置
|
||||
- **WHEN** HeadMenu 渲染
|
||||
- **THEN** operations 区域 SHALL 使用右侧 margin 向内收缩,避免紧贴浏览器右边缘
|
||||
|
||||
@@ -29,4 +33,4 @@ Dashboard SHALL 使用 TDesign Layout 组件体系构建页面骨架,包含顶
|
||||
|
||||
#### Scenario: 页面背景色
|
||||
- **WHEN** Dashboard 页面渲染
|
||||
- **THEN** 页面背景色 SHALL 使用 `var(--td-bg-color-page)`,内容卡片浮于浅灰背景之上
|
||||
- **THEN** 页面背景色 SHALL 使用 `var(--td-bg-color-page)`,内容卡片浮于当前 TDesign 主题背景之上
|
||||
|
||||
73
openspec/specs/theme-mode-preference/spec.md
Normal file
73
openspec/specs/theme-mode-preference/spec.md
Normal file
@@ -0,0 +1,73 @@
|
||||
## Purpose
|
||||
|
||||
定义 Dashboard 主题模式选择、系统主题跟随、浏览器本地持久化和 TDesign 主题变量应用行为。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 主题模式选择器
|
||||
Dashboard SHALL 在 Header 右侧提供主题模式 RadioGroup,允许用户选择"系统""明亮""黑暗"三种模式。
|
||||
|
||||
#### Scenario: 主题模式选项渲染
|
||||
- **WHEN** Dashboard 页面渲染
|
||||
- **THEN** HeadMenu operations 区域 SHALL 在刷新频率选择器前显示 RadioGroup(theme="button", variant="default-filled"),选项为:系统、明亮、黑暗
|
||||
|
||||
#### Scenario: 默认选择系统
|
||||
- **WHEN** 当前浏览器没有已保存的有效主题偏好
|
||||
- **THEN** 主题模式 RadioGroup SHALL 默认选中"系统"
|
||||
|
||||
#### Scenario: 用户切换主题模式
|
||||
- **WHEN** 用户点击"系统""明亮"或"黑暗"任一主题模式选项
|
||||
- **THEN** RadioGroup SHALL 选中该选项,并触发对应主题模式生效
|
||||
|
||||
### Requirement: 主题模式生效
|
||||
系统 SHALL 根据用户主题偏好计算有效主题,并通过 `<html>` 元素的 `theme-mode` 属性应用 TDesign 主题变量。
|
||||
|
||||
#### Scenario: 系统模式跟随暗色系统
|
||||
- **WHEN** 用户主题偏好为"系统"且 `prefers-color-scheme: dark` 匹配
|
||||
- **THEN** 系统 SHALL 设置 `document.documentElement` 的 `theme-mode` 属性为 `dark`
|
||||
|
||||
#### Scenario: 系统模式跟随亮色系统
|
||||
- **WHEN** 用户主题偏好为"系统"且 `prefers-color-scheme: dark` 不匹配
|
||||
- **THEN** 系统 SHALL 设置 `document.documentElement` 的 `theme-mode` 属性为 `light`
|
||||
|
||||
#### Scenario: 系统主题变化自动更新
|
||||
- **WHEN** 用户主题偏好为"系统"且浏览器系统主题在明亮和黑暗之间变化
|
||||
- **THEN** 系统 SHALL 自动更新 `theme-mode` 属性为新的有效主题
|
||||
|
||||
#### Scenario: 明亮模式固定主题
|
||||
- **WHEN** 用户主题偏好为"明亮"
|
||||
- **THEN** 系统 SHALL 设置 `theme-mode` 属性为 `light`,且系统主题变化 SHALL NOT 改变该属性
|
||||
|
||||
#### Scenario: 黑暗模式固定主题
|
||||
- **WHEN** 用户主题偏好为"黑暗"
|
||||
- **THEN** 系统 SHALL 设置 `theme-mode` 属性为 `dark`,且系统主题变化 SHALL NOT 改变该属性
|
||||
|
||||
### Requirement: 主题偏好本地持久化
|
||||
系统 SHALL 将用户选择的主题偏好保存到当前浏览器本地存储,并在后续页面加载时恢复。
|
||||
|
||||
#### Scenario: 保存用户选择
|
||||
- **WHEN** 用户切换主题模式
|
||||
- **THEN** 系统 SHALL 将对应偏好值写入 `localStorage` 的 `dial.theme.preference` 键
|
||||
|
||||
#### Scenario: 恢复已保存偏好
|
||||
- **WHEN** 页面加载且 `localStorage` 的 `dial.theme.preference` 键保存了有效偏好值
|
||||
- **THEN** 系统 SHALL 使用该偏好初始化主题模式 RadioGroup 和有效主题
|
||||
|
||||
#### Scenario: 非法本地偏好回退
|
||||
- **WHEN** 页面加载且 `dial.theme.preference` 保存了非 `system`、`light`、`dark` 的值
|
||||
- **THEN** 系统 SHALL 忽略该值并按"系统"模式初始化
|
||||
|
||||
#### Scenario: 本地存储不可用
|
||||
- **WHEN** 浏览器读取或写入 `localStorage` 抛出异常
|
||||
- **THEN** Dashboard SHALL 继续正常渲染,并按内存中的主题偏好应用主题
|
||||
|
||||
### Requirement: 启动期主题恢复
|
||||
系统 SHALL 在 React App 首次渲染前尽早应用一次有效主题,降低暗色环境下的亮色闪烁。
|
||||
|
||||
#### Scenario: 渲染前应用已保存偏好
|
||||
- **WHEN** 前端入口初始化且浏览器已保存有效主题偏好
|
||||
- **THEN** 系统 SHALL 在创建 React root 前根据该偏好设置 `<html>` 的 `theme-mode` 属性
|
||||
|
||||
#### Scenario: 渲染前应用系统偏好
|
||||
- **WHEN** 前端入口初始化且浏览器没有有效主题偏好
|
||||
- **THEN** 系统 SHALL 在创建 React root 前根据 `prefers-color-scheme: dark` 设置 `<html>` 的 `theme-mode` 属性
|
||||
Reference in New Issue
Block a user