webpack-theme-color-replacer dynamically modifies Ant Design Vue theme color

  1. Enter the topic, first we install antd
    npm i --save ant-design-vue@next
  2. Introduced in main.js
    import Vue from 'vue';
    import App from './App.vue';
    import router from './router';
    import store from './store';
    import utils from './config/util.js';
    
    Vue.prototype.$utils = utils;
    Vue.prototype.axiosPost = utils.axiosPost; //axios post 请求
    Vue.prototype.axiosGet = utils.axiosGet; //axios get 请求
    Vue.config.productionTip = false;
    import 'ant-design-vue/dist/antd.css';  这里引入css
    //按需引入组件
    import {
      ConfigProvider,
      Layout,
      Input,
      InputNumber,
      Button,
      Switch,
      Radio,
      Checkbox,
      Select,
      Card,
      Form,
      Row,
      Col,
      Modal,
      Table,
      Tabs,
      Icon,
      Badge,
      Popover,
      Dropdown,
      List,
      Avatar,
      Breadcrumb,
      Steps,
      Spin,
      Menu,
      Drawer,
      Tooltip,
      Alert,
      Tag,
      Divider,
      DatePicker,
      TimePicker,
      Upload,
      Progress,
      Skeleton,
      Popconfirm,
      PageHeader,
      Result,
      Statistic,
      Descriptions,
      Space,
      message,
      notification,
    } from 'ant-design-vue';
    Vue.use(ConfigProvider);
    Vue.use(Layout);
    Vue.use(Input);
    Vue.use(InputNumber);
    Vue.use(Button);
    Vue.use(Switch);
    Vue.use(Radio);
    Vue.use(Checkbox);
    Vue.use(Select);
    Vue.use(Card);
    Vue.use(Form);
    Vue.use(Row);
    Vue.use(Col);
    Vue.use(Modal);
    Vue.use(Table);
    Vue.use(Tabs);
    Vue.use(Icon);
    Vue.use(Badge);
    Vue.use(Popover);
    Vue.use(Dropdown);
    Vue.use(List);
    Vue.use(Avatar);
    Vue.use(Breadcrumb);
    Vue.use(Steps);
    Vue.use(Spin);
    Vue.use(Menu);
    Vue.use(Drawer);
    Vue.use(Tooltip);
    Vue.use(Alert);
    Vue.use(Tag);
    Vue.use(Divider);
    Vue.use(DatePicker);
    Vue.use(TimePicker);
    Vue.use(Upload);
    Vue.use(Progress);
    Vue.use(Skeleton);
    Vue.use(Popconfirm);
    Vue.use(PageHeader);
    Vue.use(Result);
    Vue.use(Statistic);
    Vue.use(Descriptions);
    Vue.use(Space);
    
    new Vue({
      router,
      store,
      render: h => h(App),
    }).$mount('#app');
    

  3. Then install webpack-theme-color-replacer
    npm install webpack-theme-color-replacer
  4.  Then we create these 3 js files under config​​​​

     

  5.  theme-color-replacer.plugin.config.js file, the code is as follows:

    const ThemeColorReplacer = require('webpack-theme-color-replacer');
    const generate = require('@ant-design/colors/lib/generate').default;
    
    const getAntdSerials = color => {
      // 淡化(即less的tint)
      const lightens = new Array(9).fill().map((t, i) => {
        return ThemeColorReplacer.varyColor.lighten(color, i / 10);
      });
      const colorPalettes = generate(color);
      const rgb = ThemeColorReplacer.varyColor
        .toNum3(color.replace('#', ''))
        .join(',');
      return lightens.concat(colorPalettes).concat(rgb);
    };
    
    const themePluginOption = {
      fileName: 'css/theme-colors-[contenthash:8].css',
      matchColors: getAntdSerials('#1890ff'), // 主色系列
      // 改变样式选择器,解决样式覆盖问题
      changeSelector(selector) {
        switch (selector) {
          case '.ant-calendar-today .ant-calendar-date':
            return (
              ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' +
              selector
            );
          case '.ant-btn:focus,.ant-btn:hover':
            return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)';
          case '.ant-btn.active,.ant-btn:active':
            return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)';
          case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
          case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon':
            return ':not(.ant-steps-item-process)' + selector;
          // fixed https://github.com/vueComponent/ant-design-vue-pro/issues/876
          case '.ant-steps-item-process .ant-steps-item-icon':
            return ':not(.ant-steps-item-custom)' + selector;
          case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
          case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
            return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover';
          case '.ant-menu-horizontal > .ant-menu-item-selected > a':
          case '.ant-menu-horizontal>.ant-menu-item-selected>a':
            return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a';
          case '.ant-menu-horizontal > .ant-menu-item > a:hover':
          case '.ant-menu-horizontal>.ant-menu-item>a:hover':
            return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover';
          default:
            return selector;
        }
      },
    };
    
    const createThemeColorReplacerPlugin = () =>
      new ThemeColorReplacer(themePluginOption);
    
    module.exports = createThemeColorReplacerPlugin;
    
  6. themeColor.js, the code is as follows:

    import client from 'webpack-theme-color-replacer/client';
    import generate from '@ant-design/colors/lib/generate';
    
    export default {
      getAntdSerials(color) {
        // 淡化(即less的tint)
        const lightens = new Array(9).fill().map((t, i) => {
          return client.varyColor.lighten(color, i / 10);
        });
        // colorPalette变换得到颜色值
        const colorPalettes = generate(color);
        const rgb = client.varyColor.toNum3(color.replace('#', '')).join(',');
        return lightens.concat(colorPalettes).concat(rgb);
      },
      changeColor(newColor) {
        var options = {
          newColors: this.getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors`
          changeUrl(cssUrl) {
            return `/${cssUrl}`; // while router is not `hash` mode, it needs absolute path
          },
        };
        return client.changer.changeColor(options, Promise);
      },
    };
    
  7. themesettingConfig.js , the code is as follows:
     

    import themeColor from './themeColor.js';
    import message from 'ant-design-vue/es/message';
    import store from '../store';
    // color Array
    const colorList = [
      {
        key: '薄暮',
        color: '#F5222D',
      },
      {
        key: '火山',
        color: '#FA541C',
      },
      {
        key: '日暮',
        color: '#FAAD14',
      },
      {
        key: '明青',
        color: '#13C2C2',
      },
      {
        key: '极光绿',
        color: '#52C41A',
      },
      {
        key: '拂晓蓝(默认)',
        color: '#1890FF',
      },
      {
        key: '极客蓝',
        color: '#2F54EB',
      },
      {
        key: '酱紫',
        color: '#722ED1',
      },
    ];
    
    // 更新主题方法
    const updateTheme = newPrimaryColor => {
      console.log('newPrimaryColor:', newPrimaryColor);
      store.commit('setprimaryColor', newPrimaryColor);
      const hideMessage = message.loading('正在切换主题!', 0);
      themeColor.changeColor(newPrimaryColor).finally(() => {
        console.log('切换成功');
        setTimeout(() => {
          hideMessage();
        }, 10);
      });
    };
    
    export { updateTheme, colorList };
    
  8. root directory vue.config.js  code
     

    const CompressionPlugin = require('compression-webpack-plugin');
    const createThemeColorReplacerPlugin = require('./src/config/theme-color-replacer.plugin.config.js');
    
    module.exports = {
      configureWebpack: {
        plugins: [
          createThemeColorReplacerPlugin() // webpack plugins
        ]
      }
    };
    
  9. Then we create a new SettingDrawer.vue component to use
     
    <template>
      <div class="setting-drawer">
        <a-drawer
          width="300"
          placement="right"
          @close="onClose"
          :closable="false"
          :visible="visible"
        >
          <div class="setting-drawer-index-content">
            <div :style="{ marginBottom: '24px' }">
              <h3 class="setting-drawer-index-title">整体风格设置</h3>
            </div>
    
            <div :style="{ marginBottom: '24px' }">
              <h3 class="setting-drawer-index-title">主题色</h3>
    
              <div style="height: 20px">
                <a-tooltip
                  class="setting-drawer-theme-color-colorBlock"
                  v-for="(item, index) in colorList"
                  :key="index"
                >
                  <template slot="title">
                    {
         
         { item.key }}
                  </template>
                  <a-tag :color="item.color" @click="changeColor(item.color)">
                    <!-- <a-icon
                      type="check"
                      v-if="item.color === primaryColor"
                    ></a-icon> -->
                  </a-tag>
                </a-tooltip>
              </div>
            </div>
            <a-divider />
            <div :style="{ marginBottom: '24px' }">
              <h3 class="setting-drawer-index-title">导航模式</h3>
    
              <div>
                <a-switch :default-checked="false" @change="changeMode" /> Change
                Mode
                <br />
                <span className="ant-divider" style="margin: 10px" />
                <br />
                <a-switch :default-checked="false" @change="changeCollapsed" />
                Change Theme
              </div>
            </div>
            <a-divider />
          </div>
          <div class="setting-drawer-index-handle" @click="toggle" slot="handle">
            <a-icon type="setting" v-if="!visible" />
            <a-icon type="close" v-else />
          </div>
        </a-drawer>
      </div>
    </template>
    
    <script>
    import { updateTheme, colorList } from '../config/themesettingConfig';
    export default {
      components: {},
      mixins: [],
      data() {
        return {
          visible: false,
          colorList,
        };
      },
      watch: {},
      mounted() {},
      methods: {
        changeMode(checked) {
          this.$parent.changeMode(checked);
        },
        changeCollapsed(checked) {
          this.$parent.changeCollapsed(checked);
        },
        showDrawer() {
          this.visible = true;
        },
        onClose() {
          this.visible = false;
        },
        toggle() {
          this.visible = !this.visible;
        },
        changeColor(color) {
          console.log('color:', color);
          updateTheme(color);
        },
      },
    };
    </script>
    
    <style lang="less" >
    .setting-drawer-index-content {
      .setting-drawer-index-blockChecbox {
        display: flex;
    
        .setting-drawer-index-item {
          margin-right: 16px;
          position: relative;
          border-radius: 4px;
          cursor: pointer;
    
          img {
            width: 48px;
          }
    
          .setting-drawer-index-selectIcon {
            position: absolute;
            top: 0;
            right: 0;
            width: 100%;
            padding-top: 15px;
            padding-left: 24px;
            height: 100%;
            color: #1890ff;
            font-size: 14px;
            font-weight: 700;
          }
        }
      }
      .setting-drawer-theme-color-colorBlock {
        width: 20px;
        height: 20px;
        border-radius: 2px;
        float: left;
        cursor: pointer;
        margin-right: 8px;
        padding-left: 0px;
        padding-right: 0px;
        text-align: center;
        color: #fff;
        font-weight: 700;
    
        i {
          font-size: 14px;
        }
      }
    }
    
    .setting-drawer-index-handle {
      position: absolute;
      top: 240px;
      background: #1890ff;
      width: 48px;
      height: 48px;
      right: 300px;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
      pointer-events: auto;
      z-index: 1001;
      text-align: center;
      font-size: 16px;
      border-radius: 4px 0 0 4px;
    
      i {
        color: rgb(255, 255, 255);
        font-size: 20px;
      }
    }
    </style>
    

    Just import and use components

Guess you like

Origin blog.csdn.net/slow097/article/details/125633774