chore: 📌 优化后台风格显示

This commit is contained in:
Stev_Wang
2026-01-03 20:11:05 +08:00
parent a950d1d526
commit 5b8999b188
5 changed files with 172 additions and 27 deletions

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'; import { useState, useMemo } from 'react';
import { Layout, Menu, Button, theme, App } from 'antd'; import { Layout, Menu, Button, theme, App } from 'antd';
import { import {
MenuFoldOutlined, MenuFoldOutlined,
@@ -6,9 +6,12 @@ import {
DashboardOutlined, DashboardOutlined,
SettingOutlined, SettingOutlined,
LogoutOutlined, LogoutOutlined,
SunOutlined,
MoonOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Outlet, useNavigate, useLocation } from 'react-router-dom'; import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import { useAuthStore } from '../stores/authStore'; import { useAuthStore } from '../stores/authStore';
import { useThemeStore } from '../stores/themeStore';
import { adminAuthService } from '../services/adminAuthService'; import { adminAuthService } from '../services/adminAuthService';
const { Header, Sider, Content, Footer } = Layout; const { Header, Sider, Content, Footer } = Layout;
@@ -23,6 +26,16 @@ const AdminLayout = () => {
const adminUser = useAuthStore((state) => state.adminUser); const adminUser = useAuthStore((state) => state.adminUser);
const logout = useAuthStore((state) => state.logout); const logout = useAuthStore((state) => state.logout);
const { message } = App.useApp(); const { message } = App.useApp();
const { themeMode, toggleTheme } = useThemeStore();
// 根据当前路径计算应该展开的菜单
const openKeys = useMemo(() => {
const path = location.pathname;
if (path.startsWith('/admin/users')) {
return ['user'];
}
return [];
}, [location.pathname]);
const handleLogout = async () => { const handleLogout = async () => {
try { try {
@@ -56,26 +69,34 @@ const AdminLayout = () => {
return ( return (
<Layout style={{ minHeight: '100vh' }}> <Layout style={{ minHeight: '100vh' }}>
<Sider trigger={null} collapsible collapsed={collapsed}> <Sider
trigger={null}
collapsible
collapsed={collapsed}
style={{
background: themeMode === 'dark' ? '#001529' : '#ffffff',
}}
>
<div <div
style={{ style={{
height: 32, height: 32,
margin: 16, margin: 16,
background: 'rgba(255, 255, 255, 0.2)', background: themeMode === 'dark' ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.05)',
borderRadius: 6, borderRadius: 6,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
color: 'white', color: themeMode === 'dark' ? 'white' : '#1890ff',
fontWeight: 'bold', fontWeight: 'bold',
}} }}
> >
{collapsed ? '运营' : '运营管理系统'} {collapsed ? '运营' : '运营管理系统'}
</div> </div>
<Menu <Menu
theme="dark" theme={themeMode === 'dark' ? 'dark' : 'light'}
mode="inline" mode="inline"
selectedKeys={[location.pathname]} selectedKeys={[location.pathname]}
openKeys={openKeys}
items={menuItems} items={menuItems}
onClick={({ key }) => navigate(key)} onClick={({ key }) => navigate(key)}
/> />
@@ -99,14 +120,23 @@ const AdminLayout = () => {
fontSize: '16px', fontSize: '16px',
width: 64, width: 64,
height: 64, height: 64,
outline: 'none',
}} }}
/> />
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<Button
type="text"
icon={themeMode === 'dark' ? <SunOutlined /> : <MoonOutlined />}
onClick={toggleTheme}
style={{ outline: 'none' }}
title={themeMode === 'dark' ? '切换到亮色主题' : '切换到暗色主题'}
/>
<span>, {adminUser?.username}</span> <span>, {adminUser?.username}</span>
<Button <Button
type="text" type="text"
icon={<LogoutOutlined />} icon={<LogoutOutlined />}
onClick={handleLogout} onClick={handleLogout}
style={{ outline: 'none' }}
> >
退 退
</Button> </Button>
@@ -123,7 +153,12 @@ const AdminLayout = () => {
> >
<Outlet /> <Outlet />
</Content> </Content>
<Footer style={{ textAlign: 'center' }}> <Footer
style={{
textAlign: 'center',
background: themeMode === 'dark' ? '#001529' : '#ffffff',
}}
>
西 ©2025 Created by JGE 西 ©2025 Created by JGE
</Footer> </Footer>
</Layout> </Layout>

View File

@@ -0,0 +1,34 @@
import { ReactNode, useMemo } from 'react';
import { ConfigProvider, theme } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import { useThemeStore } from '../stores/themeStore';
import { getThemeByMode } from '../theme';
interface ThemeProviderProps {
children: ReactNode;
}
// 主题提供者组件
export const ThemeProvider = ({ children }: ThemeProviderProps) => {
const { themeMode } = useThemeStore();
// 根据主题模式获取主题配置和算法
const currentTheme = useMemo(() => {
const customTheme = getThemeByMode(themeMode);
const algorithm = themeMode === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm;
return {
algorithm,
...customTheme,
};
}, [themeMode]);
return (
<ConfigProvider
locale={zhCN}
theme={currentTheme}
>
{children}
</ConfigProvider>
);
};

View File

@@ -1,24 +1,17 @@
import { StrictMode } from 'react'; import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { RouterProvider } from 'react-router-dom'; import { RouterProvider } from 'react-router-dom';
import { ConfigProvider, App, theme } from 'antd'; import { App } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import './index.css'; import './index.css';
import router from './router'; import router from './router';
import { appTheme } from './theme'; import { ThemeProvider } from './components/ThemeProvider';
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<StrictMode> <StrictMode>
<ConfigProvider <ThemeProvider>
locale={zhCN}
theme={{
algorithm: theme.defaultAlgorithm,
...appTheme,
}}
>
<App> <App>
<RouterProvider router={router} /> <RouterProvider router={router} />
</App> </App>
</ConfigProvider> </ThemeProvider>
</StrictMode>, </StrictMode>,
); );

View File

@@ -0,0 +1,34 @@
import { create } from 'zustand';
// 主题类型
export type ThemeMode = 'light' | 'dark';
// 从 localStorage 读取主题模式
const getInitialThemeMode = (): ThemeMode => {
const savedTheme = localStorage.getItem('adminThemeMode');
if (savedTheme === 'light' || savedTheme === 'dark') {
return savedTheme;
}
return 'dark'; // 默认暗色主题
};
// 主题状态接口
interface ThemeState {
themeMode: ThemeMode;
toggleTheme: () => void;
setTheme: (mode: ThemeMode) => void;
}
// 主题状态管理
export const useThemeStore = create<ThemeState>((set) => ({
themeMode: getInitialThemeMode(),
toggleTheme: () => set((state) => {
const newMode = state.themeMode === 'light' ? 'dark' : 'light';
localStorage.setItem('adminThemeMode', newMode);
return { themeMode: newMode };
}),
setTheme: (mode) => {
localStorage.setItem('adminThemeMode', mode);
set({ themeMode: mode });
},
}));

View File

@@ -1,8 +1,9 @@
// 全局主题配置 // 全局主题配置
import type { ThemeConfig } from 'antd'; import type { ThemeConfig } from 'antd';
import type { ThemeMode } from '../stores/themeStore';
// 自定义主题配置 // 亮色主题配置
export const appTheme: ThemeConfig = { export const lightTheme: ThemeConfig = {
token: { token: {
// 主色调 // 主色调
colorPrimary: '#1890ff', colorPrimary: '#1890ff',
@@ -22,18 +23,66 @@ export const appTheme: ThemeConfig = {
components: { components: {
// Layout 组件主题 // Layout 组件主题
Layout: { Layout: {
headerBg: '#001529', // 顶部导航和侧边栏背景色 headerBg: '#ffffff', // 顶部导航背景色(亮色)
footerBg: '#001529', // 页脚背景色 footerBg: '#ffffff', // 页脚背景色(亮色)
siderBg: '#001529', // 侧边栏背景色 siderBg: '#ffffff', // 侧边栏背景色(亮色)
}, },
// Menu 组件主题 // Menu 组件主题
Menu: { Menu: {
darkItemSelectedBg: '#1890ff', // 菜单激活背景色 itemSelectedBg: '#e6f7ff', // 菜单激活背景色(亮色)
darkItemHoverBg: 'rgba(24, 144, 255, 0.2)', // 菜单悬停背景色 itemHoverBg: 'rgba(24, 144, 255, 0.1)', // 菜单悬停背景色(亮色)
darkItemColor: 'rgba(255, 255, 255, 0.65)', // 菜单项文字颜色 itemColor: 'rgba(0, 0, 0, 0.65)', // 菜单项文字颜色(亮色)
darkItemSelectedColor: '#fff', // 菜单激活文字颜色 itemSelectedColor: '#1890ff', // 菜单激活文字颜色(亮色)
darkItemHoverColor: '#fff', // 菜单悬停文字颜色 itemHoverColor: '#1890ff', // 菜单悬停文字颜色(亮色)
itemSelectedStyle: {
boxShadow: 'none', // 去除菜单激活项的阴影
},
}, },
}, },
}; };
// 暗色主题配置
export const darkTheme: ThemeConfig = {
token: {
// 主色调
colorPrimary: '#1890ff',
colorSuccess: '#52c41a',
colorWarning: '#faad14',
colorError: '#ff4d4f',
colorInfo: '#1890ff',
// 字体设置
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
fontSize: 14,
// 圆角
borderRadius: 3,
},
components: {
// Layout 组件主题
Layout: {
headerBg: '#001529', // 顶部导航和侧边栏背景色(暗色)
footerBg: '#001529', // 页脚背景色(暗色)
siderBg: '#001529', // 侧边栏背景色(暗色)
},
// Menu 组件主题
Menu: {
darkItemSelectedBg: '#1890ff', // 菜单激活背景色(暗色)
darkItemHoverBg: 'rgba(24, 144, 255, 0.2)', // 菜单悬停背景色(暗色)
darkItemColor: 'rgba(255, 255, 255, 0.65)', // 菜单项文字颜色(暗色)
darkItemSelectedColor: '#fff', // 菜单激活文字颜色(暗色)
darkItemHoverColor: '#fff', // 菜单悬停文字颜色(暗色)
},
},
};
// 根据主题模式获取主题配置
export const getThemeByMode = (mode: ThemeMode): ThemeConfig => {
return mode === 'light' ? lightTheme : darkTheme;
};
// 默认主题配置(向后兼容)
export const appTheme: ThemeConfig = darkTheme;