LayoutMenu.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import type { PropType } from 'vue';
  2. import type { Menu } from '/@/router/types';
  3. import { computed, defineComponent, unref, ref, onMounted, watch } from 'vue';
  4. import { BasicMenu } from '/@/components/Menu/index';
  5. import Logo from '/@/layouts/logo/index.vue';
  6. import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
  7. // store
  8. import { appStore } from '/@/store/modules/app';
  9. import { menuStore } from '/@/store/modules/menu';
  10. import {
  11. getMenus,
  12. getFlatMenus,
  13. getShallowMenus,
  14. getChildrenMenus,
  15. getFlatChildrenMenus,
  16. getCurrentParentPath,
  17. } from '/@/router/menus/index';
  18. import { useRouter } from 'vue-router';
  19. import { useThrottle } from '/@/hooks/core/useThrottle';
  20. import { permissionStore } from '/@/store/modules/permission';
  21. import './index.less';
  22. export default defineComponent({
  23. name: 'DefaultLayoutMenu',
  24. props: {
  25. theme: {
  26. type: String as PropType<string>,
  27. default: '',
  28. },
  29. splitType: {
  30. type: Number as PropType<MenuSplitTyeEnum>,
  31. default: MenuSplitTyeEnum.NONE,
  32. },
  33. parentMenuPath: {
  34. type: String as PropType<string>,
  35. default: '',
  36. },
  37. showSearch: {
  38. type: Boolean as PropType<boolean>,
  39. default: true,
  40. },
  41. isTop: {
  42. type: Boolean as PropType<boolean>,
  43. default: false,
  44. },
  45. menuMode: {
  46. type: [String] as PropType<MenuModeEnum | null>,
  47. default: '',
  48. },
  49. },
  50. setup(props) {
  51. // Menu array
  52. const menusRef = ref<Menu[]>([]);
  53. // flat menu array
  54. const flatMenusRef = ref<Menu[]>([]);
  55. const { currentRoute, push } = useRouter();
  56. // get app config
  57. const getProjectConfigRef = computed(() => {
  58. return appStore.getProjectConfig;
  59. });
  60. // get is Horizontal
  61. const getIsHorizontalRef = computed(() => {
  62. return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL;
  63. });
  64. const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
  65. // Route change split menu
  66. watch(
  67. [() => unref(currentRoute).path, () => props.splitType],
  68. async ([path, splitType]: [string, MenuSplitTyeEnum]) => {
  69. if (splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return;
  70. const parentPath = await getCurrentParentPath(path);
  71. parentPath && throttleHandleSplitLeftMenu(parentPath);
  72. },
  73. {
  74. immediate: true,
  75. }
  76. );
  77. // Menu changes
  78. watch(
  79. [() => permissionStore.getLastBuildMenuTimeState, () => permissionStore.getBackMenuListState],
  80. () => {
  81. genMenus();
  82. }
  83. );
  84. // split Menu changes
  85. watch([() => appStore.getProjectConfig.menuSetting.split], () => {
  86. if (props.splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return;
  87. genMenus();
  88. });
  89. // Handle left menu split
  90. async function handleSplitLeftMenu(parentPath: string) {
  91. const isSplitMenu = unref(getProjectConfigRef).menuSetting.split;
  92. if (!isSplitMenu) return;
  93. const { splitType } = props;
  94. // spilt mode left
  95. if (splitType === MenuSplitTyeEnum.LEFT) {
  96. const children = await getChildrenMenus(parentPath);
  97. if (!children) {
  98. appStore.commitProjectConfigState({
  99. menuSetting: {
  100. hidden: false,
  101. },
  102. });
  103. flatMenusRef.value = [];
  104. menusRef.value = [];
  105. return;
  106. }
  107. const flatChildren = await getFlatChildrenMenus(children);
  108. appStore.commitProjectConfigState({
  109. menuSetting: {
  110. hidden: true,
  111. },
  112. });
  113. flatMenusRef.value = flatChildren;
  114. menusRef.value = children;
  115. }
  116. }
  117. // get menus
  118. async function genMenus() {
  119. const isSplitMenu = unref(getProjectConfigRef).menuSetting.split;
  120. // normal mode
  121. const { splitType } = props;
  122. if (splitType === MenuSplitTyeEnum.NONE || !isSplitMenu) {
  123. flatMenusRef.value = await getFlatMenus();
  124. menusRef.value = await getMenus();
  125. return;
  126. }
  127. // split-top
  128. if (splitType === MenuSplitTyeEnum.TOP) {
  129. const parentPath = await getCurrentParentPath(unref(currentRoute).path);
  130. menuStore.commitCurrentTopSplitMenuPathState(parentPath);
  131. const shallowMenus = await getShallowMenus();
  132. flatMenusRef.value = shallowMenus;
  133. menusRef.value = shallowMenus;
  134. return;
  135. }
  136. }
  137. function handleMenuClick(menu: Menu) {
  138. const { path } = menu;
  139. if (path) {
  140. const { splitType } = props;
  141. // split mode top
  142. if (splitType === MenuSplitTyeEnum.TOP) {
  143. menuStore.commitCurrentTopSplitMenuPathState(path);
  144. }
  145. push(path);
  146. }
  147. }
  148. async function beforeMenuClickFn(menu: Menu) {
  149. const { meta: { externalLink } = {} } = menu;
  150. if (externalLink) {
  151. window.open(externalLink, '_blank');
  152. return false;
  153. }
  154. return true;
  155. }
  156. function handleClickSearchInput() {
  157. if (menuStore.getCollapsedState) {
  158. menuStore.commitCollapsedState(false);
  159. }
  160. }
  161. const showSearchRef = computed(() => {
  162. const { showSearch, type, mode } = unref(getProjectConfigRef).menuSetting;
  163. return (
  164. showSearch &&
  165. props.showSearch &&
  166. !(type === MenuTypeEnum.MIX && mode === MenuModeEnum.HORIZONTAL)
  167. );
  168. });
  169. onMounted(() => {
  170. genMenus();
  171. });
  172. return () => {
  173. const {
  174. showLogo,
  175. menuSetting: {
  176. type: menuType,
  177. mode,
  178. theme,
  179. collapsed,
  180. collapsedShowTitle,
  181. collapsedShowSearch,
  182. accordion,
  183. },
  184. } = unref(getProjectConfigRef);
  185. const isSidebarType = menuType === MenuTypeEnum.SIDEBAR;
  186. const isShowLogo = showLogo && isSidebarType;
  187. const themeData = props.theme || theme;
  188. return (
  189. <BasicMenu
  190. beforeClickFn={beforeMenuClickFn}
  191. onMenuClick={handleMenuClick}
  192. type={menuType}
  193. mode={props.menuMode || mode}
  194. class="layout-menu"
  195. collapsedShowTitle={collapsedShowTitle}
  196. theme={themeData}
  197. showLogo={isShowLogo}
  198. search={unref(showSearchRef) && (collapsedShowSearch ? true : !collapsed)}
  199. items={unref(menusRef)}
  200. flatItems={unref(flatMenusRef)}
  201. onClickSearchInput={handleClickSearchInput}
  202. appendClass={props.splitType === MenuSplitTyeEnum.TOP}
  203. isTop={props.isTop}
  204. accordion={accordion}
  205. >
  206. {{
  207. header: () =>
  208. isShowLogo && (
  209. <Logo
  210. showTitle={!collapsed}
  211. class={[`layout-menu__logo`, themeData]}
  212. theme={themeData}
  213. />
  214. ),
  215. }}
  216. </BasicMenu>
  217. );
  218. };
  219. },
  220. });