AppAsideForTab2.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <template>
  2. <el-scrollbar v-if="showTabs">
  3. <el-menu
  4. :collapse="isCollapse"
  5. router
  6. unique-opened
  7. :default-active="activeMenuItem.index"
  8. :default-openeds="openKeys"
  9. @select="handleMenuClick"
  10. >
  11. <template v-if="menuList.length">
  12. <template v-for="item in menuList" :key="item.index">
  13. <el-sub-menu v-if="item.children && item.children.length" :index="item.index">
  14. <template #title>
  15. <el-icon v-if="item.icon"><component :is="item.icon" /></el-icon>
  16. <span>{{ item.label }}</span>
  17. </template>
  18. <el-menu-item
  19. v-for="child in item.children"
  20. :key="child.index"
  21. :index="child.index"
  22. >
  23. <el-icon v-if="child.icon"><component :is="child.icon" /></el-icon>
  24. <span>{{ child.label }}</span>
  25. </el-menu-item>
  26. </el-sub-menu>
  27. <el-menu-item v-else :index="item.index">
  28. <el-icon v-if="item.icon"><component :is="item.icon" /></el-icon>
  29. <span>{{ item.label }}</span>
  30. </el-menu-item>
  31. </template>
  32. </template>
  33. <template v-else>
  34. <el-menu-item disabled>未知的 Tab:{{ activeTab }}</el-menu-item>
  35. </template>
  36. </el-menu>
  37. </el-scrollbar>
  38. </template>
  39. <script setup lang="ts">
  40. import { inject, reactive, watch, toRefs, computed } from "vue";
  41. import { useRouter, useRoute, type RouteLocationAsPathGeneric, type RouteLocationAsRelativeGeneric } from "vue-router";
  42. import { ElMessage } from "element-plus";
  43. import { tabMenuMap } from "./menuItems2";
  44. console.log('当前使用的侧边栏组件:AppAsideForTab2.vue') // AppAsideForTab2.vue 中添加
  45. const props = defineProps({
  46. activeTab: {
  47. type: String,
  48. required: true,
  49. default: "introduction",
  50. },
  51. showTabs: {
  52. type: Boolean,
  53. required: true,
  54. default: true,
  55. },
  56. });
  57. const { activeTab } = toRefs(props);
  58. const isCollapse = inject("isCollapse", false);
  59. const router = useRouter();
  60. const route = useRoute();
  61. const activeMenuItem = reactive({ index: route.path });
  62. const openKeys = reactive<string[]>([]);
  63. const menuList = computed(() => {
  64. return tabMenuMap[activeTab.value] || [];
  65. });
  66. // 查找当前路由对应的父级菜单index,用于默认展开子菜单
  67. function findOpenKeys(menuItems: any[], currentPath: string, parents: string[] = []): string[] {
  68. for (const item of menuItems) {
  69. if (item.index === currentPath) {
  70. return parents;
  71. } else if (item.children) {
  72. const found = findOpenKeys(item.children, currentPath, [...parents, item.index]);
  73. if (found.length) return found;
  74. }
  75. }
  76. return [];
  77. }
  78. watch(
  79. () => route.path,
  80. (newPath) => {
  81. activeMenuItem.index = newPath;
  82. openKeys.splice(0, openKeys.length, ...findOpenKeys(menuList.value, newPath));
  83. },
  84. { immediate: true }
  85. );
  86. watch(
  87. activeTab,
  88. (newVal) => {
  89. const first = tabMenuMap[newVal]?.[0];
  90. if (first) activeMenuItem.index = first.index;
  91. else activeMenuItem.index = "";
  92. openKeys.splice(0, openKeys.length, ...findOpenKeys(menuList.value, activeMenuItem.index));
  93. },
  94. { immediate: true }
  95. );
  96. async function handleMenuClick(index: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric) {
  97. try {
  98. if (index === route.path) return;
  99. await router.push(index);
  100. activeMenuItem.index = index as string;
  101. } catch (error) {
  102. console.error(`[AppAsideForTab2] 路由跳转失败: ${index}`, error);
  103. ElMessage.error("导航失败,请检查网络连接或联系管理员。");
  104. }
  105. }
  106. </script>
  107. <style scoped>
  108. /* 给 el-menu 组件设置背景渐变 */
  109. :deep(.el-menu) {
  110. background: linear-gradient(to bottom, #B7F1FC , #FFF8F0) !important;
  111. height: 100%;
  112. border-right: none !important;
  113. padding-top: 12px;
  114. }
  115. /* 保持滚动条内容透明 */
  116. :deep(.el-scrollbar__wrap),
  117. :deep(.el-scrollbar__view) {
  118. background: transparent !important;
  119. height: 100%;
  120. display: flex;
  121. flex-direction: column;
  122. }
  123. /* 菜单项和子菜单标题 */
  124. :deep(.el-menu-item),
  125. :deep(.el-sub-menu__title) {
  126. margin-left: 0 !important;
  127. margin-right: 0 !important;
  128. width: 100%;
  129. box-sizing: border-box;
  130. padding-left: 40px !important;
  131. padding-right: 20px !important;
  132. }
  133. /* 子菜单中菜单项背景色 */
  134. :deep(.el-sub-menu .el-menu-item) {
  135. background-color: rgba(252, 234, 183, 0.3) !important;
  136. }
  137. /* Hover 效果 */
  138. :deep(.el-menu-item:hover),
  139. :deep(.el-sub-menu__title:hover) {
  140. background-color: rgba(16, 146, 216, 0.1) !important;
  141. color: #1092D8 !important;
  142. }
  143. /* 激活高亮 */
  144. :deep(.el-menu-item.is-active),
  145. :deep(.el-sub-menu__title.is-active) {
  146. background: linear-gradient(to right, #1092D8, #02C3AD) !important;
  147. color: #ffffff !important;
  148. border-radius: 8px !important;
  149. font-weight: 600 !important;
  150. box-shadow: 0 2px 8px rgba(16, 146, 216, 0.25) !important;
  151. }
  152. /* 子菜单标题样式 + 图标右移 */
  153. :deep(.el-sub-menu__title) {
  154. display: flex;
  155. align-items: center;
  156. padding-left: 20px !important;
  157. }
  158. /* 下拉图标右移 */
  159. :deep(.el-sub-menu__icon-arrow) {
  160. margin-left: auto;
  161. margin-right: -160px;
  162. transition: transform 0.3s ease;
  163. }
  164. </style>