1
0

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:
2026-05-15 22:18:29 +08:00
parent 8793fbd786
commit c46ab14cce
14 changed files with 419 additions and 66 deletions

View File

@@ -9,6 +9,7 @@ import { TargetBoard } from "./components/TargetBoard";
import { TargetDetailDrawer } from "./components/TargetDetailDrawer";
import { useDashboard } from "./hooks/use-queries";
import { useTargetDetail } from "./hooks/use-target-detail";
import { type ThemePreference, useThemePreference } from "./hooks/use-theme-preference";
const { Content, Header } = Layout;
const DEFAULT_REFRESH_INTERVAL_MS = 30000;
@@ -24,9 +25,15 @@ const REFRESH_OPTIONS = [
{ label: "1分钟", value: 60000 },
{ label: "5分钟", value: 300000 },
] as const;
const THEME_OPTIONS = [
{ label: "系统", value: "system" },
{ label: "明亮", value: "light" },
{ label: "黑暗", value: "dark" },
] as const;
export function App() {
const [refreshInterval, setRefreshInterval] = useState(DEFAULT_REFRESH_INTERVAL_MS);
const { preference: themePreference, setPreference: setThemePreference } = useThemePreference();
const dashboardRefetchInterval = refreshInterval === 0 ? false : refreshInterval;
const {
data: dashboard,
@@ -58,6 +65,10 @@ export function App() {
setRefreshInterval(value);
};
const handleThemeChange = (value: ThemePreference) => {
setThemePreference(value);
};
return (
<Layout className="dashboard">
<Header>
@@ -69,7 +80,14 @@ export function App() {
</span>
}
operations={
<div className="dashboard-refresh-control">
<div className="dashboard-header-controls">
<RadioGroup
onChange={handleThemeChange}
options={THEME_OPTIONS.map((option) => ({ label: option.label, value: option.value }))}
theme="button"
value={themePreference}
variant="default-filled"
/>
<RadioGroup
onChange={handleIntervalChange}
options={REFRESH_OPTIONS.map((option) => ({ label: option.label, value: option.value }))}