在后台管理系统页面中一般都会有导航栏标签这一功能,它可以让我们点击过的菜单以 tab 标签栏的形式展现出来,同时右键标签可以展示刷新页面
,关闭当前
,关闭其它
,全部关闭
选项,如下图所示
本篇文章将介绍导航栏标签的具体实现
新增标签
elementplus 为我们提供了 Tag 组件,我们可以直接使用
首先在全局状态管理器store/index.ts
中定义一个 state:navTags 用于存放点击过的标签数据,这些数据包含的字段包括菜单名name
以及跳转路由path
,同时里面默认添加一个首页的标签。还需要定义一个添加标签的函数放到 actions 中
当点击菜单的时候需要在 navTags 新增一条标签数据,因此可以在路由守卫中调用 addTags 函数
js
//router/index.ts
router.beforeEach(async (to, from, next) => {
//.....
if (homeStore.menuList.length) {
//....
homeStore.addTags({ name: to.name as string, path: to.path });
next();
return;
}
....
next({ ...to, replace: true });
});
export default router;
然后在 navbar 中使用 tag 组件,同时添加一些样式
这里有一个属性 effect,可以设置标签主题,当当前标签的 path 和当前页面路由 path 一样的话就给它设置为dark
主题。获取当前页面的 path 其实很简单,在navbar.vue
中监听路由变化即可
此时点击菜单的时候我们已经可以将标签一个个添加上去了,此时我们还需要让标签被点击然后跳转到对应页面
js
const handelTo = (item: any) => {
router.push(item.path);
};
删除标签
Tag 组件可以设置 closable 定义一个标签是否可移除,同时提供一个点击 x 的 close 事件,我们可以在这个事件中删除当前标签在navTags
中的数据,这里我们还需要判断删除的是不是当前页面,如果是的话则还需要跳转到最后一个标签选项
js
<el-tag
v-for="(item, index) in homeStore.navTags"
@click="handelTo(item)"
:key="item.name"
class="ml-2 cursor-pointer flex-shrink-0"
:effect="currentPath === item.path ? 'dark' : ''"
type="primary"
:closable="item.path != '/'"
@close="handleClose(index, item.path)"
>
{{ item.name }}
</el-tag>
js
const handleClose = (index: number, path: string) => {
homeStore.navTags.splice(index, 1);
//判断是否为当前页面,如果是则跳转至最后一个标签页
if (path === currentPath.value) {
const length = homeStore.navTags.length;
length && router.push(homeStore.navTags[length - 1].path);
}
};
到这里导航标签功能基本完成,接下来来实现点击鼠标右键出现刷新页面
,关闭当前
等选项功能
右键列表功能
在 src/component 中新增一个 tagsview 组件用于实现右键列表功能
js
<template>
<div class="bg-white shadow-lg rounded-md text-[14px] overflow-hidden cursor-pointer">
<div class="p-1 hover:bg-[#E0E0E0] flex items-center" @click="refresh">
<el-icon>
<Refresh />
</el-icon><span>刷新页面</span>
</div>
<div class="p-1 hover:bg-[#E0E0E0] flex items-center" @click="emits('closeCur')">
<el-icon>
<CloseBold />
</el-icon><span>关闭当前</span>
</div>
<div class="p-1 hover:bg-[#E0E0E0] flex items-center" @click="closeOtherTags">
<el-icon>
<Close />
</el-icon><span>关闭其它</span>
</div>
<div class="p-1 hover:bg-[#E0E0E0] flex items-center" @click="closeAllTags">
<el-icon>
<Close />
</el-icon><span>全部关闭</span>
</div>
</div>
</template>
<script lang="ts" setup>
import home from ".././../store";
import { useRouter } from "vue-router";
const router = useRouter();
const homeStore = home();
type Props = {
tag: {
path: string;
name: string;
};
};
const props = defineProps<Props>();
type Emits = {
(e: "closeCur"): void;
(e: "closeTagView"): void;
};
const emits = defineEmits<Emits>();
//刷新页面
const refresh = () => {
window.location.reload();
};
//关闭其它
const closeOtherTags = () => {
homeStore.$patch({
navTags: [{ name: "首页", path: "/" }, props.tag],
});
router.push(props.tag.path);
emits("closeTagView");
};
//关闭所有
const closeAllTags = () => {
homeStore.$patch({
navTags: [{ name: "首页", path: "/" }],
});
router.push("/");
emits("closeTagView");
};
</script>
vue 中鼠标右击事件可以使用@contextmenu
,同时我们需要知道当前点击的位置,然后根据当前位置定位,所以可以将事件参数$event
传入,并且要将当前点击的标签参数传入
js
<el-tag
@contextmenu.prevent="openMenu($event, item.path)"
v-for="(item, index) in homeStore.navTags"
@click="handelTo(item)"
:key="item.name"
class="ml-2 cursor-pointer flex-shrink-0"
:effect="currentPath === item.path ? 'dark' : ''"
type="primary"
:closable="item.path != '/'"
@close="handleClose(index, item.path)"
>
{{ item.name }}
<teleport to="body">
<tagsview v-if="isTagView && currentTagPath === item.path" @closeTagView="isTagView = false" :tag="item"
class="w-[100px] fixed" :style="tagViewStyle" @closeCur="handleClose(index, item.path)" />
</teleport>
</el-tag>
这里我们可以通过$event
中的clientX
和clientY
获取到当前点击位置距离浏览器窗口的坐标,即固定定位(fixed),刚好可以将列表定位到这里。 同时添加一个document
的点击事件用于关闭列表
js
const listener = () => {
isTagView.value = false;
document.removeEventListener("click", listener);
};
const tagViewStyle = ref({});
const openMenu = (e: any, path: string) => {
isTagView.value = true;
document.addEventListener("click", listener);
tagViewStyle.value = {
left: e.clientX + "px",
top: e.clientY + "px",
};
};
到这里简单的右键列表功能就已经实现了