feat: 添加移动端下拉菜单和主题色设置

- 新增 MobileMenu 组件,小屏幕使用汉堡菜单替代多个按钮
- 重构 LanguageSettings 为统一下拉框样式
- 新增设置页面,支持主题色切换
- 翻译页添加源语言选择器
- 更新 8 种语言的 i18n 翻译
This commit is contained in:
2026-03-10 13:44:52 +08:00
parent 6b9fba254d
commit abcae1b8d1
22 changed files with 877 additions and 177 deletions

View File

@@ -0,0 +1,76 @@
"use client";
import { createContext, useContext, useEffect, useState } from "react";
import {
THEME_PRESETS,
DEFAULT_THEME,
getThemePreset,
applyThemeColors,
type ThemePreset,
} from "@/shared/theme-presets";
type ThemeContextType = {
currentTheme: string;
themePreset: ThemePreset;
setTheme: (themeId: string) => void;
availableThemes: ThemePreset[];
};
const ThemeContext = createContext<ThemeContextType | null>(null);
const STORAGE_KEY = "theme-preset";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [currentTheme, setCurrentTheme] = useState<string>(DEFAULT_THEME);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
const savedTheme = localStorage.getItem(STORAGE_KEY);
if (savedTheme && getThemePreset(savedTheme)) {
setCurrentTheme(savedTheme);
}
}, []);
useEffect(() => {
if (!mounted) return;
const preset = getThemePreset(currentTheme);
if (preset) {
applyThemeColors(preset);
localStorage.setItem(STORAGE_KEY, currentTheme);
}
}, [currentTheme, mounted]);
const setTheme = (themeId: string) => {
if (getThemePreset(themeId)) {
setCurrentTheme(themeId);
}
};
const themePreset = getThemePreset(currentTheme) || THEME_PRESETS[0];
if (!mounted) {
return null;
}
return (
<ThemeContext.Provider
value={{
currentTheme,
themePreset,
setTheme,
availableThemes: THEME_PRESETS,
}}
>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}