MenuLayout.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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. :deep(.el-menu-item) {
  74. transition: unset;
  75. }
  76. .home-container {
  77. display: flex;
  78. gap: 10px;
  79. padding: 10px;
  80. height: 100%;
  81. }
  82. .aside {
  83. display: flex;
  84. flex-direction: column;
  85. width: 228px;
  86. height: 100%;
  87. flex-shrink: 0;
  88. border-radius: 4px;
  89. background-color: $white-color;
  90. &__header {
  91. width: inherit;
  92. height: 10px;
  93. flex-shrink: 0;
  94. }
  95. &__main {
  96. width: inherit;
  97. flex: 1;
  98. }
  99. }
  100. .main {
  101. flex: 1;
  102. overflow: auto;
  103. border-radius: 4px;
  104. }
  105. .el-menu-vertical {
  106. width: 100%;
  107. height: 100%;
  108. border: none;
  109. border-radius: 4px;
  110. .el-menu-item,
  111. :deep(.el-sub-menu__title) {
  112. font-size: 16px;
  113. color: #333;
  114. }
  115. .el-menu-item.is-active {
  116. color: $white-color;
  117. background-color: $primary-color;
  118. }
  119. }
  120. .el-menu-item,
  121. .el-sub-menu {
  122. svg {
  123. color: $primary-color;
  124. }
  125. }
  126. .el-menu-item {
  127. &.is-active {
  128. svg {
  129. color: #fff;
  130. }
  131. }
  132. }
  133. :deep(.el-sub-menu__title),
  134. :deep(.el-menu-item) {
  135. display: flex;
  136. align-items: center;
  137. gap: 12px;
  138. }
  139. </style>