主题定制
Zap Admin Vue3提供了灵活的主题定制功能,支持动态切换主题、暗黑模式以及自定义主题变量。
主题系统架构
主题系统采用CSS变量和SCSS变量相结合的方式实现:
src/styles/theme/
- 主题样式目录src/stores/app.js
- 主题状态管理src/utils/theme.js
- 主题工具函数
主题变量配置
在 src/styles/theme/_variables.scss
中定义主题变量:
// 基础颜色变量
$--color-primary: #409EFF;
$--color-success: #67C23A;
$--color-warning: #E6A23C;
$--color-danger: #F56C6C;
$--color-info: #909399;
// 文本颜色
$--color-text-primary: #303133;
$--color-text-regular: #606266;
$--color-text-secondary: #909399;
$--color-text-placeholder: #C0C4CC;
// 边框颜色
$--border-color-base: #DCDFE6;
$--border-color-light: #E4E7ED;
$--border-color-lighter: #EBEEF5;
$--border-color-extra-light: #F2F6FC;
// 背景颜色
$--background-color-base: #F5F7FA;
// 导出CSS变量
:root {
--color-primary: #{$--color-primary};
--color-success: #{$--color-success};
--color-warning: #{$--color-warning};
--color-danger: #{$--color-danger};
--color-info: #{$--color-info};
// 其他CSS变量...
}
主题切换实现
使用Pinia管理主题状态,并通过CSS变量动态切换主题:
// src/stores/app.js
export const useAppStore = defineStore('app', {
state: () => ({
theme: 'light',
themeColor: '#409EFF'
}),
actions: {
setTheme(theme) {
this.theme = theme
setTheme(theme)
},
setThemeColor(color) {
this.themeColor = color
setThemeColor(color)
}
}
})
// src/utils/theme.js
export function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme)
if (theme === 'dark') {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}
export function setThemeColor(color) {
document.documentElement.style.setProperty('--color-primary', color)
}
暗黑模式
暗黑模式通过覆盖CSS变量实现:
/* src/styles/theme/dark.scss */
[data-theme='dark'] {
--color-primary: #{$--color-primary};
--color-text-primary: #E5EAF3;
--color-text-regular: #CFD3DC;
--color-text-secondary: #A3A6AD;
--color-text-placeholder: #8D9095;
--border-color-base: #4C4D4F;
--border-color-light: #414243;
--border-color-lighter: #363637;
--border-color-extra-light: #2B2B2C;
--background-color-base: #1A1A1A;
--background-color-light: #1F1F1F;
}
主题切换组件
提供主题切换组件供用户选择主题:
// src/components/ThemePicker.vue
<template>
<div class="theme-picker">
<el-tooltip content="亮色模式">
<el-button
:type="theme === 'light' ? 'primary' : 'text'"
@click="handleThemeChange('light')"
>
<el-icon :size="20"><Sunny /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="暗黑模式">
<el-button
:type="theme === 'dark' ? 'primary' : 'text'"
@click="handleThemeChange('dark')"
>
<el-icon :size="20"><Moon /></el-icon>
</el-button>
</el-tooltip>
<el-color-picker
v-model="themeColor"
:predefine="predefineColors"
@change="handleThemeColorChange"
/>
</div>
</template>
<script setup>
import { Sunny, Moon } from '@element-plus/icons-vue'
import { computed } from 'vue'
import { useAppStore } from '@/stores/app'
const appStore = useAppStore()
const theme = computed(() => appStore.theme)
const themeColor = computed({
get: () => appStore.themeColor,
set: (val) => appStore.setThemeColor(val)
})
const predefineColors = [
'#409EFF',
'#67C23A',
'#E6A23C',
'#F56C6C',
'#909399',
'#722ED1',
'#13C2C2',
'#FA8C16'
]
const handleThemeChange = (val) => {
appStore.setTheme(val)
}
const handleThemeColorChange = (val) => {
appStore.setThemeColor(val)
}
</script>
主题持久化
使用localStorage持久化用户选择的主题:
// src/utils/theme.js
export function initTheme() {
const appStore = useAppStore()
// 从localStorage读取主题设置
const savedTheme = localStorage.getItem('theme')
const savedColor = localStorage.getItem('themeColor')
if (savedTheme) {
appStore.setTheme(savedTheme)
}
if (savedColor) {
appStore.setThemeColor(savedColor)
}
// 监听主题变化
watch(() => appStore.theme, (val) => {
localStorage.setItem('theme', val)
})
watch(() => appStore.themeColor, (val) => {
localStorage.setItem('themeColor', val)
})
}
响应式主题
根据系统偏好自动切换主题:
// src/utils/theme.js
export function setupThemeListener() {
const appStore = useAppStore()
// 检测系统主题偏好
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handleSystemThemeChange = (e) => {
const newTheme = e.matches ? 'dark' : 'light'
appStore.setTheme(newTheme)
}
// 初始设置
handleSystemThemeChange(darkModeMediaQuery)
// 监听系统主题变化
darkModeMediaQuery.addEventListener('change', handleSystemThemeChange)
// 清理函数
return () => {
darkModeMediaQuery.removeEventListener('change', handleSystemThemeChange)
}
}
主题最佳实践
- 使用CSS变量定义主题颜色,便于动态修改
- 为组件提供主题相关的props,增强灵活性
- 在SCSS中使用变量而非硬编码颜色值
- 提供合理的默认主题和暗黑主题
- 支持系统主题偏好自动切换