背景:多个页面一起使用的时候常常会用到标签页功能,但如ElementPlus框架只提供了标签页功能可是不能结合VueRouter路由来实现链接直接打开标签,下面介绍一下如何使用链接来打开标签和根据链接切换当前标签。
先看看效果:
一. 准备工作
1. 新建一个Vue3项目
2. 安装Element Plus (npm install element-plus)
3. 安装Vue Router (npm install vue-router@4)
二. 添加路由配置文件
我这里将配置文件命名为/configs/route.config.js
import { defineAsyncComponent } from 'vue' //对于标签页用到的页面可以按需进行引入
export default [{
path: '/',
name: '标签页测试项目',
//标签页引用的显示模版
component: import('@/components/template/DefaultTemplate.vue'),
meta: {
//缓存标签页里的组件
keepAlive: true,
//导航中不显示此链接
hidden: true
}
}, {
path: '/product',
name: '产品管理',
redirect: '/product/index',
children: [{
path: '/product/index',
name: '产品',
component: defineAsyncComponent(() => import('@/pages/product/index.vue')),
//归属那个标签页模版
meta: { tagsPath: '/' }
},
{
path: '/product/type',
name: '产品分类',
component: defineAsyncComponent(() => import('@/pages/product/type.vue')),
//归属那个标签页模版
meta: { tagsPath: '/' }
}]
}, {
path: '/login',
name: '用户登陆',
component: import('@/pages/user/login.vue'),
meta: {
hidden: true
}
}]
三. App.vue文件
<script></script>
<template>
<!-- 如果keepAlive则使用keep-alive标签 -->
<router-view v-slot="{ Component }" v-if="$route.meta.keepAlive">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
<router-view v-else></router-view>
</template>
<style scoped></style>
四. main.js文件,这里要注意,因为整个应用可能不止只有标签页所以需要在全局router.beforeEach钩子上处理非标签页的和标签页的跳转,标签页跳转需要带上标签页唯一的链接地址作为参数。
import { createApp } from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router'
import routeConfig from '@/configs/route.config.js'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
const router = createRouter({
history: createWebHashHistory(),
routes: routeConfig
});
//绑定路由钩子处理路由事件
router.beforeEach((to, from, next) => {
//非标签页子页面,正常导航
if(!to.meta.tagsPath) {
next()
} else {
//标签页子页面,导航到标签页
const query = Object.assign({}, to.query);
//标签子页面的地址作为参数传入标签页
query.path = to.path;
next({
//导航到标签页
path: to.meta.tagsPath,
query
})
}
})
app.use(router)
app.mount('#app')
五. 标签页面
我这里将标签页面文件命名为/components/template/DefaultTemplate.vue,主要保护导航和标签(Tabs)两个大模块,关键点在于beforeRouterUpdate路由信息更新时,需要处理好标签的打开状态。
<script>
export default {
data: () => {
return {
appName: "测试标签页面",
routes: [],
//当前打开的导航
currentPath: '',
//当前打开的标签
currentTagName: '',
tabs: []
};
},
mounted() {
//初始化导航信息
this.initRoutes();
//获取实践路由导航页面,打开标签
const path = this.$route.query.path;
this.addTag(path);
},
beforeRouteUpdate(to, from) {
//当停留在标签页且路由更新时,打开标签
this.addTag(to.query.path);
},
methods: {
initRoutes() {
const menuRoutes = [];
this.$router.getRoutes().forEach(route => {
if(!route.meta || !route.meta.hidden) {
const menuRoute = {
name: route.name,
path: route.path,
children: []
}
menuRoutes.push(menuRoute);
route.children && route.children.forEach(child => {
const index = menuRoutes.findIndex(x => x.path == child.path);
//删除子导航条目
if(index > -1)
menuRoutes.splice(index, 1);
menuRoute.children.push({
name: child.name,
path: child.path,
component: child.component
})
});
}
});
this.routes = menuRoutes;
},
onTagRemove(targetName) {
const index = this.tabs.findIndex(x => x.path == targetName);
this.tabs.splice(index, 1);
this.currentTagName = this.tabs[0].path;
this.currentPath = this.tabs[0].path;
//处理路由,跳转到第一个标签的路由
this.$router.replace(this.currentPath)
},
//打开标签
addTag(path) {
//找到符合当前导航路径的路由信息
const route = this.findRouteByPath(this.routes, path);
const exists = this.tabs.find(x => x.path == route.path);
//已打开标签不重复打开
if(!exists) {
this.tabs.push(route);
}
this.currentTagName = route.path;
this.currentPath = route.path;
},
findRouteByPath(routes, path) {
for (let i = 0; i < routes.length; i++) {
const route = routes[i];
if(route.path == path)
return route;
if(route.children) {
const childRoute = this.findRouteByPath(route.children, path);
if(childRoute)
return childRoute;
}
}
}
}
}
</script>
<template>
<el-container>
<el-header>
<el-menu
:default-active="currentPath"
class="el-menu-demo"
mode="horizontal"
:ellipsis="false"
:router="true">
<h1>{{ appName }}</h1>
<el-sub-menu
v-for="route in routes"
:key="route.path"
:index="route.path">
<template #title>{{route.name}}</template>
<el-menu-item
v-for="child in route.children"
:key="child.path"
:index="child.path">{{child.name}}</el-menu-item>
</el-sub-menu>
</el-menu>
</el-header>
<el-main>
<el-tabs
type="border-card"
v-model="currentTagName"
:closable="tabs.length > 1"
@tab-remove="onTagRemove">
<el-tab-pane
v-for="tab in tabs"
:key="tab.path"
:label="tab.name"
:name="tab.path">
<component :is="tab.component"></component>
</el-tab-pane>
</el-tabs>
</el-main>
<el-footer></el-footer>
</el-container>
</template>
添加上面代码后,标签页就可以实现效果图一样的功能效果了。