AppAside.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <template>
  2. <el-scrollbar v-if="showTabs && activeTab !== 'selectCityAndCounty'">
  3. <el-menu
  4. :collapse="isCollapse"
  5. router
  6. unique-opened
  7. :default-active="activeMenuItem.index"
  8. class="professional-menu"
  9. >
  10. <template v-for="item in filteredMenuItems" :key="item.index">
  11. <el-sub-menu v-if="item.children" :index="item.index">
  12. <template #title>
  13. <el-icon><component :is="item.icon" /></el-icon>
  14. <span>{{ item.label }}</span>
  15. </template>
  16. <el-menu-item
  17. v-for="child in item.children"
  18. :key="child.index"
  19. :index="child.index"
  20. @click="handleMenuClick(child.index)"
  21. >
  22. <el-icon><component :is="child.icon" /></el-icon>
  23. <span>{{ child.label }}</span>
  24. </el-menu-item>
  25. </el-sub-menu>
  26. <el-menu-item
  27. v-else
  28. :index="item.index"
  29. @click="handleMenuClick(item.index)"
  30. >
  31. <el-icon><component :is="item.icon" /></el-icon>
  32. <span>{{ item.label }}</span>
  33. </el-menu-item>
  34. </template>
  35. </el-menu>
  36. </el-scrollbar>
  37. </template>
  38. <script setup lang="ts">
  39. import { reactive, computed, inject, toRefs, watch } from 'vue';
  40. import { useRouter } from 'vue-router';
  41. import { ElMessage } from 'element-plus';
  42. import { menuItems } from './menuItems'; // 建议你将 menuItems 独立维护在该文件中
  43. const props = defineProps({
  44. activeTab: {
  45. type: String,
  46. required: true,
  47. default: 'introduction'
  48. },
  49. showTabs: {
  50. type: Boolean,
  51. required: false,
  52. default: true
  53. }
  54. });
  55. const { activeTab } = toRefs(props);
  56. const isCollapse = inject('isCollapse', false);
  57. const userPermissions = inject('userPermissions', [] as string[]);
  58. const router = useRouter();
  59. const activeMenuItem = reactive({ index: '' });
  60. const filteredMenuItems = computed(() =>
  61. menuItems
  62. .filter(item => item.tab === activeTab.value)
  63. .map(item => {
  64. if (!item.children) return item;
  65. return {
  66. ...item,
  67. children: item.children.filter(
  68. child => !child.permission || userPermissions.includes(child.permission)
  69. )
  70. };
  71. })
  72. );
  73. function handleMenuClick(index: string) {
  74. try {
  75. if (router.currentRoute.value.path !== index) {
  76. router.push(index);
  77. activeMenuItem.index = index;
  78. }
  79. } catch (error) {
  80. ElMessage.error('导航失败,请检查网络或联系管理员');
  81. }
  82. }
  83. watch(
  84. activeTab,
  85. newVal => {
  86. const current = filteredMenuItems.value?.[0];
  87. activeMenuItem.index = current?.children?.[0]?.index || current?.index || '';
  88. },
  89. { immediate: true }
  90. );
  91. </script>
  92. <style scoped>
  93. :deep(.el-scrollbar) {
  94. background: linear-gradient(to bottom, #B7F1FC , #FFF8F0) !important;
  95. height: 100%;
  96. border-right: none !important;
  97. padding-top: 12px;
  98. }
  99. :deep(.el-scrollbar__wrap),
  100. :deep(.el-scrollbar__view) {
  101. background: transparent;
  102. height: 100%;
  103. display: flex;
  104. flex-direction: column;
  105. }
  106. .professional-menu {
  107. background: transparent;
  108. border-right: none;
  109. padding-top: 12px;
  110. flex: 1;
  111. display: flex;
  112. flex-direction: column;
  113. min-height: 100%;
  114. }
  115. :deep(.el-menu-item),
  116. :deep(.el-sub-menu__title) {
  117. margin-left: 0 !important;
  118. margin-right: 0 !important;
  119. width: 100%;
  120. box-sizing: border-box;
  121. padding-left: 40px !important;
  122. padding-right: 20px !important;
  123. }
  124. :deep(.el-sub-menu .el-menu-item) {
  125. background-color: rgba(252, 234, 183, 0.3) !important;
  126. }
  127. :deep(.el-sub-menu .el-menu-item:not(:last-child)) {
  128. margin-bottom: 0;
  129. }
  130. :deep(.el-sub-menu__title) {
  131. font-size: 18px;
  132. font-weight: 500;
  133. color: #000000;
  134. border-radius: 6px;
  135. padding: 12px 16px !important;
  136. transition: all 0.2s ease;
  137. }
  138. /* Hover 效果 */
  139. :deep(.el-menu-item:hover),
  140. :deep(.el-sub-menu__title:hover) {
  141. background-color: rgba(16, 146, 216, 0.1);
  142. color: #1092D8;
  143. }
  144. /* 激活高亮 */
  145. :deep(.el-menu-item.is-active),
  146. :deep(.el-sub-menu__title.is-active) {
  147. background: linear-gradient(to right, #1092D8, #02C3AD);
  148. color: #ffffff !important;
  149. border-radius: 8px;
  150. font-weight: 600;
  151. box-shadow: 0 2px 8px rgba(16, 146, 216, 0.25);
  152. }
  153. /* 子菜单标题样式 + 图标右移 */
  154. :deep(.el-sub-menu__title) {
  155. display: flex;
  156. align-items: center;
  157. padding-left: 20px !important;
  158. }
  159. /* 下拉图标右移 */
  160. :deep(.el-sub-menu__icon-arrow) {
  161. margin-left: auto;
  162. margin-right: -160px;
  163. transition: transform 0.3s ease;
  164. }
  165. </style>