mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-06-06 00:18:51 +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;
|
flex-wrap: nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -1,19 +1,28 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from "@/config";
|
||||||
|
import LinkItem from "./linkItem.vue";
|
||||||
import { menuType } from "../../types";
|
import { menuType } from "../../types";
|
||||||
import extraIcon from "./extraIcon.vue";
|
import extraIcon from "./extraIcon.vue";
|
||||||
import { ReText } from "@/components/ReText";
|
import { ReText } from "@/components/ReText";
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
import { transformI18n } from "@/plugins/i18n";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
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 ArrowUp from "@iconify-icons/ep/arrow-up-bold";
|
||||||
import EpArrowDown from "@iconify-icons/ep/arrow-down-bold";
|
import EpArrowDown from "@iconify-icons/ep/arrow-down-bold";
|
||||||
import ArrowLeft from "@iconify-icons/ep/arrow-left-bold";
|
import ArrowLeft from "@iconify-icons/ep/arrow-left-bold";
|
||||||
import ArrowRight from "@iconify-icons/ep/arrow-right-bold";
|
import ArrowRight from "@iconify-icons/ep/arrow-right-bold";
|
||||||
|
|
||||||
|
const attrs = useAttrs();
|
||||||
const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
|
const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -32,6 +41,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const getNoDropdownStyle = computed((): CSSProperties => {
|
const getNoDropdownStyle = computed((): CSSProperties => {
|
||||||
return {
|
return {
|
||||||
|
width: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center"
|
alignItems: "center"
|
||||||
};
|
};
|
||||||
@ -96,61 +106,66 @@ function resolvePath(routePath) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-menu-item
|
<link-item
|
||||||
v-if="
|
v-if="
|
||||||
hasOneShowingChild(props.item.children, props.item) &&
|
hasOneShowingChild(props.item.children, props.item) &&
|
||||||
(!onlyOneChild.children || onlyOneChild.noShowingChildren)
|
(!onlyOneChild.children || onlyOneChild.noShowingChildren)
|
||||||
"
|
"
|
||||||
:index="resolvePath(onlyOneChild.path)"
|
:to="item"
|
||||||
:class="{ 'submenu-title-noDropdown': !isNest }"
|
|
||||||
:style="getNoDropdownStyle"
|
|
||||||
>
|
>
|
||||||
<div
|
<el-menu-item
|
||||||
v-if="toRaw(props.item.meta.icon)"
|
:index="resolvePath(onlyOneChild.path)"
|
||||||
class="sub-menu-icon"
|
:class="{ 'submenu-title-noDropdown': !isNest }"
|
||||||
:style="getSubMenuIconStyle"
|
:style="getNoDropdownStyle"
|
||||||
|
v-bind="attrs"
|
||||||
>
|
>
|
||||||
<component
|
<div
|
||||||
:is="
|
v-if="toRaw(props.item.meta.icon)"
|
||||||
useRenderIcon(
|
class="sub-menu-icon"
|
||||||
toRaw(onlyOneChild.meta.icon) ||
|
:style="getSubMenuIconStyle"
|
||||||
(props.item.meta && toRaw(props.item.meta.icon))
|
>
|
||||||
)
|
<component
|
||||||
"
|
:is="
|
||||||
/>
|
useRenderIcon(
|
||||||
</div>
|
toRaw(onlyOneChild.meta.icon) ||
|
||||||
<el-text
|
(props.item.meta && toRaw(props.item.meta.icon))
|
||||||
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>
|
</div>
|
||||||
</template>
|
<el-text
|
||||||
</el-menu-item>
|
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
|
<el-sub-menu
|
||||||
v-else
|
v-else
|
||||||
ref="subMenu"
|
ref="subMenu"
|
||||||
|
@ -62,6 +62,7 @@ export interface setType {
|
|||||||
|
|
||||||
export type menuType = {
|
export type menuType = {
|
||||||
id?: number;
|
id?: number;
|
||||||
|
name?: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
noShowingChildren?: boolean;
|
noShowingChildren?: boolean;
|
||||||
children?: menuType[];
|
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 超出内容区可滚动 */
|
/* popper menu 超出内容区可滚动 */
|
||||||
.pure-scrollbar {
|
.pure-scrollbar {
|
||||||
max-height: calc(100vh - calc(50px * 2.5));
|
max-height: calc(100vh - calc(50px * 2.5));
|
||||||
@ -130,11 +135,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: inline-block;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-left: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu {
|
.el-menu {
|
||||||
@ -331,9 +334,18 @@
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 无子菜单时激活 border-bottom */
|
||||||
|
a > .is-active.submenu-title-noDropdown {
|
||||||
|
border-bottom: 2px solid var(--el-menu-active-color);
|
||||||
|
}
|
||||||
|
|
||||||
.el-menu--popup {
|
.el-menu--popup {
|
||||||
background-color: $subMenuBg !important;
|
background-color: $subMenuBg !important;
|
||||||
|
|
||||||
|
a > .is-active.submenu-title-noDropdown {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
.el-menu-item {
|
.el-menu-item {
|
||||||
color: $menuText;
|
color: $menuText;
|
||||||
background-color: $subMenuBg;
|
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 {
|
.el-menu .el-sub-menu__title {
|
||||||
min-width: $sideBarWidth !important;
|
min-width: $sideBarWidth !important;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user