Files
photo-live/apps/admin/src/hooks/core/useTheme.ts
T
浅念 a6cc190751 feat(admin): art-design-pro integration with local session auth
P1b Task 9: apps/admin based on art-design-pro v3.0.2 (tag, commit
d224dd4; source recorded in apps/admin/SOURCE.md), trimmed to
layout/login/i18n/request/table-form core. Login wired to real
POST /v1/auth/password (single-token session), Bearer header on all
requests, 401 interceptor logout+redirect, request-driven sliding
renewal (refresh at ~80% TTL, single-flight), best-effort
POST /v1/auth/logout on user-menu logout. One protected stub page
(相册管理). Dev port 5176 strictPort + /v1 proxy to 8080. Admin keeps
its own eslint9 flat config; root eslint ignores apps/admin/**.
vitest unit tests for renewal pacing + Playwright e2e (guard redirect,
localized wrong-password error, login lands on stub, token persists
across reload, logout clears session).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-18 11:47:32 +08:00

175 lines
4.9 KiB
TypeScript

/**
* useTheme - 系统主题管理
*
* 提供完整的主题切换和管理功能,支持亮色、暗色和自动模式。
* 自动处理主题切换时的过渡效果,确保切换流畅无闪烁。
*
* ## 主要功能
*
* 1. 主题切换 - 支持亮色、暗色、自动三种主题模式
* 2. 自动模式 - 根据系统偏好自动切换主题
* 3. 颜色适配 - 自动调整主题色的明暗变体(9 个层级)
* 4. 过渡优化 - 切换时临时禁用过渡效果,避免闪烁
* 5. 状态持久化 - 主题设置自动保存到 store
*
* ## 使用示例
*
* ```typescript
* const { switchThemeStyles } = useTheme()
*
* // 切换到暗色主题
* switchThemeStyles(SystemThemeEnum.DARK)
*
* // 切换到亮色主题
* switchThemeStyles(SystemThemeEnum.LIGHT)
*
* // 切换到自动模式(跟随系统)
* switchThemeStyles(SystemThemeEnum.AUTO)
* ```
*
* @module useTheme
* @author Art Design Pro Team
*/
import { useSettingStore } from '@/store/modules/setting'
import { SystemThemeEnum } from '@/enums/appEnum'
import AppConfig from '@/config'
import { SystemThemeTypes } from '@/types/store'
import { getDarkColor, getLightColor, setElementThemeColor } from '@/utils/ui'
import { usePreferredDark } from '@vueuse/core'
import { watch } from 'vue'
export function useTheme() {
const settingStore = useSettingStore()
// 禁用过渡效果
const disableTransitions = () => {
const style = document.createElement('style')
style.setAttribute('id', 'disable-transitions')
style.textContent = '* { transition: none !important; }'
document.head.appendChild(style)
}
// 启用过渡效果
const enableTransitions = () => {
const style = document.getElementById('disable-transitions')
if (style) {
style.remove()
}
}
// 设置系统主题
const setSystemTheme = (theme: SystemThemeEnum, themeMode?: SystemThemeEnum) => {
// 临时禁用过渡效果
disableTransitions()
const el = document.getElementsByTagName('html')[0]
const isDark = theme === SystemThemeEnum.DARK
if (!themeMode) {
themeMode = theme
}
const currentTheme = AppConfig.systemThemeStyles[theme as keyof SystemThemeTypes]
if (currentTheme) {
el.setAttribute('class', currentTheme.className)
}
// 设置按钮颜色加深或变浅
const primary = settingStore.systemThemeColor
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(
`--el-color-primary-light-${i}`,
isDark ? `${getDarkColor(primary, i / 10)}` : `${getLightColor(primary, i / 10)}`
)
}
// 更新store中的主题设置
settingStore.setGlopTheme(theme, themeMode)
// 使用 requestAnimationFrame 确保在下一帧恢复过渡效果
requestAnimationFrame(() => {
requestAnimationFrame(() => {
enableTransitions()
})
})
}
// 使用 VueUse 的 usePreferredDark 检测系统主题偏好
const prefersDark = usePreferredDark()
// 自动设置系统主题
const setSystemAutoTheme = () => {
const theme = prefersDark.value ? SystemThemeEnum.DARK : SystemThemeEnum.LIGHT
setSystemTheme(theme, SystemThemeEnum.AUTO)
}
// 切换主题
const switchThemeStyles = (theme: SystemThemeEnum) => {
if (theme === SystemThemeEnum.AUTO) {
setSystemAutoTheme()
} else {
setSystemTheme(theme)
}
}
return {
setSystemTheme,
setSystemAutoTheme,
switchThemeStyles,
prefersDark
}
}
/**
* 初始化主题系统
*/
export function initializeTheme() {
const settingStore = useSettingStore()
const prefersDark = usePreferredDark()
// 根据系统偏好应用主题
const applyThemeByMode = () => {
const el = document.getElementsByTagName('html')[0]
let actualTheme = settingStore.systemThemeType
// 如果是 AUTO 模式,检测系统偏好
if (settingStore.systemThemeMode === SystemThemeEnum.AUTO) {
actualTheme = prefersDark.value ? SystemThemeEnum.DARK : SystemThemeEnum.LIGHT
// 更新实际应用的主题类型
settingStore.systemThemeType = actualTheme
}
// 设置主题 class
const currentTheme = AppConfig.systemThemeStyles[actualTheme as keyof SystemThemeTypes]
if (currentTheme) {
el.setAttribute('class', currentTheme.className)
}
// 设置主题颜色
setElementThemeColor(settingStore.systemThemeColor)
// 设置圆角
document.documentElement.style.setProperty('--custom-radius', `${settingStore.customRadius}rem`)
}
// 应用主题
applyThemeByMode()
// 如果是 AUTO 模式,监听系统主题变化(使用 VueUse 的响应式特性)
if (settingStore.systemThemeMode === SystemThemeEnum.AUTO) {
watch(
prefersDark,
() => {
// 只有在 AUTO 模式下才响应系统主题变化
if (settingStore.systemThemeMode === SystemThemeEnum.AUTO) {
applyThemeByMode()
}
},
{ immediate: false }
)
}
}