likes
comments
collection
share

Vue3+Vite 实现布局以及黑夜模式切换、一键换肤功能

作者站长头像
站长
· 阅读数 1

项目搭建部分已经完成,可见:

接下来实现Layout布局并实现白天夜晚模式的切换以及换肤。

实现Layout布局

引入ElementPlus

npm i element-plus

main.js引入

import App from './App.vue'
import { createApp } from 'vue'

+ import ElementPlus from 'element-plus'
+ import 'element-plus/dist/index.css' // 样式
+ import * as ElementPlusIconsVue from '@element-plus/icons-vue' // icon

const app = createApp(App)

// 批量注册icon
+ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+   app.component(key, component)
+ }

+ app.use(ElementPlus)

搭建布局

由于我们要实现的只是页面的布局,所以可以新建一个文件夹用于存放layout相关文件。

在src目录下,创建一个layout文件夹,新建Layout.vue文件。一般的后台管理系统都分为 左侧目录、顶部导航、中间内容三个部分,如图。那么我们可以将这三个部分分为三个组件:sideMenunavBar、Content部分则是View。

Vue3+Vite 实现布局以及黑夜模式切换、一键换肤功能 更多内容见github-bluewhale Vue3+Vite 实现布局以及黑夜模式切换、一键换肤功能 接着在App.vue文件中引入Layout组件。插槽部分则引入路由组件router-view

<script setup>
import MainLayout from './layout/Layout.vue'
</script>

<template>
  <Suspense>
    <MainLayout>
      <router-view />
    </MainLayout>
  </Suspense>
</template>

<style lang="scss" scoped></style>

关于<Suspense> ,是Vue3的一个新的内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。

实现暗夜模式切换

方案一:切换类名样式覆盖

  1. assets文件夹下,新建全局css文件global.scss
  2. 设置两个样式类,一个day,一个dark
  3. 在页面上添加选择器,触发选择器改变元素类名。
/* 日间模式 start */
#app.day {
  background-color: #f0f2f5 ;
  .box {
    background-color: #ffffff;
  }
}
/* 日间模式 end */

/* 夜间模式 start */
#app.dark {
  background-color: #1a2035;
  color: #fff;
  .box {
    background-color: #313852;
  }
  .menu {
    background: linear-gradient(195deg,#323a54,#1a2035);
  }
}
/* 夜间模式 end */
<script setup>
import { ref } from 'vue'
const mode = ref('day')
function changeMode(value) {
  document.getElementById('app').className = value
}
</script>

<template>
...
<el-switch v-model="mode" style="--el-switch-on-color: #313852" active-value="dark" inactive-value="day" @change="changeMode">
...
</template>

// index.html
<body>
    <div id="app" class="day" />
</body>

方案二:设置css变量,切换类名

  1. 定义根作用域下变量 以及 不同模式类名下变量取值
  2. 在需要变化的类下使用变量
  3. 切换时更改根元素类名
/* 定义根作用域下的变量 */
:root {
  --theme-background: #f0f2f5;
  --theme-box-backgroud#ffffff;
}
/* 更改dark类名下变量的取值 */
.dark{
  --theme-box-backgroud: #313852;
  --theme-background: #1a2035;
}
/* 更改day类名下变量的取值 */
.day{
  --theme-background: #f0f2f5;
  --theme-box-backgroud#ffffff;
}

/* 使用变量 */
#app {
    background: var(--theme-background);
    .box {
        background: var(--theme-box-backgroud);
    }
}
<script setup>
import { ref } from 'vue'
const mode = ref('day')
function changeMode(value) {
  document.html.className = value
}
</script>

<template>
...
<el-switch v-model="mode" style="--el-switch-on-color: #313852" active-value="dark" inactive-value="day" @change="changeMode">
...
</template>

实现换肤

系统换肤主要是两个方面:elementUI组件方面还有一些自定义组件方面。

自定义组件换肤

参考暗夜模式切换,首先定义几个全局的样式变量。

Element 修改主题色

官网使用方法

无法实现动态一键换肤

新建 element-variable.scss

@forward 'element-plus/theme-chalk/src/common/var.scss' with (
  $colors: (
    "primary" : (
      "base" : #f0f2f5
    ),
    "success": (
      "base": #21ba45,
     ),
    "warning": (
      "base": #f2711c,
    ),
    "danger": (
      "base": #db2828,
    ),
    "error": (
      "base": #db2828,
    ),
    "info": (
      "base": #42b8dd,
    )
  )
);

@use "element-plus/theme-chalk/src/index.scss" as *; 

出现报错: @forward rules must be written before any other rules.

解决方法: 将引入文件放在最前面, 并且不能在main.js文件里引入

// vite.config.js
export default defineConfig({
  ...
  css: {
    preprocessorOptions: {
      scss: {
        // ❌
        // additionalData: '@import "@/src/assets/style/variable.scss";@import "@/src/assets/style/element-variable.scss";',
        // ✅
        additionalData: '@import "@/src/assets/style/element-variable.scss";@import "@/src/assets/style/variable.scss";',
        javascriptEnabled: true
      }
    }
  }
})
实现一键换肤

新建一个theme.scss样式文件,前提 mian.js文件里需要引入element样式:import 'element-plus/dist/index.css'

// 此处设置样式并未生效,element的样式文件优先级更高,仅用于以下变量使用。
:root {
  --el-color-primary: #e91e63;
  --el-color-primary-light-3: #f06292;
  --el-color-primary-light-5: #f48fb1;
  --el-color-primary-light-7: #f8bcd0;
  --el-color-primary-light-8: #fbd2e0;
  --el-color-primary-light-9: #fde9ef;
  --el-color-primary-dark-2: #ba184f;
}
// 此处变量用于非elemnt组件
$primary-color: var(--el-color-primary);

// 在vite.config文件中引入
css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/assets/style/theme.scss";`,
        javascriptEnabled: true
      }
    }
  }

然后新建一个stye.js的文件用于实现一键换肤功能。这里用的比较笨的方法,写死了颜色。之前阅读了element的源码,使用scss去计算light不同值的颜色,这块内容还没学到位。学会了再更新。

// style.js
const colors = {
  pink: {
    '--el-color-primary': '#e91e63',
    '--el-color-primary-light-3': '#f06292',
    '--el-color-primary-light-5': '#f48fb1',
    '--el-color-primary-light-7': '#f8bcd0',
    '--el-color-primary-light-8': '#fbd2e0',
    '--el-color-primary-light-9': '#fde9ef',
    '--el-color-primary-dark-2': '#ba184f'
  },
  blue: {
    '--el-color-primary': '#409eff',
    '--el-color-primary-light-3': '#409eff',
    '--el-color-primary-light-5': '#a0cfff',
    '--el-color-primary-light-7': '#c6e2ff',
    '--el-color-primary-light-8': '#d9ecff',
    '--el-color-primary-light-9': '#ecf5ff',
    '--el-color-primary-dark-2': '#337ecc'
  },
  purple: {
    '--el-color-primary': '#9847c4',
    '--el-color-primary-light-3': '#b77ed6',
    '--el-color-primary-light-5': '#cca3e2',
    '--el-color-primary-light-7': '#e0c8ed',
    '--el-color-primary-light-8': '#eadaf3',
    '--el-color-primary-light-9': '#f5edf9',
    '--el-color-primary-dark-2': '#7a399d'
  },
  orange: {
    '--el-color-primary': '#ffa726',
    '--el-color-primary-light-3': '#ffc167',
    '--el-color-primary-light-5': '#ffd393',
    '--el-color-primary-light-7': '#ffe5be',
    '--el-color-primary-light-8': '#ffedd4',
    '--el-color-primary-light-9': '#fff6e9',
    '--el-color-primary-dark-2': '#cc861e'
  }
}

export function setTheme(params) {
  const localColor = localStorage.getItem('themeColor') || 'pink'
  const themeColor = params || localColor
  const el = document.documentElement
  for (let key in colors[themeColor]) {
    el.style.setProperty(key, colors[themeColor][key])
  }
  // 本地缓存
  localStorage.setItem('themeColor', themeColor)
}

然后可以在main.js初始化主题,也可以不初始化,则使用的是element原生的颜色。在vue文件里同样引入setTheme方法,切换即可。

// main.js
import { setTheme } from './utils/style'
// 初始化
setTheme('pink')
<div class="cricle" style="background: #e91e63;" @click="setTheme('pink')"></div>
<div class="cricle" style="background: #409eff;" @click="setTheme('blue')"></div>
<div class="cricle" style="background: #9847c4;" @click="setTheme('purple')"></div>
<div class="cricle" style="background: #ffa726;" @click="setTheme('orange')"></div>