整合主题管理器,优化主题切换逻辑

This commit is contained in:
jxxghp
2025-07-25 13:39:10 +08:00
parent cf72b2cdb9
commit f36c1bd2b5
6 changed files with 450 additions and 215 deletions

212
src/utils/themeManager.ts Normal file
View File

@@ -0,0 +1,212 @@
// 主题管理器 - 动态加载主题CSS
export interface ThemeConfig {
name: string
cssPath: string
isLoaded: boolean
}
class ThemeManager {
private themes: Map<string, ThemeConfig> = new Map()
private currentTheme: string = 'default'
private loadedLinks: Map<string, HTMLLinkElement> = new Map()
constructor() {
// 注册所有可用主题
this.registerTheme('default', '')
this.registerTheme('light', '')
this.registerTheme('dark', '')
this.registerTheme('purple', '')
this.registerTheme('auto', '')
// 只有透明主题有特定的CSS文件
this.registerTheme('transparent', './src/styles/themes/transparent.css')
}
/**
* 注册主题
*/
registerTheme(name: string, cssPath: string): void {
this.themes.set(name, {
name,
cssPath,
isLoaded: false,
})
}
/**
* 获取当前主题
*/
getCurrentTheme(): string {
return this.currentTheme
}
/**
* 设置主题
*/
async setTheme(themeName: string): Promise<void> {
if (!this.themes.has(themeName)) {
console.warn(`Theme "${themeName}" not found`)
return
}
const theme = this.themes.get(themeName)!
// 清理其他主题的CSS除了当前要设置的主题
this.unloadOtherThemes()
// 如果主题有CSS文件则加载CSS
if (theme.cssPath) {
try {
await this.loadThemeCSS(themeName, theme.cssPath)
} catch (error) {
console.error(`Failed to load CSS for theme "${themeName}":`, error)
// 即使CSS加载失败也继续应用主题使用默认样式
}
}
// 应用主题无论是否有CSS文件
this.applyTheme(themeName)
}
/**
* 加载主题CSS文件
*/
private async loadThemeCSS(themeName: string, cssPath: string): Promise<void> {
// 如果已经加载过,直接返回
if (this.loadedLinks.has(themeName)) {
return
}
try {
// 动态导入CSS模块
if (themeName === 'transparent') {
await import('@/styles/themes/transparent.scss')
this.themes.get(themeName)!.isLoaded = true
return
}
// 对于其他主题使用传统的link方式
const link = document.createElement('link')
link.rel = 'stylesheet'
link.type = 'text/css'
link.href = cssPath
link.id = `theme-${themeName}`
// 等待CSS加载完成
await new Promise<void>((resolve, reject) => {
link.onload = () => {
this.loadedLinks.set(themeName, link)
this.themes.get(themeName)!.isLoaded = true
resolve()
}
link.onerror = () => {
reject(new Error(`Failed to load theme CSS: ${cssPath}`))
}
})
// 添加到head
document.head.appendChild(link)
} catch (error) {
console.error(`Error loading theme "${themeName}":`, error)
throw error
}
}
/**
* 应用主题到DOM
*/
private applyTheme(themeName: string): void {
// 移除之前的主题属性
document.documentElement.removeAttribute('data-theme')
// 设置新主题除了default主题
if (themeName !== 'default') {
document.documentElement.setAttribute('data-theme', themeName)
}
this.currentTheme = themeName
// 触发主题变更事件
this.dispatchThemeChangeEvent(themeName)
}
/**
* 卸载主题CSS
*/
unloadTheme(themeName: string): void {
const theme = this.themes.get(themeName)
if (!theme) return
// 对于动态导入的CSS我们无法直接卸载但可以标记为未加载
if (themeName === 'transparent') {
theme.isLoaded = false
return
}
// 对于传统link方式加载的CSS
const link = this.loadedLinks.get(themeName)
if (link) {
link.remove()
this.loadedLinks.delete(themeName)
theme.isLoaded = false
}
}
/**
* 卸载所有主题CSS除了当前主题
*/
unloadOtherThemes(): void {
for (const [themeName] of this.themes) {
if (themeName !== this.currentTheme && this.themes.get(themeName)?.isLoaded) {
this.unloadTheme(themeName)
}
}
}
/**
* 获取已注册的主题列表
*/
getAvailableThemes(): string[] {
return Array.from(this.themes.keys())
}
/**
* 检查主题是否已加载
*/
isThemeLoaded(themeName: string): boolean {
return this.themes.get(themeName)?.isLoaded || false
}
/**
* 触发主题变更事件
*/
private dispatchThemeChangeEvent(themeName: string): void {
const event = new CustomEvent('themechange', {
detail: { theme: themeName },
})
document.dispatchEvent(event)
}
/**
* 监听主题变更事件
*/
onThemeChange(callback: (theme: string) => void): void {
document.addEventListener('themechange', (event: any) => {
callback(event.detail.theme)
})
}
/**
* 移除主题变更监听器
*/
offThemeChange(callback: (theme: string) => void): void {
document.removeEventListener('themechange', (event: any) => {
callback(event.detail.theme)
})
}
}
// 创建单例实例
export const themeManager = new ThemeManager()
// 导出类型
export type { ThemeManager }