AppAside.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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. .professional-menu .el-menu {
  94. background-color: #f4f6f9;
  95. border-right: none;
  96. margin-top: 20px;
  97. box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);
  98. font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  99. border-radius: 0 12px 12px 0;
  100. }
  101. .professional-menu .el-menu-item,
  102. .professional-menu .el-submenu__title {
  103. color: #333;
  104. font-size: 15px;
  105. padding: 14px 20px;
  106. transition: background-color 0.3s ease, color 0.3s ease;
  107. border-radius: 8px;
  108. }
  109. .professional-menu .el-menu-item:hover,
  110. .professional-menu .el-submenu__title:hover {
  111. background-color: #e6f0ff;
  112. color: #007bff;
  113. }
  114. .professional-menu .el-menu-item.is-active {
  115. background-color: #007bff;
  116. color: #fff;
  117. font-weight: bold;
  118. box-shadow: inset 0 0 8px rgba(0, 123, 255, 0.4);
  119. }
  120. .professional-menu .el-icon {
  121. margin-right: 8px;
  122. }
  123. </style>