133 lines
3.8 KiB
TypeScript
133 lines
3.8 KiB
TypeScript
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
|
import ReactDOM from 'react-dom/client';
|
|
import { ConfigProvider, App as AntApp, theme as antdTheme } from 'antd';
|
|
import zhCN from 'antd/locale/zh_CN';
|
|
import { BrowserRouter } from 'react-router-dom';
|
|
import App from './App';
|
|
import './styles.css';
|
|
|
|
type ThemeMode = 'light' | 'dark';
|
|
|
|
interface ThemeCtx {
|
|
mode: ThemeMode;
|
|
toggle: () => void;
|
|
setMode: (m: ThemeMode) => void;
|
|
}
|
|
|
|
const ThemeContext = createContext<ThemeCtx>({
|
|
mode: 'light',
|
|
toggle: () => {},
|
|
setMode: () => {}
|
|
});
|
|
|
|
export const useTheme = () => useContext(ThemeContext);
|
|
|
|
function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
const [mode, setMode] = useState<ThemeMode>(() => {
|
|
const saved = localStorage.getItem('app-theme');
|
|
if (saved === 'dark' || saved === 'light') return saved;
|
|
return 'light';
|
|
});
|
|
|
|
useEffect(() => {
|
|
document.documentElement.setAttribute('data-theme', mode);
|
|
localStorage.setItem('app-theme', mode);
|
|
}, [mode]);
|
|
|
|
const isDark = mode === 'dark';
|
|
|
|
const themeConfig = useMemo(
|
|
() => ({
|
|
algorithm: isDark ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
|
|
token: {
|
|
colorPrimary: isDark ? '#e07b3e' : '#c2541f',
|
|
colorInfo: isDark ? '#e07b3e' : '#c2541f',
|
|
colorBgBase: isDark ? '#1a1816' : '#faf9f5',
|
|
colorBgContainer: isDark ? '#221f1c' : '#ffffff',
|
|
colorBgElevated: isDark ? '#2b2824' : '#ffffff',
|
|
colorBgLayout: isDark ? '#1a1816' : '#faf9f5',
|
|
colorBorder: isDark ? '#36322c' : '#ebe7da',
|
|
colorBorderSecondary: isDark ? '#2b2824' : '#f0ece0',
|
|
colorText: isDark ? '#f3efe6' : '#2a2622',
|
|
colorTextSecondary: isDark ? '#b6afa3' : '#6b6660',
|
|
colorTextTertiary: isDark ? '#7e7869' : '#a09a8e',
|
|
colorSuccess: isDark ? '#7fb87d' : '#4f8a4d',
|
|
colorWarning: isDark ? '#d49a4a' : '#b8782a',
|
|
colorError: isDark ? '#e07060' : '#c0392b',
|
|
borderRadius: 10,
|
|
borderRadiusLG: 12,
|
|
borderRadiusSM: 6,
|
|
fontFamily:
|
|
"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif",
|
|
fontSize: 14,
|
|
controlHeight: 36,
|
|
wireframe: false
|
|
},
|
|
components: {
|
|
Button: {
|
|
borderRadius: 10,
|
|
fontWeight: 500,
|
|
controlHeight: 36,
|
|
primaryShadow: 'none',
|
|
defaultShadow: 'none'
|
|
},
|
|
Card: {
|
|
borderRadiusLG: 14
|
|
},
|
|
Input: {
|
|
borderRadius: 10,
|
|
controlHeight: 38
|
|
},
|
|
Modal: {
|
|
borderRadiusLG: 16,
|
|
paddingContentHorizontalLG: 24
|
|
},
|
|
Tabs: {
|
|
itemHoverColor: isDark ? '#e07b3e' : '#c2541f',
|
|
itemSelectedColor: isDark ? '#e07b3e' : '#c2541f',
|
|
inkBarColor: isDark ? '#e07b3e' : '#c2541f'
|
|
},
|
|
Menu: {
|
|
itemBorderRadius: 8,
|
|
itemSelectedBg: isDark ? '#2d2017' : '#fdf2ea',
|
|
itemSelectedColor: isDark ? '#e07b3e' : '#c2541f'
|
|
},
|
|
Drawer: {
|
|
paddingLG: 20
|
|
},
|
|
Tag: {
|
|
borderRadiusSM: 6
|
|
}
|
|
}
|
|
}),
|
|
[isDark]
|
|
);
|
|
|
|
const ctxValue = useMemo<ThemeCtx>(
|
|
() => ({
|
|
mode,
|
|
toggle: () => setMode((m) => (m === 'dark' ? 'light' : 'dark')),
|
|
setMode
|
|
}),
|
|
[mode]
|
|
);
|
|
|
|
return (
|
|
<ThemeContext.Provider value={ctxValue}>
|
|
<ConfigProvider locale={zhCN} theme={themeConfig}>
|
|
<AntApp>{children}</AntApp>
|
|
</ConfigProvider>
|
|
</ThemeContext.Provider>
|
|
);
|
|
}
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
<React.StrictMode>
|
|
<ThemeProvider>
|
|
<BrowserRouter basename={import.meta.env.BASE_URL}>
|
|
<App />
|
|
</BrowserRouter>
|
|
</ThemeProvider>
|
|
</React.StrictMode>
|
|
);
|