Sider.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <template>
  2. <el-menu
  3. :default-openeds="openKeys"
  4. :default-active="getSelectedKeys"
  5. :theme="getTheme"
  6. :collapse="getCollapsed"
  7. :collapse-transition="false"
  8. :unique-opened="getUniqueOpened"
  9. :mode="props.mode"
  10. style="width: 100%"
  11. @select="menuItemClick"
  12. @open="subMenuItemClick"
  13. >
  14. <template v-for="item in menus" :key="item.key">
  15. <el-menu-item v-if="!item.children" :key="getMenuKey(item.key)" :index="item.key">
  16. <el-icon v-if="item.icon"><component :is="item.icon" /></el-icon>
  17. <template #title>
  18. <span>{{ item.title }}</span>
  19. </template>
  20. </el-menu-item>
  21. <template v-else><ReSubMenu :parent="item" /></template>
  22. </template>
  23. </el-menu>
  24. </template>
  25. <script lang="ts" setup>
  26. import { ref, computed, watch, onMounted, unref, inject } from 'vue';
  27. import { useRoute, useRouter } from 'vue-router';
  28. import { generatorMenu, generatorMenuMix } from '@/utils';
  29. import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
  30. import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
  31. import { useProjectSettingStore } from '@/store/modules/projectSetting';
  32. import ReSubMenu from './ReSubMenu.vue';
  33. const collapsed = inject('collapsed');
  34. const { getMenuSetting, getNavMode, getNavTheme } = useProjectSetting();
  35. const navMode = getNavMode;
  36. defineEmits(['update:collapsed']);
  37. const props = defineProps({
  38. mode: {
  39. type: String,
  40. default: 'vertical',
  41. },
  42. theme: {
  43. type: String,
  44. default: 'dark',
  45. },
  46. //位置
  47. location: {
  48. type: String,
  49. default: 'left',
  50. },
  51. });
  52. // 当前路由
  53. const currentRoute = useRoute();
  54. const router = useRouter();
  55. const settingStore = useProjectSettingStore();
  56. const menus = ref<any>([]);
  57. const headerMenuSelectKey = ref<string>('');
  58. // 获取当前打开的子菜单
  59. const matched = currentRoute.matched;
  60. const activeMenu = currentRoute.meta?.activeMenu || null; // activeMenu undefined,null 或 空字符串,统一变为 null。
  61. const selectedKeys = ref<string>((activeMenu ?? currentRoute.name) as string);
  62. const openKeys = ref(matched && matched.length ? matched.map((item) => item.name) : []);
  63. const asyncRouteStore = useAsyncRouteStore();
  64. const getCollapsed = computed(() => {
  65. let location = props.location;
  66. return location === 'left' ? unref(collapsed) : false;
  67. });
  68. const getUniqueOpened = computed(() => {
  69. return getMenuSetting.value.uniqueOpened;
  70. });
  71. const getSelectedKeys = computed(() => {
  72. let location = props.location;
  73. console.log('location', location);
  74. console.log('selectedKeys', selectedKeys.value);
  75. return location === 'left' || (location === 'header' && unref(navMode) === 'horizontal')
  76. ? unref(selectedKeys)
  77. : unref(headerMenuSelectKey);
  78. });
  79. // 监听分割菜单
  80. watch(
  81. () => settingStore.menuSetting.mixMenu,
  82. () => {
  83. updateMenu();
  84. },
  85. );
  86. // 跟随页面路由变化,切换菜单选中状态
  87. watch(
  88. () => currentRoute.fullPath,
  89. () => {
  90. updateMenu();
  91. const matched = currentRoute.matched;
  92. openKeys.value = matched.map((item) => item.name);
  93. const activeMenu: string = (currentRoute.meta?.activeMenu as string) || '';
  94. selectedKeys.value = activeMenu ? activeMenu : (currentRoute.name as string);
  95. },
  96. );
  97. function getMenuKey(key) {
  98. return `${props.location}_${key}`;
  99. }
  100. function updateMenu() {
  101. if (!settingStore.menuSetting.mixMenu) {
  102. menus.value = generatorMenu(asyncRouteStore.getMenus);
  103. } else {
  104. //混合菜单
  105. const firstRouteName: string = (currentRoute.matched[0].name as string) || '';
  106. menus.value = generatorMenuMix(asyncRouteStore.getMenus, firstRouteName, props.location);
  107. const activeMenu: string = currentRoute?.matched[0].meta?.activeMenu as string;
  108. headerMenuSelectKey.value = (activeMenu ? activeMenu : firstRouteName) || '';
  109. }
  110. }
  111. // const getMenuMode = computed(() => {
  112. // if (props.mode === 'vertical') {
  113. // return getMenuIsPop.value ? 'pop' : 'vertical';
  114. // } else {
  115. // return props.mode;
  116. // }
  117. // });
  118. const getTheme = computed(() => {
  119. return getNavTheme.value === 'light' ? 'light' : props.theme;
  120. });
  121. //点击菜单项时触发
  122. function menuItemClick(key) {
  123. if (/http(s)?:/.test(key)) {
  124. window.open(key);
  125. } else {
  126. router.push({ name: key });
  127. }
  128. }
  129. function subMenuItemClick(_, keys) {
  130. if (!keys) return;
  131. const latestOpenKey = keys.find((key) => openKeys.value.indexOf(key) === -1);
  132. const isExistChildren = findChildrenLen(latestOpenKey as string);
  133. openKeys.value = isExistChildren ? (latestOpenKey ? [latestOpenKey] : []) : keys;
  134. }
  135. //查找是否存在子路由
  136. function findChildrenLen(key: string) {
  137. if (!key) return false;
  138. const subRouteChildren: string[] = [];
  139. for (const { children, key } of unref(menus)) {
  140. if (children && children.length) {
  141. subRouteChildren.push(key as string);
  142. }
  143. }
  144. return subRouteChildren.includes(key);
  145. }
  146. onMounted(() => {
  147. updateMenu();
  148. });
  149. </script>
  150. <style scoped lang="scss">
  151. .my-icon {
  152. position: relative;
  153. margin-right: 5px;
  154. width: 0.75em;
  155. height: 0.75em;
  156. filter: drop-shadow(var(--el-menu-text-color) 80px 0);
  157. transform: translate(-80px);
  158. }
  159. :deep(.el-menu-item.is-active) {
  160. .my-icon {
  161. filter: drop-shadow(var(--el-menu-hover-text-color) 80px 0);
  162. transform: translate(-80px);
  163. }
  164. }
  165. :deep(.el-menu-item:hover) {
  166. .my-icon {
  167. filter: drop-shadow(var(--el-menu-hover-text-color) 80px 0);
  168. transform: translate(-80px);
  169. }
  170. }
  171. </style>