From d5ba218b10b91e437ef1c8171d0ce9d77f95fc1d Mon Sep 17 00:00:00 2001 From: alger Date: Wed, 23 Jul 2025 22:45:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E7=B3=BB=E7=BB=9F=E5=88=87=E6=8D=A2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: #387 --- src/i18n/lang/en-US/settings.ts | 2 + src/i18n/lang/zh-CN/settings.ts | 2 + src/i18n/lang/zh-Hant/settings.ts | 2 + src/main/set.json | 4 +- src/renderer/store/modules/settings.ts | 68 ++++++++++++++++++++++++-- src/renderer/utils/theme.ts | 26 ++++++++++ src/renderer/views/set/index.vue | 27 ++++++++-- 7 files changed, 122 insertions(+), 9 deletions(-) diff --git a/src/i18n/lang/en-US/settings.ts b/src/i18n/lang/en-US/settings.ts index 3e7c60f..ecd3e9e 100644 --- a/src/i18n/lang/en-US/settings.ts +++ b/src/i18n/lang/en-US/settings.ts @@ -15,6 +15,8 @@ export default { basic: { themeMode: 'Theme Mode', themeModeDesc: 'Switch between light/dark theme', + autoTheme: 'Follow System', + manualTheme: 'Manual Switch', language: 'Language Settings', languageDesc: 'Change display language', font: 'Font Settings', diff --git a/src/i18n/lang/zh-CN/settings.ts b/src/i18n/lang/zh-CN/settings.ts index bbed282..c4f14d3 100644 --- a/src/i18n/lang/zh-CN/settings.ts +++ b/src/i18n/lang/zh-CN/settings.ts @@ -15,6 +15,8 @@ export default { basic: { themeMode: '主题模式', themeModeDesc: '切换日间/夜间主题', + autoTheme: '跟随系统', + manualTheme: '手动切换', language: '语言设置', languageDesc: '切换显示语言', font: '字体设置', diff --git a/src/i18n/lang/zh-Hant/settings.ts b/src/i18n/lang/zh-Hant/settings.ts index dee218d..1248f8d 100644 --- a/src/i18n/lang/zh-Hant/settings.ts +++ b/src/i18n/lang/zh-Hant/settings.ts @@ -15,6 +15,8 @@ export default { basic: { themeMode: '主題模式', themeModeDesc: '切換日間/夜間主題', + autoTheme: '跟隨系統', + manualTheme: '手動切換', language: '語言設定', languageDesc: '切換顯示語言', font: '字體設定', diff --git a/src/main/set.json b/src/main/set.json index 8e4233f..59ae862 100644 --- a/src/main/set.json +++ b/src/main/set.json @@ -25,5 +25,7 @@ "enableMusicUnblock": true, "enabledMusicSources": ["migu", "kugou", "pyncmd", "bilibili"], "showTopAction": false, - "contentZoomFactor": 1 + "contentZoomFactor": 1, + "autoTheme": false, + "manualTheme": "light" } diff --git a/src/renderer/store/modules/settings.ts b/src/renderer/store/modules/settings.ts index 51846f1..7299999 100644 --- a/src/renderer/store/modules/settings.ts +++ b/src/renderer/store/modules/settings.ts @@ -4,7 +4,7 @@ import { ref } from 'vue'; import setDataDefault from '@/../main/set.json'; import { isElectron } from '@/utils'; -import { applyTheme, getCurrentTheme, ThemeType } from '@/utils/theme'; +import { applyTheme, getCurrentTheme, getSystemTheme, watchSystemTheme, ThemeType } from '@/utils/theme'; export const useSettingsStore = defineStore('settings', () => { const theme = ref(getCurrentTheme()); @@ -18,6 +18,9 @@ export const useSettingsStore = defineStore('settings', () => { ]); const showDownloadDrawer = ref(false); + // 系统主题监听器清理函数 + let systemThemeCleanup: (() => void) | null = null; + // 先声明 setData ref 但不初始化 const setData = ref({}); @@ -56,8 +59,57 @@ export const useSettingsStore = defineStore('settings', () => { setData.value = getInitialSettings(); const toggleTheme = () => { - theme.value = theme.value === 'dark' ? 'light' : 'dark'; - applyTheme(theme.value); + if (setData.value.autoTheme) { + // 如果是自动模式,切换到手动模式并设置相反的主题 + const newTheme = theme.value === 'dark' ? 'light' : 'dark'; + setSetData({ + autoTheme: false, + manualTheme: newTheme + }); + theme.value = newTheme; + applyTheme(newTheme); + // 停止监听系统主题 + if (systemThemeCleanup) { + systemThemeCleanup(); + systemThemeCleanup = null; + } + } else { + // 手动模式下正常切换 + const newTheme = theme.value === 'dark' ? 'light' : 'dark'; + theme.value = newTheme; + setSetData({ manualTheme: newTheme }); + applyTheme(newTheme); + } + }; + + const setAutoTheme = (auto: boolean) => { + setSetData({ autoTheme: auto }); + + if (auto) { + // 启用自动模式 + const systemTheme = getSystemTheme(); + theme.value = systemTheme; + applyTheme(systemTheme); + + // 开始监听系统主题变化 + systemThemeCleanup = watchSystemTheme((newTheme) => { + if (setData.value.autoTheme) { + theme.value = newTheme; + applyTheme(newTheme); + } + }); + } else { + // 切换到手动模式 + const manualTheme = setData.value.manualTheme || 'light'; + theme.value = manualTheme; + applyTheme(manualTheme); + + // 停止监听系统主题 + if (systemThemeCleanup) { + systemThemeCleanup(); + systemThemeCleanup = null; + } + } }; const setMiniMode = (value: boolean) => { @@ -106,7 +158,14 @@ export const useSettingsStore = defineStore('settings', () => { }; const initializeTheme = () => { - applyTheme(theme.value); + // 根据设置初始化主题 + if (setData.value.autoTheme) { + setAutoTheme(true); + } else { + const manualTheme = setData.value.manualTheme || getCurrentTheme(); + theme.value = manualTheme; + applyTheme(manualTheme); + } }; const initializeSystemFonts = async () => { @@ -133,6 +192,7 @@ export const useSettingsStore = defineStore('settings', () => { showDownloadDrawer, setSetData, toggleTheme, + setAutoTheme, setMiniMode, setShowUpdateModal, setShowArtistDrawer, diff --git a/src/renderer/utils/theme.ts b/src/renderer/utils/theme.ts index d77cb61..0a8b3b1 100644 --- a/src/renderer/utils/theme.ts +++ b/src/renderer/utils/theme.ts @@ -1,5 +1,13 @@ export type ThemeType = 'dark' | 'light'; +// 检测系统主题 +export const getSystemTheme = (): ThemeType => { + if (typeof window !== 'undefined' && window.matchMedia) { + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + } + return 'light'; +}; + // 应用主题 export const applyTheme = (theme: ThemeType) => { // 使用 Tailwind 的暗色主题类 @@ -17,3 +25,21 @@ export const applyTheme = (theme: ThemeType) => { export const getCurrentTheme = (): ThemeType => { return (localStorage.getItem('theme') as ThemeType) || 'light'; }; + +// 监听系统主题变化 +export const watchSystemTheme = (callback: (theme: ThemeType) => void) => { + if (typeof window !== 'undefined' && window.matchMedia) { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handler = (e: MediaQueryListEvent) => { + callback(e.matches ? 'dark' : 'light'); + }; + + mediaQuery.addEventListener('change', handler); + + // 返回清理函数 + return () => { + mediaQuery.removeEventListener('change', handler); + }; + } + return () => {}; +}; diff --git a/src/renderer/views/set/index.vue b/src/renderer/views/set/index.vue index 079db71..783c734 100644 --- a/src/renderer/views/set/index.vue +++ b/src/renderer/views/set/index.vue @@ -25,10 +25,25 @@
{{ t('settings.basic.themeMode') }}
{{ t('settings.basic.themeModeDesc') }}
- - - - +
+
+ + + + + + {{ setData.autoTheme ? t('settings.basic.autoTheme') : t('settings.basic.manualTheme') }} + +
+ + + + +
@@ -590,6 +605,10 @@ const isDarkTheme = computed({ set: () => settingsStore.toggleTheme() }); +const handleAutoThemeChange = (value: boolean) => { + settingsStore.setAutoTheme(value); +}; + const openAuthor = () => { window.open(setData.value.authorUrl); };