diff --git a/web/src/App.tsx b/web/src/App.tsx
index 95b732c..ae4b80f 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -2,6 +2,7 @@ import {RouterProvider} from 'react-router-dom';
import {App as AntdApp, ConfigProvider} from 'antd';
import zhCN from 'antd/locale/zh_CN';
import router from './router';
+import {ThemeProvider} from '@/portal/contexts/ThemeContext';
import 'dayjs/locale/zh-cn';
import dayjs from 'dayjs';
import './App.css';
@@ -11,11 +12,13 @@ dayjs.locale('zh-cn');
function App() {
return (
-
-
-
-
-
+
+
+
+
+
+
+
);
}
diff --git a/web/src/admin/pages/AdminLayout/index.tsx b/web/src/admin/pages/AdminLayout/index.tsx
index 13cb79c..210a369 100644
--- a/web/src/admin/pages/AdminLayout/index.tsx
+++ b/web/src/admin/pages/AdminLayout/index.tsx
@@ -22,6 +22,7 @@ import {cn} from '@/lib/utils';
import {getServerVersion, type VersionInfo} from "@/api/version.ts";
import {flushSync} from "react-dom";
import {ScrollArea} from "@radix-ui/react-scroll-area";
+import {useTheme} from '@/portal/contexts/ThemeContext';
interface NavItem {
key: string;
@@ -39,8 +40,8 @@ const AdminLayout = () => {
const {message: messageApi, modal} = AntApp.useApp();
const [userInfo, setUserInfo] = useState(null);
const [version, setVersion] = useState();
- const [isDarkMode, setIsDarkMode] = useState(false);
- const darkModeButtonRef = useRef(null);
+ const {appliedTheme, setTheme} = useTheme();
+ const themeButtonRef = useRef(null);
const menuItems: NavItem[] = useMemo(
() => [
@@ -105,20 +106,6 @@ const AdminLayout = () => {
});
}, [navigate, location]);
- // 初始化暗色主题
- useEffect(() => {
- const savedTheme = localStorage.getItem('theme');
- const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
- const shouldBeDark = savedTheme === 'dark' || (!savedTheme && prefersDark);
-
- setIsDarkMode(shouldBeDark);
- if (shouldBeDark) {
- document.documentElement.classList.add('dark');
- } else {
- document.documentElement.classList.remove('dark');
- }
- }, []);
-
const handleLogout = () => {
modal.confirm({
title: '确认退出',
@@ -149,37 +136,27 @@ const AdminLayout = () => {
navigate(item.path);
};
- // 切换暗色主题的函数,带动画效果
- const toggleDarkMode = async (newIsDarkMode: boolean) => {
+ // 切换主题的函数,带动画效果
+ const toggleTheme = async () => {
+ const newTheme = appliedTheme === 'dark' ? 'light' : 'dark';
+
if (
- !darkModeButtonRef.current ||
+ !themeButtonRef.current ||
!document.startViewTransition ||
window.matchMedia('(prefers-reduced-motion: reduce)').matches
) {
// 如果不支持 View Transition API 或用户偏好减少动画,直接切换
- setIsDarkMode(newIsDarkMode);
- if (newIsDarkMode) {
- document.documentElement.classList.add('dark');
- } else {
- document.documentElement.classList.remove('dark');
- }
- localStorage.setItem('theme', newIsDarkMode ? 'dark' : 'light');
+ setTheme(newTheme);
return;
}
await document.startViewTransition(() => {
flushSync(() => {
- setIsDarkMode(newIsDarkMode);
- if (newIsDarkMode) {
- document.documentElement.classList.add('dark');
- } else {
- document.documentElement.classList.remove('dark');
- }
- localStorage.setItem('theme', newIsDarkMode ? 'dark' : 'light');
+ setTheme(newTheme);
});
}).ready;
- const {top, left, width, height} = darkModeButtonRef.current.getBoundingClientRect();
+ const {top, left, width, height} = themeButtonRef.current.getBoundingClientRect();
const x = left + width / 2;
const y = top + height / 2;
const right = window.innerWidth - left;
@@ -207,7 +184,7 @@ const AdminLayout = () => {
return (
@@ -250,19 +227,22 @@ const AdminLayout = () => {
>
部署指南
+
+ {/* 主题切换按钮 */}
+