Przeglądaj źródła

主题设置增加风格设置

tongshangming 3 lat temu
rodzic
commit
bc23d0beef

+ 1 - 0
src/App.vue

@@ -5,6 +5,7 @@ const locale = zhCn
 
 const themeStore = useThemeStore()
 themeStore.initThemeColor()
+themeStore.initThemeStyle()
 </script>
 
 <template>

+ 2 - 0
src/assets/base.css

@@ -53,6 +53,8 @@
 :root {
   --menu-width: 220px;
   --main-padding: 16px;
+  --el-header-height: 60px;
+  --el-aside-width: 220px;
 }
 
 *,

+ 9 - 0
src/assets/main.css

@@ -52,4 +52,13 @@ a {
   width: 178px;
   height: 178px;
   text-align: center;
+}
+
+.line2{
+	max-height: 46px;
+	overflow: hidden;
+	text-overflow: ellipsis;
+	display: -webkit-box;
+	-webkit-line-clamp: 2;
+	-webkit-box-orient: vertical;
 }

+ 49 - 0
src/assets/svg/header-theme-dark.svg

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
+    <title>Group 5 Copy 5</title>
+    <desc>Created with Sketch.</desc>
+    <defs>
+        <filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox"
+                id="filter-1">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1"
+                            result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.15 0" type="matrix"
+                           in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixOuter1"></feMergeNode>
+                <feMergeNode in="SourceGraphic"></feMergeNode>
+            </feMerge>
+        </filter>
+        <rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
+        <filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox"
+                id="filter-4">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1"
+                            result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.1 0" type="matrix"
+                           in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
+            <g id="Group-8" transform="translate(1167.000000, 0.000000)">
+                <g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
+                    <mask id="mask-3" fill="white">
+                        <use xlink:href="#path-2"></use>
+                    </mask>
+                    <g id="Rectangle-18">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                        <use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    </g>
+                    <rect id="Rectangle-11" fill="#303648" mask="url(#mask-3)" x="-1" y="0" width="49"
+                          height="10"></rect>
+                    <rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16"
+                          height="44"></rect>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 49 - 0
src/assets/svg/nav-theme-dark.svg

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
+    <title>Group 5 Copy 5</title>
+    <desc>Created with Sketch.</desc>
+    <defs>
+        <filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox"
+                id="filter-1">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1"
+                            result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.15 0" type="matrix"
+                           in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixOuter1"></feMergeNode>
+                <feMergeNode in="SourceGraphic"></feMergeNode>
+            </feMerge>
+        </filter>
+        <rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
+        <filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox"
+                id="filter-4">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1"
+                            result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.1 0" type="matrix"
+                           in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
+            <g id="Group-8" transform="translate(1167.000000, 0.000000)">
+                <g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
+                    <mask id="mask-3" fill="white">
+                        <use xlink:href="#path-2"></use>
+                    </mask>
+                    <g id="Rectangle-18">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                        <use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    </g>
+                    <rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="-1" y="0" width="49"
+                          height="10"></rect>
+                    <rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16"
+                          height="44"></rect>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 49 - 0
src/assets/svg/nav-theme-light.svg

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
+    <title>Group 5</title>
+    <desc>Created with Sketch.</desc>
+    <defs>
+        <filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox"
+                id="filter-1">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1"
+                            result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.15 0" type="matrix"
+                           in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixOuter1"></feMergeNode>
+                <feMergeNode in="SourceGraphic"></feMergeNode>
+            </feMerge>
+        </filter>
+        <rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
+        <filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox"
+                id="filter-4">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1"
+                            result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.1 0" type="matrix"
+                           in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="setting-copy-2" transform="translate(-1254.000000, -136.000000)">
+            <g id="Group-8" transform="translate(1167.000000, 0.000000)">
+                <g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 137.000000)">
+                    <mask id="mask-3" fill="white">
+                        <use xlink:href="#path-2"></use>
+                    </mask>
+                    <g id="Rectangle-18">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                        <use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    </g>
+                    <rect id="Rectangle-18" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="16"
+                          height="44"></rect>
+                    <rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="-1" y="0" width="49"
+                          height="10"></rect>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 1 - 0
src/components.d.ts

@@ -13,6 +13,7 @@ declare module '@vue/runtime-core' {
     ElEditor: typeof import('./components/ElEditor.vue')['default']
     Exception: typeof import('./components/Exception.vue')['default']
     FormComp: typeof import('./components/form/FormComp.vue')['default']
+    GlobalAside: typeof import('./components/GlobalAside.vue')['default']
     GlobalFooter: typeof import('./components/GlobalFooter.vue')['default']
     GlobalHeader: typeof import('./components/GlobalHeader.vue')['default']
     GlobalMenu: typeof import('./components/GlobalMenu.vue')['default']

+ 51 - 0
src/components/GlobalAside.vue

@@ -0,0 +1,51 @@
+<script lang="ts" setup>
+import { useMenuStore } from '@/stores/menu'
+import { useThemeStore } from '@/stores/theme'
+import config from '@/config/defaultSetting'
+import router from '@/router'
+
+const handleLogoClick = () => {
+  router.push({ name: config.homeRouteName })
+}
+
+const menuStore = useMenuStore()
+const collapse = computed(() => menuStore.collapse)
+
+const themeStore = useThemeStore()
+const themeStyle = computed(() => themeStore.themeStyle)
+const logoStyle = computed(() => {
+  if (themeStyle.value.name === 'nav-light') {
+    return {
+      color: themeStyle.value.textColor,
+      backgroundColor: themeStyle.value.bgColor
+    }
+  }
+  return {
+    color: '#fff',
+    backgroundColor: themeStyle.value.bgColor
+  }
+})
+</script>
+
+<template>
+  <div
+    class="logo flex items-center justify-center cursor-pointer px-10px"
+    :class="{ collapse: collapse }"
+    :style="logoStyle"
+    @click="handleLogoClick"
+  >
+    <img :src="config.logo" class="h-36px" />
+    <div class="ml-2 line2" :class="{ hidden: collapse }">{{ config.title }}</div>
+  </div>
+  <global-menu />
+</template>
+
+<style lang="scss" scoped>
+.logo {
+  width: var(--menu-width);
+  height: var(--el-header-height);
+}
+.collapse {
+  width: calc(var(--el-menu-icon-width) + var(--el-menu-base-level-padding) * 2);
+}
+</style>

+ 23 - 22
src/components/GlobalHeader.vue

@@ -1,16 +1,26 @@
 <script setup lang="ts">
 import { useMenuStore } from '@/stores/menu'
 import { useUserStore } from '@/stores/user'
-import config from '@/config/defaultSetting'
+import { useThemeStore } from '@/stores/theme'
 import avatar from '@/assets/images/avatar.png'
 import { ElMessageBox } from 'element-plus'
-import router from '@/router'
 
-const iconSize = ref(20)
+const themeStore = useThemeStore()
+const themeStyle = computed(() => themeStore.themeStyle)
+const headerStyle = computed(() => {
+  if (themeStyle.value.name === 'header-dark') {
+    return {
+      color: '#fff',
+      backgroundColor: themeStyle.value.bgColor
+    }
+  }
+  return {
+    // color: themeStyle.value.textColor,
+    backgroundColor: '#fff'
+  }
+})
 
-const handleLogoClick = () => {
-  router.push({ name: config.homeRouteName })
-}
+const iconSize = ref(20)
 
 // 菜单收缩展开切换
 const menuStore = useMenuStore()
@@ -46,20 +56,12 @@ const settingVisible = ref(false)
 </script>
 
 <template>
-  <header class="layout-header">
-    <div
-      class="logo flex items-center justify-center cursor-pointer"
-      :class="{ collapse: value }"
-      @click="handleLogoClick"
-    >
-      <img :src="config.logo" class="h-36px" />
-      <div class="ml-2" :class="{ hidden: value }">{{ config.title }}</div>
-    </div>
+  <header class="layout-header" :style="headerStyle">
     <div class="flex flex-grow items-center justify-between pr-4">
       <el-space :size="20">
         <el-button link @click="handleToggle">
-          <icon-menu-unfold-one :size="iconSize" v-if="!value" />
-          <icon-menu-fold-one :size="iconSize" v-else />
+          <icon-menu-unfold-one :size="iconSize" :fill="headerStyle.color" v-if="!value" />
+          <icon-menu-fold-one :size="iconSize" :fill="headerStyle.color" v-else />
         </el-button>
         <el-breadcrumb class="breadcrumb">
           <el-breadcrumb-item v-for="item in matched.slice(1, matched.length)" :key="item.path">
@@ -69,11 +71,11 @@ const settingVisible = ref(false)
       </el-space>
       <div class="flex items-center">
         <el-button link @click="toggleScreen">
-          <icon-off-screen :size="iconSize" v-if="isFullscreen" />
-          <icon-full-screen :size="iconSize" v-else />
+          <icon-off-screen :size="iconSize" :fill="headerStyle.color" v-if="isFullscreen" />
+          <icon-full-screen :size="iconSize" :fill="headerStyle.color" v-else />
         </el-button>
         <el-button link class="mr-4" @click="settingVisible = true">
-          <icon-setting-two :size="iconSize" />
+          <icon-setting-two :size="iconSize" :fill="headerStyle.color" />
         </el-button>
         <el-avatar class="mr-10px" :src="user.photo">
           <img :src="avatar" alt="" />
@@ -81,7 +83,7 @@ const settingVisible = ref(false)
         <el-dropdown>
           <span class="el-dropdown-link">
             {{ user.name }}
-            <el-icon class="el-icon--right">
+            <el-icon class="el-icon--right" :color="headerStyle.color">
               <arrow-down />
             </el-icon>
           </span>
@@ -107,7 +109,6 @@ const settingVisible = ref(false)
 
 <style scoped lang="scss">
 .layout-header {
-  background-color: #fff;
   height: 100%;
   display: flex;
   justify-content: space-between;

+ 16 - 4
src/components/GlobalMenu.vue

@@ -1,7 +1,11 @@
 <script setup lang="ts">
 import { useRouterStore } from '@/stores/router'
+import { useThemeStore } from '@/stores/theme'
 import { useMenuStore } from '@/stores/menu'
 
+const themeStore = useThemeStore()
+const themeStyle = computed(() => themeStore.themeStyle)
+
 const menuStore = useMenuStore()
 
 // 生成菜单部分开始
@@ -42,6 +46,7 @@ const handleSelect = (index: string) => {
     :collapse-transition="false"
     class="layout-menu"
     :class="{ 'menu-normal': !menuStore.collapse }"
+    :style="{ color: themeStyle.textColor }"
     @select="handleSelect"
   >
     <component :is="item.menuName" :index="item.path" v-for="(item, index) in menuRouter" :key="index">
@@ -64,19 +69,26 @@ const handleSelect = (index: string) => {
   </el-menu>
 </template>
 
-<style scoped>
+<style lang="scss">
 .menu-normal {
   width: var(--menu-width);
 }
 .layout-menu {
-  background-color: #fff;
-  height: calc(100vh - 60px);
+  height: calc(100vh - var(--el-header-height));
   border: none;
   flex-shrink: 0;
   overflow: auto;
 }
 .layout-menu .el-menu-item.is-active {
-  background-color: var(--el-color-primary);
+  background-color: var(--el-color-primary) !important;
   color: #fff;
 }
+.nav-dark,
+.header-dark {
+  .el-menu-item:hover,
+  .el-sub-menu__title:hover {
+    color: #fff;
+    background-color: transparent;
+  }
+}
 </style>

+ 36 - 1
src/components/GlobalSetting.vue

@@ -1,5 +1,9 @@
 <script setup lang="ts">
+import navDark from '@/assets/svg/nav-theme-dark.svg'
+import navLight from '@/assets/svg/nav-theme-light.svg'
+import headerDark from '@/assets/svg/header-theme-dark.svg'
 import { useThemeStore } from '@/stores/theme'
+
 const props = defineProps({
   modelValue: Boolean
 })
@@ -38,11 +42,33 @@ const themeColors = [
   '#6d4c41',
   '#546e7a'
 ]
+
+const themeStyle = computed(() => themeStore.themeStyle)
+const styleList = [
+  {
+    name: 'nav-dark',
+    img: navDark,
+    bgColor: '#001529',
+    textColor: '#BBB'
+  },
+  {
+    name: 'nav-light',
+    img: navLight,
+    bgColor: '#fff',
+    textColor: '#303133'
+  },
+  {
+    name: 'header-dark',
+    img: headerDark,
+    bgColor: '#001529',
+    textColor: '#BBB'
+  }
+]
 </script>
 
 <template>
   <el-drawer v-model="visible" title="主题配置" size="350px">
-    <el-divider>系统主题 </el-divider>
+    <el-divider>系统主题</el-divider>
     <el-space wrap>
       <div
         class="flex items-center justify-center w-30px h-30px"
@@ -54,6 +80,15 @@ const themeColors = [
         <el-icon size="22" color="#fff" v-if="themeColor === item"><icon-check></icon-check></el-icon>
       </div>
     </el-space>
+    <el-divider>整体风格</el-divider>
+    <el-space wrap :size="20">
+      <div class="relative" v-for="(item, index) in styleList" :key="index" @click="themeStore.setThemeStyle(item)">
+        <img :src="item.img" alt="" />
+        <div class="absolute right-10px bottom-10px" v-if="item.name === themeStyle.name">
+          <el-icon :color="themeColor" size="18"><Check /></el-icon>
+        </div>
+      </div>
+    </el-space>
   </el-drawer>
 </template>
 

+ 9 - 1
src/config/defaultSetting.ts

@@ -1,6 +1,14 @@
+import navLight from '@/assets/svg/nav-theme-light.svg'
+
 export default {
   title: '方是科技管理系统',
   logo: '/logo.png',
   homeRouteName: 'home',
-  themeColor: '#1890ff'
+  themeColor: '#1890ff',
+  themeStyle: {
+    name: 'nav-light',
+    img: navLight,
+    bgColor: '#fff',
+    textColor: '--el-menu-text-color'
+  } // nav-dark  nav-light  header-dark
 }

+ 6 - 4
src/layouts/BasicLayout.vue

@@ -2,11 +2,13 @@
 
 <template>
   <el-container class="layout-container">
-    <el-header>
-      <global-header />
-    </el-header>
+    <el-aside>
+      <global-aside />
+    </el-aside>
     <el-container>
-      <global-menu />
+      <el-header>
+        <global-header />
+      </el-header>
       <el-main style="padding: 0">
         <global-tabs></global-tabs>
         <div style="padding: var(--main-padding); height: calc(100% - 41px)">

+ 18 - 1
src/stores/theme.ts

@@ -1,8 +1,10 @@
 import config from '@/config/defaultSetting'
 import colorUtil from '@/utils/color'
+import type { themeStyle } from '@/types/themeStyle'
 
 export const useThemeStore = defineStore('theme', () => {
   const themeColor = useStorage('themeColor', config.themeColor)
+  const themeStyle = useStorage('themeStyle', config.themeStyle)
 
   const setThemeColor = (color: string) => {
     themeColor.value = color
@@ -19,9 +21,24 @@ export const useThemeStore = defineStore('theme', () => {
     }
   }
 
+  const setThemeStyle = (style: themeStyle) => {
+    themeStyle.value = style
+    initThemeStyle()
+  }
+  const initThemeStyle = () => {
+    const style = themeStyle.value
+    document.documentElement.style.setProperty('--el-menu-bg-color', style.bgColor)
+    document.documentElement.style.setProperty('--el-menu-text-color', style.textColor)
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    document.querySelector('html')!.className = style.name
+  }
+
   return {
     themeColor,
     setThemeColor,
-    initThemeColor
+    initThemeColor,
+    themeStyle,
+    setThemeStyle,
+    initThemeStyle
   }
 })

+ 6 - 0
src/types/themeStyle.ts

@@ -0,0 +1,6 @@
+export type themeStyle = {
+  name: string
+  img: string
+  bgColor: string
+  textColor: string
+}