## Purpose 定义 Header 刷新频率选择器组件的交互行为:频率切换、倒计时显示、手动刷新按钮、布局稳定性。 ## Requirements ### Requirement: 刷新频率选择器 HeadMenu operations 区域 SHALL 提供 RadioGroup 组件供用户选择刷新频率。 #### Scenario: RadioGroup 渲染 - **WHEN** Dashboard 页面渲染 - **THEN** HeadMenu operations 区域 SHALL 显示 RadioGroup(theme="button", variant="default-filled"),选项为:手动、10秒、30秒、1分钟、5分钟 #### Scenario: 默认选中 - **WHEN** 页面首次加载 - **THEN** RadioGroup SHALL 默认选中"30秒" #### Scenario: 切换频率立即刷新 - **WHEN** 用户切换刷新频率选项 - **THEN** 系统 SHALL 立即触发一次数据刷新,然后应用新的刷新间隔 ### Requirement: 倒计时显示 RadioGroup 右侧 SHALL 显示距下次自动刷新的倒计时。倒计时逻辑 SHALL 封装在独立的 `RefreshCountdown` 组件中,App 组件 SHALL NOT 持有每秒更新的 `now` state。自动倒计时数字 SHALL 使用 `@number-flow/react` 提供滚动过渡,非倒计时状态 SHALL 保持普通文本或按钮语义。 #### Scenario: RefreshCountdown 组件封装 - **WHEN** Dashboard 页面渲染 - **THEN** 倒计时显示 SHALL 由独立的 `RefreshCountdown` 组件负责,该组件内部持有 `now` state 和每秒 `setInterval`,渲染边界限制在该组件内部 #### Scenario: RefreshCountdown props - **WHEN** RefreshCountdown 组件渲染 - **THEN** 组件 SHALL 接收 `dashboardUpdatedAt: number`、`refreshInterval: number`、`isFetching: boolean`、`isManualRefresh: boolean`、`onRefresh: () => void` 作为 props #### Scenario: NumberFlow 数字滚动 - **WHEN** 自动刷新模式下已完成首次刷新且当前未处于刷新中状态 - **THEN** 倒计时数字 SHALL 使用 `@number-flow/react` 的 `NumberFlow` 渲染,并使用向下滚动趋势表达倒计时递减 #### Scenario: 秒级间隔格式 - **WHEN** 自动刷新间隔小于 60 秒 - **THEN** 倒计时 SHALL 显示为"xx秒"格式(如"26秒") #### Scenario: 分钟级稳定格式 - **WHEN** 自动刷新间隔大于等于 60 秒 - **THEN** 倒计时 SHALL 显示为"x分xx秒"格式,秒数 SHALL 固定为两位(如"4分30秒"、"0分09秒") #### Scenario: 时间数字边界 - **WHEN** 分钟级倒计时中的秒数在 59 到 00 边界变化 - **THEN** 秒数十位 SHALL 按时间显示规则限制在 0 到 5 之间滚动 #### Scenario: 无前缀 - **WHEN** 倒计时显示 - **THEN** 可见倒计时文本 SHALL 不包含任何前缀(如"下一次刷新:"),直接显示时间 #### Scenario: 可访问文本 - **WHEN** NumberFlow 倒计时渲染 - **THEN** 倒计时容器 SHALL 暴露与当前倒计时等价的可访问文本,供测试和辅助技术读取 #### Scenario: 刷新中状态 - **WHEN** 数据正在刷新(isFetching=true 且 isLoading=false) - **THEN** 倒计时文本 SHALL 显示为"刷新中..." #### Scenario: 等待首次刷新状态 - **WHEN** 自动刷新模式下尚未完成首次刷新 - **THEN** 倒计时文本 SHALL 显示为"等待首次刷新" ### Requirement: App 组件渲染隔离 App 组件 SHALL NOT 持有任何高频更新的 state(如每秒更新的时钟),确保 App 的重渲染频率与数据刷新频率一致(默认 30 秒一次)。 #### Scenario: App 无 now state - **WHEN** App 组件渲染 - **THEN** App SHALL NOT 包含 `useState` 管理的时钟 state,也 SHALL NOT 包含每秒触发的 `setInterval` #### Scenario: App 重渲染频率 - **WHEN** Dashboard 处于自动刷新模式 - **THEN** App 组件的重渲染 SHALL 仅由 TanStack Query 的 refetch 触发(频率等于用户选择的刷新间隔),而非每秒触发 ### Requirement: 手动刷新按钮 选择"手动"模式时,倒计时区域 SHALL 替换为刷新按钮。 #### Scenario: 手动模式显示按钮 - **WHEN** 用户选择"手动"刷新频率 - **THEN** 倒计时区域 SHALL 替换为刷新图标按钮 #### Scenario: 点击刷新 - **WHEN** 用户点击刷新按钮 - **THEN** 系统 SHALL 触发一次数据刷新 #### Scenario: 刷新中禁用 - **WHEN** 数据正在刷新 - **THEN** 刷新按钮 SHALL 显示 loading 状态且 disabled,防止连续点击 ### Requirement: 布局稳定性 倒计时/按钮容器 SHALL 保持布局稳定,避免内容变化导致的抖动。NumberFlow 倒计时 SHALL 通过分组同步和等宽数字样式降低位数、单位和动画变化带来的布局偏移。 #### Scenario: 数字等宽 - **WHEN** 倒计时数字变化 - **THEN** 容器和 NumberFlow 倒计时 SHALL 使用 tabular-nums 字体特性,确保数字等宽不抖动 #### Scenario: NumberFlow 分组同步 - **WHEN** 分钟级倒计时同时渲染分钟和秒数 - **THEN** 分钟和秒数 SHALL 使用 `NumberFlowGroup` 同步布局变化 #### Scenario: 格式切换不抖动 - **WHEN** 倒计时在按钮、秒级文本和分钟级文本之间切换 - **THEN** 容器 SHALL 使用 min-width 确保最小宽度,避免 RadioGroup 位移