vue-element如何實現(xiàn)動態(tài)換膚存儲
需要實現(xiàn)的效果
選擇顏色塊或者顏色選擇器切換網(wǎng)站主題色,選擇主題后保存到本地,下次打開頁面是緩存的主題色




原理
根據(jù)ElementUI官網(wǎng)的自定義主題,新建一個樣式文件:element-variables(或者參考官網(wǎng)生成),在文件中寫入:
/* 改變主題色變量 */
$--color-primary: #409eff;
$--color-success: #67c23a;
/*....還可以定義更多變量*/
/* 改變 icon 字體路徑變量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
//主要的一步:
:export {
theme: $--color-primary;
}
在項目store文件夾中新建theme.js文件,用于存儲主題色變化狀態(tài),代碼如下
//引入element-variables文件,文件路徑根據(jù)自己項目存放位置來
import variables from '@/assets/css/element-variables.scss'
const state = {
theme: variables.theme
}
const getters = {
theme: function (state) {
return state.theme;
}
};
const mutations = {
CHANGE_SETTING: (state, { key, value }) => {
// eslint-disable-next-line no-prototype-builtins
if (state.hasOwnProperty(key)) {
state[key] = value
}
}
}
const actions = {
changeSetting({ commit }, data) {
commit('CHANGE_SETTING', data)
}
}
export default {
namespaced: true,
state,
mutations,
actions,
getters
}
然后在store.js中引入theme.js
import Vue from 'vue';
import Vuex from 'vuex';
import theme from '@/store/theme.js';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
theme
}
});
切換主圖部分template代碼如下:
<el-form-item label="可選主題" label-width="140px">
<div class="color-box">
<div
v-for="(item,i) in themeArr"
:key="i"
:class="['color-item',theme===item?'active':'']"
:style="{backgroundColor:item}"
@click="changeTheme(item)">
<span v-if="theme===item" class="iconfont icon-f-right"></span>
</div>
</div>
</el-form-item>
<el-form-item label="自定義主題" label-width="140px">
<el-color-picker
v-model="theme"
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
class="theme-picker"
popper-class="theme-picker-dropdown"
/>
</el-form-item>
script代碼
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
export default {
data() {
return {
themeArr: ['#409EFF','#202225','#F56C6C','#FFAB42','#17a969','#888C8F'],
chalk: '',
theme: ORIGINAL_THEME,
}
},
watch: {
//異步監(jiān)聽theme的變化
async theme(val,oldVal) {
if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
// console.log(themeCluster, originalCluster)
const $message = this.$message({
message: ' Compiling the theme',
customClass: 'theme-message',
type: 'success',
duration: 0,
iconClass: 'el-icon-loading'
})
const getHandler = (variable, id) => {
return () => {
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
let styleTag = document.getElementById(id)
if (!styleTag) {
styleTag = document.createElement('style')
styleTag.setAttribute('id', id)
document.head.appendChild(styleTag)
}
styleTag.innerText = newStyle
}
}
// 初次進入或刷新時動態(tài)加載CSS文件
if (!this.chalk) {
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
await this.getCSSString(url, 'chalk')
}
const chalkHandler = getHandler('chalk', 'chalk-style')
chalkHandler()
const styles = [].slice.call(document.querySelectorAll('style'))
.filter(style => {
const text = style.innerText
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
})
styles.forEach(style => {
const { innerText } = style
if (typeof innerText !== 'string') return
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
})
// 將修改的主題保存到本地,下次打開頁面是保存的主題色
localStorage.setItem('theme',val)
//調(diào)用vuex中改變主題色方法
this.$store.dispatch('theme/changeSetting', {
key: 'theme',
value: val
})
$message.close()
},
},
methods: {
updateStyle(style, oldCluster, newCluster) {
let newStyle = style
oldCluster.forEach((color, index) => {
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
})
return newStyle
},
getCSSString(url, variable) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
resolve()
}
}
xhr.open('GET', url)
xhr.send()
})
},
getThemeCluster(theme) {
const tintColor = (color, tint) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
if (tint === 0) { // when primary color is in its rgb space
return [red, green, blue].join(',')
} else {
red += Math.round(tint * (255 - red))
green += Math.round(tint * (255 - green))
blue += Math.round(tint * (255 - blue))
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
}
const shadeColor = (color, shade) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
red = Math.round((1 - shade) * red)
green = Math.round((1 - shade) * green)
blue = Math.round((1 - shade) * blue)
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
const clusters = [theme]
for (let i = 0; i <= 9; i++) {
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
}
clusters.push(shadeColor(theme, 0.1))
return clusters
},
// 顏色塊點擊切換主題事件
changeTheme(item) {
this.theme = item
this.$store.dispatch('theme/changeSetting', {
key: 'theme',
value: item
})
},
},
mounted() {
//從本地存儲獲取保存的主題
if(localStorage.getItem("theme")) {
this.theme = localStorage.getItem("theme")
this.$store.dispatch('theme/changeSetting', {
key: 'theme',
value: this.theme
})
}
}
}
顏色塊選擇樣式style:
.color-box {
display: flex;
flex-wrap: wrap;
.color-item {
position: relative;
margin: 2px;
width: 34px;
height: 34px;
border-radius: 3px;
border: 2px solid transparent;
cursor: pointer;
span {
position: absolute;
right: 0;
top: 0;
width: 15px;
height: 15px;
background-color: #000;
color: #fff;
font-size: 10px;
text-align: center;
}
&.active {
border-color: #000;
}
}
}
最后感謝:vue-element-admin作者 本實現(xiàn)方案借鑒于此
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Vue2中如何使用全局事件總線實現(xiàn)任意組件間通信
全局事件總線就是一種組件間通信的方式,適用于任意組件間通信,下面這篇文章主要給大家介紹了關于Vue2中如何使用全局事件總線實現(xiàn)任意組件間通信的相關資料,需要的朋友可以參考下2022-12-12
VUE數(shù)組根據(jù)索引刪除數(shù)據(jù),頁面同時更新的實現(xiàn)方法
這篇文章主要介紹了VUE數(shù)組根據(jù)索引刪除數(shù)據(jù),頁面同時更新的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
el-table表格動態(tài)合并相同數(shù)據(jù)單元格(可指定列+自定義合并)
工作時遇到的el-table合并單元格的需求,本文主要介紹了el-table表格動態(tài)合并相同數(shù)據(jù)單元格(可指定列+自定義合并),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-07-07
vue項目引入百度地圖BMapGL鼠標繪制和BMap輔助工具
這篇文章主要為大家介紹了vue項目引入百度地圖BMapGL鼠標繪制和BMap輔助工具的踩坑分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02

