vue3+element-plus项目主题色切换;element-plus换肤
文章目录
- 前言
- 一、实现方案
- 1.使用css变量+class
- 2. 切换主题
- 3. 实现思路
- 4.对比其他方案
- 二、具体实现
- 1. 默认主题色
- 2. 定义一套或多套定制主题色
- 2.1新增src/style/theme.scss
- 2.2main.js引入
- 3. 构建配置
- 4. 切换逻辑和持久化
- 4.1 动态添加class
- 4.2 持久化
- 5. 组件引入
- 6. main.js初始化主题色
- 二、 涉及变更
- 1. element-plus、vxe-table、antv/g6、echarts等第三方组件库
- 2. 原生html
- 3. 背景图
- 三、其他换色思路
- 1.主题方式
- 2.别的博主实现方式
前言
提示:该方案足以实现切换主题色功能,具体截图待补充
场景:当前vue3+element-plus的项目,要求实现点击按钮切换主题色
注意项:切换主题色,不仅涉及到element-plus的组件样式,还需要手动变更原生的html等div样式;如果涉及第三方组件库(例如:vxe-table、echarts)同样需要修改样式
暂时截图
一、实现方案
1.使用css变量+class
css变量使用说明
原因:
- element-plus和vxe-table等组件库,内部样式均是通过使用通用的css变量(例如–el-color-primary、–el-color-primary-light-5等),通过修改对应css变量即可修改组件样式
- 通过自定义通用css变量,多页面共用,统一维护;
2. 切换主题
参考element-plus组件库切换主题色逻辑:
- 动态给html标签添加class类名,针对不同类名修改css变量值和定制化样式,以此实现系统换肤
3. 实现思路
- 定义多个主题(默认、暗黑、自定义品牌色)
- 修改与 绑定全局 CSS 变量
- 切换时修改 HTML 或 body 的 class
- 主题切换状态持久化(localStorage)
- 页面组件响应式刷新主题色(通过变量)
4.对比其他方案
当前方案:使用css变量+class
其他:
1.使用主题SCSS 多套主题构建:
优点:风格完全隔离,稳定性强
缺点:基于element-plus封装的公司组件库,项目也引入使用了,但是这种方式就无法渗透改变公司组件库样式(而上述css变量+class却可以实现全局所有样式的修改)
二、具体实现
首先需要配置一套默认的主题色,可直接参考链接:配置默认主题色
1. 默认主题色
src/style/element.scss
// index.scss
@forward "element-plus/theme-chalk/src/common/var.scss" with ($colors: ("white": #ffffff,"black": #000000,"primary": ("base": #007d7b),"success": ("base": #8bc34a),"warning": ("base": #ffc107),"danger": ("base": #f56c6c),"error": ("base": #ff5722),"info": ("base": #909399))
);
2. 定义一套或多套定制主题色
2.1新增src/style/theme.scss
至于如何找到element-plus的css变量,可以直接看控制台的:root变量
/** element内置暗黑主题 */
@use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;// 可以进行一些样式的覆盖
html {--v-bg-color: #1fff; // 新增公共变量 -其他页面可用使用 color: var(--v-bg-color)
}html.skins {--my-css-wbg: #223648;// Element Plus 主色--el-color-primary: #0084cb;--el-color-success: #2bc667;--el-color-warning: #f4c42d;--el-color-danger: #f56c6c;--el-color-error: #ff5722;--el-color-info: #909399;color-scheme: dark;--el-color-white: #ffffff;--el-color-black: #333;--el-color-primary-light-3: #4da9db;--el-color-primary-light-5: #80c2e5;--el-color-primary-light-7: #b3daef;--el-color-primary-light-8: #cce6f5;--el-color-primary-light-9: #e6f3fa;--el-color-primary-dark-2: #006aa2;--el-color-success-light-3: #6bd795;--el-color-success-light-5: #95e3b3;--el-color-success-light-7: #bfeed1;--el-color-success-light-8: #d5f4e1;--el-color-success-light-9: #eaf9f0;--el-color-success-dark-2: #229e52;--el-color-warning-light-3: #f7d66c;--el-color-warning-light-5: #fae296;--el-color-warning-light-7: #fcedc0;--el-color-warning-light-8: #fdf3d5;--el-color-warning-light-9: #fef9ea;--el-color-warning-dark-2: #c39d24;--el-color-danger-light-3: #f89898;--el-color-danger-light-5: #fab6b6;--el-color-danger-light-7: #fcd3d3;--el-color-danger-light-8: #fde2e2;--el-color-danger-light-9: #fef0f0;--el-color-danger-dark-2: #c45656;--el-color-error-light-3: #ff8964;--el-color-error-light-5: #ffab91;--el-color-error-light-7: #ffcdbd;--el-color-error-light-8: #ffddd3;--el-color-error-light-9: #ffeee9;--el-color-error-dark-2: #cc461b;--el-color-info-light-3: #b1b3b8;--el-color-info-light-5: #c8c9cc;--el-color-info-light-7: #dedfe0;--el-color-info-light-8: #e9e9eb;--el-color-info-light-9: #f4f4f5;--el-color-info-dark-2: #7d8085;--el-bg-color: #f1ff;--el-bg-color-page: #f2f3f5;--el-bg-color-overlay: #ffffff;--el-text-color-primary: #303133;--el-text-color-regular: #606266;--el-text-color-secondary: #909399;--el-text-color-placeholder: #a8abb2;--el-text-color-disabled: #c0c4cc;--el-border-color: #dcdfe6;--el-border-color-light: #e4e7ed;--el-border-color-lighter: #ebeef5;--el-border-color-extra-light: #f2f6fc;--el-border-color-dark: #d4d7de;--el-border-color-darker: #cdd0d6;--el-fill-color: #f0f2f5;--el-fill-color-light: #f5f7fa;--el-fill-color-lighter: #fafafa;--el-fill-color-extra-light: #fafcff;--el-fill-color-dark: #ebedf0;--el-fill-color-darker: #e6e8eb;--el-fill-color-blank: #ffffff;--el-box-shadow: 0px 12px 32px 4px rgba(0, 0, 0, 0.04), 0px 8px 20px rgba(0, 0, 0, 0.08);--el-box-shadow-light: 0px 0px 12px rgba(0, 0, 0, 0.12);--el-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.12);--el-box-shadow-dark: 0px 16px 48px 16px rgba(0, 0, 0, 0.08), 0px 12px 32px rgba(0, 0, 0, 0.12), 0px 8px 16px -8px rgba(0, 0, 0, 0.16);--el-disabled-bg-color: var(--el-fill-color-light);--el-disabled-text-color: var(--el-text-color-placeholder);--el-disabled-border-color: var(--el-border-color-light);--el-overlay-color: rgba(0, 0, 0, 0.8);--el-overlay-color-light: rgba(0, 0, 0, 0.7);--el-overlay-color-lighter: rgba(0, 0, 0, 0.5);--el-mask-color: rgba(255, 255, 255, 0.9);--el-mask-color-extra-light: rgba(255, 255, 255, 0.3);--el-border-width: 1px;--el-border-style: solid;--el-border-color-hover: var(--el-text-color-disabled);--el-border: var(--el-border-width) var(--el-border-style) var(--el-border-color);--el-svg-monochrome-grey: var(--el-border-color);--el-input-bg-color: var(--my-css-wbg);--el-fill-color-blank: var(--my-css-wbg);--vxe-primary-color: #0084cb;--vxe-ui-layout-background-color: #223648;--vxe-ui-table-header-background-color: #20496e; // 表格头背景--vxe-ui-table-row-striped-background-color: #243c52;--vxe-ui-font-color: #ffffff; // 表格字体颜// vxe-table.vxe-toolbar-custom-target {background-color: var(--vxe-primary-color)!important;}.wbg {background-color: var(--my-css-wbg);}.theme_div {background-color: #1fff;}
}
2.2main.js引入
@use "element-plus/theme-chalk/src/index.scss";
@import "./theme.scss";
3. 构建配置
vite.config.js
{ defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'export default defineConfig({css: {preprocessorOptions: {scss: {additionalData: `@use "@/style/element.scss" as *; `}}}
})
4. 切换逻辑和持久化
新增src/utils/theme.js
export const currentTheme = ref((localStorage.getItem('app_theme')) || 'light'
)
export function toggleTheme() {currentTheme.value = currentTheme.value === 'light' ? 'dark' : 'light'localStorage.setItem('app_theme', currentTheme.value)//页面挂载时,根据数据库的用户皮肤主题,渲染页面的主题applyTheme2(currentTheme.value)
}
export function applyTheme(theme) {if (theme === 'light') {//此时为默认状态,移除html的skins样式document.documentElement.classList.remove('skins'); //该段代码表示:移除index.html的<html>标签上的class="skins"样式} else {//此时为换肤状态,添加html的skins样式document.documentElement.classList.add('skins'); //该段代码表示:添加index.html的<html>标签上的class="skins"样式}
}
4.1 动态添加class
通过classList.remove和classList.add添加移除class类名
4.2 持久化
持久化:通过localStorage本地缓存主题变量名称即可
目的:1.刷新浏览器,保留主题变量即保留主题色
2.其他页面方便获取主题色变量;用于判断实现差异化等功能
5. 组件引入
在src/layout/layHeader.vue文件引入
<el-button @click="toggleTheme">{{ currentTheme === "light" ? "模式1" : "模式2" }}
</el-button>import { currentTheme, toggleTheme } from '../utils/theme'
6. main.js初始化主题色
解决刷新后主题色丢失问题
import { applyTheme } from '../utils/theme'
applyTheme(localStorage.getItem('app_theme'))
二、 涉及变更
1. element-plus、vxe-table、antv/g6、echarts等第三方组件库
组件、hover效果、disabled效果、tips、弹框、遮罩层、列表
2. 原生html
主要涉及到的前期,固定写死的class和style固定样式;
例如:
• 列表的链接项样式
• .wbg类名样式
使用css变量统一修改
3. 背景图
需要提供明暗两套背景图;通过主题变量名称currentTheme区分判断
三、其他换色思路
1.主题方式
参考链接
参考链接
2.别的博主实现方式
参考2