MenuLayout.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <!-- 带有二级菜单的layout -->
  2. <template>
  3. <div class="component-container home-container">
  4. <aside class="aside">
  5. <header class="aside__header" />
  6. <main class="aside__main">
  7. <el-menu :default-active="selectedKeys" :default-openeds="openKeys" router class="el-menu-vertical">
  8. <template v-for="item in subMenus" :key="item.name">
  9. <el-sub-menu v-if="item.children" :index="item.path">
  10. <template #title>
  11. <component v-if="item.meta?.icon" :is="item.meta.icon" />
  12. <span class="menu-title">{{ item.meta?.title }}</span>
  13. </template>
  14. <el-menu-item v-for="child in item.children" :key="child.name" :index="child.path">
  15. <div style="margin-left: 12px">
  16. {{ child.meta?.title }}
  17. </div>
  18. </el-menu-item>
  19. </el-sub-menu>
  20. <el-menu-item v-else :index="item.path">
  21. <component v-if="item.meta?.icon" :is="item.meta?.icon" />
  22. <span class="menu-title">
  23. {{ item.meta?.title }}
  24. </span>
  25. </el-menu-item>
  26. </template>
  27. </el-menu>
  28. </main>
  29. </aside>
  30. <div class="main">
  31. <router-view></router-view>
  32. </div>
  33. </div>
  34. </template>
  35. <script setup lang="ts">
  36. import { ref, watch, computed } from 'vue';
  37. import { useRoute } from 'vue-router';
  38. // 当前路由
  39. const currentRoute = useRoute();
  40. const activeMenu = currentRoute.meta?.activeMenu || null; // activeMenu undefined,null 或 空字符串,统一变为 null。
  41. const selectedKeys = ref<string>((activeMenu ?? currentRoute.path) as string);
  42. const openKeys = ref<string[]>([]);
  43. // 将菜单数组过滤掉隐藏的菜单
  44. const filterHiddenMenus = (menus: any[]) => {
  45. return menus.filter((menu) => !menu.meta?.hidden);
  46. };
  47. function filterHiddenItems(arr: any[]): any[] {
  48. return arr.filter((item) => {
  49. if (item.meta && item.meta.hidden) {
  50. return false;
  51. }
  52. if (item.children && Array.isArray(item.children)) {
  53. item.children = filterHiddenItems(item.children);
  54. }
  55. return true;
  56. });
  57. }
  58. const subMenus = computed(() => {
  59. return filterHiddenItems(currentRoute.matched[0].children);
  60. });
  61. // 跟随页面路由变化,切换菜单选中状态
  62. watch(
  63. () => currentRoute.fullPath,
  64. () => {
  65. const matched = currentRoute.matched;
  66. openKeys.value = matched.map((item) => item.name) as string[];
  67. const activeMenu: string = (currentRoute.meta?.activeMenu as string) || '';
  68. selectedKeys.value = activeMenu ? activeMenu : (currentRoute.path as string);
  69. },
  70. );
  71. </script>
  72. <style scoped lang="scss">
  73. .home-container {
  74. display: flex;
  75. gap: 10px;
  76. padding: 10px;
  77. height: 100%;
  78. }
  79. .aside {
  80. display: flex;
  81. flex-direction: column;
  82. width: 270px;
  83. height: 100%;
  84. flex-shrink: 0;
  85. border-radius: 4px;
  86. background-color: $white-color;
  87. &__header {
  88. width: inherit;
  89. height: 10px;
  90. flex-shrink: 0;
  91. }
  92. &__main {
  93. width: inherit;
  94. flex: 1;
  95. }
  96. }
  97. .main {
  98. flex: 1;
  99. overflow: auto;
  100. border-radius: 4px;
  101. }
  102. .el-menu-vertical {
  103. width: 100%;
  104. height: 100%;
  105. border: none;
  106. border-radius: 4px;
  107. .el-menu-item,
  108. :deep(.el-sub-menu__title) {
  109. font-size: 18px;
  110. color: #333;
  111. }
  112. .el-menu-item.is-active {
  113. color: $white-color;
  114. background-color: $primary-color;
  115. }
  116. }
  117. .el-menu-item,
  118. .el-sub-menu {
  119. svg {
  120. color: $primary-color;
  121. }
  122. }
  123. .el-menu-item {
  124. &.is-active {
  125. svg {
  126. color: #fff;
  127. }
  128. }
  129. }
  130. :deep(.el-sub-menu__title),
  131. :deep(.el-menu-item) {
  132. display: flex;
  133. align-items: center;
  134. gap: 12px;
  135. }
  136. </style>