use-mixed-menu.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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 } = 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.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. rootMenuPath.value = rootMenu?.path ?? '';
  82. splitSideMenus.value = rootMenu?.children ?? [];
  83. if (splitSideMenus.value.length === 0) {
  84. navigation(key);
  85. } else if (rootMenu && preferences.sidebar.autoActivateChild) {
  86. navigation(
  87. defaultSubMap.has(rootMenu.path)
  88. ? (defaultSubMap.get(rootMenu.path) as string)
  89. : rootMenu.path,
  90. );
  91. }
  92. };
  93. /**
  94. * 侧边菜单展开事件
  95. * @param key 路由路径
  96. * @param parentsPath 父级路径
  97. */
  98. const handleMenuOpen = (key: string, parentsPath: string[]) => {
  99. if (parentsPath.length <= 1 && preferences.sidebar.autoActivateChild) {
  100. navigation(
  101. defaultSubMap.has(key) ? (defaultSubMap.get(key) as string) : key,
  102. );
  103. }
  104. };
  105. /**
  106. * 计算侧边菜单
  107. * @param path 路由路径
  108. */
  109. function calcSideMenus(path: string = route.path) {
  110. let { rootMenu } = findRootMenuByPath(menus.value, path);
  111. if (!rootMenu) {
  112. rootMenu = menus.value.find((item) => item.path === path);
  113. }
  114. const result = findRootMenuByPath(rootMenu?.children || [], path, 1);
  115. mixedRootMenuPath.value = result.rootMenuPath ?? '';
  116. mixExtraMenus.value = result.rootMenu?.children ?? [];
  117. rootMenuPath.value = rootMenu?.path ?? '';
  118. splitSideMenus.value = rootMenu?.children ?? [];
  119. }
  120. watch(
  121. () => route.path,
  122. (path) => {
  123. const currentPath = (route?.meta?.activePath as string) ?? path;
  124. calcSideMenus(currentPath);
  125. if (rootMenuPath.value)
  126. defaultSubMap.set(rootMenuPath.value, currentPath);
  127. },
  128. { immediate: true },
  129. );
  130. // 初始化计算侧边菜单
  131. onBeforeMount(() => {
  132. calcSideMenus(route.meta?.activePath || route.path);
  133. });
  134. return {
  135. handleMenuSelect,
  136. handleMenuOpen,
  137. headerActive,
  138. headerMenus,
  139. sidebarActive,
  140. sidebarMenus,
  141. mixHeaderMenus,
  142. mixExtraMenus,
  143. sidebarVisible,
  144. };
  145. }
  146. export { useMixedMenu };