天天看點

vue+element實作自定義主題功能

  1. 第一步按照element官網介紹安裝依賴
npm i element-theme -g
		npm i element-theme-chalk -D
           

執行代碼 et -i 根目錄會生成element-variables.scss檔案

修改$–color-primary:你喜歡的顔色值執行et 然後你就會看到theme檔案夾下的font檔案了

  1. main.js中引入

    import '../theme/index.css'

  2. 封裝元件名為ThemePicker 代碼如下
<template>
			  <el-color-picker
			    class="theme-picker"
			    popper-class="theme-picker-dropdown"
			    v-model="theme"
			    :size="size">
			  </el-color-picker>
			</template>
			
			<script>
			
			const version = require('element-ui/package.json').version // element-ui version from node_modules
			const ORIGINAL_THEME = '#409EFF' // default color
			export default {
			  name: 'ThemePicker',
			  props: {
			    default: { // 初始化主題,可由外部傳入
			      type: String,
			      //default: '#EB815B'
			      default: ""+localStorage.getItem("tremePackers")+""
			    },
			    size: { // 初始化主題,可由外部傳入
			      type: String,
			      default: 'small'
			    }
			  },
			  data() {
			    return {
			      chalk: '', // content of theme-chalk css
			      theme: ORIGINAL_THEME,
			      showSuccess: true, // 是否彈出換膚成功消息
			    }
			  },
			  mounted() {
			    if(this.default != null) {
			      this.theme = this.default
			      this.$emit('onThemeChange', this.theme)
			      this.showSuccess = false
			    }
			  },
			  watch: {
			    theme(val, oldVal) {
			      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
			        }
			      }
			
			      const chalkHandler = getHandler('chalk', 'chalk-style')
			
			      if (!this.chalk) {
			        const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
			        this.getCSSString(url, chalkHandler, 'chalk')
			      } else {
			        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)
			      })
			
			      // 響應外部操作
			      this.$emit('onThemeChange', val)
			      //存入localStorage
			      localStorage.setItem('tremePackers',val);
			      if(this.showSuccess) {
			        this.$message({
			          message: '換膚成功',
			          type: 'success'
			        })
			      } else {
			        this.showSuccess = true
			      }
			    }
			  },
			  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, callback, variable) {
			      const xhr = new XMLHttpRequest()
			      xhr.onreadystatechange = () => {
			        if (xhr.readyState === 4 && xhr.status === 200) {
			          this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
			          callback()
			        }
			      }
			      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
			    }
			  }
			}
			</script>
			
			<style>
			.theme-picker .el-color-picker__trigger {
			  vertical-align: middle;
			}
			
			.theme-picker-dropdown .el-color-dropdown__link-btn {
			  display: none;
			}
			</style>
           

4.頁面中使用

<template>
    <theme-picker  class="right-menu-item" style="padding-top:10px;" @change="themeChange" />
</template>
<script>
import ThemePicker from '@/components/ThemePicker'

export default {
  components: {
    ThemePicker
  },

  data() {
      return {
        
      };
  },
  methods: {
     themeChange(val) {
      this.$store.dispatch('settings/changeSetting', {
        key: 'theme',
        value: val
      })
    },
  }
}
</script>

           

//最後自己再調一下樣式什麼的就可以了