动态路由和tab页切换路由
一、在当前页面监听路由的改变
应用场景:我们在需要进入这个页面前判断,上一个页面是不是我们允许的页面,不是的话不能进入;或者离开这个页面的时候判断,进入的是不是允许的页面,不是则拦截。
以下三个函数与created是同级
1、beforeRouterEnter 进入前拦截路由
beforeRouteEnter(to,from, next) {
console.log(to)
console.log(from)
console.log(next)
if (from.name !== 'A') {
// vm 就是当前组件的实例相当于上面的 this,所以在 next 方法里你就可以把 vm 当 this 来用了。
next(vm => vm.$router.push({ name: 'B'}))
}
next()
},
2、beforeRouterUpdate 路由更新时拦截
在当前路由改变,但是该组件被复用时调用,
对于一个带有动态参数的路径 /good/:id,在/good/1和/good/2之间跳转的时候,
由于会渲染同样的good组件,因此组件实例会被复用,而这个钩子就会在这个情况下被调用
beforeRouteUpdate(to, from,next) {
// 可以访问组件实例 `this`
console.log(from, 'from');
if(from.name == 'additional') {
this.$refs.formPad.resetFields();
}
next();
},
3、beforeRouterLeave 离开该路由时调用
beforeRouteLeave(to,from, next) {
// 可以访问组件实例 `this`
console.log(this, 'beforeRouteLeave'); //当前组件实例
console.log(to, '组件独享守卫beforeRouteLeave第一个参数');
console.log(from, '组件独享守卫beforeRouteLeave第二个参数');
next();
}
二、动态路由
动态路由配置,在系统里添加组件,保存到数据库,在前端不写死路由,动态生成,如果有一个组件位置更改了,直接在系统里进行更改,不用再从router文件中更改。
1、在系统中填入组件信息
一级路由:Layout
二级路由:AppMain
三级路由:对应组件位置
2、在permission.js路由拦截器中处理路由
3、filterAsyncRouter处理后台返回的路由数据
这里的path我是直接取的name名,如果想要更完美,可以直接写好路由相应的属性,后期不用更改
三、tab页动态路由切换
思路:
1.将打开的所有路由放到一个栈里(tabRouterList:[]),tabs显示遍历tabRouterList,
2.初始状态,将首页推入栈,并设置激活状态
3.当切换路由时(监听路由变化),判断栈里是否存在这个路由
若存在,只改变激活状态;若不存在,则推入栈,并改变激活状态
4.tabs切换,调用@tab-click="tabClick"方法,跳转路由(路有变化,走上一步“若存在,只改变激活状态”)
5.tabs移除,调用@tab-remove="tabRemove"方法,移除栈(tabRouterList)中对应的路由,若移除的路由是激活状态,那么路由跳转到栈中最后一个(路有变化),若移除的路由非激活状态,不做修改
6.tab点击展示下拉菜单功能
1、tab页
tabRouter页面
<template>
<div class="tabNav">
<el-tabs
v-model="activeIndex"
type="card"
closable
v-if="tabList.length"
@tab-click='tabClick'
@tab-remove='tabRemove'
@contextmenu.prevent.native="tabRightClick($event)"
>
<el-tab-pane
:key="Math.random()"
v-for="(item, index) in tabList"
:label="item.title"
:name="item.path"
>
</el-tab-pane>
</el-tabs>
<div v-show="dropdownShow">
<ul :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<li><el-button type="text" @click="curTabReload()" size="mini">重新加载</el-button></li>
<li><el-button type="text" @click="closeAllTabs()" size="mini">关闭所有</el-button></li>
<li><el-button type="text" @click="closeOtherTabs()" size="mini">关闭其他</el-button></li>
</ul>
</div>
</div>
</template>
export default {
computed: {
tabList () {
return this.$store.state.tabRouterPath.tabRouterList
},
activeIndex:{
get () {
return this.$store.state.tabRouterPath.activeIndex
},
set (val) {
this.$store.commit('setActiveIndex', val);
}
}
},
}
2、准备状态管理(tabRouterPath.js)
import router from '@/router'
const app = {
state: {
curContextTabId:'',//右键点击tab的id
tabRouterList:[],//初始化tab路由列表
activeIndex:'/home'//激活状态
},
getters: {
},
mutations: {
// 添加tabs路由
setTabRouter(state, data) {
state.tabRouterList.push(data)
},
// 删除tabs路由
delTabRouter(state, data) {
let index = 0
for(let option of state.tabRouterList) {
if(option.path == data) {
break
}
index++
}
state.tabRouterList.splice(index,1)
// 删完路由后也要保存到session中
sessionStorage.setItem('tabRouterList', JSON.stringify(state.tabRouterList))
},
// 设置当前激活的tabs
setActiveIndex(state,index) {
state.activeIndex = index
},
}
}
export default app
3、在Layout页面初始化路由状态
mounted(){
// 设置激活状态保留所有路由,则可以在监听路由的时候把路由保存在sessinStorage里
// 在home页初始路由状态
// 刷新时以当前路由做为tab加入tabs
// 当前路由不是首页时,则在session中取出路由列表渲染,并设置激活状态
// 当前路由是首页时,判断是第一次进入页面还是后期刷新,如果session中没有数据就添加首页进去,如果有就全部渲染
// 注意:在点击路由时要把tabRouterList保存到session中,删除也是如此
let tabList = JSON.parse(sessionStorage.getItem('tabRouterList'))
if(this.$route.path !== '/' && this.$route.path !== '/home') {
tabList.forEach(item => {
this.$store.commit('setTabRouter',{path:item.path,name:item.name,title: item.title})
})
this.$store.commit('setActiveIndex',this.$route.path)
} else {
this.$store.state.tabRouterPath.tabRouterList = []
if(tabList){
tabList.forEach(item => {
this.$store.commit('setTabRouter',{path:item.path,name:item.name,title: item.title})
})
} else {
this.$store.commit('setTabRouter',{path:'/home',name:'home',title: '首页'})
sessionStorage.setItem('tabRouterList', JSON.stringify(this.$store.state.tabRouterPath.tabRouterList))
}
this.$store.commit('setActiveIndex','/home')
this.$router.push('/home')
}
},
4、监听路由状态
watch: {
// 监听路由变化
'$route'(to,from) {
// 判断路由是否已经打开
// 已经打开的,将其设置为active
// 未打开的,将其放入队列里
let flag = false
for(let item of this.tabList) {
if(item.name == to.name) {
this.$store.commit('setActiveIndex',to.path)
flag = true
break
}
}
// 如果要跳转的路由是选择机构页面,则不存路由
if(to.path == '/mechanism') {
flag = true
}
// 如果页面是隐藏状态,也不添加到路由列表中(比如:添加 修改 详情页面)
if(to.meta.hidden == 1) {
flag = true
}
if(!flag) {
this.$store.commit('setTabRouter',{path:to.path, name:to.name, title:to.meta.title})
this.$store.commit('setActiveIndex',to.path)
// 将路由保存在sessionStorage中
sessionStorage.setItem('tabRouterList', JSON.stringify(this.$store.state.tabRouterPath.tabRouterList))
}
},
5、tab方法
// tab标签点击时,切换相应的路由
tabClick(tab){
this.$router.push({path:this.activeIndex})
},
// 移除tab标签
tabRemove(targetName) {
// 首页不删
if(targetName == '/home') {
return
}
this.$store.commit('delTabRouter',targetName)
if(this.activeIndex == targetName) {
// 设置当前激活的路由
if(this.tabList && this.tabList.length >=1 ){
this.$store.commit('setActiveIndex',this.tabList[this.tabList.length-1].path)
this.$router.push({path: this.activeIndex})
} else {
this.$router.push({path: '/'})
this.$store.state.tabRouterPath.activeIndex = '/home';
this.$store.commit('setTabRouter',{path:'/home',name:'home',title:'首页'})
}
}
},
6、tab下拉菜单方法
①.tab右键单击事件
// tab右键单击事件
tabRightClick(e){
e.preventDefault(); //防止默认菜单弹出
if (e.srcElement.id) {
let currentContextTabId = e.srcElement.id.split("-")[1];
this.dropdownShow = true;
this.left = e.clientX;
this.top = e.clientY;
}
},
②.关闭下拉菜单
// 关闭下拉菜单
closeDropMenu() {
this.dropdownShow = false;
},
③.下拉菜单关闭点击事件
// 重新加载
curTabReload(){
location.reload()
},
// 关闭所有标签页
closeAllTabs() {
this.$store.commit("closeAllTabs");
this.dropdownShow = false;
},
// 关闭其它标签页
closeOtherTabs() {
this.$store.commit("closeOtherTabs");
this.dropdownShow = false;
},
④.vuex
// 保存右键点击tab的id
saveCurContextTabId(state, curContextTabId) {
state.curContextTabId = curContextTabId
},
// 关闭所有标签
closeAllTabs(state) {
state.tabRouterList = [];
this.commit('setTabRouter',{path:'/home',name:'home',title:'首页'})
sessionStorage.setItem('tabRouterList', JSON.stringify(state.tabRouterList))
this.commit("activeIndex", "/home")
router.push("/home")
},
// 关闭其它标签页
closeOtherTabs(state) {
let tabs = state.tabRouterList;
let activeIndex = state.activeIndex
console.log(activeIndex)
let curId // 左键点击时的tab在整个tabs数组中的id
tabs.forEach((tab, index) => {
if (tab.path == activeIndex) {
curId = index
}
})
if(activeIndex == '/home') {
state.tabRouterList = [state.tabRouterList[curId]]
} else {
state.tabRouterList = [{path:'/home',name:'home',title:'首页'},state.tabRouterList[curId]]
}
sessionStorage.setItem('tabRouterList', JSON.stringify(state.tabRouterList))
}
⑤.watch监听下拉菜单
关闭右键菜单,有时候打开右键菜单没有进行其它操作,右键菜单一直显示
dropdownShow(value) {
if (this.dropdownShow) {
document.body.addEventListener("click", this.closeDropMenu);
} else {
document.body.removeEventListener("click", this.closeDropMenu);
}
}
⑥.右键显示的菜单样式
.contextmenu {
width: 100px;
margin: 0;
border: 1px solid #ccc;
background: #fff;
z-index: 3000;
position: fixed;
list-style-type: none;
padding: 5px 0;
border-radius: 4px;
font-size: 14px;
color: #4b46a7;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.2);
}
.contextmenu li {
margin: 0;
padding: 0px 22px;
}
.contextmenu li:hover {
background: #ebeef5;
button{
color: #4b46a7;
}
cursor: pointer;
}
.contextmenu li button{
color: #2c3e50;
}