增加自动亮色/暗色主题切换 + 清理 pyc 文件

- 新建 store/theme.tsx: 亮色/暗色/跟随系统三种模式
- App.tsx: ThemeProvider + ConfigProvider 暗色算法
- MainLayout: 侧边栏主题切换按钮,Sider/Content 随主题变化
- 所有页面硬编码颜色替换为 antd token
- AssignmentsPage: Tag 用预设颜色名适配暗色,未选中背景用 colorBgContainer
- LoginPage: 暗色深色渐变背景
- 从 git 移除所有 __pycache__/*.pyc 文件

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
yml2213
2026-06-22 20:09:57 +08:00
co-authored by Claude Fable 5
parent 2f9b1a8eb2
commit 4522421293
26 changed files with 212 additions and 73 deletions
+65
View File
@@ -0,0 +1,65 @@
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react';
export type ThemeMode = 'light' | 'dark' | 'system';
const STORAGE_KEY = 'theme_mode';
const DEFAULT_MODE: ThemeMode = 'system';
interface ThemeContextValue {
mode: ThemeMode;
isDark: boolean;
setMode: (mode: ThemeMode) => void;
toggle: () => void;
}
const ThemeContext = createContext<ThemeContextValue>({
mode: DEFAULT_MODE,
isDark: false,
setMode: () => {},
toggle: () => {},
});
export function ThemeProvider({ children }: { children: ReactNode }) {
const [mode, setModeState] = useState<ThemeMode>(() => {
const saved = localStorage.getItem(STORAGE_KEY);
return (saved as ThemeMode) || DEFAULT_MODE;
});
// 监听系统主题变化
const [systemIsDark, setSystemIsDark] = useState(() =>
window.matchMedia('(prefers-color-scheme: dark)').matches
);
useEffect(() => {
const mql = window.matchMedia('(prefers-color-scheme: dark)');
const handler = (e: MediaQueryListEvent) => setSystemIsDark(e.matches);
mql.addEventListener('change', handler);
return () => mql.removeEventListener('change', handler);
}, []);
const isDark = mode === 'dark' || (mode === 'system' && systemIsDark);
const setMode = useCallback((newMode: ThemeMode) => {
setModeState(newMode);
localStorage.setItem(STORAGE_KEY, newMode);
}, []);
const toggle = useCallback(() => {
setMode(isDark ? 'light' : 'dark');
}, [isDark, setMode]);
// 同步到 HTML root,方便 CSS 使用
useEffect(() => {
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
}, [isDark]);
return (
<ThemeContext.Provider value={{ mode, isDark, setMode, toggle }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
return useContext(ThemeContext);
}