主题定制

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中使用变量而非硬编码颜色值
  • 提供合理的默认主题和暗黑主题
  • 支持系统主题偏好自动切换