提交 a6882db1 编写于 作者: 郝先瑞

feat: 系统设置添加主题动态切换

上级 108aeeb4
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref, watch} from "vue"; import {computed, onMounted, ref, watch} from "vue";
import {useAppStoreHook} from "@/store/modules/app"; import {useAppStoreHook} from "@/store/modules/app";
import {ElConfigProvider} from 'element-plus' import {ElConfigProvider} from 'element-plus'
...@@ -29,5 +29,9 @@ watch(language, (value) => { ...@@ -29,5 +29,9 @@ watch(language, (value) => {
// 初始化立即执行, // 初始化立即执行,
immediate: true immediate: true
}) })
onMounted(()=>{
const style = localStorage.getItem("style");
document.documentElement.style.cssText = style as string;
})
</script> </script>
...@@ -2,31 +2,28 @@ ...@@ -2,31 +2,28 @@
<div ref="rightPanel" :class="{show:show}" class="rightPanel-container"> <div ref="rightPanel" :class="{show:show}" class="rightPanel-container">
<div class="rightPanel-background"/> <div class="rightPanel-background"/>
<div class="rightPanel"> <div class="rightPanel">
<div class="handle-button" :style="{'top':buttonTop+'px','background-color':theme}" @click="show=!show"> <div class="handle-button" :style="{'top':buttonTop+'px','background-color':theme}" @click="show=!show">
<Close style="width: 1em; height: 1em;vertical-align: middle " v-show="show"/> <Close style="width: 1em; height: 1em;vertical-align: middle " v-show="show"/>
<Setting style="width:1em; height:1em;vertical-align: middle " v-show="!show"/> <Setting style="width:1em; height:1em;vertical-align: middle " v-show="!show"/>
</div> </div>
<div class="rightPanel-items"> <div class="rightPanel-items">
<slot/> <slot/>
</div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, onBeforeUnmount, onMounted, ref, watchEffect} from "vue"; import {computed, onBeforeUnmount, onMounted, ref, watch} from "vue";
import {addClass, removeClass} from '@/utils/index' import {addClass, removeClass} from '@/utils/index'
import {useSettingStoreHook} from "@/store/modules/settings"; import {useSettingStoreHook} from "@/store/modules/settings";
// 图标依赖 // 图标依赖
import {Close, Setting} from '@element-plus/icons-vue' import {Close, Setting} from '@element-plus/icons-vue'
import {ElColorPicker} from "element-plus";
const props = defineProps({ const props = defineProps({
clickNotClose: {
default: false,
type: Boolean
},
buttonTop: { buttonTop: {
default: 250, default: 250,
type: Number type: Number
...@@ -37,11 +34,12 @@ const theme = computed(() => useSettingStoreHook().theme) ...@@ -37,11 +34,12 @@ const theme = computed(() => useSettingStoreHook().theme)
const show = ref(false) const show = ref(false)
watchEffect(() => { watch(show, (value) => {
if (show.value && !props.clickNotClose) { console.log('show', value)
if (value) {
addEventClick() addEventClick()
} }
if (show.value) { if (value) {
addClass(document.body, 'showRightPanel') addClass(document.body, 'showRightPanel')
} else { } else {
removeClass(document.body, 'showRightPanel') removeClass(document.body, 'showRightPanel')
...@@ -53,24 +51,29 @@ function addEventClick() { ...@@ -53,24 +51,29 @@ function addEventClick() {
} }
function closeSidebar(evt: any) { function closeSidebar(evt: any) {
const parent = evt.target.closest('.rightPanel')
// 主题选择点击不关闭
let parent = evt.target.closest('.theme-picker-dropdown')
if (parent) {
return
}
parent = evt.target.closest('.rightPanel')
if (!parent) { if (!parent) {
show.value = false show.value = false
window.removeEventListener('click', closeSidebar) window.removeEventListener('click', closeSidebar)
} }
} }
const rightPanel = ref(null) const rightPanel = ref(ElColorPicker)
function insertToBody() { function insertToBody() {
console.log('insertToBody', rightPanel)
const elx = rightPanel.value as any const elx = rightPanel.value as any
const body = document.querySelector('body') as any const body = document.querySelector('body') as any
body.insertBefore(elx, body.firstChild) body.insertBefore(elx, body.firstChild)
} }
onMounted(() => { onMounted(() => {
console.log('theme', useSettingStoreHook().theme)
insertToBody() insertToBody()
}) })
......
<template>
<el-color-picker
v-model="theme"
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d' ]"
class="theme-picker"
popper-class="theme-picker-dropdown"
/>
</template>
<script setup lang="ts">
import {computed, nextTick, watch} from "vue";
import {useSettingStoreHook} from "@/store/modules/settings";
import {useTagsViewStoreHook} from "@/store/modules/tagsView";
import {useRoute, useRouter} from "vue-router";
// 参考连接:https://juejin.cn/post/7024025899813044232#heading-1
import {mix} from "@/utils";
// 白色混合色
const mixWhite = "#ffffff";
// 黑色混合色
const mixBlack = "#000000";
const node = document.documentElement;
const theme = computed(() => useSettingStoreHook().theme)
watch(theme, (color: string) => {
node.style.setProperty("--el-color-primary", color);
localStorage.setItem("theme", color)
for (let i = 1; i < 10; i += 1) {
node.style.setProperty(`--el-color-primary-light-${i}`, mix(color, mixWhite, i * 0.1));
}
node.style.setProperty("--el-color-primary-dark", mix(color, mixBlack, 0.1));
localStorage.setItem("style", node.style.cssText);
})
</script>
<style>
.theme-message,
.theme-picker-dropdown {
z-index: 99999 !important;
}
.theme-picker .el-color-picker__trigger {
height: 26px !important;
width: 26px !important;
padding: 2px;
}
.theme-picker-dropdown .el-color-dropdown__link-btn {
display: none;
}
</style>
<template> <template>
<div class="drawer-container"> <div class="drawer-container">
<div>
<h3 class="drawer-title">系统布局配置</h3> <h3 class="drawer-title">系统布局配置</h3>
<div class="drawer-item">
<span>主题颜色</span>
<div style="float: right;height: 26px;margin: -3px 8px 0 0;">
<theme-picker @change="themeChange"/>
</div>
</div>
<div class="drawer-item"> <div class="drawer-item">
<span>开启 Tags-View</span> <span>开启 Tags-View</span>
<el-switch v-model="tagsView" class="drawer-switch"/> <el-switch v-model="state.tagsView" class="drawer-switch"/>
</div> </div>
<div class="drawer-item"> <div class="drawer-item">
...@@ -17,40 +22,38 @@ ...@@ -17,40 +22,38 @@
<span>侧边栏 Logo</span> <span>侧边栏 Logo</span>
<el-switch v-model="sidebarLogo" class="drawer-switch"/> <el-switch v-model="sidebarLogo" class="drawer-switch"/>
</div> </div>
</div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import {defineComponent, reactive, toRefs, watch} from "vue" import {reactive, toRefs, watch} from "vue";
import { useSettingStoreHook } from "@/store/modules/settings"; import {useSettingStoreHook} from "@/store/modules/settings";
export default defineComponent({ import ThemePicker from '@/components/ThemePicker/index.vue';
setup() {
const state = reactive({ const state = reactive({
fixedHeader:useSettingStoreHook().fixedHeader, fixedHeader: useSettingStoreHook().fixedHeader,
tagsView:useSettingStoreHook().tagsView, tagsView: useSettingStoreHook().tagsView,
sidebarLogo:useSettingStoreHook().sidebarLogo, sidebarLogo: useSettingStoreHook().sidebarLogo
themeChange: (val) => { })
useSettingStoreHook().changeSetting( { key: 'theme', val })
} const {fixedHeader, tagsView, sidebarLogo} = toRefs(state)
})
watch(()=>state.fixedHeader,(value)=>{ function themeChange(val: any) {
useSettingStoreHook().changeSetting( { key: 'fixedHeader', value }) useSettingStoreHook().changeSetting({key: 'theme', value: val})
}) }
watch(() => state.tagsView, (value) => { watch(() => state.fixedHeader, (value) => {
useSettingStoreHook().changeSetting( { key: 'showTagsView', value }) useSettingStoreHook().changeSetting({key: 'fixedHeader', value: value})
}) })
watch(() => state.sidebarLogo, (value) => { watch(() => state.tagsView, (value) => {
useSettingStoreHook().changeSetting( { key: 'sidebarLogo', value }) useSettingStoreHook().changeSetting({key: 'showTagsView', value: value})
})
return {
...toRefs(state)
}
}
}) })
watch(() => state.sidebarLogo, (value) => {
useSettingStoreHook().changeSetting({key: 'sidebarLogo', value: value})
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
......
<template> <template>
<div :class="classObj" class="app-wrapper"> <div :class="classObj" class="app-wrapper">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/> <div v-if="device==='mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar class="sidebar-container"/> <sidebar class="sidebar-container"/>
<div :class="{hasTagsView:needTagsView}" class="main-container"> <div :class="{hasTagsView:needTagsView}" class="main-container">
<div :class="{'fixed-header':fixedHeader}"> <div :class="{'fixed-header':fixedHeader}">
......
...@@ -2,13 +2,14 @@ import {defineStore} from "pinia"; ...@@ -2,13 +2,14 @@ import {defineStore} from "pinia";
import {store} from "@/store"; import {store} from "@/store";
import {SettingState} from "@/store/interface"; import {SettingState} from "@/store/interface";
import defaultSettings from '../../settings' import defaultSettings from '../../settings'
const {showSettings, tagsView, fixedHeader, sidebarLogo} = defaultSettings const {showSettings, tagsView, fixedHeader, sidebarLogo} = defaultSettings
import variables from '@/styles/element-variables.module.scss' import variables from '@/styles/element-variables.module.scss'
export const useSettingStore = defineStore({ export const useSettingStore = defineStore({
id: "setting", id: "setting",
state: (): SettingState => ({ state: (): SettingState => ({
theme: variables.theme, theme: localStorage.get("theme") || variables.theme,
showSettings: showSettings, showSettings: showSettings,
tagsView: tagsView, tagsView: tagsView,
fixedHeader: fixedHeader, fixedHeader: fixedHeader,
......
:root { :root {
--el-color-primary: #409EFF; // 这里可以设置你自定义的颜色变量
// 这个是element主要按钮:active的颜色,当主题更改后此变量的值也随之更改
--el-color-primary-dark: #0d84ff;
}
/* 核心组件的变量,下面这些样式是必须要写的 */
.el-link.el-link--primary:hover {
color: var(--el-color-primary-light-2) !important;
}
.el-tag {
--el-tag-bg-color: var(--el-color-primary-light-9);
--el-tag-border-color: var(--el-color-primary-light-8);
--el-tag-text-color: var(--el-color-primary);
--el-tag-hover-color: var(--el-color-primary);
}
.el-button--default:active {
color: var(--el-color-primary-dark) !important;
border-color: var(--el-color-primary-dark) !important;
}
.el-button--primary {
--el-button-text-color: var(--el-color-white) !important;
--el-button-bg-color: var(--el-color-primary) !important;
--el-button-border-color: var(--el-color-primary) !important;
--el-button-hover-bg-color: var(--el-color-primary-light-2) !important;
--el-button-hover-border-color: var(--el-color-primary-light-2) !important;
--el-button-active-bg-color: var(--el-color-primary-dark) !important;
--el-button-active-border-color: var(--el-color-primary-dark) !important;
} }
// 覆盖 element-plus 的样式 // 覆盖 element-plus 的样式
......
/** /**
* I think element-ui's default theme color is too light for long-term use. * I think element default theme color is too light for long-term use.
* So I modified the default color and you can modify it to your liking. * So I modified the default color and you can modify it to your liking.
* https://vitejs.cn/guide/features.html#postcss * https://vitejs.cn/guide/features.html#postcss
**/ **/
......
...@@ -27,4 +27,21 @@ export function removeClass(ele: HTMLElement, cls: string) { ...@@ -27,4 +27,21 @@ export function removeClass(ele: HTMLElement, cls: string) {
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
ele.className = ele.className.replace(reg, ' ') ele.className = ele.className.replace(reg, ' ')
} }
} }
\ No newline at end of file
export function mix(color1: string, color2: string, weight: number) {
weight = Math.max(Math.min(Number(weight), 1), 0);
let r1 = parseInt(color1.substring(1, 3), 16);
let g1 = parseInt(color1.substring(3, 5), 16);
let b1 = parseInt(color1.substring(5, 7), 16);
let r2 = parseInt(color2.substring(1, 3), 16);
let g2 = parseInt(color2.substring(3, 5), 16);
let b2 = parseInt(color2.substring(5, 7), 16);
let r = Math.round(r1 * (1 - weight) + r2 * weight);
let g = Math.round(g1 * (1 - weight) + g2 * weight);
let b = Math.round(b1 * (1 - weight) + b2 * weight);
const rStr = ("0" + (r || 0).toString(16)).slice(-2);
const gStr = ("0" + (g || 0).toString(16)).slice(-2);
const bStr = ("0" + (b || 0).toString(16)).slice(-2);
return "#" + rStr + gStr + bStr;
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册