diff --git a/frontend/src/views/user/Settings.vue b/frontend/src/views/user/Settings.vue index 80f22db..74333a4 100644 --- a/frontend/src/views/user/Settings.vue +++ b/frontend/src/views/user/Settings.vue @@ -62,6 +62,7 @@ @@ -107,6 +108,7 @@ @@ -320,6 +322,7 @@ import { ref, onMounted } from 'vue' import { useAuthStore } from '@/stores/auth' import { meApi, type Profile } from '@/api/me' +import { useDarkMode, type ThemeMode } from '@/composables/useDarkMode' import Card from '@/components/ui/card.vue' import Button from '@/components/ui/button.vue' import Badge from '@/components/ui/badge.vue' @@ -338,6 +341,7 @@ import { log } from '@/utils/logger' const authStore = useAuthStore() const { success, error: showError } = useToast() +const { setThemeMode } = useDarkMode() const profile = ref(null) @@ -375,20 +379,8 @@ function handleThemeChange(value: string) { themeSelectOpen.value = false updatePreferences() - // 应用主题 - if (value === 'dark') { - document.documentElement.classList.add('dark') - } else if (value === 'light') { - document.documentElement.classList.remove('dark') - } else { - // system: 跟随系统 - const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches - if (prefersDark) { - document.documentElement.classList.add('dark') - } else { - document.documentElement.classList.remove('dark') - } - } + // 使用 useDarkMode 统一切换主题 + setThemeMode(value as ThemeMode) } function handleLanguageChange(value: string) { @@ -418,10 +410,16 @@ async function loadProfile() { async function loadPreferences() { try { const prefs = await meApi.getPreferences() + + // 主题以本地 localStorage 为准(useDarkMode 在应用启动时已初始化) + // 这样可以避免刷新页面时主题被服务端旧值覆盖 + const { themeMode: currentThemeMode } = useDarkMode() + const localTheme = currentThemeMode.value + preferencesForm.value = { avatar_url: prefs.avatar_url || '', bio: prefs.bio || '', - theme: prefs.theme || 'light', + theme: localTheme, // 使用本地主题,而非服务端返回值 language: prefs.language || 'zh-CN', timezone: prefs.timezone || 'Asia/Shanghai', notifications: { @@ -431,11 +429,12 @@ async function loadPreferences() { } } - // 应用主题 - if (preferencesForm.value.theme === 'dark') { - document.documentElement.classList.add('dark') - } else if (preferencesForm.value.theme === 'light') { - document.documentElement.classList.remove('dark') + // 如果本地主题和服务端不一致,同步到服务端(静默更新,不提示用户) + const serverTheme = prefs.theme || 'light' + if (localTheme !== serverTheme) { + meApi.updatePreferences({ theme: localTheme }).catch(() => { + // 静默失败,不影响用户体验 + }) } } catch (error) { log.error('加载偏好设置失败:', error) diff --git a/src/services/user/preference.py b/src/services/user/preference.py index 8ca2874..7adc603 100644 --- a/src/services/user/preference.py +++ b/src/services/user/preference.py @@ -71,8 +71,8 @@ class PreferenceService: raise NotFoundException("Provider not found or inactive") preferences.default_provider_id = default_provider_id if theme is not None: - if theme not in ["light", "dark", "auto"]: - raise ValueError("Invalid theme. Must be 'light', 'dark', or 'auto'") + if theme not in ["light", "dark", "auto", "system"]: + raise ValueError("Invalid theme. Must be 'light', 'dark', 'auto', or 'system'") preferences.theme = theme if language is not None: preferences.language = language