切换elementUI主题的公共组件

我心飞翔 分类:javascript

实现的功能

1.切换皮肤后,登录页显示不同的背景图片,字体颜色,以及选中状态的背景颜色。

2.引入的elementUI组件,也随之切换对应的主题颜色。

3.通过vuex状态管理器,储存当前所选的主题属性(包括背景颜色,字体颜色,选中状态颜色,等等。可以根据不同需求进行配置)。在任一组件中,都可以通store调用主题属性。并且,在css文件中可以直接调用主题属性。

页面-2-首页1.png

页面-2-首页2.png

切换主题的SwitchSkin组件代码

<template>
<!-- 主题样式选择器 -->
<div class="theme-picker">
<div
class="selected-theme webkit-transition"
:style="{ background: selectedColor || themeSelectedColor }"
>
<div class="logo">
<img src="static/img/theme/skin.png" alt="" />
</div>
<div class="text">切换皮肤</div>
</div>
<div
v-for="(item, index) in themeColorList"
:key="index"
class="theme-item"
@click="themeChange(item.color)"
>
<div class="bg" :style="{ background: item.color + '4d' }" />
<div class="color" :style="{ background: item.color }">
<img
v-show="item.color == selectedColor"
src="static/img/theme/check.png"
alt=""
/>
</div>
<div class="text">
{{ item.text }}
</div>
</div>
</div>
</template>
​
<script>
import { ThemeChangeMixin } from "@/mixins/ThemeChangeMixin";
export default {
name: "",
components: {},
mixins: [ThemeChangeMixin],
props: {},
data() {
return {};
},
computed: {},
created() {},
mounted() {},
methods: {},
};
</script>
<style lang="scss" scoped>
/* 主题样式选择 */
.theme-picker {
position: absolute;
right: 0;
width: 40px;
height: 40px;
top: 80px;
font-size: 14px;
border-radius: 7px 0 0 7px;
overflow: hidden;
-webkit-transition: all 1s ease 0.1s;
-o-transition: all 1s ease 0.1s;
-moz-transition: all 1s ease 0.1s;
transition: all 1s ease 0.1s;
&:hover {
width: 118px;
right: 0;
height: 120px;
.selected-theme {
.text {
display: block;
}
}
}
.selected-theme {
display: flex;
align-items: center;
height: 40px;
cursor: pointer;
.logo {
width: 16px;
height: 16px;
margin: 3px 9px 0 12px;
-webkit-animation: swing 3s;
-webkit-animation-iteration-count: 3;
-webkit-animation-delay: 3s;
}
.text {
display: none;
color: #fff;
width: 100%;
text-align: left;
}
}
.theme-item {
display: flex;
align-items: center;
height: 40px;
border: 1px solid #e5e5e5;
background: #fff;
cursor: pointer;
&:hover {
// background: #e4e6f4 !important;
.bg {
opacity: 1;
}
}
&:last-child {
border: none;
}
.bg {
height: 40px;
width: 100%;
position: absolute;
opacity: 0;
}
.color {
width: 16px;
height: 16px;
margin: 0 9px 0 12px;
padding: 8px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
img {
width: 11px;
height: 10px;
display: block;
position: absolute;
left: 3px;
top: 3px;
}
}
.text {
color: #757575;
width: 100%;
text-align: left;
}
}
}
</style>

css3属性渐变

鼠标移到选择器上,主题选择的下拉框展开。通过css3 -webkit-transition(属性渐变)来实现,呈现一个柔和的渐变效果。

.theme-picker {
position: absolute;
right: 0;
width: 40px;
height: 40px;
top: 80px;
font-size: 14px;
border-radius: 7px 0 0 7px;
overflow: hidden;
-webkit-transition: all 1s ease 0.1s;
-o-transition: all 1s ease 0.1s;
-moz-transition: all 1s ease 0.1s;
transition: all 1s ease 0.1s;
&:hover {
width: 118px;
right: 0;
height: 120px;
.selected-theme {
.text {
display: block;
}
}
}
}

混入(mixin)文件

import { ThemeChangeMixin } from "@/mixins/ThemeChangeMixin";
export default {
name: "",
components: {},
mixins: [ThemeChangeMixin],
props: {},
data() {
return {};
},
computed: {},
created() {},
mounted() {},
methods: {},
};

主题皮肤的数据,以及切换主题的方法,在ThemeChangeMixin文件中。作为混入(mixin)对象,在切换主题的组件中使用。

关于混入(mixin)官网有详尽讲解

cn.vuejs.org/v2/guide/mi…

主题选项的数组themeColorList

 themeColorList: [
{
color: '#3E50B3',
text: '蓝色',
skin: {
themeImg: 'blue',
themeBgColor: '#D7DBEA',
themeNavColor: '#6573C4',
botColor: 'white'
}
},
{
color: '#CD2E18',
text: '红色',
skin: {
themeImg: 'red',
themeBgColor: '#FAEAE8',
themeNavColor: '#DA523E',
botColor: 'black'
}
}
]

Vuex状态管理器,切换主题

切换主题的方法如下。切换主题后,通过状态管理器Vuex改变当前的主题状态。

 /* 切换主题 */
themeChange(val) {
if (this.selectedColor == val) return
this.selectedColor = val
this.$store.dispatch('settings/changeSetting', {
key: 'theme',
value: val
})
const list = this.themeColorList
const idx = list.findIndex((item) => item.color == val)
Object.keys(list[0]['skin']).forEach((item) => {
const value = list[idx]['skin'][item]
this.$store.dispatch('settings/changeSetting', {
key: item,
value
})
})
this.changeElementTheme(val)
},

Vuex相关store文件夹下的modules里settings.js文件,配置了主题相关的state,以及mutations和actions对象。

关于vuex配置,我专门有一篇文章讲的很详尽,不再啰嗦。

const state = {
theme: '#3E50B3',
themeBgColor: '#D7DBE',
themeNavColor: '#6573C4',
themeImg: 'blue',
botColor: 'white',
websiteName: "",//系统名称
copyright: "",
companyLogo: "",
}
​
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
}

切换elementUI主题

在changeElementTheme方法中,加载不同elementUI主题颜色文件。

ThemeChangeMixin.js文件


/**
切换皮肤,改变主题的mixin
*/
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // deexport const ThemeChangeMixin = {
data() {
return {
chalk: '', // content of theme-chalk css
selectedColor: '',
themeSelectedColor: this.$store.state.settings.theme,
themeColorList: [
{
color: '#3E50B3',
text: '蓝色',
skin: {
themeImg: 'blue',
themeBgColor: '#D7DBEA',
themeNavColor: '#6573C4',
botColor: 'white'
}
},
{
color: '#CD2E18',
text: '红色',
skin: {
themeImg: 'red',
themeBgColor: '#FAEAE8',
themeNavColor: '#DA523E',
botColor: 'black'
}
}
]
}
},
created() {
this.selectedColor = this.themeSelectedColor
},
methods: {
/* 切换主题 */
themeChange(val) {
if (this.selectedColor == val) return
this.selectedColor = val
this.$store.dispatch('settings/changeSetting', {
key: 'theme',
value: val
})
const list = this.themeColorList
const idx = list.findIndex((item) => item.color == val)
Object.keys(list[0]['skin']).forEach((item) => {
const value = list[idx]['skin'][item]
this.$store.dispatch('settings/changeSetting', {
key: item,
value
})
})
this.changeElementTheme(val)
},
async changeElementTheme(val) {
const oldVal = this.chalk ? this.themeSelectedColor : ORIGINAL_THEME
if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
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
}
}
if (!this.chalk) {
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
await this.getCSSString(url, 'chalk')
}
const that = this
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
)
})
},
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
}
}
}

CSS var() 函数

以上,通过JS改变了主题颜色。那么,可以在CSS文件中直接使用主题颜色吗?答案是可以的,通过CSS var() 函数。

我在App.vue文件中用如下方式,引入了主题颜色,包括背景颜色,主题图片,底部颜色,等等。可以根据具体需求自己配置。

<template>
<div
id="app"
:style="{
'--themeColor': theme,
'--themeBgColor': themeBgColor,
'--themeNavColor': themeNavColor,
'--themeImg': themeImg,
'--botColor': botColor,
}"
>
<router-view />
</div>
</template>

然后在css文件中,就可以通过css的var函数调用该属性

.name {
&:hover {
color: var(--themeColor);
transform: scale(1.1);
}
}

App.vue完整代码


<template>
<div
id="app"
:style="{
'--themeColor': theme,
'--themeBgColor': themeBgColor,
'--themeNavColor': themeNavColor,
'--themeImg': themeImg,
'--botColor': botColor,
}"
>
<router-view />
</div>
</template>
​
<script>
import { ThemeChangeMixin } from '@/mixins/ThemeChangeMixin'const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // deexport default {
name: 'App',
mixins: [ThemeChangeMixin],
computed: {
theme() {
return this.$store.state.settings.theme
},
themeBgColor(){
return this.$store.state.settings.themeBgColor
},
themeNavColor(){
return this.$store.state.settings.themeNavColor
},
themeImg() {
return this.$store.state.settings.themeImg
},
botColor() {
return this.$store.state.settings.botColor
}
},
mounted() {
this.handleUserTheme()
},
methods: {
async handleUserTheme() {
const val = this.theme
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
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
}
}
if (!this.chalk) {
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
await this.getCSSString(url, 'chalk')
}
const that = this
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
)
})
}
}
}

回复

我来回复
  • 暂无回复内容