From e212a0ab75e1c59d0d030ac0b2f07048f6a0f9cf Mon Sep 17 00:00:00 2001
From: RuoYi <yzz_ivy@163.com>
Date: 星期三, 04 十二月 2024 20:32:06 +0800
Subject: [PATCH] 支持开启暗黑模式

---
 src/assets/styles/sidebar.scss           |    6 
 src/layout/components/Navbar.vue         |   28 ++
 src/layout/components/TagsView/index.vue |   41 ++-
 src/assets/styles/variables.module.scss  |  235 ++++++++++++++++---
 src/store/modules/settings.js            |   14 +
 src/assets/icons/svg/sunny.svg           |    1 
 src/assets/styles/ruoyi.scss             |   64 +++--
 /dev/null                                |  156 -------------
 src/components/Hamburger/index.vue       |    1 
 src/layout/components/Sidebar/Logo.vue   |   30 ++
 src/main.js                              |    4 
 src/components/TopNav/index.vue          |    4 
 src/layout/components/Sidebar/index.vue  |   66 ++++
 src/assets/icons/svg/moon.svg            |    1 
 src/layout/components/Settings/index.vue |   13 
 src/components/Crontab/index.vue         |    1 
 16 files changed, 392 insertions(+), 273 deletions(-)

diff --git a/src/assets/icons/svg/moon.svg b/src/assets/icons/svg/moon.svg
new file mode 100644
index 0000000..ec72d77
--- /dev/null
+++ b/src/assets/icons/svg/moon.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733303018722" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1447" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M368.832 67.2c51.328-16.384 89.216 34.112 75.712 76.416a346.816 346.816 0 0 0 435.84 435.84c42.304-13.44 92.8 24.384 76.48 75.712A467.968 467.968 0 1 1 368.832 67.2z m-35.776 122.688a368.832 368.832 0 1 0 501.056 501.056 445.952 445.952 0 0 1-501.056-501.056z" p-id="1448"></path></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/sunny.svg b/src/assets/icons/svg/sunny.svg
new file mode 100644
index 0000000..cc628bf
--- /dev/null
+++ b/src/assets/icons/svg/sunny.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733303115132" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12397" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 890.432c18.432 0 33.408 14.976 33.408 33.408v66.752a33.408 33.408 0 0 1-66.816 0v-66.752c0-18.432 14.976-33.408 33.408-33.408z m-267.52-110.848a33.408 33.408 0 0 1 0 47.232l-47.296 47.232a33.408 33.408 0 0 1-47.232-47.232l47.232-47.232a33.408 33.408 0 0 1 47.232 0z m582.336 0l47.232 47.232a33.408 33.408 0 0 1-47.232 47.232l-47.232-47.232a33.408 33.408 0 1 1 47.232-47.232zM512 200.32a311.68 311.68 0 1 1 0 623.296 311.68 311.68 0 0 1 0-623.36z m0 66.752a244.864 244.864 0 1 0 0 489.728 244.864 244.864 0 0 0 0-489.728zM100.16 478.592a33.408 33.408 0 1 1 0 66.816H33.408a33.408 33.408 0 0 1 0-66.816h66.752z m890.432 0a33.408 33.408 0 0 1 0 66.816h-66.752a33.408 33.408 0 1 1 0-66.816h66.752zM197.184 149.952l47.232 47.232a33.408 33.408 0 1 1-47.232 47.232l-47.232-47.232a33.408 33.408 0 0 1 47.232-47.232z m676.864 0a33.408 33.408 0 0 1 0 47.232l-47.232 47.232a33.408 33.408 0 1 1-47.232-47.232l47.232-47.232a33.408 33.408 0 0 1 47.232 0zM512 0c18.432 0 33.408 14.976 33.408 33.408v66.752a33.408 33.408 0 1 1-66.816 0V33.408C478.592 14.976 493.568 0 512 0z" p-id="12398"></path></svg>
\ No newline at end of file
diff --git a/src/assets/styles/ruoyi.scss b/src/assets/styles/ruoyi.scss
index fa551d3..4996ae5 100644
--- a/src/assets/styles/ruoyi.scss
+++ b/src/assets/styles/ruoyi.scss
@@ -1,4 +1,4 @@
- /**
+/**
  * 閫氱敤css鏍峰紡甯冨眬澶勭悊
  * Copyright (c) 2019 ruoyi
  */
@@ -102,38 +102,53 @@
 
 /** 琛ㄦ牸甯冨眬 **/
 .pagination-container {
-    position: relative;
-    height: 32px;
-    margin-bottom: 10px;
-    margin-top: 15px;
-    padding: 10px 20px !important;
+  position: relative;
+  height: 25px;
+  margin-bottom: 10px;
+  margin-top: 15px;
+  padding: 10px 20px !important;
+  background-color: transparent !important;
 }
 
+/* 鍒嗛〉鍣ㄥ畾浣� */
+.pagination-container .el-pagination {
+  position: absolute;
+  right: 0;
+  top: 0;
+}
+
+/* 寮圭獥涓殑鍒嗛〉鍣� */
 .el-dialog .pagination-container {
-    position: static !important;
+  position: static !important;
+  margin: 10px 0 0 0;
+  padding: 0 !important;
+  
+  .el-pagination {
+    position: static;
+  }
+}
+
+/* 绉诲姩绔�傞厤 */
+@media (max-width: 768px) {
+  .pagination-container {
+    .el-pagination {
+      > .el-pagination__jump {
+        display: none !important;
+      }
+      > .el-pagination__sizes {
+        display: none !important;
+      }
+    }
+  }
 }
 
 /* tree border */
 .tree-border {
     margin-top: 5px;
-    border: 1px solid #e5e6e7;
-    background: #FFFFFF none;
+    border: 1px solid var(--el-border-color-light, #e5e6e7);
+    background: var(--el-bg-color, #FFFFFF) none;
     border-radius:4px;
     width: 100%;
-}
-
-.pagination-container .el-pagination {
-	right: 0;
-	position: absolute;
-}
-
-@media ( max-width : 768px) {
-  .pagination-container .el-pagination > .el-pagination__jump {
-    display: none !important;
-  }
-  .pagination-container .el-pagination > .el-pagination__sizes {
-    display: none !important;
-  }
 }
 
 .el-table .fixed-width .el-button--small {
@@ -282,6 +297,5 @@
 
 /* 鍒嗗壊闈㈡澘鏍峰紡 */
 .splitpanes.default-theme .splitpanes__pane {
-	background-color: #fff!important;
+  background-color: var(--splitpanes-default-bg) !important;
 }
-
diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss
index df7a62f..89820d1 100644
--- a/src/assets/styles/sidebar.scss
+++ b/src/assets/styles/sidebar.scss
@@ -1,7 +1,7 @@
 #app {
 
   .main-container {
-    height: 100%;
+    min-height: 100%;
     transition: margin-left .28s;
     margin-left: $base-sidebar-width;
     position: relative;
@@ -12,10 +12,8 @@
   }
 
   .sidebar-container {
-    -webkit-transition: width .28s;
     transition: width 0.28s;
     width: $base-sidebar-width !important;
-    background-color: $base-menu-background;
     height: 100%;
     position: fixed;
     font-size: 0px;
@@ -103,7 +101,7 @@
 
     & .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
     & .theme-dark .el-sub-menu .el-menu-item {
-      background-color: $base-sub-menu-background !important;
+      background-color: $base-sub-menu-background;
 
       &:hover {
         background-color: $base-sub-menu-hover !important;
diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss
index e065775..27941ab 100644
--- a/src/assets/styles/variables.module.scss
+++ b/src/assets/styles/variables.module.scss
@@ -8,58 +8,211 @@
 $yellow: #FEC171;
 $panGreen: #30B08F;
 
-// 榛樿鑿滃崟涓婚椋庢牸
+// 榛樿涓婚鍙橀噺
+$menuText: #bfcbd9;
+$menuActiveText: #409eff;
+$menuBg: #304156;
+$menuHover: #263445;
+
+// 娴呰壊涓婚theme-light
+$menuLightBg: #ffffff;
+$menuLightHover: #f0f1f5;
+$menuLightText: #303133;
+$menuLightActiveText: #409EFF;
+
+// 鍩虹鍙橀噺
+$base-sidebar-width: 200px;
+$sideBarWidth: 200px;
+
+// 鑿滃崟鏆楄壊鍙橀噺
 $base-menu-color: #bfcbd9;
 $base-menu-color-active: #f4f4f5;
 $base-menu-background: #304156;
-$base-logo-title-color: #ffffff;
-
-$base-menu-light-color: rgba(0, 0, 0, 0.7);
-$base-menu-light-background: #ffffff;
-$base-logo-light-title-color: #001529;
-
 $base-sub-menu-background: #1f2d3d;
 $base-sub-menu-hover: #001528;
 
-// 鑷畾涔夋殫鑹茶彍鍗曢鏍�
-/**
-$base-menu-color:hsla(0,0%,100%,.65);
-$base-menu-color-active:#fff;
-$base-menu-background:#001529;
-$base-logo-title-color: #ffffff;
-
-$base-menu-light-color:rgba(0,0,0,.70);
-$base-menu-light-background:#ffffff;
-$base-logo-light-title-color: #001529;
-
-$base-sub-menu-background:#000c17;
-$base-sub-menu-hover:#001528;
-*/
-
+// 缁勪欢鍙橀噺
 $--color-primary: #409EFF;
 $--color-success: #67C23A;
 $--color-warning: #E6A23C;
 $--color-danger: #F56C6C;
 $--color-info: #909399;
 
-$base-sidebar-width: 200px;
-
-// the :export directive is the magic sauce for webpack
-// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
 :export {
-  menuColor: $base-menu-color;
-  menuLightColor: $base-menu-light-color;
-  menuColorActive: $base-menu-color-active;
-  menuBackground: $base-menu-background;
-  menuLightBackground: $base-menu-light-background;
-  subMenuBackground: $base-sub-menu-background;
-  subMenuHover: $base-sub-menu-hover;
-  sideBarWidth: $base-sidebar-width;
-  logoTitleColor: $base-logo-title-color;
-  logoLightTitleColor: $base-logo-light-title-color;
-  primaryColor: $--color-primary;
-  successColor: $--color-success;
-  dangerColor: $--color-danger;
-  infoColor: $--color-info;
-  warningColor: $--color-warning;
+  menuText: $menuText;
+  menuActiveText: $menuActiveText;
+  menuBg: $menuBg;
+  menuHover: $menuHover;
+  menuLightBg: $menuLightBg;
+  menuLightHover: $menuLightHover;
+  menuLightText: $menuLightText;
+  menuLightActiveText: $menuLightActiveText;
+  sideBarWidth: $sideBarWidth;
+  // 瀵煎嚭鍩虹棰滆壊
+  blue: $blue;
+  lightBlue: $light-blue;
+  red: $red;
+  pink: $pink;
+  green: $green;
+  tiffany: $tiffany;
+  yellow: $yellow;
+  panGreen: $panGreen;
+  // 瀵煎嚭缁勪欢棰滆壊
+  colorPrimary: $--color-primary;
+  colorSuccess: $--color-success;
+  colorWarning: $--color-warning;
+  colorDanger: $--color-danger;
+  colorInfo: $--color-info;
 }
+
+// CSS鍙橀噺瀹氫箟
+:root {
+  /* 浜壊妯″紡鍙橀噺 */
+  --sidebar-bg: #{$menuBg};
+  --sidebar-text: #{$menuText};
+  --menu-hover: #{$menuHover};
+  
+  --navbar-bg: #ffffff;
+  --navbar-text: #303133;
+  
+  /* splitpanes default-theme 鍙橀噺 */
+  --splitpanes-default-bg: #ffffff;
+
+}
+
+// 鏆楅粦妯″紡鍙橀噺
+html.dark {
+  /* 榛樿閫氱敤 */
+  --el-bg-color: #141414;
+  --el-bg-color-overlay: #1d1e1f;
+  --el-text-color-primary: #ffffff;
+  --el-text-color-regular: #d0d0d0;
+  --el-border-color: #434343;
+  --el-border-color-light: #434343;
+
+  /* 渚ц竟鏍� */
+  --sidebar-bg: #141414;
+  --sidebar-text: #ffffff;
+  --menu-hover: #2d2d2d;
+  --menu-active-text: #{$menuActiveText};
+
+  /* 椤堕儴瀵艰埅鏍� */
+  --navbar-bg: #141414;
+  --navbar-text: #ffffff;
+  --navbar-hover: #141414;
+
+  /* 鏍囩鏍� */
+  --tags-bg: #141414;
+  --tags-item-bg: #1d1e1f;
+  --tags-item-border: #303030;
+  --tags-item-text: #d0d0d0;
+  --tags-item-hover: #2d2d2d;
+  --tags-close-hover: #64666a;
+
+  /* splitpanes 缁勪欢鏆楅粦妯″紡鍙橀噺 */
+  --splitpanes-bg: #141414;
+  --splitpanes-border: #303030;
+  --splitpanes-splitter-bg: #1d1e1f;
+  --splitpanes-splitter-hover-bg: #2d2d2d;
+
+  /* blockquote 鏆楅粦妯″紡鍙橀噺 */
+  --blockquote-bg: #1d1e1f;
+  --blockquote-border: #303030;
+  --blockquote-text: #d0d0d0;
+  
+  /* Cron 鏃堕棿琛ㄨ揪寮� 妯″紡鍙橀噺 */
+  --cron-border: #303030;
+
+  /* splitpanes default-theme 鏆楅粦妯″紡鍙橀噺 */
+  --splitpanes-default-bg: #141414;
+
+  /* 渚ц竟鏍忚彍鍗曡鐩� */
+   .sidebar-container {
+    & .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
+    & .theme-dark .el-sub-menu .el-menu-item {
+      background-color: var(--el-bg-color) !important;
+    }
+  }
+
+  /* 椤堕儴鏍忔爮鑿滃崟瑕嗙洊 */
+  .el-menu--horizontal {
+    .el-menu-item {
+      &:not(.is-disabled) {
+        &:hover,
+        &:focus {
+          background-color: var(--navbar-hover) !important;
+        }
+      }
+    }
+  }
+
+  /* 鍒嗗壊绐楁牸瑕嗙洊 */
+  .splitpanes {
+    background-color: var(--splitpanes-bg);
+
+    .splitpanes__pane {
+      background-color: var(--splitpanes-bg);
+      border-color: var(--splitpanes-border);
+    }
+
+    .splitpanes__splitter {
+      background-color: var(--splitpanes-splitter-bg);
+      border-color: var(--splitpanes-border);
+
+      &:hover {
+        background-color: var(--splitpanes-splitter-hover-bg);
+      }
+
+      &:before,
+      &:after {
+        background-color: var(--splitpanes-border);
+      }
+    }
+  }
+
+  /* 琛ㄦ牸鏍峰紡瑕嗙洊 */
+  .el-table {
+    --el-table-header-bg-color: var(--el-bg-color-overlay) !important;
+    --el-table-header-text-color: var(--el-text-color-regular) !important;
+    --el-table-border-color: var(--el-border-color-light) !important;
+    --el-table-row-hover-bg-color: var(--el-bg-color-overlay) !important;
+
+    .el-table__header-wrapper, .el-table__fixed-header-wrapper {
+      th {
+        background-color: var(--el-bg-color-overlay, #f8f8f9) !important;
+        color: var(--el-text-color-regular, #515a6e);
+      }
+    }
+  }
+
+  /* 鏍戠粍浠堕珮浜牱寮忚鐩� */
+  .el-tree {
+    .el-tree-node.is-current > .el-tree-node__content {
+      background-color: var(--el-bg-color-overlay) !important;
+      color: var(--el-color-primary);
+    }
+
+    .el-tree-node__content:hover {
+      background-color: var(--el-bg-color-overlay);
+    }
+  }
+  
+  /* 涓嬫媺鑿滃崟鏍峰紡瑕嗙洊 */
+  .el-dropdown-menu__item:not(.is-disabled):focus, .el-dropdown-menu__item:not(.is-disabled):hover{
+    background-color: var(--navbar-hover) !important;
+  }
+
+  /* blockquote鏍峰紡瑕嗙洊 */
+  blockquote {
+    background-color: var(--blockquote-bg) !important;
+    border-left-color: var(--blockquote-border) !important;
+    color: var(--blockquote-text) !important;
+  }
+  
+  /* 鏃堕棿琛ㄨ揪寮忔爣棰樻牱寮忚鐩� */
+  .popup-result .title {
+    background: var(--cron-border);
+  }
+
+}
+
diff --git a/src/components/Crontab/index.vue b/src/components/Crontab/index.vue
index f1d7201..71f3824 100644
--- a/src/components/Crontab/index.vue
+++ b/src/components/Crontab/index.vue
@@ -251,7 +251,6 @@
 .popup-main {
     position: relative;
     margin: 10px auto;
-    background: #fff;
     border-radius: 5px;
     font-size: 12px;
     overflow: hidden;
diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue
index 8b6f7a8..ab1dc36 100644
--- a/src/components/Hamburger/index.vue
+++ b/src/components/Hamburger/index.vue
@@ -7,6 +7,7 @@
       xmlns="http://www.w3.org/2000/svg"
       width="64"
       height="64"
+      fill="currentColor"
     >
       <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
     </svg>
diff --git a/src/components/TopNav/index.vue b/src/components/TopNav/index.vue
index e628094..1f666d3 100644
--- a/src/components/TopNav/index.vue
+++ b/src/components/TopNav/index.vue
@@ -196,7 +196,7 @@
 
 /* 鑳屾櫙鑹查殣钘� */
 .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
-  background-color: #ffffff !important;
+  background-color: #ffffff;
 }
 
 /* 鍥炬爣鍙抽棿璺� */
@@ -211,4 +211,6 @@
   margin-left: 8px;
   margin-top: 0px;
 }
+
+
 </style>
diff --git a/src/components/TreeSelect/index.vue b/src/components/TreeSelect/index.vue
deleted file mode 100644
index 82cafb7..0000000
--- a/src/components/TreeSelect/index.vue
+++ /dev/null
@@ -1,156 +0,0 @@
-<template>
-  <div class="el-tree-select">
-    <el-select
-      style="width: 100%"
-      v-model="valueId"
-      ref="treeSelect"
-      :filterable="true"
-      :clearable="true"
-      @clear="clearHandle"
-      :filter-method="selectFilterData"
-      :placeholder="placeholder"
-    >
-      <el-option :value="valueId" :label="valueTitle">
-        <el-tree
-          id="tree-option"
-          ref="selectTree"
-          :accordion="accordion"
-          :data="options"
-          :props="objMap"
-          :node-key="objMap.value"
-          :expand-on-click-node="false"
-          :default-expanded-keys="defaultExpandedKey"
-          :filter-node-method="filterNode"
-          @node-click="handleNodeClick"
-        ></el-tree>
-      </el-option>
-    </el-select>
-  </div>
-</template>
-
-<script setup>
-
-const { proxy } = getCurrentInstance();
-
-const props = defineProps({
-  /* 閰嶇疆椤� */
-  objMap: {
-    type: Object,
-    default: () => {
-      return {
-        value: 'id', // ID瀛楁鍚�
-        label: 'label', // 鏄剧ず鍚嶇О
-        children: 'children' // 瀛愮骇瀛楁鍚�
-      }
-    }
-  },
-  /* 鑷姩鏀惰捣 */
-  accordion: {
-    type: Boolean,
-    default: () => {
-      return false
-    }
-  },
-  /**褰撳墠鍙屽悜鏁版嵁缁戝畾鐨勫�� */
-  value: {
-    type: [String, Number],
-    default: ''
-  },
-  /**褰撳墠鐨勬暟鎹� */
-  options: {
-    type: Array,
-    default: () => []
-  },
-  /**杈撳叆妗嗗唴閮ㄧ殑鏂囧瓧 */
-  placeholder: {
-    type: String,
-    default: ''
-  }
-})
-
-const emit = defineEmits(['update:value']);
-
-const valueId = computed({
-  get: () => props.value,
-  set: (val) => {
-    emit('update:value', val)
-  }
-});
-const valueTitle = ref('');
-const defaultExpandedKey = ref([]);
-
-function initHandle() {
-  nextTick(() => {
-    const selectedValue = valueId.value;
-    if(selectedValue !== null && typeof (selectedValue) !== 'undefined') {
-      const node = proxy.$refs.selectTree.getNode(selectedValue)
-      if (node) {
-        valueTitle.value = node.data[props.objMap.label]
-        proxy.$refs.selectTree.setCurrentKey(selectedValue) // 璁剧疆榛樿閫変腑
-        defaultExpandedKey.value = [selectedValue] // 璁剧疆榛樿灞曞紑
-      }
-    } else {
-      clearHandle()
-    }
-  })
-}
-function handleNodeClick(node) {
-  valueTitle.value = node[props.objMap.label]
-  valueId.value = node[props.objMap.value];
-  defaultExpandedKey.value = [];
-  proxy.$refs.treeSelect.blur()
-  selectFilterData('')
-}
-function selectFilterData(val) {
-  proxy.$refs.selectTree.filter(val)
-}
-function filterNode(value, data) {
-  if (!value) return true
-  return data[props.objMap['label']].indexOf(value) !== -1
-}
-function clearHandle() {
-  valueTitle.value = ''
-  valueId.value = ''
-  defaultExpandedKey.value = [];
-  clearSelected()
-}
-function clearSelected() {
-  const allNode = document.querySelectorAll('#tree-option .el-tree-node')
-  allNode.forEach((element) => element.classList.remove('is-current'))
-}
-
-onMounted(() => {
-  initHandle()
-})
-
-watch(valueId, () => {
-  initHandle();
-})
-</script>
-
-<style lang='scss' scoped>
-@import "@/assets/styles/variables.module.scss";
-.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
-  padding: 0;
-  background-color: #fff;
-  height: auto;
-}
-
-.el-select-dropdown__item.selected {
-  font-weight: normal;
-}
-
-ul li .el-tree .el-tree-node__content {
-  height: auto;
-  padding: 0 20px;
-  box-sizing: border-box;
-}
-
-:deep(.el-tree-node__content:hover),
-:deep(.el-tree-node__content:active),
-:deep(.is-current > div:first-child),
-:deep(.el-tree-node__content:focus) {
-  background-color: mix(#fff, $--color-primary, 90%);
-  color: $--color-primary;
-}
-</style>
\ No newline at end of file
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index f669a6d..e0e2df8 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -18,6 +18,13 @@
 
         <screenfull id="screenfull" class="right-menu-item hover-effect" />
 
+        <el-tooltip content="涓婚妯″紡" effect="dark" placement="bottom">
+          <div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme">
+            <svg-icon v-if="settingsStore.isDark" icon-class="sunny" />
+            <svg-icon v-if="!settingsStore.isDark" icon-class="moon" />
+          </div>
+        </el-tooltip>
+
         <el-tooltip content="甯冨眬澶у皬" effect="dark" placement="bottom">
           <size-select id="size-select" class="right-menu-item hover-effect" />
         </el-tooltip>
@@ -98,6 +105,10 @@
 function setLayout() {
   emits('setLayout');
 }
+
+function toggleTheme() {
+  settingsStore.toggleTheme()
+}
 </script>
 
 <style lang='scss' scoped>
@@ -105,7 +116,7 @@
   height: 50px;
   overflow: hidden;
   position: relative;
-  background: #fff;
+  background: var(--navbar-bg);
   box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
 
   .hamburger-container {
@@ -150,7 +161,7 @@
       padding: 0 8px;
       height: 100%;
       font-size: 18px;
-      color: #5a5e66;
+      color: var(--navbar-text);
       vertical-align: text-bottom;
 
       &.hover-effect {
@@ -161,6 +172,19 @@
           background: rgba(0, 0, 0, 0.025);
         }
       }
+
+      &.theme-switch-wrapper {
+        display: flex;
+        align-items: center;
+
+        svg {
+          transition: transform 0.3s;
+          
+          &:hover {
+            transform: scale(1.15);
+          }
+        }
+      }
     }
 
     .avatar-container {
diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue
index 7164a17..7f2bcb7 100644
--- a/src/layout/components/Settings/index.vue
+++ b/src/layout/components/Settings/index.vue
@@ -149,13 +149,15 @@
 <style lang='scss' scoped>
 .setting-drawer-title {
   margin-bottom: 12px;
-  color: rgba(0, 0, 0, 0.85);
+  color: var(--el-text-color-primary, rgba(0, 0, 0, 0.85));
   line-height: 22px;
   font-weight: bold;
+
   .drawer-title {
     font-size: 14px;
   }
 }
+
 .setting-drawer-block-checbox {
   display: flex;
   justify-content: flex-start;
@@ -174,13 +176,6 @@
       height: 48px;
     }
 
-    .custom-img {
-      width: 48px;
-      height: 38px;
-      border-radius: 5px;
-      box-shadow: 1px 1px 2px #898484;
-    }
-
     .setting-drawer-block-checbox-selectIcon {
       position: absolute;
       top: 0;
@@ -197,7 +192,7 @@
 }
 
 .drawer-item {
-  color: rgba(0, 0, 0, 0.65);
+  color: var(--el-text-color-regular, rgba(0, 0, 0, 0.65));
   padding: 12px 0;
   font-size: 14px;
 
diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue
index 288e030..751b56e 100644
--- a/src/layout/components/Sidebar/Logo.vue
+++ b/src/layout/components/Sidebar/Logo.vue
@@ -1,22 +1,22 @@
 <template>
-  <div class="sidebar-logo-container" :class="{ 'collapse': collapse }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
+  <div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
     <transition name="sidebarLogoFade">
       <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
         <img v-if="logo" :src="logo" class="sidebar-logo" />
-        <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
+        <h1 v-else class="sidebar-title">{{ title }}</h1>
       </router-link>
       <router-link v-else key="expand" class="sidebar-logo-link" to="/">
         <img v-if="logo" :src="logo" class="sidebar-logo" />
-        <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
+        <h1 class="sidebar-title">{{ title }}</h1>
       </router-link>
     </transition>
   </div>
 </template>
 
 <script setup>
-import variables from '@/assets/styles/variables.module.scss'
 import logo from '@/assets/logo/logo.png'
 import useSettingsStore from '@/store/modules/settings'
+import variables from '@/assets/styles/variables.module.scss'
 
 defineProps({
   collapse: {
@@ -28,9 +28,27 @@
 const title = import.meta.env.VITE_APP_TITLE;
 const settingsStore = useSettingsStore();
 const sideTheme = computed(() => settingsStore.sideTheme);
+
+// 鑾峰彇Logo鑳屾櫙鑹�
+const getLogoBackground = computed(() => {
+  if (settingsStore.isDark) {
+    return 'var(--sidebar-bg)';
+  }
+  return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg;
+});
+
+// 鑾峰彇Logo鏂囧瓧棰滆壊
+const getLogoTextColor = computed(() => {
+  if (settingsStore.isDark) {
+    return 'var(--sidebar-text)';
+  }
+  return sideTheme.value === 'theme-dark' ? '#fff' : variables.menuLightText;
+});
 </script>
 
 <style lang="scss" scoped>
+@import '@/assets/styles/variables.module.scss';
+
 .sidebarLogoFade-enter-active {
   transition: opacity 1.5s;
 }
@@ -45,7 +63,7 @@
   width: 100%;
   height: 50px;
   line-height: 50px;
-  background: #2b2f3a;
+  background: v-bind(getLogoBackground);
   text-align: center;
   overflow: hidden;
 
@@ -63,7 +81,7 @@
     & .sidebar-title {
       display: inline-block;
       margin: 0;
-      color: #fff;
+      color: v-bind(getLogoTextColor);
       font-weight: 600;
       line-height: 50px;
       font-size: 14px;
diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue
index 646bb63..fd78773 100644
--- a/src/layout/components/Sidebar/index.vue
+++ b/src/layout/components/Sidebar/index.vue
@@ -1,16 +1,17 @@
 <template>
-  <div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
+  <div :class="{ 'has-logo': showLogo }" class="sidebar-container">
     <logo v-if="showLogo" :collapse="isCollapse" />
-    <el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
+    <el-scrollbar wrap-class="scrollbar-wrapper">
       <el-menu
         :default-active="activeMenu"
         :collapse="isCollapse"
-        :background-color="sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
-        :text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
+        :background-color="getMenuBackground"
+        :text-color="getMenuTextColor"
         :unique-opened="true"
         :active-text-color="theme"
         :collapse-transition="false"
         mode="vertical"
+        :class="sideTheme"
       >
         <sidebar-item
           v-for="(route, index) in sidebarRouters"
@@ -36,19 +37,68 @@
 const settingsStore = useSettingsStore()
 const permissionStore = usePermissionStore()
 
-const sidebarRouters =  computed(() => permissionStore.sidebarRouters);
+const sidebarRouters = computed(() => permissionStore.sidebarRouters);
 const showLogo = computed(() => settingsStore.sidebarLogo);
 const sideTheme = computed(() => settingsStore.sideTheme);
 const theme = computed(() => settingsStore.theme);
 const isCollapse = computed(() => !appStore.sidebar.opened);
 
+// 鑾峰彇鑿滃崟鑳屾櫙鑹�
+const getMenuBackground = computed(() => {
+  if (settingsStore.isDark) {
+    return 'var(--sidebar-bg)';
+  }
+  return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg;
+});
+
+// 鑾峰彇鑿滃崟鏂囧瓧棰滆壊
+const getMenuTextColor = computed(() => {
+  if (settingsStore.isDark) {
+    return 'var(--sidebar-text)';
+  }
+  return sideTheme.value === 'theme-dark' ? variables.menuText : variables.menuLightText;
+});
+
 const activeMenu = computed(() => {
   const { meta, path } = route;
-  // if set path, the sidebar will highlight the path you set
   if (meta.activeMenu) {
     return meta.activeMenu;
   }
   return path;
-})
-
+});
 </script>
+
+<style lang="scss" scoped>
+.sidebar-container {
+  background-color: v-bind(getMenuBackground);
+  
+  .scrollbar-wrapper {
+    background-color: v-bind(getMenuBackground);
+  }
+
+  .el-menu {
+    border: none;
+    height: 100%;
+    width: 100% !important;
+    
+    .el-menu-item, .el-sub-menu__title {
+      &:hover {
+        background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
+      }
+    }
+
+    .el-menu-item {
+      color: v-bind(getMenuTextColor);
+      
+      &.is-active {
+        color: var(--menu-active-text, #409eff);
+        background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
+      }
+    }
+
+    .el-sub-menu__title {
+      color: v-bind(getMenuTextColor);
+    }
+  }
+}
+</style>
diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue
index 2cd6617..b8460a5 100644
--- a/src/layout/components/TagsView/index.vue
+++ b/src/layout/components/TagsView/index.vue
@@ -257,13 +257,14 @@
 }
 </script>
 
-<style lang='scss' scoped>
+<style lang="scss" scoped>
 .tags-view-container {
   height: 34px;
   width: 100%;
-  background: #fff;
-  border-bottom: 1px solid #d8dce5;
-  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
+  background: var(--tags-bg, #fff);
+  border-bottom: 1px solid var(--tags-item-border, #d8dce5);
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
+
   .tags-view-wrapper {
     .tags-view-item {
       display: inline-block;
@@ -271,25 +272,29 @@
       cursor: pointer;
       height: 26px;
       line-height: 26px;
-      border: 1px solid #d8dce5;
-      color: #495060;
-      background: #fff;
+      border: 1px solid var(--tags-item-border, #d8dce5);
+      color: var(--tags-item-text, #495060);
+      background: var(--tags-item-bg, #fff);
       padding: 0 8px;
       font-size: 12px;
       margin-left: 5px;
       margin-top: 4px;
+
       &:first-of-type {
         margin-left: 15px;
       }
+
       &:last-of-type {
         margin-right: 15px;
       }
+
       &.active {
         background-color: #42b983;
         color: #fff;
         border-color: #42b983;
+
         &::before {
-          content: "";
+          content: '';
           background: #fff;
           display: inline-block;
           width: 8px;
@@ -301,9 +306,10 @@
       }
     }
   }
+
   .contextmenu {
     margin: 0;
-    background: #fff;
+    background: var(--el-bg-color-overlay, #fff);
     z-index: 3000;
     position: absolute;
     list-style-type: none;
@@ -311,14 +317,17 @@
     border-radius: 4px;
     font-size: 12px;
     font-weight: 400;
-    color: #333;
-    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
+    color: var(--tags-item-text, #333);
+    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
+    border: 1px solid var(--el-border-color-light, #e4e7ed);
+
     li {
       margin: 0;
       padding: 7px 16px;
       cursor: pointer;
+
       &:hover {
-        background: #eee;
+        background: var(--tags-item-hover, #eee);
       }
     }
   }
@@ -335,15 +344,17 @@
       vertical-align: 2px;
       border-radius: 50%;
       text-align: center;
-      transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+      transition: all .3s cubic-bezier(.645, .045, .355, 1);
       transform-origin: 100% 50%;
+
       &:before {
-        transform: scale(0.6);
+        transform: scale(.6);
         display: inline-block;
         vertical-align: -3px;
       }
+
       &:hover {
-        background-color: #b4bccc;
+        background-color: var(--tags-close-hover, #b4bccc);
         color: #fff;
         width: 12px !important;
         height: 12px !important;
diff --git a/src/main.js b/src/main.js
index a7645fb..c01e80d 100644
--- a/src/main.js
+++ b/src/main.js
@@ -4,6 +4,7 @@
 
 import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
+import 'element-plus/theme-chalk/dark/css-vars.css'
 import locale from 'element-plus/es/locale/lang/zh-cn'
 
 import '@/assets/styles/index.scss' // global css
@@ -39,8 +40,6 @@
 import ImageUpload from "@/components/ImageUpload"
 // 鍥剧墖棰勮缁勪欢
 import ImagePreview from "@/components/ImagePreview"
-// 鑷畾涔夋爲閫夋嫨缁勪欢
-import TreeSelect from '@/components/TreeSelect'
 // 瀛楀吀鏍囩缁勪欢
 import DictTag from '@/components/DictTag'
 
@@ -59,7 +58,6 @@
 // 鍏ㄥ眬缁勪欢鎸傝浇
 app.component('DictTag', DictTag)
 app.component('Pagination', Pagination)
-app.component('TreeSelect', TreeSelect)
 app.component('FileUpload', FileUpload)
 app.component('ImageUpload', ImageUpload)
 app.component('ImagePreview', ImagePreview)
diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js
index 3e41513..64b413a 100644
--- a/src/store/modules/settings.js
+++ b/src/store/modules/settings.js
@@ -1,5 +1,9 @@
 import defaultSettings from '@/settings'
+import { useDark, useToggle } from '@vueuse/core'
 import { useDynamicTitle } from '@/utils/dynamicTitle'
+
+const isDark = useDark()
+const toggleDark = useToggle(isDark)
 
 const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings
 
@@ -17,7 +21,8 @@
       tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
       fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
       sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo,
-      dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle
+      dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle,
+      isDark: isDark.value
     }),
     actions: {
       // 淇敼甯冨眬璁剧疆
@@ -30,7 +35,12 @@
       // 璁剧疆缃戦〉鏍囬
       setTitle(title) {
         this.title = title
-        useDynamicTitle();
+        useDynamicTitle()
+      },
+      // 鍒囨崲鏆楅粦妯″紡
+      toggleTheme() {
+        this.isDark = !this.isDark
+        toggleDark()
       }
     }
   })

--
Gitblit v1.9.3