vue项目中引入element-ui时,如何更改主题色
在我们做项目时,我们经常会遇到切换主题色的功能,下面我们就来说一下通过颜色选择器我们就能改变项目的主题颜色
代码如下:
颜色选择器所在组件:
top-theme.vue内容如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 3 <el-color-picker size="small" class="theme-picker" popper-class="theme-picker-dropdown" v-model="themeVal"></el-color-picker> 4 </template> 5 6 <script> 7 import theme from "@/mixins/theme"; 8 9 export default { 10 name: "topTheme", 11 mixins: [theme()], 12 data() { 13 return { 14 chalk: "" 15 }; 16 }, 17 18 }; 19 </script> 20 21 <style scoped> 22 .theme-picker .el-color-picker__trigger { 23 vertical-align: middle; 24 } 25 26 .theme-picker-dropdown .el-color-dropdown__link-btn { 27 display: none; 28 } 29 </style>
其中mixins下的theme.js如下:
内容为:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 * Copyright (c) 2018-2025, lengleng All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * Neither the name of the pig4cloud.com developer nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * Author: lengleng (wangiegie@gmail.com) 16 */ 17 18 import { 19 mapGetters 20 } from "vuex"; 21 const version = require("element-ui/package.json").version; // element-ui version from node_modules 22 const ORIGINAL_THEME = "#409EFF"; // default color 23 export default function () { 24 return { 25 data() { 26 return { 27 themeVal: ORIGINAL_THEME 28 } 29 }, 30 created() { 31 this.themeVal = this.theme; 32 }, 33 watch: { 34 themeVal(val, oldVal) { 35 this.$store.commit("SET_THEME", val); 36 this.updateTheme(val, oldVal); 37 } 38 }, 39 computed: { 40 ...mapGetters(["theme"]) 41 }, 42 methods: { 43 updateTheme(val, oldVal) { 44 if (typeof val !== "string") return; 45 const head = document.getElementsByTagName("head")[0]; 46 const themeCluster = this.getThemeCluster(val.replace("#", "")); 47 const originalCluster = this.getThemeCluster(oldVal.replace("#", "")); 48 const getHandler = (variable, id) => { 49 return () => { 50 const originalCluster = this.getThemeCluster( 51 ORIGINAL_THEME.replace("#", "") 52 ); 53 const newStyle = this.updateStyle( 54 this[variable], 55 originalCluster, 56 themeCluster 57 ); 58 59 let styleTag = document.getElementById(id); 60 if (!styleTag) { 61 styleTag = document.createElement("style"); 62 styleTag.setAttribute("id", id); 63 head.appendChild(styleTag); 64 } 65 styleTag.innerText = newStyle; 66 }; 67 }; 68 69 const chalkHandler = getHandler("chalk", "chalk-style"); 70 71 if (!this.chalk) { 72 const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`; 73 this.getCSSString(url, chalkHandler, "chalk"); 74 } else { 75 chalkHandler(); 76 } 77 78 const link = [].slice.call( 79 document.getElementsByTagName("head")[0].getElementsByTagName("link") 80 ); 81 for (let i = link.length - 3; i < link.length; i++) { 82 const style = link[i]; 83 this.getCSSString(style.href, innerText => { 84 const originalCluster = this.getThemeCluster( 85 ORIGINAL_THEME.replace("#", "") 86 ); 87 const newStyle = this.updateStyle( 88 innerText, 89 originalCluster, 90 themeCluster 91 ); 92 let styleTag = document.getElementById(i); 93 if (!styleTag) { 94 styleTag = document.createElement("style"); 95 styleTag.id = i; 96 styleTag.innerText = newStyle; 97 head.appendChild(styleTag); 98 } 99 }); 100 } 101 102 const styles = [].slice 103 .call(document.querySelectorAll("style")) 104 .filter(style => { 105 const text = style.innerText; 106 return ( 107 new RegExp(oldVal, "i").test(text) && !/Chalk Variables/.test(text) 108 ); 109 }); 110 styles.forEach(style => { 111 const { 112 innerText 113 } = style; 114 if (typeof innerText !== "string") return; 115 style.innerText = this.updateStyle( 116 innerText, 117 originalCluster, 118 themeCluster 119 ); 120 }); 121 }, 122 updateStyle(style, oldCluster, newCluster) { 123 let newStyle = style; 124 oldCluster.forEach((color, index) => { 125 newStyle = newStyle.replace(new RegExp(color, "ig"), newCluster[index]); 126 }); 127 return newStyle; 128 }, 129 getCSSString(url, callback, variable) { 130 const xhr = new XMLHttpRequest(); 131 xhr.onreadystatechange = () => { 132 if (xhr.readyState === 4 && xhr.status === 200) { 133 if (variable) { 134 this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, ""); 135 } 136 callback(xhr.responseText); 137 } 138 }; 139 xhr.open("GET", url); 140 xhr.send(); 141 }, 142 getThemeCluster(theme) { 143 const tintColor = (color, tint) => { 144 let red = parseInt(color.slice(0, 2), 16); 145 let green = parseInt(color.slice(2, 4), 16); 146 let blue = parseInt(color.slice(4, 6), 16); 147 148 if (tint === 0) { 149 // when primary color is in its rgb space 150 return [red, green, blue].join(","); 151 } else { 152 red += Math.round(tint * (255 - red)); 153 green += Math.round(tint * (255 - green)); 154 blue += Math.round(tint * (255 - blue)); 155 156 red = red.toString(16); 157 green = green.toString(16); 158 blue = blue.toString(16); 159 160 return `#${red}${green}${blue}`; 161 } 162 }; 163 164 const shadeColor = (color, shade) => { 165 let red = parseInt(color.slice(0, 2), 16); 166 let green = parseInt(color.slice(2, 4), 16); 167 let blue = parseInt(color.slice(4, 6), 16); 168 169 red = Math.round((1 - shade) * red); 170 green = Math.round((1 - shade) * green); 171 blue = Math.round((1 - shade) * blue); 172 173 red = red.toString(16); 174 green = green.toString(16); 175 blue = blue.toString(16); 176 177 return `#${red}${green}${blue}`; 178 }; 179 180 const clusters = [theme]; 181 for (let i = 0; i <= 9; i++) { 182 clusters.push(tintColor(theme, Number((i / 10).toFixed(2)))); 183 } 184 clusters.push(shadeColor(theme, 0.1)); 185 return clusters; 186 } 187 } 188 } 189 }
涉及到的vuex相关模块为:
其中validate.js中有一系列正则校验的相关代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 * Copyright (c) 2018-2025, lengleng All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * Neither the name of the pig4cloud.com developer nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * Author: lengleng (wangiegie@gmail.com) 16 */ 17 18 /** 19 * Created by jiachenpan on 16/11/18. 20 */ 21 22 export function isvalidUsername(str) { 23 const valid_map = ['admin', 'editor'] 24 return valid_map.indexOf(str.trim()) >= 0 25 } 26 27 /* 合法uri*/ 28 export function validateURL(textval) { 29 const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ 30 return urlregex.test(textval) 31 } 32 33 /* 小写字母*/ 34 export function validateLowerCase(str) { 35 const reg = /^[a-z]+$/ 36 return reg.test(str) 37 } 38 39 /* 大写字母*/ 40 export function validateUpperCase(str) { 41 const reg = /^[A-Z]+$/ 42 return reg.test(str) 43 } 44 45 /* 大小写字母*/ 46 export function validatAlphabets(str) { 47 const reg = /^[A-Za-z]+$/ 48 return reg.test(str) 49 } 50 /*验证pad还是pc*/ 51 export const vaildatePc = function () { 52 const userAgentInfo = navigator.userAgent; 53 const Agents = ["Android", "iPhone", 54 "SymbianOS", "Windows Phone", 55 "iPad", "iPod"]; 56 let flag = true; 57 for (var v = 0; v < Agents.length; v++) { 58 if (userAgentInfo.indexOf(Agents[v]) > 0) { 59 flag = false; 60 break; 61 } 62 } 63 return flag; 64 } 65 /** 66 * validate email 67 * @param email 68 * @returns {boolean} 69 */ 70 export function validateEmail(email) { 71 const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 72 return re.test(email) 73 } 74 75 /** 76 * 判断身份证号码 77 */ 78 export function cardid(code) { 79 let list = []; 80 let result = true; 81 let msg = ''; 82 var city = { 83 11: "北京", 84 12: "天津", 85 13: "河北", 86 14: "山西", 87 15: "内蒙古", 88 21: "辽宁", 89 22: "吉林", 90 23: "黑龙江 ", 91 31: "上海", 92 32: "江苏", 93 33: "浙江", 94 34: "安徽", 95 35: "福建", 96 36: "江西", 97 37: "山东", 98 41: "河南", 99 42: "湖北 ", 100 43: "湖南", 101 44: "广东", 102 45: "广西", 103 46: "海南", 104 50: "重庆", 105 51: "四川", 106 52: "贵州", 107 53: "云南", 108 54: "西藏 ", 109 61: "陕西", 110 62: "甘肃", 111 63: "青海", 112 64: "宁夏", 113 65: "新疆", 114 71: "台湾", 115 81: "香港", 116 82: "澳门", 117 91: "国外 " 118 }; 119 if (!validatenull(code)) { 120 if (code.length == 18) { 121 if (!code || !/(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(code)) { 122 msg = "证件号码格式错误"; 123 } else if (!city[code.substr(0, 2)]) { 124 msg = "地址编码错误"; 125 } else { 126 //18位身份证需要验证最后一位校验位 127 code = code.split(''); 128 //∑(ai×Wi)(mod 11) 129 //加权因子 130 var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; 131 //校验位 132 var parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2, 'x']; 133 var sum = 0; 134 var ai = 0; 135 var wi = 0; 136 for (var i = 0; i < 17; i++) { 137 ai = code[i]; 138 wi = factor[i]; 139 sum += ai * wi; 140 } 141 var last = parity[sum % 11]; 142 if (parity[sum % 11] != code[17]) { 143 msg = "证件号码校验位错误"; 144 } else { 145 result = false; 146 } 147 148 } 149 } else { 150 msg = "证件号码长度不为18位"; 151 } 152 153 } else { 154 msg = "证件号码不能为空"; 155 } 156 list.push(result); 157 list.push(msg); 158 return list; 159 } 160 /** 161 * 判断手机号码是否正确 162 */ 163 export function isvalidatemobile(phone) { 164 let list = []; 165 let result = true; 166 let msg = ''; 167 var isPhone = /^0\d{2,3}-?\d{7,8}$/; 168 //增加134 减少|1349[0-9]{7},增加181,增加145,增加17[678] 169 var isMob = /^((\+?86)|(\(\+86\)))?(13[0123456789][0-9]{8}|15[012356789][0-9]{8}|18[012356789][0-9]{8}|14[57][0-9]{8}|17[3678][0-9]{8})$/; 170 if (!validatenull(phone)) { 171 if (phone.length == 11) { 172 if (isPhone.test(phone)) { 173 msg = '手机号码格式不正确'; 174 } else { 175 result = false; 176 } 177 } else { 178 msg = '手机号码长度不为11位'; 179 } 180 } else { 181 msg = '手机号码不能为空'; 182 } 183 list.push(result); 184 list.push(msg); 185 return list; 186 } 187 /** 188 * 判断姓名是否正确 189 */ 190 export function validatename(name) { 191 var regName = /^[\u4e00-\u9fa5]{2,4}$/; 192 if (!regName.test(name)) return false; 193 return true; 194 }; 195 /** 196 * 判断是否为整数 197 */ 198 export function validatenum(num, type) { 199 let regName = /[^\d.]/g; 200 if (type == 1) { 201 if (!regName.test(num)) return false; 202 } else if (type == 2) { 203 regName = /[^\d]/g; 204 if (!regName.test(num)) return false; 205 } 206 return true; 207 }; 208 /** 209 * 判断是否为小数 210 */ 211 export function validatenumord(num, type) { 212 let regName = /[^\d.]/g; 213 if (type == 1) { 214 if (!regName.test(num)) return false; 215 } else if (type == 2) { 216 regName = /[^\d.]/g; 217 if (!regName.test(num)) return false; 218 } 219 return true; 220 }; 221 /** 222 * 判断是否为空 223 */ 224 export function validatenull(val) { 225 if (val instanceof Array) { 226 if (val.length == 0) return true; 227 } else if (val instanceof Object) { 228 if (JSON.stringify(val) === '{}') return true; 229 } else { 230 if (val == 'null' || val == null || val == 'undefined' || val == undefined || val == '') return true; 231 return false; 232 } 233 return false; 234 235 };
utils下store.js内容如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 * Copyright (c) 2018-2025, lengleng All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * Neither the name of the pig4cloud.com developer nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * Author: lengleng (wangiegie@gmail.com) 16 */ 17 18 import { validatenull } from '@/utils/validate' 19 /** 20 * 存储localStorage 21 */ 22 export const setStore = (params) => { 23 const { 24 name, 25 content, 26 type, 27 datetime 28 } = params 29 const obj = { 30 dataType: typeof (content), 31 content: content, 32 type: type, 33 datetime: new Date().getTime() 34 } 35 if (type) window.sessionStorage.setItem(name, JSON.stringify(obj)) 36 else window.localStorage.setItem(name, JSON.stringify(obj)) 37 } 38 /** 39 * 获取localStorage 40 */ 41 export const getStore = (params) => { 42 const { 43 name, 44 type 45 } = params 46 let obj = {} 47 let content 48 obj = window.localStorage.getItem(name) 49 if (validatenull(obj)) obj = window.sessionStorage.getItem(name) 50 if (validatenull(obj)) return 51 obj = JSON.parse(obj) 52 if (obj.dataType === 'string') { 53 content = obj.content 54 } else if (obj.dataType === 'number') { 55 content = Number(obj.content) 56 } else if (obj.dataType === 'boolean') { 57 content = eval(obj.content) 58 } else if (obj.dataType === 'object') { 59 content = obj.content 60 } 61 return content 62 } 63 /** 64 * 删除localStorage 65 */ 66 export const removeStore = params => { 67 let { 68 name 69 } = params 70 window.localStorage.removeItem(name) 71 window.sessionStorage.removeItem(name) 72 }
store文件夹下index.js内容如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Created by xieli on 2018/8/7 0007. 3 */ 4 import Vue from 'vue' 5 import Vuex from 'vuex' 6 import common from './modules/common' 7 import getters from './getters' 8 9 Vue.use(Vuex) 10 const store = new Vuex.Store({ 11 modules: { 12 common, 13 }, 14 getters, 15 }) 16 17 export default store
modules文件夹下common.js内容如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 * Copyright (c) 2018-2025, lengleng All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * Neither the name of the pig4cloud.com developer nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * Author: lengleng (wangiegie@gmail.com) 16 */ 17 18 import { 19 setStore, 20 getStore, 21 removeStore 22 } from '@/utils/store' 23 import { 24 validatenull 25 } from '@/utils/validate' 26 27 28 const common = { 29 30 state: { 31 theme: getStore({ 32 name: 'theme' 33 }) || '#409EFF', 34 bg:"主题颜色" 35 }, 36 mutations: { 37 SET_THEME: (state, color) => { 38 state.theme = color 39 setStore({ 40 name: 'theme', 41 content: state.theme 42 }) 43 }, 44 45 } 46 } 47 export default common
store文件夹下getters.js内容如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 * Copyright (c) 2018-2025, lengleng All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * Neither the name of the pig4cloud.com developer nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * Author: lengleng (wangiegie@gmail.com) 16 */ 17 18 const getters = { 19 theme: state => state.common.theme, 20 /*bg: state => state.common.bg,*/ 21 22 } 23 export default getters
其中核心文件为mixins下的theme.js
项目地址:
https://github.com/yuwenjing0727/electric-ui.git