Jelajahi Sumber

feat(布局): 重构侧边栏并添加主题切换组件

重构侧边栏组件结构,将SiderBar重命名为Sidebar并实现动态菜单功能
在头部添加自定义主题切换组件,优化样式和交互效果
更新登出跳转路径为/auth
添加ElSwitch组件类型声明
piks 1 Minggu lalu
induk
melakukan
66b4a8e95f

+ 1 - 0
components.d.ts

@@ -24,6 +24,7 @@ declare module 'vue' {
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
+    ElSwitch: typeof import('element-plus/es')['ElSwitch']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']

+ 84 - 5
src/components/ThemeToggle/index.vue

@@ -1,18 +1,97 @@
 <template>
-  <el-switch v-model="isDark" :active-action-icon="Moon" :inactive-action-icon="Sunny" inline-prompt
-    @change="onToggle" />
+  <div class="theme-toggle" @click="onToggle">
+    <div class="toggle-bg" :class="{ 'right': isDark }">
+      <el-icon :size="18" class="bg-icon">
+        <component :is="isDark ? Moon : Sunny" />
+      </el-icon>
+    </div>
+    <div class="toggle-option" :class="{ 'active': !isDark }">
+      <el-icon :size="18">
+        <Sunny />
+      </el-icon>
+    </div>
+    <div class="toggle-option" :class="{ 'active': isDark }">
+      <el-icon :size="18">
+        <Moon />
+      </el-icon>
+    </div>
+  </div>
 </template>
 
 <script setup lang="ts">
 import { Sunny, Moon } from '@element-plus/icons-vue'
-import { isDark, toggleTheme } from '@/composables/useTheme'
+import { isDark } from '@/composables/useTheme'
 
 function onToggle() {
-  // 可选:添加过渡动画
+  isDark.value = !isDark.value
   document.documentElement.classList.add('theme-transition')
-  toggleTheme()
   setTimeout(() => {
     document.documentElement.classList.remove('theme-transition')
   }, 400)
 }
 </script>
+
+<style scoped lang="scss">
+.theme-toggle {
+  position: relative;
+  display: flex;
+  width: 80px;
+  height: 36px;
+  border-radius: 18px;
+  background: rgba(128, 128, 128, 0.2);
+  cursor: pointer;
+  overflow: hidden;
+  user-select: none;
+  box-sizing: border-box;
+}
+
+.toggle-bg {
+  position: absolute;
+  top: 2px;
+  left: 2px;
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
+  z-index: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border: 1px solid #F5E4FF;
+  background: linear-gradient(180deg, rgba(239, 64, 255, 0.50) 0%, rgba(133, 47, 255, 0.50) 100%);
+  box-shadow: 0 11.392px 22.336px 0 rgba(130, 53, 185, 0.09), 0 -2px 1px 0 rgba(200, 151, 255, 0.40) inset;
+  backdrop-filter: blur(2px);
+  color: #fff;
+}
+
+.toggle-bg.right {
+  transform: translateX(44px);
+}
+
+.toggle-bg .bg-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.toggle-option {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 2;
+  color: rgba(255, 255, 255, 0.5);
+  transition: color 0.3s;
+  position: relative;
+}
+
+.toggle-option.active {
+  color: transparent;
+}
+
+.toggle-option .el-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+</style>

+ 5 - 4
src/layout/components/Header.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import { useUserStore } from '@/store/modules/user'
-
+import ThemeToggle from '@/components/ThemeToggle/index.vue'
 const userStore = useUserStore()
 
 function logout() {
@@ -12,11 +12,14 @@ function logout() {
   <header class="header">
     <div class="header-left">
       <h1 class="title">后台管理系统</h1>
+      <ThemeToggle />
     </div>
     <div class="header-right">
       <el-dropdown>
         <div class="user-info">
-          <el-icon><User /></el-icon>
+          <el-icon>
+            <User />
+          </el-icon>
           <span>{{ userStore.name || 'admin' }}</span>
         </div>
         <template #dropdown>
@@ -37,7 +40,6 @@ function logout() {
   align-items: center;
   height: 100%;
   padding: 0 20px;
-  color: #fff;
 
   .title {
     font-size: 20px;
@@ -50,7 +52,6 @@ function logout() {
     align-items: center;
     gap: 8px;
     cursor: pointer;
-    color: rgba(255, 255, 255, 0.85);
 
     &:hover {
       color: #fff;

+ 1 - 1
src/layout/components/SiderBar/index.vue → src/layout/components/Sidebar/index.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { computed } from 'vue'
 import { useRoute } from 'vue-router'
-import SidebarItem from '../Sidebar/SidebarItem.vue'
+import SidebarItem from './SidebarItem.vue'
 import { usePermissionStore } from '@/store/modules/permission'
 
 const route = useRoute()

+ 1 - 1
src/layout/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import SiderBar from './components/SiderBar/index.vue'
+import SiderBar from './components/Sidebar/index.vue'
 import Header from './components/Header.vue'
 import AppMain from './components/AppMain.vue'
 </script>

+ 1 - 1
src/store/modules/user.ts

@@ -41,7 +41,7 @@ export const useUserStore = defineStore('user', () => {
     roles.value = []
     localStorage.removeItem(TOKEN_KEY)
     // 刷新页面是最可靠的清除动态路由方式
-    window.location.replace('/login')
+    window.location.replace('/auth')
   }
 
   return { token, name, roles, avatar, isLoggedIn, login, getUserInfo, resetToken, logout }