Ben Lin
2024-08-22 c9f779c19d920efcca14fd17bbcca8254f92790d
src/components/Menu/src/BasicMenu.vue
@@ -1,7 +1,7 @@
<template>
  <Menu
    :selectedKeys="selectedKeys"
    :defaultSelectedKeys="defaultSelectedKeys"
    :selectedKeys="menuState.selectedKeys"
    :defaultSelectedKeys="menuState.defaultSelectedKeys"
    :mode="mode"
    :openKeys="getOpenKeys"
    :inlineIndent="inlineIndent"
@@ -17,147 +17,132 @@
    </template>
  </Menu>
</template>
<script lang="ts">
<script lang="ts" setup>
  import type { MenuState } from './types';
  import { computed, defineComponent, unref, reactive, watch, toRefs, ref } from 'vue';
  import { Menu } from 'ant-design-vue';
  import { computed, unref, reactive, watch, toRefs, ref } from 'vue';
  import { Menu, MenuProps } from 'ant-design-vue';
  import BasicSubMenuItem from './components/BasicSubMenuItem.vue';
  import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
  import { MenuModeEnum, MenuTypeEnum } from '@/enums/menuEnum';
  import { useOpenKeys } from './useOpenKeys';
  import { RouteLocationNormalizedLoaded, useRouter } from 'vue-router';
  import { isFunction } from '/@/utils/is';
  import { isFunction } from '@/utils/is';
  import { basicProps } from './props';
  import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  import { REDIRECT_NAME } from '/@/router/constant';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { getCurrentParentPath } from '/@/router/menus';
  import { listenerRouteChange } from '/@/logics/mitt/routeChange';
  import { getAllParentPath } from '/@/router/helper/menuHelper';
  import { useMenuSetting } from '@/hooks/setting/useMenuSetting';
  import { REDIRECT_NAME } from '@/router/constant';
  import { useDesign } from '@/hooks/web/useDesign';
  import { getCurrentParentPath } from '@/router/menus';
  import { listenerRouteChange } from '@/logics/mitt/routeChange';
  import { getAllParentPath } from '@/router/helper/menuHelper';
  export default defineComponent({
    name: 'BasicMenu',
    components: {
      Menu,
      BasicSubMenuItem,
    },
    props: basicProps,
    emits: ['menuClick'],
    setup(props, { emit }) {
      const isClickGo = ref(false);
      const currentActiveMenu = ref('');
  defineOptions({ name: 'BasicMenu' });
      const menuState = reactive<MenuState>({
        defaultSelectedKeys: [],
        openKeys: [],
        selectedKeys: [],
        collapsedOpenKeys: [],
      });
  const props = defineProps(basicProps);
      const { prefixCls } = useDesign('basic-menu');
      const { items, mode, accordion } = toRefs(props);
  const emit = defineEmits(['menuClick']);
      const { getCollapsed, getTopMenuAlign, getSplit } = useMenuSetting();
  const isClickGo = ref(false);
  const currentActiveMenu = ref('');
      const { currentRoute } = useRouter();
      const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys(
        menuState,
        items,
        mode as any,
        accordion,
      );
      const getIsTopMenu = computed(() => {
        const { type, mode } = props;
        return (
          (type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL) ||
          (props.isHorizontal && unref(getSplit))
        );
      });
      const getMenuClass = computed(() => {
        const align = props.isHorizontal && unref(getSplit) ? 'start' : unref(getTopMenuAlign);
        return [
          prefixCls,
          `justify-${align}`,
          {
            [`${prefixCls}__second`]: !props.isHorizontal && unref(getSplit),
            [`${prefixCls}__sidebar-hor`]: unref(getIsTopMenu),
          },
        ];
      });
      const getInlineCollapseOptions = computed(() => {
        const isInline = props.mode === MenuModeEnum.INLINE;
        const inlineCollapseOptions: { inlineCollapsed?: boolean } = {};
        if (isInline) {
          inlineCollapseOptions.inlineCollapsed = props.mixSider ? false : unref(getCollapsed);
        }
        return inlineCollapseOptions;
      });
      listenerRouteChange((route) => {
        if (route.name === REDIRECT_NAME) return;
        handleMenuChange(route);
        currentActiveMenu.value = route.meta?.currentActiveMenu as string;
        if (unref(currentActiveMenu)) {
          menuState.selectedKeys = [unref(currentActiveMenu)];
          setOpenKeys(unref(currentActiveMenu));
        }
      });
      !props.mixSider &&
        watch(
          () => props.items,
          () => {
            handleMenuChange();
          },
        );
      async function handleMenuClick({ key }: { key: string; keyPath: string[] }) {
        const { beforeClickFn } = props;
        if (beforeClickFn && isFunction(beforeClickFn)) {
          const flag = await beforeClickFn(key);
          if (!flag) return;
        }
        emit('menuClick', key);
        isClickGo.value = true;
        menuState.selectedKeys = [key];
      }
      async function handleMenuChange(route?: RouteLocationNormalizedLoaded) {
        if (unref(isClickGo)) {
          isClickGo.value = false;
          return;
        }
        const path =
          (route || unref(currentRoute)).meta?.currentActiveMenu ||
          (route || unref(currentRoute)).path;
        setOpenKeys(path);
        if (unref(currentActiveMenu)) return;
        if (props.isHorizontal && unref(getSplit)) {
          const parentPath = await getCurrentParentPath(path);
          menuState.selectedKeys = [parentPath];
        } else {
          const parentPaths = await getAllParentPath(props.items, path);
          menuState.selectedKeys = parentPaths;
        }
      }
      return {
        handleMenuClick,
        getInlineCollapseOptions,
        getMenuClass,
        handleOpenChange,
        getOpenKeys,
        ...toRefs(menuState),
      };
    },
  const menuState = reactive<MenuState>({
    defaultSelectedKeys: [],
    openKeys: [],
    selectedKeys: [],
    collapsedOpenKeys: [],
  });
  const { prefixCls } = useDesign('basic-menu');
  const { items, mode, accordion } = toRefs(props);
  const { getCollapsed, getTopMenuAlign, getSplit } = useMenuSetting();
  const { currentRoute } = useRouter();
  const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys(
    menuState,
    items,
    mode as any,
    accordion,
  );
  const getIsTopMenu = computed(() => {
    const { type, mode } = props;
    return (
      (type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL) ||
      (props.isHorizontal && unref(getSplit))
    );
  });
  const getMenuClass = computed(() => {
    const align = props.isHorizontal && unref(getSplit) ? 'start' : unref(getTopMenuAlign);
    return [
      prefixCls,
      `justify-${align}`,
      {
        [`${prefixCls}__second`]: !props.isHorizontal && unref(getSplit),
        [`${prefixCls}__sidebar-hor`]: unref(getIsTopMenu),
      },
    ];
  });
  const getInlineCollapseOptions = computed(() => {
    const isInline = props.mode === MenuModeEnum.INLINE;
    const inlineCollapseOptions: { inlineCollapsed?: boolean } = {};
    if (isInline) {
      inlineCollapseOptions.inlineCollapsed = props.mixSider ? false : unref(getCollapsed);
    }
    return inlineCollapseOptions;
  });
  listenerRouteChange((route) => {
    if (route.name === REDIRECT_NAME) return;
    handleMenuChange(route);
    currentActiveMenu.value = route.meta?.currentActiveMenu as string;
    if (unref(currentActiveMenu)) {
      menuState.selectedKeys = [unref(currentActiveMenu)];
      setOpenKeys(unref(currentActiveMenu));
    }
  });
  !props.mixSider &&
    watch(
      () => props.items,
      () => {
        handleMenuChange();
      },
    );
  const handleMenuClick: MenuProps['onClick'] = async ({ key }) => {
    const { beforeClickFn } = props;
    if (beforeClickFn && isFunction(beforeClickFn)) {
      const flag = await beforeClickFn(key);
      if (!flag) return;
    }
    emit('menuClick', key);
    isClickGo.value = true;
    menuState.selectedKeys = [key];
  };
  async function handleMenuChange(route?: RouteLocationNormalizedLoaded) {
    if (unref(isClickGo)) {
      isClickGo.value = false;
      return;
    }
    const path =
      (route || unref(currentRoute)).meta?.currentActiveMenu || (route || unref(currentRoute)).path;
    setOpenKeys(path);
    if (unref(currentActiveMenu)) return;
    if (props.isHorizontal && unref(getSplit)) {
      const parentPath = await getCurrentParentPath(path);
      menuState.selectedKeys = [parentPath];
    } else {
      const parentPaths = await getAllParentPath(props.items, path);
      menuState.selectedKeys = parentPaths;
    }
  }
</script>
<style lang="less">
  @import url('./index.less');