当前位置:   article > 正文

Vue+Less/Scss实现主题切换功能_vue皮肤颜色切换

vue皮肤颜色切换

前言:目前,在众多的后台管理系统中,换肤功能已是一个很常见的功能。用户可以根据自己的喜好,设置页面的主题,从而实现个性化定制。

目前,我所了解到的换肤方式,也是我目前所掌握的两种换肤方式,想同大家一起分享。

一、Less/Scss变量换肤

具体实现:

1、初始化vue项目

2、安装插件:

npm install style-resources-loader -D

npm install vue-cli-plugin-style-resources-loader -D

当然也要安装less、less-loader等插件,具体的安装配置,请自行google

3、新建theme.less文件用于全局样式配置。在src目录下新建theme文件夹,在这个文件夹下新建theme.less文件。具体如下:

  1. /src/theme/theme.less
  2. // 默认的主题颜色
  3. @primaryColor: var(--primaryColor, #000);
  4. @primaryTextColor: var(--primaryTextColor, green);
  5. // 导出变量
  6. :export {
  7. name: "less";
  8. primaryColor: @primaryColor;
  9. primaryTextColor: @primaryTextColor;
  10. }

4、配置vue.config.js文件,实现全局使用变量实现换肤

  1. const path = require("path");
  2. module.exports = {
  3. pluginOptions: {
  4. "style-resources-loader": {
  5. preProcessor: "less",
  6. patterns: [
  7. // 这个是加上自己的路径,不能使用(如下:alias)中配置的别名路径
  8. path.resolve(__dirname, "./src/theme/theme.less"),
  9. ],
  10. },
  11. },
  12. };

5、具体的使用:

  1. <template>
  2. <div class="hello">
  3. <p>我是测试文字</p>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name: "HelloWorld",
  9. };
  10. </script>
  11. <style scoped lang="less">
  12. .hello {
  13. p {
  14. color: @primaryTextColor;
  15. }
  16. }
  17. </style>

备注:如果是用scss也基本同以上用法,只是scss的变量名用‘$’作为前缀,less使用@

至此,我们已经实现了静态更换皮肤,那如何实现动态换肤呢,最重要的就是以下的文件了。

我们可以多配置几种默认主题

6、在theme文件夹下新建model.js文件,用于存放默认主题

  1. // 一套默认主题以及一套暗黑主题
  2. // 一套默认主题以及一套暗黑主题
  3. export const themes = {
  4. default: {
  5. primaryColor: `${74}, ${144},${226}`,
  6. primaryTextColor: `${74}, ${144},${226}`,
  7. },
  8. dark: {
  9. primaryColor: `${0},${0},${0}`,
  10. primaryTextColor: `${0},${0},${0}`,
  11. },
  12. };

7、实现动态切换:

在/src/theme文件夹下新建theme.js文件,代码如下:

  1. import { themes } from "./model";
  2. // 修改页面中的样式变量值
  3. const changeStyle = (obj) => {
  4. for (let key in obj) {
  5. document
  6. .getElementsByTagName("body")[0]
  7. .style.setProperty(`--${key}`, obj[key]);
  8. }
  9. };
  10. // 改变主题的方法
  11. export const setTheme = (themeName) => {
  12. localStorage.setItem("theme", themeName); // 保存主题到本地,下次进入使用该主题
  13. const themeConfig = themes[themeName];
  14. // 如果有主题名称,那么则采用我们定义的主题
  15. if (themeConfig) {
  16. localStorage.setItem("primaryColor", themeConfig.primaryColor); // 保存主题色到本地
  17. localStorage.setItem("primaryTextColor", themeConfig.primaryTextColor); // 保存文字颜色到本地
  18. changeStyle(themeConfig); // 改变样式
  19. } else {
  20. let themeConfig = {
  21. primaryColor: localStorage.getItem("primaryColor"),
  22. primaryTextColor: localStorage.getItem("primaryTextColor"),
  23. };
  24. changeStyle(themeConfig);
  25. }
  26. };

8、切换主题

this.setTheme('dark')

二、element-UI组件的换肤

1、一般elementUI主题色都有这样一个文件element-variables.scss:

  1. /**
  2. * I think element-ui's default theme color is too light for long-term use.
  3. * So I modified the default color and you can modify it to your liking.
  4. **/
  5. /* theme color */
  6. $--color-primary: #1890ff;
  7. $--color-success: #13ce66;
  8. $--color-warning: #ffba00;
  9. $--color-danger: #ff4949;
  10. // $--color-info: #1E1E1E;
  11. $--button-font-weight: 400;
  12. // $--color-text-regular: #1f2d3d;
  13. $--border-color-light: #dfe4ed;
  14. $--border-color-lighter: #e6ebf5;
  15. $--table-border: 1px solid #dfe6ec;
  16. /* icon font path, required */
  17. $--font-path: "~element-ui/lib/theme-chalk/fonts";
  18. @import "~element-ui/packages/theme-chalk/src/index";
  19. // the :export directive is the magic sauce for webpack
  20. // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
  21. :export {
  22. theme: $--color-primary;
  23. }

2、main.js中引用

import './styles/element-variables.scss'

3、在store文件夹下新建settings.js文件,用于页面基础设置

  1. import variables from '@/styles/element-variables.scss'
  2. const state = {
  3. theme: variables.theme
  4. }
  5. const mutations = {
  6. CHANGE_SETTING: (state, { key, value }) => {
  7. // eslint-disable-next-line no-prototype-builtins
  8. if (state.hasOwnProperty(key)) {
  9. state[key] = value
  10. }
  11. }
  12. }
  13. const actions = {
  14. changeSetting({ commit }, data) {
  15. commit('CHANGE_SETTING', data)
  16. }
  17. }
  18. export default {
  19. namespaced: true,
  20. state,
  21. mutations,
  22. actions
  23. }

4、一般换肤都是需要有个颜色选择器,用于皮肤设置

在src目录下新建ThemePicker文件夹,新建index.vue文件。

  1. <template>
  2. <el-color-picker
  3. v-model="theme"
  4. :predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
  5. class="theme-picker"
  6. popper-class="theme-picker-dropdown"
  7. />
  8. </template>
  9. <script>
  10. const version = require('element-ui/package.json').version // element-ui version from node_modules
  11. const ORIGINAL_THEME = '#409EFF' // default color
  12. export default {
  13. data() {
  14. return {
  15. chalk: '', // content of theme-chalk css
  16. theme: ''
  17. }
  18. },
  19. computed: {
  20. defaultTheme() {
  21. return this.$store.state.settings.theme
  22. }
  23. },
  24. watch: {
  25. defaultTheme: {
  26. handler: function(val, oldVal) {
  27. this.theme = val
  28. },
  29. immediate: true
  30. },
  31. async theme(val) {
  32. const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
  33. if (typeof val !== 'string') return
  34. const themeCluster = this.getThemeCluster(val.replace('#', ''))
  35. const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
  36. console.log(themeCluster, originalCluster)
  37. const $message = this.$message({
  38. message: ' Compiling the theme',
  39. customClass: 'theme-message',
  40. type: 'success',
  41. duration: 0,
  42. iconClass: 'el-icon-loading'
  43. })
  44. const getHandler = (variable, id) => {
  45. return () => {
  46. const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
  47. const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
  48. let styleTag = document.getElementById(id)
  49. if (!styleTag) {
  50. styleTag = document.createElement('style')
  51. styleTag.setAttribute('id', id)
  52. document.head.appendChild(styleTag)
  53. }
  54. styleTag.innerText = newStyle
  55. }
  56. }
  57. if (!this.chalk) {
  58. const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
  59. await this.getCSSString(url, 'chalk')
  60. }
  61. const chalkHandler = getHandler('chalk', 'chalk-style')
  62. chalkHandler()
  63. const styles = [].slice.call(document.querySelectorAll('style'))
  64. .filter(style => {
  65. const text = style.innerText
  66. return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
  67. })
  68. styles.forEach(style => {
  69. const { innerText } = style
  70. if (typeof innerText !== 'string') return
  71. style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
  72. })
  73. this.$emit('change', val)
  74. $message.close()
  75. }
  76. },
  77. methods: {
  78. updateStyle(style, oldCluster, newCluster) {
  79. let newStyle = style
  80. oldCluster.forEach((color, index) => {
  81. newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
  82. })
  83. return newStyle
  84. },
  85. getCSSString(url, variable) {
  86. return new Promise(resolve => {
  87. const xhr = new XMLHttpRequest()
  88. xhr.onreadystatechange = () => {
  89. if (xhr.readyState === 4 && xhr.status === 200) {
  90. this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
  91. resolve()
  92. }
  93. }
  94. xhr.open('GET', url)
  95. xhr.send()
  96. })
  97. },
  98. getThemeCluster(theme) {
  99. const tintColor = (color, tint) => {
  100. let red = parseInt(color.slice(0, 2), 16)
  101. let green = parseInt(color.slice(2, 4), 16)
  102. let blue = parseInt(color.slice(4, 6), 16)
  103. if (tint === 0) { // when primary color is in its rgb space
  104. return [red, green, blue].join(',')
  105. } else {
  106. red += Math.round(tint * (255 - red))
  107. green += Math.round(tint * (255 - green))
  108. blue += Math.round(tint * (255 - blue))
  109. red = red.toString(16)
  110. green = green.toString(16)
  111. blue = blue.toString(16)
  112. return `#${red}${green}${blue}`
  113. }
  114. }
  115. const shadeColor = (color, shade) => {
  116. let red = parseInt(color.slice(0, 2), 16)
  117. let green = parseInt(color.slice(2, 4), 16)
  118. let blue = parseInt(color.slice(4, 6), 16)
  119. red = Math.round((1 - shade) * red)
  120. green = Math.round((1 - shade) * green)
  121. blue = Math.round((1 - shade) * blue)
  122. red = red.toString(16)
  123. green = green.toString(16)
  124. blue = blue.toString(16)
  125. return `#${red}${green}${blue}`
  126. }
  127. const clusters = [theme]
  128. for (let i = 0; i <= 9; i++) {
  129. clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
  130. }
  131. clusters.push(shadeColor(theme, 0.1))
  132. return clusters
  133. }
  134. }
  135. }
  136. </script>
  137. <style>
  138. .theme-message,
  139. .theme-picker-dropdown {
  140. z-index: 99999 !important;
  141. }
  142. .theme-picker .el-color-picker__trigger {
  143. height: 26px !important;
  144. width: 26px !important;
  145. padding: 2px;
  146. }
  147. .theme-picker-dropdown .el-color-dropdown__link-btn {
  148. display: none;
  149. }
  150. </style>

5、在使用ThemePicker组件的位置,去调用vuex中的changeSetting函数

  1. <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
  2. import ThemePicker from '@/components/ThemePicker'
  3. export default {
  4. components: { ThemePicker },
  5. methods:{
  6. themeChange(val) {
  7. this.$store.dispatch('settings/changeSetting', {
  8. key: 'theme',
  9. value: val
  10. })
  11. }
  12. }
  13. }

至此,就可以实现elementUI组件的换肤功能了

总结:其实上边两种方式换肤的实现思路都差不多,下边那篇自己理解得不是很好,欢迎补充

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/82262
推荐阅读
相关标签
  

闽ICP备14008679号