vue3 主题模式 结合 element-plus的主题
npm i element-plus --save-dev
在 Vue 3 中,实现主题模式主要有以下几种方式
1.使用 CSS 变量(自定义属性)
CSS 变量是一种在 CSS 中定义可重用值的方式。在主题模式中,可以将颜色、字体大小等样式属性设置为 CSS 变量。然后通过 JavaScript 或 Vue 的响应式特性来切换这些变量的值,从而实现主题的切换
var.css
:root {
--login-bg-color: #293146;
--left-menu-max-width: 200px;
--left-menu-min-width: 64px;
--left-menu-bg-color: #001529;
--left-menu-bg-light-color: #0f2438;
--left-menu-bg-active-color: var(--el-color-primary);
--left-menu-text-color: #bfcbd9;
--left-menu-text-active-color: #fff;
--left-menu-collapse-bg-active-color: var(--el-color-primary);
--logo-height: 50px;
--logo-title-text-color: #fff;
--top-header-bg-color: '#fff';
--top-header-text-color: 'inherit';
--top-header-hover-color: #f6f6f6;
--top-tool-height: var(--logo-height);
--top-tool-p-x: 0;
--tags-view-height: 35px;
--tab-menu-max-width: 80px;
--tab-menu-min-width: 30px;
--tab-menu-collapse-height: 36px;
}
.dark {
--app-content-bg-color: var(--el-bg-color);
}
html,
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
*,
:after,
:before {
margin: 0;
padding: 0;
box-sizing: border-box;
}
index.scss
@use './var.css';
@use 'element-plus/theme-chalk/dark/css-vars.css';
2.在main 引入 index.scss
// 引入全局样式
import '@/styles/index.scss'
3.在 Vue 组件中动态切换主题
<script setup lang="ts">
import { useAppStore } from '@/store/modules/app' //引入的状态管理
import { ThemeSwitch } from '@/components/ThemeSwitch' // 引入的组件黑白切换
const appStore = useAppStore()
const textColor = computed(() => appStore.getTextColor)
appStore.initTheme() // Pinia 实现状态管理
</script>
<template>
<!--element-plus ElConfigProvider 组件的的封装-->
<ConfigGlobal>
<!-- <p style="color: var(--el-text-color-primary);">p标签</p> -->
<p :style="{'color':textColor}" >p标签</p>
<ThemeSwitch></ThemeSwitch>
</ConfigGlobal>
</template>
<style scoped lang="scss">
$prefix-cls: #{$namespace}-app;
.size {
width: 100%;
height: 100%;
}
html,
body {
@extend .size;
padding: 0 !important;
margin: 0;
overflow: hidden;
#app {
@extend .size;
}
}
.#{$prefix-cls}-grey-mode {
filter: grayscale(100%);
}
</style>
注意 :CSS 变量与样式冲突解决
如果你自定义的主题与 Element Plus 的默认样式冲突,可以通过在你的主题 CSS 文件中明确覆盖 Element Plus 的变量来解决。 在 var.css 中,确保所有需要覆盖的变量都被正确设置 element-plus 在vite.config.ts 配置自动导入
store/modules/app.ts
import { defineStore } from "pinia";
// import { store } from '../index'
// import { colorIsDark, hexToRGB, lighten, mix } from '@/utils/color'
import { setCssVar,humpToUnderline } from '@/utils'
import { useDark } from '@vueuse/core' 安装 @vueuse/core
export const useAppStore = defineStore('app', {
state: () => {
return {
isCollapse: false, // 菜单是否折叠
isFullScreen: false, // 页面是否全屏
isDark:false, // 是否是暗黑模式
textColor: '#303133',
theme:{
//文字的颜色
// 主题色
elColorPrimary: '#409eff',
// 左侧菜单边框颜色
leftMenuBorderColor: 'inherit',
// 左侧菜单背景颜色
leftMenuBgColor: '#001529',
// 左侧菜单浅色背景颜色
leftMenuBgLightColor: '#0f2438',
// 左侧菜单选中背景颜色
leftMenuBgActiveColor: 'var(--el-color-primary)',
// 左侧菜单收起选中背景颜色
leftMenuCollapseBgActiveColor: 'var(--el-color-primary)',
// 左侧菜单字体颜色
leftMenuTextColor: '#bfcbd9',
// 左侧菜单选中字体颜色
leftMenuTextActiveColor: '#fff',
// logo字体颜色
logoTitleTextColor: '#fff',
// logo边框颜色
logoBorderColor: 'inherit',
// 头部背景颜色
topHeaderBgColor: '#fff',
// 头部字体颜色
topHeaderTextColor: 'inherit',
// 头部悬停颜色
topHeaderHoverColor: '#f6f6f6',
// 头部边框颜色
topToolBorderColor: '#eee'
}
}
},
getters:{
getIsDark(): boolean {
return this.isDark
},
getTextColor(): string {
return this.textColor
}
},
actions: {
setIsDark(isDark: boolean) {
this.isDark = isDark
if (this.isDark) {
// setCssVar('--el-text-color-primary', 'red')
document.documentElement.classList.add('dark')
document.documentElement.classList.remove('light')
} else {
// setCssVar('--el-text-color-primary', '#303133')
document.documentElement.classList.add('light')
document.documentElement.classList.remove('dark')
}
this.textColor = this.isDark? 'red' : '#303133'
// wsCache.set(CACHE_KEY.IS_DARK, this.isDark)
},
setCssVarTheme() {
for (const key in this.theme) {
setCssVar(`--${humpToUnderline(key)}`, this.theme[key])
}
// this.setPrimaryLight()
},
setPrimaryLight() {
if (this.theme.elColorPrimary) {
const elColorPrimary = this.theme.elColorPrimary
const color = this.isDark ? '#000000' : '#ffffff'
const lightList = [3, 5, 7, 8, 9]
lightList.forEach((v) => {
setCssVar(`--el-color-primary-light-${v}`, mix(color, elColorPrimary, v / 10))
})
setCssVar(`--el-color-primary-dark-2`, mix(color, elColorPrimary, 0.2))
}
},
initTheme() {
const isDark = useDark({
valueDark: 'dark',
valueLight: 'light'
})
isDark.value = this.getIsDark
},
},
persist: true
})
// export const useAppStoreWithOut = () => {
// return useAppStore(store)
// }
// 使用到的函数
export const setCssVar = (prop: string, val: any, dom = document.documentElement) => {
dom.style.setProperty(prop, val)
}
/**
* @param str 需要转下划线的驼峰字符串
* @returns 字符串下划线
*/
export const humpToUnderline = (str: string): string => {
return str.replace(/([A-Z])/g, '-$1').toLowerCase()
}
themeSwitch.vue
<script lang="ts" setup>
import { useAppStore } from '@/store/modules/app'
import { useIcon } from '@/hooks/web/useIcon'
import { useDesign } from '@/hooks/web/useDesign'
defineOptions({ name: 'ThemeSwitch' })
const { getPrefixCls } = useDesign()
// const prefixCls = getPrefixCls('theme-switch')
const prePrefixCls = 'ad-theme-switch'
const Sun = useIcon({ icon: 'emojione-monotone:sun', color: '#fde047' })
const CrescentMoon = useIcon({ icon: 'emojione-monotone:crescent-moon', color: '#fde047' })
const appStore = useAppStore()
// 初始化获取是否是暗黑主题
// const isDark = ref(appStore.getIsDark)
// 设置switch的背景颜色
const blackColor = 'var(--el-color-black)'
// const themeChange = (val: boolean) => {
// appStore.setIsDark(val)
// }
const isDark = computed({
get() {
return appStore.getIsDark
},
set(val: boolean) {
appStore.setIsDark(val)
}
})
</script>
<template>
<el-switch
v-model="isDark"
:active-color="blackColor"
:active-icon="Sun"
:border-color="blackColor"
:class="prefixCls"
:inactive-color="blackColor"
:inactive-icon="CrescentMoon"
inline-prompt
/>
</template>
<style lang="scss" scoped>
:deep(.el-switch__core .el-switch__inner .is-icon) {
overflow: visible;
}
</style>
初始化的主题 ConfigGlobal.vue
<script setup lang="ts">
import { ElConfigProvider } from 'element-plus'
import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign'
const { variables } = useDesign() // 命名规则
const appStore = useAppStore()
// 初始化所有主题色
onMounted(() => {
appStore.setCssVarTheme()
})
</script>
<template>
<ElConfigProvider
:namespace="variables.elNamespace"
:message="{ max: 1 }"
>
<slot></slot>
</ElConfigProvider>
</template>