vite+vue+ts换肤效果

452 阅读3分钟

vite项目页面换肤

本项目仅展示body主题换肤,如有需要请灵活运用~~,效果图如下:

图一:

image.png

图二:

image.png

1 依赖

本项目是基于vite+ts+vue创建的基础项目,依赖element plus(请按照官方文档自行安装),sass,换肤主要依赖@zougt/vite-plugin-theme-preprocessor:默认支持sass并且在vite.config.js配置后会全局引入这些样式,了解更多@zougt/vite-plugin-theme-preprocessor用法:请点击这里:地址

npm install @zougt/vite-plugin-theme-preprocessor

package.json:如缺少什么依赖请自行安装~~

image.png

2 vite.config.js基本配置

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor";
import Components from "unplugin-vue-components/vite";
import AutoImport from 'unplugin-auto-import/vite';
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import { genScssMultipleScopeVars } from "./src/hooks/common.js";
​
​
// https://vitejs.dev/config/
export default defineConfig({
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true
      },
      scss: {
        javascriptEnabled: true
      }
    }
  },
  optimizeDeps: {
    exclude: ["@zougt/vite-plugin-theme-preprocessor/dist/browser-utils"]
  },
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
    vue(),
    // 自定义主题
    themePreprocessorPlugin({
      scss: {
        multipleScopeVars: genScssMultipleScopeVars(),
        // 默认取 multipleScopeVars[0].scopeName
        // defaultScopeName: "",
        // 在生产模式是否抽取独立的主题css文件,extract为true以下属性有效
        extract: true,
        // 独立主题css文件的输出路径,默认取 viteConfig.build.assetsDir 相对于 (viteConfig.build.outDir)
        // outputDir: "",
        // 会选取defaultScopeName对应的主题css文件在html添加link
        themeLinkTagId: "head",
        // "head"||"head-prepend" || "body" ||"body-prepend"
        themeLinkTagInjectTo: "head",
        // 可以自定义css文件名称的函数
        // customThemeCssFileName: scopeName => scopeName
        // 是否对抽取的css文件内对应scopeName的权重类名移除
        removeCssScopeName: false
      }
    })
  ],
})
​

3 代码

  • src/hooks/common.js
//在实际应用中path和varsContent用一个其实就可以了
type MultipleScopeVarsItem = {
    scopeName: string;//需要用到的主题色的模块
    path: string;//也可以写一个scss文件定义一些主题样式
    varsContent: string;//主题色的内容
};
​
const themeColors: any = {
    default: {
        color: "#409EFF",
        subMenuActiveText: "#fff",
        menuBg: "#1c2233",
        menuHover: "#4091f7",
        subMenuBg: "#161b29",
        subMenuActiveBg: "#343f5d",
        navTextColor: "#fff",
        menuText: "rgb(254 254 254 / 65%)",
        sidebarLogo: "#1c2233",
        menuTitleHover: "#fff",
        menuActiveBefore: "#4091f7"
    },
    light: {
        color: "#409EFF",
        subMenuActiveText: "#409eff",
        menuBg: "#fff",
        menuHover: "#e0ebf6",
        subMenuBg: "#fff",
        subMenuActiveBg: "#e0ebf6",
        navTextColor: "#7a80b4",
        menuText: "#7a80b4",
        sidebarLogo: "#fff",
        menuTitleHover: "#000",
        menuActiveBefore: "#4091f7"
    },
    dusk: {
        color: "#f5222d",
        subMenuActiveText: "#fff",
        menuBg: "#2a0608",
        menuHover: "#e13c39",
        subMenuBg: "#000",
        subMenuActiveBg: "#e13c39",
        navTextColor: "#red",
        menuText: "rgb(254 254 254 / 65.1%)",
        sidebarLogo: "#42090c",
        menuTitleHover: "#fff",
        menuActiveBefore: "#e13c39"
    },
    volcano: {
        color: "#fa541c",
        subMenuActiveText: "#fff",
        menuBg: "#2b0e05",
        menuHover: "#e85f33",
        subMenuBg: "#0f0603",
        subMenuActiveBg: "#e85f33",
        navTextColor: "#fff",
        menuText: "rgb(254 254 254 / 65%)",
        sidebarLogo: "#441708",
        menuTitleHover: "#fff",
        menuActiveBefore: "#e85f33"
    },
    yellow: {
        color: "#fadb14",
        subMenuActiveText: "#d25f00",
        menuBg: "#2b2503",
        menuHover: "#f6da4d",
        subMenuBg: "#0f0603",
        subMenuActiveBg: "#f6da4d",
        navTextColor: "#fff",
        menuText: "rgb(254 254 254 / 65%)",
        sidebarLogo: "#443b05",
        menuTitleHover: "#fff",
        menuActiveBefore: "#f6da4d"
    },
    mingQing: {
        color: "#13c2c2",
        subMenuActiveText: "#fff",
        menuBg: "#032121",
        menuHover: "#59bfc1",
        subMenuBg: "#000",
        subMenuActiveBg: "#59bfc1",
        navTextColor: "#7a80b4",
        menuText: "#7a80b4",
        sidebarLogo: "#053434",
        menuTitleHover: "#fff",
        menuActiveBefore: "#59bfc1"
    },
    auroraGreen: {
        color: "#52c41a",
        subMenuActiveText: "#fff",
        menuBg: "#0b1e15",
        menuHover: "#60ac80",
        subMenuBg: "#000",
        subMenuActiveBg: "#60ac80",
        navTextColor: "#7a80b4",
        menuText: "#7a80b4",
        sidebarLogo: "#112f21",
        menuTitleHover: "#fff",
        menuActiveBefore: "#60ac80"
    },
    pink: {
        color: "#eb2f96",
        subMenuActiveText: "#fff",
        menuBg: "#28081a",
        menuHover: "#d84493",
        subMenuBg: "#000",
        subMenuActiveBg: "#d84493",
        navTextColor: "#7a80b4",
        menuText: "#7a80b4",
        sidebarLogo: "#3f0d29",
        menuTitleHover: "#fff",
        menuActiveBefore: "#d84493"
    },
    saucePurple: {
        color: "#722ed1",
        subMenuActiveText: "#fff",
        menuBg: "#130824",
        menuHover: "#693ac9",
        subMenuBg: "#000",
        subMenuActiveBg: "#693ac9",
        navTextColor: "#7a80b4",
        menuText: "#7a80b4",
        sidebarLogo: "#1f0c38",
        menuTitleHover: "#fff",
        menuActiveBefore: "#693ac9"
    }
};
export function genScssMultipleScopeVars(): MultipleScopeVarsItem[] {
    const result = [] as MultipleScopeVarsItem[];
    Object.keys(themeColors).forEach(key => {
        result.push({
            scopeName: `layout-theme-${key}`,
            varsContent: `$primary-color: ${themeColors[key].color} !default;$vxe-primary-color: $primary-color;$subMenuActiveText: ${themeColors[key].subMenuActiveText} !default;$menuBg: ${themeColors[key].menuBg} !default;$menuHover: ${themeColors[key].menuHover} !default;$subMenuBg: ${themeColors[key].subMenuBg} !default;$subMenuActiveBg: ${themeColors[key].subMenuActiveBg} !default;$navTextColor: ${themeColors[key].navTextColor} !default;$menuText: ${themeColors[key].menuText} !default;$sidebarLogo: ${themeColors[key].sidebarLogo} !default;$menuTitleHover: ${themeColors[key].menuTitleHover} !default;$menuActiveBefore: ${themeColors[key].menuActiveBefore} !default;`
        } as MultipleScopeVarsItem);
    })
    return result;
}
  • src/components/setting.vue

    <script setup lang="ts">
    import { ref } from "vue";
    //换肤插件内置方法,可以用来切换主题色
    import { toggleTheme } from "@zougt/vite-plugin-theme-preprocessor/dist/browser-utils";
    type themeColorsType = {
      color: string;
      themeColor: string;
    };
    const themeColors = ref<Array<themeColorsType>>([
      // 道奇蓝(默认)
      { color: "#1b2a47", themeColor: "default" },
      // 亮白色
      { color: "#ffffff", themeColor: "light" },
      // 猩红色
      { color: "#f5222d", themeColor: "dusk" },
      // 橙红色
      { color: "#fa541c", themeColor: "volcano" },
      // 金色
      { color: "#fadb14", themeColor: "yellow" },
      // 绿宝石
      { color: "#13c2c2", themeColor: "mingQing" },
      // 酸橙绿
      { color: "#52c41a", themeColor: "auroraGreen" },
      // 深粉色
      { color: "#eb2f96", themeColor: "pink" },
      // 深紫罗兰色
      { color: "#722ed1", themeColor: "saucePurple" }
    ]);
    ​
    let dataTheme = ref<boolean>(false);
    // 设置导航主题色
    function setLayoutThemeColor(theme: string) {
      toggleTheme({
        scopeName: `layout-theme-${theme}`
      });
    }
    // 主题色 激活选择项
    const getThemeColor = (val: string) => {
      return val;
    };
    // 日间、夜间主题切换
    function dataThemeChange() {
      if (dataTheme.value) {
        setLayoutThemeColor("light");
      } else {
        setLayoutThemeColor("default");
      }
    }
    </script><template>
      <div>
        <el-divider>主题</el-divider>
        <el-switch v-model="dataTheme"
                   inline-prompt
                   @change="dataThemeChange">
        </el-switch>
        <el-divider>主题色</el-divider>
        <ul class="theme-color">
          <li v-for="(item, index) in themeColors"
              :key="index"
              :style="`background:${item.color};`"
              @click="setLayoutThemeColor(item.themeColor)">
            <el-icon style="margin: 0.1em 0.1em 0 0"
                     :size="17"
                     :color="getThemeColor(item.themeColor)">
              <IconifyIconOffline icon="check" />
            </el-icon>
          </li>
        </ul>
      </div></template>
    <style lang="scss" scoped>
    .theme-color {
      width: 100%;
      height: 40px;
      margin-top: 20px;
      display: flex;
      justify-content: center;
    ​
      li {
        float: left;
        width: 20px;
        height: 20px;
        margin-top: 8px;
        margin-right: 8px;
        font-weight: 700;
        text-align: center;
        border-radius: 2px;
        cursor: pointer;
    ​
        &:nth-child(2) {
          border: 1px solid #ddd;
        }
      }
    }
    </style>
  • src/App.vue

    <template>
      <div>
        <setting></setting>
      </div>
    </template>
    <script setup lang="ts">
    import setting from "./components/setting.vue"
    </script>
    <style  lang="scss">
    body {
      background: $sidebarLogo;//这里背景色用了主题色,调用toggleTheme后会自动运用更改过后的主题色
      color: #fff;
    }
    </style>