BasicMenu.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <template>
  2. <Menu
  3. :selectedKeys="selectedKeys"
  4. :defaultSelectedKeys="defaultSelectedKeys"
  5. :mode="mode"
  6. :openKeys="getOpenKeys"
  7. :inlineIndent="inlineIndent"
  8. :theme="theme"
  9. @open-change="handleOpenChange"
  10. :class="getMenuClass"
  11. @click="handleMenuClick"
  12. :subMenuOpenDelay="0.2"
  13. v-bind="getInlineCollapseOptions"
  14. >
  15. <template v-for="item in items" :key="item.path">
  16. <BasicSubMenuItem :item="item" :theme="theme" :isHorizontal="isHorizontal" />
  17. </template>
  18. </Menu>
  19. </template>
  20. <script lang="ts">
  21. import type { MenuState } from './types';
  22. import { computed, defineComponent, unref, reactive, watch, toRefs, ref } from 'vue';
  23. import { Menu } from 'ant-design-vue';
  24. import BasicSubMenuItem from './components/BasicSubMenuItem.vue';
  25. import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
  26. import { useOpenKeys } from './useOpenKeys';
  27. import { RouteLocationNormalizedLoaded, useRouter } from 'vue-router';
  28. import { isFunction } from '/@/utils/is';
  29. import { basicProps } from './props';
  30. import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  31. import { REDIRECT_NAME } from '/@/router/constant';
  32. import { useDesign } from '/@/hooks/web/useDesign';
  33. import { getCurrentParentPath } from '/@/router/menus';
  34. import { listenerRouteChange } from '/@/logics/mitt/routeChange';
  35. import { getAllParentPath } from '/@/router/helper/menuHelper';
  36. export default defineComponent({
  37. name: 'BasicMenu',
  38. components: {
  39. Menu,
  40. BasicSubMenuItem,
  41. },
  42. props: basicProps,
  43. emits: ['menuClick'],
  44. setup(props, { emit }) {
  45. const isClickGo = ref(false);
  46. const currentActiveMenu = ref('');
  47. const menuState = reactive<MenuState>({
  48. defaultSelectedKeys: [],
  49. openKeys: [],
  50. selectedKeys: [],
  51. collapsedOpenKeys: [],
  52. });
  53. const { prefixCls } = useDesign('basic-menu');
  54. const { items, mode, accordion } = toRefs(props);
  55. const { getCollapsed, getTopMenuAlign, getSplit } = useMenuSetting();
  56. const { currentRoute } = useRouter();
  57. const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys(
  58. menuState,
  59. items,
  60. mode as any,
  61. accordion,
  62. );
  63. const getIsTopMenu = computed(() => {
  64. const { type, mode } = props;
  65. return (
  66. (type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL) ||
  67. (props.isHorizontal && unref(getSplit))
  68. );
  69. });
  70. const getMenuClass = computed(() => {
  71. const align = props.isHorizontal && unref(getSplit) ? 'start' : unref(getTopMenuAlign);
  72. return [
  73. prefixCls,
  74. `justify-${align}`,
  75. {
  76. [`${prefixCls}__second`]: !props.isHorizontal && unref(getSplit),
  77. [`${prefixCls}__sidebar-hor`]: unref(getIsTopMenu),
  78. },
  79. ];
  80. });
  81. const getInlineCollapseOptions = computed(() => {
  82. const isInline = props.mode === MenuModeEnum.INLINE;
  83. const inlineCollapseOptions: { inlineCollapsed?: boolean } = {};
  84. if (isInline) {
  85. inlineCollapseOptions.inlineCollapsed = props.mixSider ? false : unref(getCollapsed);
  86. }
  87. return inlineCollapseOptions;
  88. });
  89. listenerRouteChange((route) => {
  90. if (route.name === REDIRECT_NAME) return;
  91. handleMenuChange(route);
  92. currentActiveMenu.value = route.meta?.currentActiveMenu as string;
  93. if (unref(currentActiveMenu)) {
  94. menuState.selectedKeys = [unref(currentActiveMenu)];
  95. setOpenKeys(unref(currentActiveMenu));
  96. }
  97. });
  98. !props.mixSider &&
  99. watch(
  100. () => props.items,
  101. () => {
  102. handleMenuChange();
  103. },
  104. );
  105. async function handleMenuClick({ key }: { key: string; keyPath: string[] }) {
  106. const { beforeClickFn } = props;
  107. if (beforeClickFn && isFunction(beforeClickFn)) {
  108. const flag = await beforeClickFn(key);
  109. if (!flag) return;
  110. }
  111. emit('menuClick', key);
  112. isClickGo.value = true;
  113. menuState.selectedKeys = [key];
  114. }
  115. async function handleMenuChange(route?: RouteLocationNormalizedLoaded) {
  116. if (unref(isClickGo)) {
  117. isClickGo.value = false;
  118. return;
  119. }
  120. const path =
  121. (route || unref(currentRoute)).meta?.currentActiveMenu ||
  122. (route || unref(currentRoute)).path;
  123. setOpenKeys(path);
  124. if (unref(currentActiveMenu)) return;
  125. if (props.isHorizontal && unref(getSplit)) {
  126. const parentPath = await getCurrentParentPath(path);
  127. menuState.selectedKeys = [parentPath];
  128. } else {
  129. const parentPaths = await getAllParentPath(props.items, path);
  130. menuState.selectedKeys = parentPaths;
  131. }
  132. }
  133. return {
  134. handleMenuClick,
  135. getInlineCollapseOptions,
  136. getMenuClass,
  137. handleOpenChange,
  138. getOpenKeys,
  139. ...toRefs(menuState),
  140. };
  141. },
  142. });
  143. </script>
  144. <style lang="less">
  145. @import './index.less';
  146. </style>