This commit is contained in:
zhongluofeng
2026-07-17 18:28:37 +08:00
parent d8006a19e9
commit 2d9a8ddef6
57 changed files with 90 additions and 58 deletions
+53 -14
View File
@@ -60,6 +60,14 @@ export const useAppStore = defineStore('app', () => {
const modules = ref<ModuleInfo[]>(initModulesFromRegistry())
const moduleOrder = ref<string[]>(initModuleOrder())
/**
* 系统真实的深浅色偏好(不受应用 setTheme 影响)。
* 应用启动时用 matchMedia 初始化(此时未调用 setTheme,结果准确),
* 之后仅通过 Tauri onThemeChanged 事件更新(该事件反映系统主题变化,不受 setTheme 影响)。
* 用于"跟随系统"卡片色块等需要反映系统真实状态的 UI。
*/
const systemDark = ref(window.matchMedia('(prefers-color-scheme: dark)').matches)
/** 正在处理切换的模块 ID 集合(防止重复点击) */
const togglingModules = ref<Set<string>>(new Set())
@@ -322,7 +330,14 @@ export const useAppStore = defineStore('app', () => {
isDark = true
root.classList.add('dark')
} else if (theme.value === 'system') {
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches
// 通过 Tauri 获取系统真实主题,避免 matchMedia 被 setTheme 污染
try {
const tauriWindow = getCurrentWindow()
const sysTheme = await tauriWindow.theme()
isDark = sysTheme === 'dark'
} catch {
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches
}
if (isDark) {
root.classList.add('dark')
}
@@ -330,7 +345,13 @@ export const useAppStore = defineStore('app', () => {
try {
const tauriWindow = getCurrentWindow()
await tauriWindow.setTheme(isDark ? 'dark' : 'light')
if (theme.value === 'system') {
// 让窗口原生跟随系统主题,而不是固定成当前匹配值
// 固定主题会导致系统切换深浅色时窗口标题栏等原生 UI 不同步
await tauriWindow.setTheme(null)
} else {
await tauriWindow.setTheme(isDark ? 'dark' : 'light')
}
} catch (e) {
// 非 Tauri 环境下忽略
}
@@ -372,22 +393,26 @@ export const useAppStore = defineStore('app', () => {
}
}
const handleSystemThemeChange = async (e: MediaQueryListEvent) => {
// 系统主题变化时,若当前为"跟随系统"模式,同步更新 DOM 和窗口效果。
// 使用 Tauri 的 onThemeChanged 事件(而非 matchMedia),因为 setTheme('dark'/'light')
// 会改变 WebView 的 prefers-color-scheme 媒体查询结果,导致 matchMedia 误触发。
//
// 注意:onThemeChanged 也会被 setTheme('dark'/'light') 触发(非系统真实变化)。
// 因此 systemDark 只在 theme.value === 'system' 时更新:
// - system 模式下 setTheme(null) 不固定主题,onThemeChanged 反映系统真实色
// - light/dark 模式下 setTheme 引起的 onThemeChanged 被忽略,systemDark 保持不变
// - 用户切回 system 模式时,setTheme(null) 会触发 onThemeChanged 反映系统真实色,自动修正
const handleSystemThemeChange = async (newTheme: 'light' | 'dark' | null) => {
if (theme.value === 'system') {
systemDark.value = newTheme === 'dark'
const root = document.documentElement
if (e.matches) {
if (newTheme === 'dark') {
root.classList.add('dark')
} else {
root.classList.remove('dark')
}
try {
const tauriWindow = getCurrentWindow()
await tauriWindow.setTheme(e.matches ? 'dark' : 'light')
} catch (e) {
// 非 Tauri 环境下忽略
}
// 窗口已通过 setTheme(null) 原生跟随系统,无需再手动 setTheme
await applyEffect()
}
}
@@ -419,8 +444,21 @@ export const useAppStore = defineStore('app', () => {
await applyTheme()
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
mediaQuery.addEventListener('change', handleSystemThemeChange)
// 使用 Tauri 的 onThemeChanged 监听系统主题变化,而非 matchMedia。
// 因为 setTheme('dark'/'light') 会改变 WebView 的 prefers-color-scheme 媒体查询,
// 导致 matchMedia 在非 system 模式下也误触发(虽有 theme.value 守卫,但更可靠)。
try {
const tauriWindow = getCurrentWindow()
await tauriWindow.onThemeChanged(({ payload }) => {
handleSystemThemeChange(payload)
})
} catch {
// 非 Tauri 环境回退到 matchMedia
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
mediaQuery.addEventListener('change', (e) => {
handleSystemThemeChange(e.matches ? 'dark' : 'light')
})
}
isInitialized.value = true
} finally {
@@ -436,6 +474,7 @@ export const useAppStore = defineStore('app', () => {
return {
theme,
effect,
systemDark,
isAutoStart,
isInitialized,
modules,