vue3 + Element-plus 开发后台管理系统(23)

1,458 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情

后台项目前端综合解决方案之通用功能开发

动态换肤原理分析

所谓的动态换肤,说白一点就是涉及到的色值不可以写死

那么我们就先来说一下动态换肤的实现方法

scss 中,我们可以通过 $变量名:变量值 的方式定义 css 变量,然后通过该 css 变量去指定某一块 DOM 对应的颜色

此时,如果我们改变了 css 变量的值,那么对应的 DOM 的颜色也会改变,试想一下,如果有大量的 DOM 依赖于该 css 变量,那么我们改变这个变量时,是不是就会使得所有涉及的 DOM 的颜色就都会改变,这个其实就是所谓的动态换肤

而在项目中,我们要实现动态换肤需要处理的有两个方面

1、element-plus 的主题

2、非 element-plus 的主题

动态换肤实现方案分析

从上边的原理我们可以得到两个关键的信息

1、动态换肤的关键在于修改 css 的值

2、换肤需要同时兼顾 element-plus 以及 非 element-plus

根据以上信息,我们就可以得出对应的实现方案

1、创建一个组件 ThemeSelect 用来处理修改之后的 css 变量的值

2、根据新值修改 element-plus 主题色

3、根据心智修改非 element-plus 主题色

创建 ThemeSelect 组件

创建 components/ThemeSelect/index 组件

<template>
  <!-- 主题图标  
  v-bind:https://v3.cn.vuejs.org/api/instance-properties.html#attrs -->
  <el-dropdown
    v-bind="$attrs"
    trigger="click"
    class="theme"
    @command="handleSetTheme"
  >
    <div>
      <el-tooltip :content="$t('msg.navBar.themeChange')">
        <svg-icon icon="change-theme" />
      </el-tooltip>
    </div>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item command="color">
          {{ $t('msg.theme.themeColorChange') }}
        </el-dropdown-item>
      </el-dropdown-menu>
    </template>
  </el-dropdown>
  <!-- 展示弹出层 -->
  <div></div>
</template>

<script setup>
const handleSetTheme = command => {}
</script>

<style lang="scss" scoped></style>

layout/components/Navbar/index.vue 中进行引入

<div class="right-menu">
  <theme-picker class="right-menu-item hover-effect"></theme-picker>
</div>
      
import ThemePicker from '@/components/ThemeSelect/index'

创建 SelectColor 组件

在有了 ThemeSelect 之后,接下来我们处理颜色选择器组件 SelectColor,在这里我们会用到 elementel-color-picker 组件

对于 SelectColor 的处理,我们需要分成两步

1、完成 SelectColor 弹窗的双向数据绑定

2、把选中的色值进行本地缓存

首先来看第一步

创建 components/ThemePicker/components/SelectColor.vue

<template>
  <el-dialog title="提示" :model-value="modelValue" @close="closed" width="22%">
    <div class="center">
      <p class="title">{{ $t('msg.theme.themeColorChange') }}</p>
      <el-color-picker
        v-model="mColor"
        :predefine="predefineColors"
      ></el-color-picker>
    </div>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="closed">{{ $t('msg.universal.cancel') }}</el-button>
        <el-button type="primary" @click="comfirm">{{
          $t('msg.universal.confirm')
        }}</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup>
import { defineProps, defineEmits, ref } from 'vue'
defineProps({
  modelValue: {
    type: Boolean,
    required: true
  }
})
const emits = defineEmits(['update:modelValue'])

// 预定义色值
const predefineColors = [
  '#ff4500',
  '#ff8c00',
  '#ffd700',
  '#90ee90',
  '#00ced1',
  '#1e90ff',
  '#c71585',
  'rgba(255, 69, 0, 0.68)',
  'rgb(255, 120, 0)',
  'hsv(51, 100, 98)',
  'hsva(120, 40, 94, 0.5)',
  'hsl(181, 100%, 37%)',
  'hsla(209, 100%, 56%, 0.73)',
  '#c7158577'
]
// 默认色值
const mColor = ref('#00ff00')

/**
 * 关闭
 */
const closed = () => {
  emits('update:modelValue', false)
}
/**
 * 确定
 * 1. 修改主题色
 * 2. 保存最新的主题色
 * 3. 关闭 dialog
 */
const comfirm = async () => {
  // 3. 关闭 dialog
  closed()
}
</script>

<style lang="scss" scoped>
.center {
  text-align: center;
  .title {
    margin-bottom: 12px;
  }
}
</style>

ThemePicker/index 中使用该组件

<template>
  <!-- 展示弹出层 -->
  <div>
    <select-color v-model="selectColorVisible"></select-color>
  </div>
</template>

<script setup>
import SelectColor from './components/SelectColor.vue'
import { ref } from 'vue'

const selectColorVisible = ref(false)
const handleSetTheme = command => {
  selectColorVisible.value = true
}
</script>

至此双向数据绑定已经完成,下边需要进行的是色值的本地缓存

缓存方式分为两种

1、vuex

2、本地存储

constants/index 下新建常量值

// 主题色保存的 key
export const MAIN_COLOR = 'mainColor'
// 默认色值
export const DEFAULT_COLOR = '#409eff'

创建 store/modules/theme 模块,用来处理主题色相关内容

import { getItem, setItem } from '@/utils/storage'
import { MAIN_COLOR, DEFAULT_COLOR } from '@/constant'
export default {
  namespaced: true,
  state: () => ({
    mainColor: getItem(MAIN_COLOR) || DEFAULT_COLOR
  }),
  mutations: {
    /**
     * 设置主题色
     */
    setMainColor(state, newColor) {
      state.mainColor = newColor
      setItem(MAIN_COLOR, newColor)
    }
  }
}

在 store/getters 下指定快捷访问

mainColor: state => state.theme.mainColor

在 store/index 中导入 theme

import theme from './modules/theme.js'

export default createStore({
  getters,
  modules: {
    theme
  }
})

在 selectColor 中,设置初始色值和缓存色值

<script setup>
import { defineProps, defineEmits, ref } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
// 默认色值
const mColor = ref(store.getters.mainColor)
/**
 * 确定
 * 1. 修改主题色
 * 2. 保存最新的主题色
 * 3. 关闭 dialog
 */
const comfirm = async () => {
  // 2. 保存最新的主题色
  store.commit('theme/setMainColor', mColor.value)
  // 3. 关闭 dialog
  closed()
}
</script>