use-mixed-menu.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import type { MenuRecordRaw } from '@vben/types';
  2. import { computed, onBeforeMount, ref, watch } from 'vue';
  3. import { useRoute } from 'vue-router';
  4. import { preferences, usePreferences } from '@vben/preferences';
  5. import { useAccessStore } from '@vben/stores';
  6. import { findRootMenuByPath } from '@vben/utils';
  7. import { useNavigation } from './use-navigation';
  8. function useMixedMenu() {
  9. const { navigation, willOpenedByWindow } = useNavigation();
  10. const accessStore = useAccessStore();
  11. const route = useRoute();
  12. const splitSideMenus = ref<MenuRecordRaw[]>([]);
  13. const rootMenuPath = ref<string>('');
  14. const mixedRootMenuPath = ref<string>('');
  15. const mixExtraMenus = ref<MenuRecordRaw[]>([]);
  16. /** 记录当前顶级菜单下哪个子菜单最后激活 */
  17. const defaultSubMap = new Map<string, string>();
  18. const { isMixedNav, isHeaderMixedNav } = usePreferences();
  19. const needSplit = computed(
  20. () =>
  21. (preferences.navigation.split && isMixedNav.value) ||
  22. isHeaderMixedNav.value,
  23. );
  24. const sidebarVisible = computed(() => {
  25. const enableSidebar = preferences.sidebar.enable;
  26. if (needSplit.value) {
  27. return enableSidebar && splitSideMenus.value.length > 0;
  28. }
  29. return enableSidebar;
  30. });
  31. const menus = computed(() => accessStore.accessMenus);
  32. /**
  33. * 头部菜单
  34. */
  35. const headerMenus = computed(() => {
  36. if (!needSplit.value) {
  37. return menus.value;
  38. }
  39. return menus.value.map((item) => {
  40. return {
  41. ...item,
  42. children: [],
  43. };
  44. });
  45. });
  46. /**
  47. * 侧边菜单
  48. */
  49. const sidebarMenus = computed(() => {
  50. return needSplit.value ? splitSideMenus.value : menus.value;
  51. });
  52. const mixHeaderMenus = computed(() => {
  53. return isHeaderMixedNav.value ? sidebarMenus.value : headerMenus.value;
  54. });
  55. /**
  56. * 侧边菜单激活路径
  57. */
  58. const sidebarActive = computed(() => {
  59. return (route?.meta?.activePath as string) ?? route.path;
  60. });
  61. /**
  62. * 头部菜单激活路径
  63. */
  64. const headerActive = computed(() => {
  65. if (!needSplit.value) {
  66. return route.meta?.activePath ?? route.path;
  67. }
  68. return rootMenuPath.value;
  69. });
  70. /**
  71. * 菜单点击事件处理
  72. * @param key 菜单路径
  73. * @param mode 菜单模式
  74. */
  75. const handleMenuSelect = (key: string, mode?: string) => {
  76. if (!needSplit.value || mode === 'vertical') {
  77. navigation(key);
  78. return;
  79. }
  80. const rootMenu = menus.value.find((item) => item.path === key);
  81. const _splitSideMenus = rootMenu?.children ?? [];
  82. if (!willOpenedByWindow(key)) {
  83. rootMenuPath.value = rootMenu?.path ?? '';
  84. splitSideMenus.value = _splitSideMenus;
  85. }
  86. if (_splitSideMenus.length === 0) {
  87. navigation(key);
  88. } else if (rootMenu && preferences.sidebar.autoActivateChild) {
  89. navigation(
  90. defaultSubMap.has(rootMenu.path)
  91. ? (defaultSubMap.get(rootMenu.path) as string)
  92. : rootMenu.path,
  93. );
  94. }
  95. };
  96. /**
  97. * 侧边菜单展开事件
  98. * @param key 路由路径
  99. * @param parentsPath 父级路径
  100. */
  101. const handleMenuOpen = (key: string, parentsPath: string[]) => {
  102. if (parentsPath.length <= 1 && preferences.sidebar.autoActivateChild) {
  103. navigation(
  104. defaultSubMap.has(key) ? (defaultSubMap.get(key) as string) : key,
  105. );
  106. }
  107. };
  108. /**
  109. * 计算侧边菜单
  110. * @param path 路由路径
  111. */
  112. function calcSideMenus(path: string = route.path) {
  113. let { rootMenu } = findRootMenuByPath(menus.value, path);
  114. if (!rootMenu) {
  115. rootMenu = menus.value.find((item) => item.path === path);
  116. }
  117. const result = findRootMenuByPath(rootMenu?.children || [], path, 1);
  118. mixedRootMenuPath.value = result.rootMenuPath ?? '';
  119. mixExtraMenus.value = result.rootMenu?.children ?? [];
  120. rootMenuPath.value = rootMenu?.path ?? '';
  121. splitSideMenus.value = rootMenu?.children ?? [];
  122. }
  123. watch(
  124. () => route.path,
  125. (path) => {
  126. const currentPath = (route?.meta?.activePath as string) ?? path;
  127. calcSideMenus(currentPath);
  128. if (rootMenuPath.value)
  129. defaultSubMap.set(rootMenuPath.value, currentPath);
  130. },
  131. { immediate: true },
  132. );
  133. // 初始化计算侧边菜单
  134. onBeforeMount(() => {
  135. calcSideMenus(route.meta?.activePath || route.path);
  136. });
  137. return {
  138. handleMenuSelect,
  139. handleMenuOpen,
  140. headerActive,
  141. headerMenus,
  142. sidebarActive,
  143. sidebarMenus,
  144. mixHeaderMenus,
  145. mixExtraMenus,
  146. sidebarVisible,
  147. };
  148. }
  149. export { useMixedMenu };