index.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <template>
  2. <div class="layout">
  3. <aside class="layout-aside">
  4. <div class="brand">
  5. <img class="brand-logo" :src="logo" alt="logo">
  6. <span class="brand-title">飞鸟后台数据平台</span>
  7. </div>
  8. <nav class="side-menu">
  9. <div v-for="item in sideMenus" :key="item.path" class="side-menu__item"
  10. :class="{ 'is-active': isActive(item.path) }" @click="go(item.path)">
  11. <span class="side-menu__icon">
  12. <MenuIcon :name="item.icon" />
  13. </span>
  14. <span class="side-menu__text">{{ item.title }}</span>
  15. </div>
  16. </nav>
  17. </aside>
  18. <div class="layout-main">
  19. <header class="layout-header">
  20. <div class="header-right">
  21. <el-dropdown trigger="click">
  22. <div class="user-entry">
  23. <el-avatar :size="32" class="user-avatar">
  24. <el-icon :size="18">
  25. <UserFilled />
  26. </el-icon>
  27. </el-avatar>
  28. <span class="user-name">用户001</span>
  29. <el-icon class="user-arrow">
  30. <ArrowDown />
  31. </el-icon>
  32. </div>
  33. <template #dropdown>
  34. <el-dropdown-menu>
  35. <el-dropdown-item disabled>个人中心</el-dropdown-item>
  36. <el-dropdown-item divided>退出登录</el-dropdown-item>
  37. </el-dropdown-menu>
  38. </template>
  39. </el-dropdown>
  40. </div>
  41. </header>
  42. <main class="layout-content">
  43. <router-view v-slot="{ Component }">
  44. <keep-alive v-if="route.meta && route.meta.keepAlive !== false">
  45. <component :is="Component" />
  46. </keep-alive>
  47. <component v-else :is="Component" />
  48. </router-view>
  49. </main>
  50. </div>
  51. </div>
  52. </template>
  53. <script setup>
  54. import { useRoute, useRouter } from "vue-router";
  55. import { UserFilled, ArrowDown } from "@element-plus/icons-vue";
  56. import { sideMenus } from "./menu";
  57. import MenuIcon from "./MenuIcon.vue";
  58. import logo from "@/assets/layout/logo.png";
  59. const route = useRoute();
  60. const router = useRouter();
  61. const isActive = (path) => route.path === path || route.path.startsWith(path + "/");
  62. const go = (path) => {
  63. if (route.path !== path) {
  64. router.push(path);
  65. }
  66. };
  67. </script>
  68. <style lang="scss" scoped>
  69. .layout {
  70. display: flex;
  71. width: 100%;
  72. height: 100%;
  73. overflow: hidden;
  74. background: #f0f2f5;
  75. }
  76. .layout-aside {
  77. width: 280px;
  78. flex-shrink: 0;
  79. background: #1a1a1a;
  80. display: flex;
  81. flex-direction: column;
  82. color: #cfcfcf;
  83. }
  84. .brand {
  85. height: 64px;
  86. display: flex;
  87. align-items: center;
  88. gap: 10px;
  89. padding: 0 16px;
  90. border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  91. }
  92. .brand-logo {
  93. width: 29px;
  94. height: 32px;
  95. object-fit: contain;
  96. }
  97. .brand-title {
  98. font-size: 24px;
  99. letter-spacing: 2px;
  100. font-family: "PangMenZhengDao";
  101. background: linear-gradient(to bottom, #F3C11D, #ffffff);
  102. background-clip: text;
  103. color: transparent;
  104. text-decoration: none;
  105. }
  106. .side-menu {
  107. padding: 16px 12px;
  108. display: flex;
  109. flex-direction: column;
  110. gap: 8px;
  111. }
  112. .side-menu__item {
  113. height: 48px;
  114. display: flex;
  115. align-items: center;
  116. gap: 12px;
  117. padding: 0 16px;
  118. border-radius: 8px;
  119. cursor: pointer;
  120. color: #9a9a9a;
  121. transition: background 0.2s, color 0.2s;
  122. &:hover {
  123. color: #e8d48b;
  124. background: rgba(212, 175, 55, 0.08);
  125. }
  126. &.is-active {
  127. color: #d4af37;
  128. background: linear-gradient(90deg, rgba(212, 175, 55, 0.28), rgba(212, 175, 55, 0.08));
  129. }
  130. }
  131. .side-menu__icon {
  132. display: inline-flex;
  133. width: 22px;
  134. height: 22px;
  135. }
  136. .side-menu__text {
  137. font-size: 15px;
  138. }
  139. .layout-main {
  140. flex: 1;
  141. min-width: 0;
  142. display: flex;
  143. flex-direction: column;
  144. }
  145. .layout-header {
  146. height: 64px;
  147. flex-shrink: 0;
  148. background: #fff;
  149. display: flex;
  150. align-items: center;
  151. justify-content: flex-end;
  152. padding: 0 24px;
  153. box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04);
  154. }
  155. .header-right {
  156. display: flex;
  157. align-items: center;
  158. }
  159. .user-entry {
  160. display: flex;
  161. align-items: center;
  162. gap: 10px;
  163. cursor: pointer;
  164. outline: none;
  165. }
  166. .user-avatar {
  167. background: #e8e8e8;
  168. color: #8c8c8c;
  169. }
  170. .user-name {
  171. font-size: 14px;
  172. color: #333;
  173. }
  174. .user-arrow {
  175. color: #999;
  176. font-size: 12px;
  177. }
  178. .layout-content {
  179. flex: 1;
  180. min-height: 0;
  181. overflow: auto;
  182. background: #f5f5f5;
  183. }
  184. </style>