vue-cli3,vue2+elementui实现用户自定义主题色,切换主题,(2023-02-10增加功能:后台可配置主题色以及功能色)
自定义主题色包含以下两种,并且这两种的主题色需要同步
- element-ui的主题色
- 程序员自己写的样式中的主题色
我参考
- 一篇博客:vue,elementUI切换主题,自定义主题 - 小兔额乖乖 - 博客园
- 一个开源框架:腾讯的tDesign搭建的开源框架——TDesign Vue Next Starter
输出了此篇文章
说明1:小兔额乖乖文章中的项目示例,在修改主题色后,刷新一下,鼠标hover时会有问题,此问题在本篇文章中已经解决
说明2:我曾经尝试完全模仿腾讯那个框架的思路。但是此思路不适用于element-ui这个框架,会报错
说明3:下面的正文中,项目中修改主题色时,会运行 document.head.appendChild(style) ,修改一次颜色就会添加一个stylesheet,有兴趣的同学可以优化一下,改成替换掉上一次的style。
以下是正文
---------------------------------------------------------------------------------------------------------------------------------
1、创建一个vue项目
① vue-cli3创建项目
vue create vue-custom-theme
② 安装element-ui相关
安装elementUI
npm i element-ui -S
安装sass
npm install node-sass@4.11.0 sass-loader@7.1.0 -D
启动项目
npm run serve
2、引入element-ui
①在main,js中
import ElementUI from 'element-ui'
import './styles.scss'
Vue.use(ElementUI)
②给一个默认的主题色【此步骤可省略,不需要。因为可以在Theme.vue中直接从后台获取默认主题(this.$store.state.sysConfig.theme)。this.$store.state.sysConfig.theme是在后台获取的,存储在了vuex里面,获取时机是全局路由拦截,在进入页面之前获取的】
用户先从线上拉去配置主题的css文件,当用户更改颜色后,在把css文件里面所有的颜色值替换掉,然后把这个css文件重新插入到html中达到改变颜色。
在这里都需要修改再方法1的基础上进行扩展:在element-variables.scss(大部分项目都是)添加 默认我们自己设置的颜色。
当然这个颜色也可以在其他公共css修改。我是在style.scss中修改的,如下
style.scss
/* theme color */
$--color-primary: #ff6f4b;
/* icon font path, required */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
3、主题色修改
①安装两个插件
npm install css-color-function
npm install object-assign
③ 从 unpkg.com/element-ui/lib/theme-chalk/index.css 把最新css文件复制下来copy到项目静态文件目录中:
④ 接下来就是写代码了。在App.vue上引入自定义的修改主题组件,再随便弄些element组件观察变化:
⑤写组件:Theme.vue
<!-- 切换主题色 -->
<template>
<div class="handle-theme">
<el-color-picker
@change="colorChange"
v-model="colors.primary"
></el-color-picker>
</div>
</template>
<script>
import generateColors from "@/utils/color";
import objectAssign from 'object-assign';
export default {
name: 'App',
data() {
return {
originalStylesheetCount: -1, // 记录当前已引入style数量
originalStyle: '', // 获取拿到的.css的字符串
colors: {
primary: '#ff6f4b',// this.$store.state.sysConfig.theme,
success: '#00c5bd',
warning: '#f39800',
danger: '#f56c6c',
info: '#909399'
},
// primaryColor: "", //提交成功后设置默认颜色
cssUrl: [
'//unpkg.com/element-ui/lib/theme-chalk/index.css',
process.env.BASE_URL + 'static/css/index.css'
]
};
},
methods: {
initTheme() {
// 默认从线上官方拉取最新css,2秒钟后做一个检查没有拉到就从本地在拉下
// 如果是记住用户的状态就需要,在主题切换的时候记录颜色值,在下次打开的时候从新赋值
this.colors.primary = localStorage.getItem('color') || this.colors.primary; // 例如
// this.getIndexStyle(this.cssUrl[0]);
// setTimeout(() =>{
// if (this.originalStyle) {
// return;
// } else {
this.getIndexStyle(this.cssUrl[1]);
// }
// }, 2000);
this.$nextTick(() => {
// 获取页面一共引入了多少个style 文件
this.originalStylesheetCount = document.styleSheets.length;
});
},
colorChange(e) {
if (!e) return;
this.primaryColor = e;
localStorage.setItem('color', e);
let primarySeries = this.generateColors('p', e)
let successSeries = this.generateColors('s', this.colors.success)
let warningSeries = this.generateColors('w', this.colors.warning)
let dangerSeries = this.generateColors('d', this.colors.danger)
let infoSeries = this.generateColors('i', this.colors.info)
this.colors = objectAssign(
{},
this.colors,
primarySeries,
successSeries,
warningSeries,
dangerSeries,
infoSeries
);
this.writeNewStyle();
},
writeNewStyle() {
let cssText = this.originalStyle;
Object.keys(this.colors).forEach((key) => {
cssText = cssText.replace(
new RegExp('(:|\\s+)' + key, 'g'),
'$1' + this.colors[key]
);
});
const style = document.createElement('style');
style.innerText = `
:root{
--primary-color: ${this.colors.primary};
--primary-color-light-1:${this.colors['pl-1']};
--primary-color-light-2:${this.colors['pl-2']};
--primary-color-light-3:${this.colors['pl-3']};
--primary-color-light-4:${this.colors['pl-4']};
--primary-color-light-5:${this.colors['pl-5']};
--primary-color-light-6:${this.colors['pl-6']};
--primary-color-light-7:${this.colors['pl-7']};
--primary-color-light-8:${this.colors['pl-8']};
--primary-color-light-9:${this.colors['pl-9']};
}${cssText}`;
// ":root{--primary-color:" + this.colors.primary + "}" + cssText;
document.head.appendChild(style);
// if (this.originalStylesheetCount === document.styleSheets.length) {
// // 如果之前没有插入就插入
// const style = document.createElement("style");
// style.innerText =
// ":root{--primary-color:" + this.colors.primary + "}" + cssText;
// document.head.appendChild(style);
// } else {
// // 如果之前没有插入就修改
// document.head.lastChild.innerText =
// ":root{--primary-color:" +
// this.colors.primary +
// "} " +
// cssText;
// }
},
getIndexStyle(url) {
var request = new XMLHttpRequest();
request.open('GET', url);
request.onreadystatechange = () => {
if (
request.readyState === 4 &&
(request.status == 200 || request.status == 304)
) {
// 调用本地的如果拿不到会得到html,html是不行的
if (request.response && !/DOCTYPE/gi.test(request.response)) {
this.originalStyle = this.getStyleTemplate(request.response);
this.colorChange(this.colors.primary);
} else {
this.originalStyle = '';
}
} else {
this.originalStyle = '';
}
};
request.send(null);
},
getStyleTemplate(data) {
const colorMap = {
// "#3a8ee6": "shade-1",
// "#409eff": "primary",
// "#53a8ff": "light-1",
// "#66b1ff": "light-2",
// "#79bbff": "light-3",
// "#8cc5ff": "light-4",
// "#a0cfff": "light-5",
// "#b3d8ff": "light-6",
// "#c6e2ff": "light-7",
// "#d9ecff": "light-8",
// "#ecf5ff": "light-9",
'#3a8ee6': 'ps-1',
'#409eff': 'primary',
'#53a8ff': 'pl-1',
'#66b1ff': 'pl-2',
'#79bbff': 'pl-3',
'#8cc5ff': 'pl-4',
'#a0cfff': 'pl-5',
'#b3d8ff': 'pl-6',
'#c6e2ff': 'pl-7',
'#d9ecff': 'pl-8',
'#ecf5ff': 'pl-9',
'#5daf34': 'ss-1', // is-plain:active
'#67c23a': 'success', // success
'#76c84e': 'sl-1', // 没有
'#85ce61': 'sl-2', // hover color
'#95d475': 'sl-3', // 没有
'#a4da89': 'sl-4', // is-disabled hover color
'#b3e19d': 'sl-5', // is-disabled color
'#c2e7b0': 'sl-6', // border-color
'#d1edc4': 'sl-7', // 没有
'#e1f3d8': 'sl-8', // light
'#f0f9eb': 'sl-9', // lighter
'#cf9236': 'ws-1', // is-plain:active
'#E6A23C': 'warning', // success
'#e9ab50': 'wl-1', // 没有
'#ebb563': 'wl-2', // hover color
'#eebe77': 'wl-3', // 没有
'#f0c78a': 'wl-4', // is-disabled hover color
'#f3d19e': 'wl-5', // is-disabled color
'#f5dab1': 'wl-6', // border-color
'#f8e3c5': 'wl-7', // 没有
'#faecd8': 'wl-8', // light
'#fdf6ec': 'wl-9', // lighter
'#dd6161': 'ds-1', // is-plain:active
'#F56C6C': 'danger', // success
'#f67b7b': 'dl-1', // 没有
'#f78989': 'dl-2', // hover color
'#f89898': 'dl-3', // 没有
'#f9a7a7': 'dl-4', // is-disabled hover color
'#fab6b6': 'dl-5', // is-disabled color
'#fbc4c4': 'dl-6', // border-color
'#fcd3d3': 'dl-7', // 没有
'#fde2e2': 'dl-8', // light
'#fef0f0': 'dl-9', // lighter
'#82848a': 'is-1', // is-plain:active
'#909399': 'info', // success
'#9b9ea3': 'il-1', // 没有
'#a6a9ad': 'il-2', // hover color
'#b1b3b8': 'il-3', // 没有
'#bcbec2': 'il-4', // is-disabled hover color
'#c8c9cc': 'il-5', // is-disabled color
'#d3d4d6': 'il-6', // border-color
'#dedfe0': 'il-7', // 没有
'#e9e9eb': 'il-8', // light
'#f4f4f5': 'il-9'// lighter
};
Object.keys(colorMap).forEach((key) => {
const value = colorMap[key];
data = data.replace(new RegExp(key, 'ig'), value);
});
return data;
}
},
mounted() {
this.initTheme();
// 默认从线上官方拉取最新css,2秒钟后做一个检查没有拉到就从本地在拉下
// let that = this;
// 如果是记住用户的状态就需要,在主题切换的时候记录颜色值,在下次打开的时候从新赋值
// this.colors.primary = localStorage.getItem('color')||this.colors.primary//例如
// setTimeout(function() {
// if (that.originalStyle) {
// return;
// } else {
// that.getIndexStyle(that.cssUrl[1]);
// }
// }, 2000);
// this.$nextTick(() => {
// // 获取页面一共引入了多少个style 文件
// this.originalStylesheetCount = document.styleSheets.length;
// });
}
};
</script>
<style lang="scss">
</style>
使用
.boxDiv{
padding:40px;
background: var(--primary-color);
box-shadow: 0 0 10px 8px var(--primary-color-light-8);
}