refactor: 使用 Naive UI 重构后台布局与黑白双主题

This commit is contained in:
GouJ
2026-09-01 16:31:21 +08:00
parent 55516537bf
commit db8c176b55
51 changed files with 3277 additions and 2442 deletions
+91
View File
@@ -0,0 +1,91 @@
import { effectScope, nextTick, type EffectScope } from 'vue'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { useTheme } from './useTheme'
let scope: EffectScope | undefined
afterEach(() => {
scope?.stop()
scope = undefined
localStorage.clear()
delete document.documentElement.dataset.theme
document.documentElement.style.colorScheme = ''
vi.unstubAllGlobals()
vi.restoreAllMocks()
})
/** 可控系统主题,验证跟随模式与事件清理,不依赖测试机器偏好。 */
function setup(dark = false) {
const listeners = new Set<(event: MediaQueryListEvent) => void>()
vi.stubGlobal('matchMedia', () => ({
matches: dark,
addEventListener: (_: string, listener: (event: MediaQueryListEvent) => void) => listeners.add(listener),
removeEventListener: (_: string, listener: (event: MediaQueryListEvent) => void) => listeners.delete(listener)
}))
scope = effectScope()
const state = scope.run(useTheme)!
return {
...state,
listeners,
change: (matches: boolean) => listeners.forEach(listener => listener({ matches } as MediaQueryListEvent))
}
}
describe('黑白双主题', () => {
it('默认跟随系统并实时响应,销毁时移除监听', async () => {
const state = setup()
expect(state.preference.value).toBe('system')
expect(document.documentElement.dataset.theme).toBe('light')
state.change(true)
await nextTick()
expect(state.isDark.value).toBe(true)
expect(document.documentElement.style.colorScheme).toBe('dark')
expect(state.listeners.size).toBe(1)
scope!.stop()
expect(state.listeners.size).toBe(0)
})
it('手动选择优先于系统,并持久化到下一次挂载', async () => {
const state = setup(true)
state.preference.value = 'light'
await nextTick()
state.change(true)
await nextTick()
expect(state.isDark.value).toBe(false)
expect(localStorage.getItem('drama-studio-theme')).toBe('light')
scope!.stop()
const restored = setup(true)
expect(restored.preference.value).toBe('light')
expect(restored.theme.value).toBeNull()
})
it('深浅模式同时更新 Naive 主题和业务颜色标记', async () => {
const state = setup()
expect(state.overrides.value.common?.primaryColor).toBe('#202020')
state.preference.value = 'dark'
await nextTick()
expect(state.theme.value?.name).toBe('dark')
expect(state.overrides.value.common?.primaryColor).toBe('#f5f5f5')
expect(state.overrides.value.Button?.textColorPrimary).toBe('#171717')
expect(document.documentElement.dataset.theme).toBe('dark')
})
it('无效存储回退系统模式', () => {
localStorage.setItem('drama-studio-theme', 'unexpected')
const state = setup(true)
expect(state.preference.value).toBe('system')
expect(state.isDark.value).toBe(true)
})
it('禁用存储仍能切换当前会话的主题', async () => {
vi.spyOn(Storage.prototype, 'getItem').mockImplementation(() => {
throw new Error('storage blocked')
})
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
throw new Error('storage blocked')
})
const state = setup()
state.preference.value = 'dark'
await nextTick()
expect(document.documentElement.dataset.theme).toBe('dark')
})
})