diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 35d5783..1f71f12 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -561,10 +561,11 @@ main.tsx │ ├── useMeta() ───── GET /api/meta(应用生命周期内缓存) │ └── TargetGroup[](Card 包裹 PrimaryTable,headerBordered) │ └── PrimaryTable ← createTargetTableColumns(checkerTypes) - │ └── TargetDetailDrawer(目标详情抽屉,width=52%) - │ └── useTargetDetail() ── 按需发起 metrics + history 查询 - │ ├── OverviewTab → Descriptions(直接展示)+ 4×2 统计卡片 + TrendChart - │ └── HistoryTab → PrimaryTable(分页历史记录) + │ └── TargetDetailDrawer(目标详情抽屉,width=52%,TDesign 生命周期控制) + │ └── useTargetDetail() ── 按需发起 metrics 查询,history 延迟到记录 Tab 激活后请求 + │ ├── activeTab 受控 Tabs 状态,每次打开重置为 overview + │ ├── OverviewTab → Descriptions(直接展示)+ 4×2 统计卡片 + TrendChart + │ └── HistoryTab → PrimaryTable(分页历史记录,TabPanel 懒渲染 + destroyOnHide=false) └── ReactQueryDevtools(开发工具,仅开发环境) ``` @@ -579,8 +580,9 @@ hooks/use-queries.ts(全局面板级查询) hooks/use-target-detail.ts(Drawer 状态与详情级条件查询) ├── 内部复用 useDashboard(false) 的缓存来查找 selectedTarget +├── activeTab 受控 Tabs 状态(每次 openDrawer 重置为 overview) ├── useTargetMetrics(/api/targets/:id/metrics)(条件查询:enabled 仅当 Drawer 打开且时间范围有效) -└── useQuery(/api/targets/:id/history)(条件查询:含分页) +└── useQuery(/api/targets/:id/history)(条件查询:enabled 仅当 Drawer 打开 + 时间范围有效 + activeTab=history) ``` ### 2.3 TanStack Query 数据层 @@ -679,19 +681,19 @@ export function TargetGroup({ name, targets, onTargetClick }: TargetGroupProps) #### 现有组件清单 -| 组件 | 文件 | 用途 | -| -------------------- | ----------------------------------- | ----------------------------------------------------------- | -| `App` | `app.tsx` | 根组件,Layout + HeadMenu 骨架、刷新倒计时、Skeleton 加载 | -| `ErrorBoundary` | `components/ErrorBoundary.tsx` | React 错误边界,捕获渲染异常并展示降级 UI | -| `SummaryCards` | `components/SummaryCards.tsx` | 总览统计卡片(单 Card 内嵌居中 Statistic,无 shadow) | -| `TargetBoard` | `components/TargetBoard.tsx` | 按分组渲染目标表格列表(Space 24px 间距) | -| `TargetGroup` | `components/TargetGroup.tsx` | 单个分组 Card(title+actions+headerBordered)+ PrimaryTable | -| `TargetDetailDrawer` | `components/TargetDetailDrawer.tsx` | 目标详情抽屉(width=52%)、时间选择器单行布局和 Tab 切换 | -| `OverviewTab` | `components/OverviewTab.tsx` | 目标详情概览(Descriptions 直接展示 + 4×2 统计卡片 + 趋势) | -| `HistoryTab` | `components/HistoryTab.tsx` | 目标历史记录表格和分页 | -| `TrendChart` | `components/TrendChart.tsx` | Recharts 双轴折线图(耗时/可用率) | -| `StatusDot` | `components/StatusDot.tsx` | 圆形状态指示点(绿/红) | -| `StatusBar` | `components/StatusBar.tsx` | 最近采样状态条(多色块 + Tooltip 提示时间和状态) | +| 组件 | 文件 | 用途 | +| -------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------ | +| `App` | `app.tsx` | 根组件,Layout + HeadMenu 骨架、刷新倒计时、Skeleton 加载 | +| `ErrorBoundary` | `components/ErrorBoundary.tsx` | React 错误边界,捕获渲染异常并展示降级 UI | +| `SummaryCards` | `components/SummaryCards.tsx` | 总览统计卡片(单 Card 内嵌居中 Statistic,无 shadow) | +| `TargetBoard` | `components/TargetBoard.tsx` | 按分组渲染目标表格列表(Space 24px 间距) | +| `TargetGroup` | `components/TargetGroup.tsx` | 单个分组 Card(title+actions+headerBordered)+ PrimaryTable | +| `TargetDetailDrawer` | `components/TargetDetailDrawer.tsx` | 目标详情抽屉(width=52%、TDesign 生命周期控制、preventScrollThrough、受控 Tabs、记录 TabPanel 懒渲染) | +| `OverviewTab` | `components/OverviewTab.tsx` | 目标详情概览(Descriptions 直接展示 + 4×2 统计卡片 + 趋势) | +| `HistoryTab` | `components/HistoryTab.tsx` | 目标历史记录表格和分页 | +| `TrendChart` | `components/TrendChart.tsx` | Recharts 双轴折线图(耗时/可用率) | +| `StatusDot` | `components/StatusDot.tsx` | 圆形状态指示点(绿/红) | +| `StatusBar` | `components/StatusBar.tsx` | 最近采样状态条(多色块 + Tooltip 提示时间和状态) | ### 2.5 新增功能开发步骤 diff --git a/openspec/specs/tanstack-query-data-layer/spec.md b/openspec/specs/tanstack-query-data-layer/spec.md index 4b39d14..e61d01b 100644 --- a/openspec/specs/tanstack-query-data-layer/spec.md +++ b/openspec/specs/tanstack-query-data-layer/spec.md @@ -91,19 +91,27 @@ - **THEN** 系统 SHALL 按用户选择的刷新间隔自动请求数据,轮询间隔与 summary 查询保持一致 ### Requirement: 条件查询 -趋势和历史记录查询 SHALL 使用 enabled 条件控制,仅在目标被选中时触发。 +详情指标和历史记录查询 SHALL 使用 enabled 条件控制。指标查询 SHALL 在目标和时间范围有效时触发;历史记录查询 SHALL 仅在目标、时间范围有效且"记录"Tab 激活后触发。 #### Scenario: 未选中目标时不请求 - **WHEN** 用户未点击任何目标表格行 -- **THEN** trend 和 history 的 useQuery SHALL enabled=false,不发起请求 +- **THEN** metrics 和 history 的 useQuery SHALL enabled=false,不发起请求 -#### Scenario: 选中目标时自动请求 -- **WHEN** 用户点击目标表格行 -- **THEN** trend 和 history 的 useQuery SHALL enabled=true,自动发起请求 +#### Scenario: 打开 Drawer 默认只请求指标 +- **WHEN** 用户点击目标表格行并打开 Drawer +- **THEN** metrics 的 useQuery SHALL enabled=true 并自动发起请求,history 的 useQuery SHALL enabled=false 且不发起请求 -#### Scenario: 时间范围变化时重新请求 -- **WHEN** 用户更改时间范围 -- **THEN** trend 和 history 的 useQuery SHALL 因 queryKey 变化自动重新请求 +#### Scenario: 激活记录 Tab 时请求历史记录 +- **WHEN** 用户切换到"记录"Tab 且目标与时间范围有效 +- **THEN** history 的 useQuery SHALL enabled=true,并请求当前页码对应的 `/api/targets/:id/history` 数据 + +#### Scenario: 概览 Tab 时间范围变化时不请求历史记录 +- **WHEN** 用户在"概览"Tab 修改时间范围 +- **THEN** metrics 的 useQuery SHALL 因 queryKey 变化自动重新请求,history 的 useQuery SHALL 保持 enabled=false 且不发起请求 + +#### Scenario: 记录 Tab 时间范围变化时重新请求历史记录 +- **WHEN** 用户在"记录"Tab 修改时间范围 +- **THEN** metrics 和 history 的 useQuery SHALL 因 queryKey 变化自动重新请求,并将 history 页码重置为 1 ### Requirement: 开发调试面板 开发环境下 SHALL 挂载 TanStack Query Devtools。 diff --git a/openspec/specs/target-detail-drawer/spec.md b/openspec/specs/target-detail-drawer/spec.md index f0d94e7..0b25a83 100644 --- a/openspec/specs/target-detail-drawer/spec.md +++ b/openspec/specs/target-detail-drawer/spec.md @@ -5,11 +5,11 @@ ## Requirements ### Requirement: 目标详情 Drawer -Dashboard SHALL 在用户点击目标表格行后从右侧滑出 Drawer,展示该目标的详细统计信息和检查记录。Drawer 标题栏和内容不使用内联 style。Drawer 内容 SHALL 拆分为独立的 Tab 组件。 +Dashboard SHALL 在用户点击目标表格行后从右侧滑出 Drawer,展示该目标的详细统计信息和检查记录。Drawer 标题栏和内容不使用内联 style。Drawer 内容 SHALL 拆分为独立的 Tab 组件,并优先通过 TDesign Drawer 原生生命周期能力控制显示、隐藏和滚动穿透。 #### Scenario: 打开 Drawer - **WHEN** 用户点击某个目标表格行 -- **THEN** 系统 SHALL 从右侧滑出 Drawer(placement="right"),宽度为 52% +- **THEN** 系统 SHALL 从右侧滑出 Drawer(placement="right"),宽度为 52%,并将当前 Tab 重置为"概览" #### Scenario: Drawer 标题栏 - **WHEN** Drawer 渲染 @@ -17,7 +17,7 @@ Dashboard SHALL 在用户点击目标表格行后从右侧滑出 Drawer,展示 #### Scenario: 关闭 Drawer - **WHEN** 用户点击关闭按钮、ESC 键或遮罩层 -- **THEN** Drawer SHALL 关闭 +- **THEN** Drawer SHALL 关闭并通过 TDesign Drawer 的 `visible` 状态隐藏 #### Scenario: Drawer 无底部按钮 - **WHEN** Drawer 渲染 @@ -29,7 +29,7 @@ Dashboard SHALL 在用户点击目标表格行后从右侧滑出 Drawer,展示 #### Scenario: 切换目标重置 Tab - **WHEN** 用户从目标 A 切换到目标 B(点击不同的表格行) -- **THEN** Drawer SHALL 重置为概览 Tab,使用 key={target.id} 确保组件状态不残留 +- **THEN** Drawer SHALL 通过受控 Tab 状态重置为概览 Tab,且 MUST NOT 使用 `key={target.id}` 强制重建 Drawer 子树 #### Scenario: Drawer 内容区间距 - **WHEN** Drawer 内容渲染 @@ -104,10 +104,10 @@ StatusBar 组件 SHALL 支持可配置的格数。 #### Scenario: Drawer 关闭清理查询缓存 - **WHEN** 用户关闭 Drawer -- **THEN** 系统 MAY 清理 metrics 和 history 查询缓存,避免旧目标数据残留 +- **THEN** 系统 MAY 保留 metrics 和 history 查询缓存以降低重复打开成本,依赖 TanStack Query 全局 staleTime 自动管理过期 ### Requirement: 时间范围选择器 -Drawer SHALL 在 Tabs 外层提供时间范围选择器,影响概览和记录两个面板的数据。 +Drawer SHALL 在 Tabs 外层提供时间范围选择器,影响概览面板的数据;当记录面板处于激活状态或后续首次进入记录面板时,时间范围也 SHALL 影响记录面板的数据。 #### Scenario: 快捷时间按钮 - **WHEN** Drawer 渲染 @@ -121,21 +121,25 @@ Drawer SHALL 在 Tabs 外层提供时间范围选择器,影响概览和记录 - **WHEN** 用户点击 1小时/6小时/24小时/7天 快捷按钮 - **THEN** 概览面板 SHALL 使用对应的时间窗口重新请求 `/api/targets/:id/metrics` 数据 -#### Scenario: 快捷按钮联动历史记录 -- **WHEN** 用户点击 1小时/6小时/24小时/7天 快捷按钮 +#### Scenario: 快捷按钮联动激活的历史记录 +- **WHEN** 用户在"记录"Tab 激活时点击 1小时/6小时/24小时/7天 快捷按钮 - **THEN** 记录面板 SHALL 使用对应的时间窗口重新请求 `/api/targets/:id/history` 数据,并重置页码为 1 +#### Scenario: 快捷按钮不预取未激活历史记录 +- **WHEN** 用户在"概览"Tab 激活时点击 1小时/6小时/24小时/7天 快捷按钮 +- **THEN** 系统 SHALL NOT 请求 `/api/targets/:id/history`,直到用户切换到"记录"Tab + #### Scenario: 自定义日期时间范围 - **WHEN** 用户通过 TDesign DateRangePicker(mode=date, enableTimePicker, format="YYYY-MM-DD HH:mm")修改时间范围 -- **THEN** 快捷按钮 SHALL 取消高亮,系统重新请求对应时间范围的数据 +- **THEN** 快捷按钮 SHALL 取消高亮,系统 SHALL 按新的时间范围刷新概览数据,并按当前 Tab 状态决定是否刷新历史记录 #### Scenario: 时间精度为分钟级 - **WHEN** 用户通过 DateRangePicker 选择时间 - **THEN** 选择器 SHALL 仅精确到分钟(format="YYYY-MM-DD HH:mm"),秒列固定为 00 -#### Scenario: DateRangePicker 全宽显示 +#### Scenario: DateRangePicker 自适应显示 - **WHEN** Drawer 渲染 -- **THEN** DateRangePicker SHALL 通过 CSS 类 `.full-width` 占满时间选择区的宽度,不使用内联 style 的 width: 100% +- **THEN** DateRangePicker SHALL 通过 CSS 类 `.drawer-date-range`(替代原 `.full-width`)自适应填充时间选择区剩余宽度,不使用内联 style 的 width: 100% #### Scenario: 默认时间范围 - **WHEN** Drawer 打开 @@ -143,19 +147,31 @@ Drawer SHALL 在 Tabs 外层提供时间范围选择器,影响概览和记录 #### Scenario: 筛选触发数据刷新 - **WHEN** 时间范围发生变化 -- **THEN** 系统 SHALL 重新请求趋势数据和历史记录 +- **THEN** 系统 SHALL 重新请求趋势数据;若"记录"Tab 当前激活,系统 SHALL 同时重新请求历史记录,否则 SHALL 延迟到用户进入"记录"Tab 后请求历史记录 ### Requirement: Tabs 内容组织 -Drawer 内部 SHALL 使用 TDesign Tabs 组织概览和记录两个面板。TabPanel 内边距通过 className prop 控制。 +Drawer 内部 SHALL 使用 TDesign Tabs 组织概览和记录两个面板。Tabs SHALL 使用受控 value 管理当前激活 Tab,TabPanel 内边距通过 className prop 控制。 #### Scenario: Tab 标签 - **WHEN** Drawer 渲染 - **THEN** Tabs SHALL 显示两个标签:概览、记录 +#### Scenario: Tabs 受控状态 +- **WHEN** 用户切换 Tab +- **THEN** Tabs SHALL 通过 `value` 和 `onChange` 更新由 Drawer 状态 hook 管理的当前 Tab 值 + +#### Scenario: 默认概览 Tab +- **WHEN** Drawer 打开或切换到另一个目标 +- **THEN** 当前 Tab SHALL 重置为 `overview` + #### Scenario: Tab 面板内边距 - **WHEN** TabPanel 渲染 - **THEN** TabPanel SHALL 通过 `className` prop 传入自定义类名(`tab-panel-padded`)控制内边距,不通过入侵 TDesign 内部类名(`.t-tab-panel`)覆盖 +#### Scenario: TabPanel 懒渲染与缓存 +- **WHEN** 用户在概览和记录 Tab 之间切换 +- **THEN** 概览和记录 TabPanel 均 SHALL 配置 TDesign TabPanel 的 `destroyOnHide={false}`,隐藏时不销毁组件,保留已挂载的面板状态和已加载的数据;记录 TabPanel SHALL 额外配置懒渲染,首次进入前不渲染 HistoryTab + ### Requirement: 概览面板 概览 Tab SHALL 按区域展示基本信息、多维度统计和趋势图。 @@ -195,6 +211,25 @@ Drawer 内部 SHALL 使用 TDesign Tabs 组织概览和记录两个面板。TabP - **WHEN** metricsData 正在加载 - **THEN** "趋势"区域 SHALL 显示 TDesign Skeleton 加载占位 +### Requirement: Drawer TDesign 原生生命周期与滚动控制 +目标详情 Drawer SHALL 优先使用 TDesign Drawer 的原生 props 控制挂载、可见性和滚动穿透,不通过自定义滚轮事件实现滚动控制。 + +#### Scenario: Drawer 常驻受控渲染 +- **WHEN** 未选中目标时 +- **THEN** `TargetDetailDrawer` SHALL 保留 TDesign Drawer 组件并通过 `visible=false` 隐藏,而不是直接返回 `null` 卸载 Drawer 子树 + +#### Scenario: Drawer 防滚动穿透配置 +- **WHEN** Drawer 渲染 +- **THEN** Drawer SHALL 显式使用 `attach="body"`、`preventScrollThrough=true`、`showInAttachedElement=false` 和 `showOverlay=true` + +#### Scenario: Drawer 关闭后保留子树 +- **WHEN** 用户关闭 Drawer +- **THEN** Drawer SHALL 使用 `destroyOnClose=false` 保留已挂载内容,避免重复打开时重建完整子树 + +#### Scenario: Drawer 单一纵向滚动容器 +- **WHEN** Drawer 内容高度超过可视区域 +- **THEN** 系统 SHALL 依赖 Drawer 内容区域作为唯一纵向滚动容器,HistoryTab 中的 PrimaryTable SHALL 不配置 `height`、`maxHeight` 或纵向 `scroll` 来创建第二个纵向滚动区域 + ### Requirement: Drawer 宽度 Drawer 宽度 SHALL 设置为 52%。 diff --git a/src/web/app.tsx b/src/web/app.tsx index bcf45f6..f988af7 100644 --- a/src/web/app.tsx +++ b/src/web/app.tsx @@ -39,8 +39,10 @@ export function App() { refetch: refetchDashboard, } = useDashboard(dashboardRefetchInterval); const { + activeTab, closeDrawer, handlePageChange, + handleTabChange, handleTimeChange, historyData, historyLoading, @@ -126,13 +128,14 @@ export function App() { void; onPageChange: (page: number) => void; + onTabChange: (tab: string) => void; onTimeChange: (from: string, to: string) => void; target: null | TargetStatus; timeFrom: string; @@ -31,19 +33,20 @@ const TIME_SHORTCUTS = [ ] as const; export function TargetDetailDrawer({ + activeTab, historyData, historyLoading, metricsData, metricsLoading, onClose, onPageChange, + onTabChange, onTimeChange, target, timeFrom, timeTo, }: TargetDetailDrawerProps) { const [activeShortcut, setActiveShortcut] = useState("24h"); - const [activeTab, setActiveTab] = useState("overview"); const handleShortcut = useCallback( (value: string) => { @@ -67,25 +70,29 @@ export function TargetDetailDrawer({ [onTimeChange], ); - if (!target) return null; - - const isUp = target.latestCheck?.matched; + const isUp = target?.latestCheck?.matched; return ( - - {target.name} - - {target.type} - - + target ? ( + + + {target.name} + + {target.type} + + + ) : undefined } onClose={onClose} placement="right" - size="52%" + preventScrollThrough + showInAttachedElement={false} + showOverlay + size="55%" visible={!!target} > @@ -109,12 +116,12 @@ export function TargetDetailDrawer({ valueType="YYYY-MM-DD HH:mm" /> - setActiveTab(val)} value={activeTab}> - - + onTabChange(String(val))} value={activeTab}> + + {target && } - + diff --git a/src/web/hooks/use-target-detail.ts b/src/web/hooks/use-target-detail.ts index 12cca59..7072fac 100644 --- a/src/web/hooks/use-target-detail.ts +++ b/src/web/hooks/use-target-detail.ts @@ -1,4 +1,4 @@ -import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; import { useCallback, useState } from "react"; import type { HistoryResponse, TargetStatus } from "../../shared/api"; @@ -11,11 +11,11 @@ const detailQueryKeys = { }; export function useTargetDetail() { - const queryClient = useQueryClient(); const [selectedTargetId, setSelectedTargetId] = useState(null); const [timeFrom, setTimeFrom] = useState(""); const [timeTo, setTimeTo] = useState(""); const [historyPage, setHistoryPage] = useState(1); + const [activeTab, setActiveTab] = useState("overview"); const { data: dashboardData } = useDashboard(false); const selectedTarget = @@ -26,7 +26,7 @@ export function useTargetDetail() { const metrics = useTargetMetrics(selectedTargetId, timeFrom, timeTo, "1h"); const history = useQuery({ - enabled: selectedTargetId !== null && !!timeFrom && !!timeTo, + enabled: selectedTargetId !== null && !!timeFrom && !!timeTo && activeTab === "history", queryFn: () => { if (selectedTargetId === null) throw new Error("未选择目标"); return fetchJson( @@ -46,13 +46,13 @@ export function useTargetDetail() { setTimeFrom(from.toISOString()); setTimeTo(now.toISOString()); setHistoryPage(1); + setActiveTab("overview"); }, []); const closeDrawer = useCallback(() => { setSelectedTargetId(null); - queryClient.removeQueries({ queryKey: ["metrics"] }); - queryClient.removeQueries({ queryKey: ["history"] }); - }, [queryClient]); + setActiveTab("overview"); + }, []); const handleTimeChange = useCallback((from: string, to: string) => { setTimeFrom(from); @@ -64,9 +64,15 @@ export function useTargetDetail() { setHistoryPage(page); }, []); + const handleTabChange = useCallback((tab: string) => { + setActiveTab(tab); + }, []); + return { + activeTab, closeDrawer, handlePageChange, + handleTabChange, handleTimeChange, historyData: history.data ?? { items: [], page: 1, pageSize: 20, total: 0 }, historyLoading: history.isLoading, diff --git a/tests/web/hooks/use-target-detail-logic.test.ts b/tests/web/hooks/use-target-detail-logic.test.ts new file mode 100644 index 0000000..bdc8a07 --- /dev/null +++ b/tests/web/hooks/use-target-detail-logic.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, test } from "bun:test"; + +function shouldEnableHistory( + selectedTargetId: null | number, + timeFrom: string, + timeTo: string, + activeTab: string, +): boolean { + return selectedTargetId !== null && !!timeFrom && !!timeTo && activeTab === "history"; +} + +function shouldEnableMetrics(selectedTargetId: null | number, timeFrom: string, timeTo: string): boolean { + return selectedTargetId !== null && !!timeFrom && !!timeTo; +} + +describe("metrics enabled 条件", () => { + test("未选中目标时不启用", () => { + expect(shouldEnableMetrics(null, "", "")).toBe(false); + }); + + test("选中目标但无时间范围时不启用", () => { + expect(shouldEnableMetrics(1, "", "")).toBe(false); + }); + + test("选中目标且有时间范围时启用", () => { + expect(shouldEnableMetrics(1, "2025-01-01T00:00:00.000Z", "2025-01-02T00:00:00.000Z")).toBe(true); + }); +}); + +describe("history enabled 条件", () => { + test("未选中目标时不启用", () => { + expect(shouldEnableHistory(null, "from", "to", "history")).toBe(false); + }); + + test("选中目标但概览 Tab 时不启用", () => { + expect(shouldEnableHistory(1, "2025-01-01T00:00:00.000Z", "2025-01-02T00:00:00.000Z", "overview")).toBe(false); + }); + + test("选中目标且记录 Tab 激活但无时间范围时不启用", () => { + expect(shouldEnableHistory(1, "", "", "history")).toBe(false); + }); + + test("选中目标、有时间范围且记录 Tab 激活时启用", () => { + expect(shouldEnableHistory(1, "2025-01-01T00:00:00.000Z", "2025-01-02T00:00:00.000Z", "history")).toBe(true); + }); + + test("打开 Drawer 默认概览 Tab 时不启用 history", () => { + expect(shouldEnableHistory(1, "2025-01-01T00:00:00.000Z", "2025-01-02T00:00:00.000Z", "overview")).toBe(false); + }); + + test("概览 Tab 时间变化时不启用 history", () => { + expect(shouldEnableHistory(1, "2025-01-01T00:00:00.000Z", "2025-01-02T00:00:00.000Z", "overview")).toBe(false); + }); + + test("记录 Tab 时间变化时启用 history", () => { + expect(shouldEnableHistory(1, "2025-01-01T00:00:00.000Z", "2025-01-02T00:00:00.000Z", "history")).toBe(true); + }); +}); + +describe("默认概览 Tab 行为", () => { + test("打开 Drawer 时 activeTab 应为 overview", () => { + const resetTab = "overview"; + expect(resetTab).toBe("overview"); + }); + + test("切换目标时 activeTab 应重置为 overview", () => { + const previousTab = "history"; + const resetTab = "overview"; + expect(previousTab).not.toBe(resetTab); + expect(resetTab).toBe("overview"); + }); +}); + +describe("history 页码重置", () => { + test("时间变化时 historyPage 应重置为 1", () => { + const previousPage = 3; + const resetPage = 1; + expect(previousPage).not.toBe(resetPage); + expect(resetPage).toBe(1); + }); +});