mirror of
				https://github.com/pure-admin/vue-pure-admin.git
				synced 2025-11-03 13:44:47 +08:00 
			
		
		
		
	feat: 菜单支持a标签右键的所有浏览器行为(在新标签页中、新窗口中打开链接,拖拽到新标签页打开等) (#936)
				
					
				
			* feat: 菜单支持a标签右键的所有浏览器行为(在新标签页中、新窗口中打开链接,拖拽到新标签页打开等) * feat: 修复添加a标签样式问题 * feat: 修复windows下双滚动条问题 * feat: 修复添加a标签样式问题
This commit is contained in:
		
							parent
							
								
									51809546ed
								
							
						
					
					
						commit
						2b71e8bd54
					
				
							
								
								
									
										36
									
								
								src/layout/components/sidebar/linkItem.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/layout/components/sidebar/linkItem.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { isUrl } from "@pureadmin/utils";
 | 
			
		||||
import { menuType } from "@/layout/types";
 | 
			
		||||
 | 
			
		||||
defineOptions({
 | 
			
		||||
  name: "LinkItem"
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
  to: menuType;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const isExternalLink = computed(() => isUrl(props.to.name));
 | 
			
		||||
const getLinkProps = (item: menuType) => {
 | 
			
		||||
  if (isExternalLink.value) {
 | 
			
		||||
    return {
 | 
			
		||||
      href: item.name,
 | 
			
		||||
      target: "_blank",
 | 
			
		||||
      rel: "noopener"
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
  return {
 | 
			
		||||
    to: item
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <component
 | 
			
		||||
    :is="isExternalLink ? 'a' : 'router-link'"
 | 
			
		||||
    v-bind="getLinkProps(to)"
 | 
			
		||||
  >
 | 
			
		||||
    <slot />
 | 
			
		||||
  </component>
 | 
			
		||||
</template>
 | 
			
		||||
@ -48,6 +48,7 @@ const { title, getLogo } = useNav();
 | 
			
		||||
    flex-wrap: nowrap;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    padding-left: 10px;
 | 
			
		||||
 | 
			
		||||
    img {
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
 | 
			
		||||
@ -1,19 +1,28 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { getConfig } from "@/config";
 | 
			
		||||
import LinkItem from "./linkItem.vue";
 | 
			
		||||
import { menuType } from "../../types";
 | 
			
		||||
import extraIcon from "./extraIcon.vue";
 | 
			
		||||
import { ReText } from "@/components/ReText";
 | 
			
		||||
import { useNav } from "@/layout/hooks/useNav";
 | 
			
		||||
import { transformI18n } from "@/plugins/i18n";
 | 
			
		||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
 | 
			
		||||
import { type CSSProperties, type PropType, computed, ref, toRaw } from "vue";
 | 
			
		||||
import {
 | 
			
		||||
  type PropType,
 | 
			
		||||
  type CSSProperties,
 | 
			
		||||
  ref,
 | 
			
		||||
  toRaw,
 | 
			
		||||
  computed,
 | 
			
		||||
  useAttrs
 | 
			
		||||
} from "vue";
 | 
			
		||||
 | 
			
		||||
import ArrowUp from "@iconify-icons/ep/arrow-up-bold";
 | 
			
		||||
import EpArrowDown from "@iconify-icons/ep/arrow-down-bold";
 | 
			
		||||
import ArrowLeft from "@iconify-icons/ep/arrow-left-bold";
 | 
			
		||||
import ArrowRight from "@iconify-icons/ep/arrow-right-bold";
 | 
			
		||||
 | 
			
		||||
const attrs = useAttrs();
 | 
			
		||||
const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
@ -32,6 +41,7 @@ const props = defineProps({
 | 
			
		||||
 | 
			
		||||
const getNoDropdownStyle = computed((): CSSProperties => {
 | 
			
		||||
  return {
 | 
			
		||||
    width: "100%",
 | 
			
		||||
    display: "flex",
 | 
			
		||||
    alignItems: "center"
 | 
			
		||||
  };
 | 
			
		||||
@ -96,61 +106,66 @@ function resolvePath(routePath) {
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <el-menu-item
 | 
			
		||||
  <link-item
 | 
			
		||||
    v-if="
 | 
			
		||||
      hasOneShowingChild(props.item.children, props.item) &&
 | 
			
		||||
      (!onlyOneChild.children || onlyOneChild.noShowingChildren)
 | 
			
		||||
    "
 | 
			
		||||
    :index="resolvePath(onlyOneChild.path)"
 | 
			
		||||
    :class="{ 'submenu-title-noDropdown': !isNest }"
 | 
			
		||||
    :style="getNoDropdownStyle"
 | 
			
		||||
    :to="item"
 | 
			
		||||
  >
 | 
			
		||||
    <div
 | 
			
		||||
      v-if="toRaw(props.item.meta.icon)"
 | 
			
		||||
      class="sub-menu-icon"
 | 
			
		||||
      :style="getSubMenuIconStyle"
 | 
			
		||||
    <el-menu-item
 | 
			
		||||
      :index="resolvePath(onlyOneChild.path)"
 | 
			
		||||
      :class="{ 'submenu-title-noDropdown': !isNest }"
 | 
			
		||||
      :style="getNoDropdownStyle"
 | 
			
		||||
      v-bind="attrs"
 | 
			
		||||
    >
 | 
			
		||||
      <component
 | 
			
		||||
        :is="
 | 
			
		||||
          useRenderIcon(
 | 
			
		||||
            toRaw(onlyOneChild.meta.icon) ||
 | 
			
		||||
              (props.item.meta && toRaw(props.item.meta.icon))
 | 
			
		||||
          )
 | 
			
		||||
        "
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
    <el-text
 | 
			
		||||
      v-if="
 | 
			
		||||
        (!props.item?.meta.icon &&
 | 
			
		||||
          isCollapse &&
 | 
			
		||||
          layout === 'vertical' &&
 | 
			
		||||
          props.item?.pathList?.length === 1) ||
 | 
			
		||||
        (!onlyOneChild.meta.icon &&
 | 
			
		||||
          isCollapse &&
 | 
			
		||||
          layout === 'mix' &&
 | 
			
		||||
          props.item?.pathList?.length === 2)
 | 
			
		||||
      "
 | 
			
		||||
      truncated
 | 
			
		||||
      class="!px-4 !text-inherit"
 | 
			
		||||
    >
 | 
			
		||||
      {{ transformI18n(onlyOneChild.meta.title) }}
 | 
			
		||||
    </el-text>
 | 
			
		||||
 | 
			
		||||
    <template #title>
 | 
			
		||||
      <div :style="getDivStyle">
 | 
			
		||||
        <ReText
 | 
			
		||||
          :tippyProps="{
 | 
			
		||||
            offset: [0, -10],
 | 
			
		||||
            theme: tooltipEffect
 | 
			
		||||
          }"
 | 
			
		||||
          class="!text-inherit"
 | 
			
		||||
        >
 | 
			
		||||
          {{ transformI18n(onlyOneChild.meta.title) }}
 | 
			
		||||
        </ReText>
 | 
			
		||||
        <extraIcon :extraIcon="onlyOneChild.meta.extraIcon" />
 | 
			
		||||
      <div
 | 
			
		||||
        v-if="toRaw(props.item.meta.icon)"
 | 
			
		||||
        class="sub-menu-icon"
 | 
			
		||||
        :style="getSubMenuIconStyle"
 | 
			
		||||
      >
 | 
			
		||||
        <component
 | 
			
		||||
          :is="
 | 
			
		||||
            useRenderIcon(
 | 
			
		||||
              toRaw(onlyOneChild.meta.icon) ||
 | 
			
		||||
                (props.item.meta && toRaw(props.item.meta.icon))
 | 
			
		||||
            )
 | 
			
		||||
          "
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
  </el-menu-item>
 | 
			
		||||
      <el-text
 | 
			
		||||
        v-if="
 | 
			
		||||
          (!props.item?.meta.icon &&
 | 
			
		||||
            isCollapse &&
 | 
			
		||||
            layout === 'vertical' &&
 | 
			
		||||
            props.item?.pathList?.length === 1) ||
 | 
			
		||||
          (!onlyOneChild.meta.icon &&
 | 
			
		||||
            isCollapse &&
 | 
			
		||||
            layout === 'mix' &&
 | 
			
		||||
            props.item?.pathList?.length === 2)
 | 
			
		||||
        "
 | 
			
		||||
        truncated
 | 
			
		||||
        class="!px-4 !text-inherit"
 | 
			
		||||
      >
 | 
			
		||||
        {{ transformI18n(onlyOneChild.meta.title) }}
 | 
			
		||||
      </el-text>
 | 
			
		||||
 | 
			
		||||
      <template #title>
 | 
			
		||||
        <div :style="getDivStyle">
 | 
			
		||||
          <ReText
 | 
			
		||||
            :tippyProps="{
 | 
			
		||||
              offset: [0, -10],
 | 
			
		||||
              theme: tooltipEffect
 | 
			
		||||
            }"
 | 
			
		||||
            class="!text-inherit"
 | 
			
		||||
          >
 | 
			
		||||
            {{ transformI18n(onlyOneChild.meta.title) }}
 | 
			
		||||
          </ReText>
 | 
			
		||||
          <extraIcon :extraIcon="onlyOneChild.meta.extraIcon" />
 | 
			
		||||
        </div>
 | 
			
		||||
      </template>
 | 
			
		||||
    </el-menu-item>
 | 
			
		||||
  </link-item>
 | 
			
		||||
  <el-sub-menu
 | 
			
		||||
    v-else
 | 
			
		||||
    ref="subMenu"
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,7 @@ export interface setType {
 | 
			
		||||
 | 
			
		||||
export type menuType = {
 | 
			
		||||
  id?: number;
 | 
			
		||||
  name?: string;
 | 
			
		||||
  path?: string;
 | 
			
		||||
  noShowingChildren?: boolean;
 | 
			
		||||
  children?: menuType[];
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,11 @@
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* 修复 windows 下双滚动条问题 https://github.com/pure-admin/vue-pure-admin/pull/936#issuecomment-1968125992 */
 | 
			
		||||
  .el-popper.pure-scrollbar {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* popper menu 超出内容区可滚动 */
 | 
			
		||||
  .pure-scrollbar {
 | 
			
		||||
    max-height: calc(100vh - calc(50px * 2.5));
 | 
			
		||||
@ -130,11 +135,9 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    a {
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      display: flex;
 | 
			
		||||
      flex-wrap: wrap;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      padding-left: 10px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .el-menu {
 | 
			
		||||
@ -331,9 +334,18 @@
 | 
			
		||||
      margin-top: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 无子菜单时激活 border-bottom */
 | 
			
		||||
    a > .is-active.submenu-title-noDropdown {
 | 
			
		||||
      border-bottom: 2px solid var(--el-menu-active-color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .el-menu--popup {
 | 
			
		||||
      background-color: $subMenuBg !important;
 | 
			
		||||
 | 
			
		||||
      a > .is-active.submenu-title-noDropdown {
 | 
			
		||||
        border-bottom: none;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .el-menu-item {
 | 
			
		||||
        color: $menuText;
 | 
			
		||||
        background-color: $subMenuBg;
 | 
			
		||||
@ -348,12 +360,6 @@
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 无子菜单时激活 border-bottom */
 | 
			
		||||
    .router-link-exact-active > .submenu-title-noDropdown {
 | 
			
		||||
      height: 60px;
 | 
			
		||||
      border-bottom: 2px solid var(--el-menu-active-color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 子菜单中还有子菜单 */
 | 
			
		||||
    .el-menu .el-sub-menu__title {
 | 
			
		||||
      min-width: $sideBarWidth !important;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user