Ben Lin
2024-07-06 44ef538691b8be0fd925ca80c49157bad14962e8
src/layouts/default/sider/MixSider.vue
@@ -31,13 +31,19 @@
          :key="item.path"
        >
          <SimpleMenuTag :item="item" collapseParent dot />
          <img
            v-if="item.img"
            :src="item.img"
            :class="[`${prefixCls}-module__icon`, getCollapsed ? 'w-16px h-16px' : 'w-20px h-20px']"
          />
          <Icon
            v-else
            :class="`${prefixCls}-module__icon`"
            :size="getCollapsed ? 16 : 20"
            :icon="item.icon || (item.meta && item.meta.icon)"
          />
          <p :class="`${prefixCls}-module__name`">
            {{ t(item.name) }}
            {{ t(item?.meta?.title || item.name) }}
          </p>
        </li>
      </ul>
@@ -77,270 +83,236 @@
    </div>
  </div>
</template>
<script lang="ts">
  import type { Menu } from '/@/router/types';
<script lang="ts" setup>
  import type { Menu } from '@/router/types';
  import type { CSSProperties } from 'vue';
  import { computed, defineComponent, onMounted, ref, unref, watch } from 'vue';
  import { computed, onMounted, ref, unref, watch } from 'vue';
  import type { RouteLocationNormalized } from 'vue-router';
  import { ScrollContainer } from '/@/components/Container';
  import { SimpleMenu, SimpleMenuTag } from '/@/components/SimpleMenu';
  import { ScrollContainer } from '@/components/Container';
  import { SimpleMenu } from '@/components/SimpleMenu';
  import Icon from '@/components/Icon/Icon.vue';
  import { AppLogo } from '/@/components/Application';
  import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  import { usePermissionStore } from '/@/store/modules/permission';
  import { AppLogo } from '@/components/Application';
  import { useMenuSetting } from '@/hooks/setting/useMenuSetting';
  import { usePermissionStore } from '@/store/modules/permission';
  import { useDragLine } from './useLayoutSider';
  import { useGlobSetting } from '/@/hooks/setting';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { useGo } from '/@/hooks/web/usePage';
  import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
  import clickOutside from '/@/directives/clickOutside';
  import { getChildrenMenus, getCurrentParentPath, getShallowMenus } from '/@/router/menus';
  import { listenerRouteChange } from '/@/logics/mitt/routeChange';
  import { useGlobSetting } from '@/hooks/setting';
  import { useDesign } from '@/hooks/web/useDesign';
  import { useI18n } from '@/hooks/web/useI18n';
  import { useGo } from '@/hooks/web/usePage';
  import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '@/enums/appEnum';
  import vClickOutside from '@/directives/clickOutside';
  import { getChildrenMenus, getCurrentParentPath, getShallowMenus } from '@/router/menus';
  import { listenerRouteChange } from '@/logics/mitt/routeChange';
  import LayoutTrigger from '../trigger/index.vue';
  import { createAsyncComponent } from '@/utils/factory/createAsyncComponent';
  export default defineComponent({
    name: 'LayoutMixSider',
    components: {
      ScrollContainer,
      AppLogo,
      SimpleMenu,
      Icon,
      LayoutTrigger,
      SimpleMenuTag,
    },
    directives: {
      clickOutside,
    },
    setup() {
      let menuModules = ref<Menu[]>([]);
      const activePath = ref('');
      const childrenMenus = ref<Menu[]>([]);
      const openMenu = ref(false);
      const dragBarRef = ref(null);
      const sideRef = ref(null);
      const currentRoute = ref<RouteLocationNormalized | null>(null);
  const SimpleMenuTag = createAsyncComponent(
    () => import('@/components/SimpleMenu/src/SimpleMenuTag.vue'),
  );
      const { prefixCls } = useDesign('layout-mix-sider');
      const go = useGo();
      const { t } = useI18n();
      const {
        getMenuWidth,
        getCanDrag,
        getCloseMixSidebarOnChange,
        getMenuTheme,
        getMixSideTrigger,
        getRealWidth,
        getMixSideFixed,
        mixSideHasChildren,
        setMenuSetting,
        getIsMixSidebar,
        getCollapsed,
      } = useMenuSetting();
  defineOptions({ name: 'LayoutMixSider' });
      const { title } = useGlobSetting();
      const permissionStore = usePermissionStore();
  let menuModules = ref<Menu[]>([]);
  const activePath = ref('');
  const childrenMenus = ref<Menu[]>([]);
  const openMenu = ref(false);
  const dragBarRef = ref(null);
  const sideRef = ref(null);
  const currentRoute = ref<RouteLocationNormalized | null>(null);
      useDragLine(sideRef, dragBarRef, true);
  const { prefixCls } = useDesign('layout-mix-sider');
  const go = useGo();
  const { t } = useI18n();
  const {
    getMenuWidth,
    getCanDrag,
    getCloseMixSidebarOnChange,
    getMenuTheme,
    getMixSideTrigger,
    getRealWidth,
    getMixSideFixed,
    mixSideHasChildren,
    setMenuSetting,
    getIsMixSidebar,
    getCollapsed,
  } = useMenuSetting();
      const getMenuStyle = computed((): CSSProperties => {
        return {
          width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0,
          left: `${unref(getMixSideWidth)}px`,
        };
      });
  const { title } = useGlobSetting();
  const permissionStore = usePermissionStore();
      const getIsFixed = computed(() => {
        /* eslint-disable-next-line */
        mixSideHasChildren.value = unref(childrenMenus).length > 0;
        const isFixed = unref(getMixSideFixed) && unref(mixSideHasChildren);
        if (isFixed) {
          /* eslint-disable-next-line */
          openMenu.value = true;
        }
        return isFixed;
      });
  useDragLine(sideRef, dragBarRef, true);
      const getMixSideWidth = computed(() => {
        return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH;
      });
      const getDomStyle = computed((): CSSProperties => {
        const fixedWidth = unref(getIsFixed) ? unref(getRealWidth) : 0;
        const width = `${unref(getMixSideWidth) + fixedWidth}px`;
        return getWrapCommonStyle(width);
      });
      const getWrapStyle = computed((): CSSProperties => {
        const width = `${unref(getMixSideWidth)}px`;
        return getWrapCommonStyle(width);
      });
      const getMenuEvents = computed(() => {
        return !unref(getMixSideFixed)
          ? {
              onMouseleave: () => {
                setActive(true);
                closeMenu();
              },
            }
          : {};
      });
      const getShowDragBar = computed(() => unref(getCanDrag));
      onMounted(async () => {
        menuModules.value = await getShallowMenus();
      });
      // Menu changes
      watch(
        [() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList],
        async () => {
          menuModules.value = await getShallowMenus();
        },
        {
          immediate: true,
        },
      );
      listenerRouteChange((route) => {
        currentRoute.value = route;
        setActive(true);
        if (unref(getCloseMixSidebarOnChange)) {
          closeMenu();
        }
      });
      function getWrapCommonStyle(width: string): CSSProperties {
        return {
          width,
          maxWidth: width,
          minWidth: width,
          flex: `0 0 ${width}`,
        };
      }
      // Process module menu click
      async function handleModuleClick(path: string, hover = false) {
        const children = await getChildrenMenus(path);
        if (unref(activePath) === path) {
          if (!hover) {
            if (!unref(openMenu)) {
              openMenu.value = true;
            } else {
              closeMenu();
            }
          } else {
            if (!unref(openMenu)) {
              openMenu.value = true;
            }
          }
          if (!unref(openMenu)) {
            setActive();
          }
        } else {
          openMenu.value = true;
          activePath.value = path;
        }
        if (!children || children.length === 0) {
          if (!hover) go(path);
          childrenMenus.value = [];
          closeMenu();
          return;
        }
        childrenMenus.value = children;
      }
      // Set the currently active menu and submenu
      async function setActive(setChildren = false) {
        const path = currentRoute.value?.path;
        if (!path) return;
        activePath.value = await getCurrentParentPath(path);
        // hanldeModuleClick(parentPath);
        if (unref(getIsMixSidebar)) {
          const activeMenu = unref(menuModules).find((item) => item.path === unref(activePath));
          const p = activeMenu?.path;
          if (p) {
            const children = await getChildrenMenus(p);
            if (setChildren) {
              childrenMenus.value = children;
              if (unref(getMixSideFixed)) {
                openMenu.value = children.length > 0;
              }
            }
            if (children.length === 0) {
              childrenMenus.value = [];
            }
          }
        }
      }
      function handleMenuClick(path: string) {
        go(path);
      }
      function handleClickOutside() {
        setActive(true);
        closeMenu();
      }
      function getItemEvents(item: Menu) {
        if (unref(getMixSideTrigger) === 'hover') {
          return {
            onMouseenter: () => handleModuleClick(item.path, true),
            onClick: async () => {
              const children = await getChildrenMenus(item.path);
              if (item.path && (!children || children.length === 0)) go(item.path);
            },
          };
        }
        return {
          onClick: () => handleModuleClick(item.path),
        };
      }
      function handleFixedMenu() {
        setMenuSetting({
          mixSideFixed: !unref(getIsFixed),
        });
      }
      // Close menu
      function closeMenu() {
        if (!unref(getIsFixed)) {
          openMenu.value = false;
        }
      }
      return {
        t,
        prefixCls,
        menuModules,
        handleModuleClick: handleModuleClick,
        activePath,
        childrenMenus: childrenMenus,
        getShowDragBar,
        handleMenuClick,
        getMenuStyle,
        handleClickOutside,
        sideRef,
        dragBarRef,
        title,
        openMenu,
        getMenuTheme,
        getItemEvents,
        getMenuEvents,
        getDomStyle,
        handleFixedMenu,
        getMixSideFixed,
        getWrapStyle,
        getCollapsed,
      };
    },
  const getMenuStyle = computed((): CSSProperties => {
    return {
      width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0,
      left: `${unref(getMixSideWidth)}px`,
    };
  });
  const getIsFixed = computed(() => {
    /* eslint-disable-next-line */
    mixSideHasChildren.value = unref(childrenMenus).length > 0;
    const isFixed = unref(getMixSideFixed) && unref(mixSideHasChildren);
    if (isFixed) {
      /* eslint-disable-next-line */
      openMenu.value = true;
    }
    return isFixed;
  });
  const getMixSideWidth = computed(() => {
    return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH;
  });
  const getDomStyle = computed((): CSSProperties => {
    const fixedWidth = unref(getIsFixed) ? unref(getRealWidth) : 0;
    const width = `${unref(getMixSideWidth) + fixedWidth}px`;
    return getWrapCommonStyle(width);
  });
  const getWrapStyle = computed((): CSSProperties => {
    const width = `${unref(getMixSideWidth)}px`;
    return getWrapCommonStyle(width);
  });
  const getMenuEvents = computed(() => {
    return !unref(getMixSideFixed)
      ? {
          onMouseleave: () => {
            setActive(true);
            closeMenu();
          },
        }
      : {};
  });
  const getShowDragBar = computed(() => unref(getCanDrag));
  onMounted(async () => {
    menuModules.value = await getShallowMenus();
  });
  // Menu changes
  watch(
    [() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList],
    async () => {
      menuModules.value = await getShallowMenus();
    },
    {
      immediate: true,
    },
  );
  listenerRouteChange((route) => {
    currentRoute.value = route;
    setActive(true);
    if (unref(getCloseMixSidebarOnChange)) {
      closeMenu();
    }
  });
  function getWrapCommonStyle(width: string): CSSProperties {
    return {
      width,
      maxWidth: width,
      minWidth: width,
      flex: `0 0 ${width}`,
    };
  }
  // Process module menu click
  async function handleModuleClick(path: string, hover = false) {
    const children = await getChildrenMenus(path);
    if (unref(activePath) === path) {
      if (!hover) {
        if (!unref(openMenu)) {
          openMenu.value = true;
        } else {
          closeMenu();
        }
      } else {
        if (!unref(openMenu)) {
          openMenu.value = true;
        }
      }
      if (!unref(openMenu)) {
        setActive();
      }
    } else {
      openMenu.value = true;
      activePath.value = path;
    }
    if (!children || children.length === 0) {
      if (!hover) go(path);
      childrenMenus.value = [];
      closeMenu();
      return;
    }
    childrenMenus.value = children;
  }
  // Set the currently active menu and submenu
  async function setActive(setChildren = false) {
    const path = currentRoute.value?.path;
    if (!path) return;
    activePath.value = await getCurrentParentPath(path);
    // hanldeModuleClick(parentPath);
    if (unref(getIsMixSidebar)) {
      const activeMenu = unref(menuModules).find((item) => item.path === unref(activePath));
      const p = activeMenu?.path;
      if (p) {
        const children = await getChildrenMenus(p);
        if (setChildren) {
          childrenMenus.value = children;
          if (unref(getMixSideFixed)) {
            openMenu.value = children.length > 0;
          }
        }
        if (children.length === 0) {
          childrenMenus.value = [];
        }
      }
    }
  }
  function handleMenuClick(path: string) {
    go(path);
  }
  function handleClickOutside() {
    setActive(true);
    closeMenu();
  }
  function getItemEvents(item: Menu) {
    if (unref(getMixSideTrigger) === 'hover') {
      return {
        onMouseenter: () => handleModuleClick(item.path, true),
        onClick: async () => {
          const children = await getChildrenMenus(item.path);
          if (item.path && (!children || children.length === 0)) go(item.path);
        },
      };
    }
    return {
      onClick: () => handleModuleClick(item.path),
    };
  }
  function handleFixedMenu() {
    setMenuSetting({
      mixSideFixed: !unref(getIsFixed),
    });
  }
  // Close menu
  function closeMenu() {
    if (!unref(getIsFixed)) {
      openMenu.value = false;
    }
  }
</script>
<style lang="less">
  @prefix-cls: ~'@{namespace}-layout-mix-sider';
@@ -393,6 +365,10 @@
            background-color: unset;
            color: @primary-color;
          }
          &:not(&--active):hover {
            background-color: rgb(0 0 0 / 6%);
          }
        }
      }
      .@{prefix-cls}-menu-list {
@@ -415,9 +391,9 @@
    &.dark {
      &.open {
        .@{prefix-cls}-logo {
          // border-bottom: 1px solid @border-color;
        }
        // .@{prefix-cls}-logo {
        //   border-bottom: 1px solid @border-color;
        // }
        > .scrollbar {
          border-right: 1px solid @border-color;