From f5f7b15eae8a38fab98809464fbc6ca756da8c5a Mon Sep 17 00:00:00 2001 From: dushixiang Date: Sat, 24 Jan 2026 22:17:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=89=8D=E5=8F=B0=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=92=8C=E7=AE=A1=E7=90=86=E9=A1=B5=E9=9D=A2=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=B8=BB=E9=A2=98=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/App.tsx | 13 ++- web/src/admin/pages/AdminLayout/index.tsx | 58 +++------- web/src/portal/components/PublicHeader.tsx | 128 +++++---------------- web/src/portal/pages/PublicLayout.tsx | 59 +++++----- 4 files changed, 86 insertions(+), 172 deletions(-) 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 = () => { > 部署指南 + + {/* 主题切换按钮 */} + - - - - - setTheme('auto')} - > - - 跟随系统 - {theme === 'auto' && ✓} - - setTheme('light')} - > - - 浅色主题 - {theme === 'light' && ✓} - - setTheme('dark')} - > - - 暗黑主题 - {theme === 'dark' && ✓} - - - - + {/* 主题切换按钮 - Desktop */} + {/* 登录/管理后台按钮 - Desktop */} {isLoggedIn ? ( @@ -252,52 +215,23 @@ const PublicHeader = () => { {/* Divider */}
- {/* Mobile Theme Toggle DropdownMenu */} - - - - - - - - setTheme('auto')} - > - - 跟随系统 - {theme === 'auto' && ✓} - - setTheme('light')} - > - - 浅色主题 - {theme === 'light' && ✓} - - setTheme('dark')} - > - - 暗黑主题 - {theme === 'dark' && ✓} - - - - + {/* Mobile Theme Toggle Button */} + {/* Mobile Login/Admin Button */} {isLoggedIn ? ( diff --git a/web/src/portal/pages/PublicLayout.tsx b/web/src/portal/pages/PublicLayout.tsx index 59e6c6a..7387e4c 100644 --- a/web/src/portal/pages/PublicLayout.tsx +++ b/web/src/portal/pages/PublicLayout.tsx @@ -1,42 +1,39 @@ import {Outlet} from 'react-router-dom'; import PublicHeader from '@portal/components/PublicHeader'; import PublicFooter from '@portal/components/PublicFooter'; -import {ThemeProvider} from '../contexts/ThemeContext'; const PublicLayout = () => { return ( - -
- {/* 背景网格效果 */} -
- {/* 暗色模式网格 */} -
- {/* 顶部发光效果 */} -
+
+ {/* 背景网格效果 */} +
+ {/* 暗色模式网格 */} +
+ {/* 顶部发光效果 */} +
- -
-
- -
- -
+ +
+
+ +
+
- +
); };