AdminLayout.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <template>
  2. <el-container>
  3. <el-header class="header">
  4. <div class="header-left" @click="router.push('/')">
  5. <img src="/logo.svg" alt="logo" class="logo">
  6. <h2 class="title">{{ metaStore.title }}</h2>
  7. </div>
  8. <h1 class="admin-title">后台管理</h1>
  9. <div class="header-right">
  10. <template v-if="login">
  11. <el-dropdown placement="bottom">
  12. <el-button>
  13. <el-icon><UserFilled /></el-icon>
  14. {{ loginUserStore.loginUser?.user?.name }}
  15. </el-button>
  16. <template #dropdown>
  17. <el-dropdown-menu>
  18. <el-dropdown-item @click="router.push('/profile')">个人信息</el-dropdown-item>
  19. <el-dropdown-item @click="router.push('/booking')">返回前台</el-dropdown-item>
  20. <el-dropdown-item divided @click="logoutHandler">退出</el-dropdown-item>
  21. </el-dropdown-menu>
  22. </template>
  23. </el-dropdown>
  24. </template>
  25. <template v-else>
  26. <el-button type="primary" size="small" @click="router.push('/login')">登录</el-button>
  27. </template>
  28. </div>
  29. </el-header>
  30. <el-container class="main-container">
  31. <el-aside :class="['menu', { collapsed: isCollapse }]">
  32. <el-radio-group v-model="isCollapse" class="collapse-toggle">
  33. <el-radio-button :value="false">展开</el-radio-button>
  34. <el-radio-button :value="true">收起</el-radio-button>
  35. </el-radio-group>
  36. <el-menu
  37. router
  38. :collapse="isCollapse"
  39. :default-active="activeIndex"
  40. >
  41. <el-menu-item :index="menu.path" v-for="menu in navMenus" :key="menu.path">
  42. <el-icon><component :is="menu.meta?.icon" /></el-icon>
  43. <template #title>{{ menu.meta?.title }}</template>
  44. </el-menu-item>
  45. </el-menu>
  46. </el-aside>
  47. <el-main>
  48. <router-view />
  49. </el-main>
  50. </el-container>
  51. </el-container>
  52. </template>
  53. <script setup lang="ts">
  54. import { computed, ref } from 'vue'
  55. import { useRoute } from 'vue-router'
  56. import { useLoginUserStore, useMetaStore } from '../store'
  57. import router, { adminRoutes } from '../router'
  58. import { UserFilled } from '@element-plus/icons-vue'
  59. import { getAuthController } from '../api/auth-controller'
  60. const route = useRoute()
  61. const metaStore = useMetaStore()
  62. const loginUserStore = useLoginUserStore()
  63. const login = computed(() => loginUserStore.loginUser.isLogin)
  64. const isCollapse = ref(false)
  65. const activeIndex = computed(() => route.path)
  66. const navMenus = computed(() => {
  67. return (adminRoutes[0].children || []).filter(m => m.meta?.icon)
  68. })
  69. const logoutHandler = async () => {
  70. try {
  71. await getAuthController().logout()
  72. } catch {
  73. // 忽略服务端登出错误
  74. }
  75. loginUserStore.logoutUser()
  76. router.push('/login')
  77. }
  78. </script>
  79. <style scoped>
  80. .el-container {
  81. height: 100vh;
  82. }
  83. .header {
  84. display: flex;
  85. align-items: center;
  86. justify-content: space-between;
  87. padding: 0 20px;
  88. border-bottom: 1px solid #eee;
  89. }
  90. .header-left {
  91. display: flex;
  92. align-items: center;
  93. gap: 10px;
  94. cursor: pointer;
  95. }
  96. .logo {
  97. width: 40px;
  98. height: 40px;
  99. }
  100. .title {
  101. margin: 0;
  102. font-size: 20px;
  103. }
  104. .admin-title {
  105. font-size: 18px;
  106. color: var(--el-color-primary);
  107. }
  108. .header-right {
  109. display: flex;
  110. align-items: center;
  111. gap: 10px;
  112. }
  113. .main-container {
  114. flex: 1;
  115. }
  116. .menu {
  117. width: 200px;
  118. transition: width 0.3s;
  119. border-right: 1px solid #eee;
  120. }
  121. .menu.collapsed {
  122. width: 64px;
  123. }
  124. .collapse-toggle {
  125. display: flex;
  126. justify-content: center;
  127. margin: 16px 8px;
  128. }
  129. </style>