前言:需要做的效果如下图:
用到的技术栈:vue、vuex、vue-router。主要的vue-router的路由守卫。
一、首先,看一下vue-router的路由守卫
注意:这三个方法是依次调用的(有点类似于生命周期有执行顺序一样)
路由守卫在刚进入页面时,是不会执行的
回调函数中的三个参数:
二、代码实现
1. 新建 tagView.js 保存显示的Tab标签的路由信息 ==> 实现三个方法,新增、删除和清空
export default {
namespaced: true,
state: {
allVisitedTags: [ ],
},
mutations: {
// 添加一个tab标签
addVisitedTags (state, newData) {
const newArr = state.allVisitedTags;
const item_index = newArr.findIndex(e => e.path == newData.path);
if(item_index == -1){ //如果没有,直接添加
if(newArr.length <= 10){ //如果在10条tab之内,直接添加
newArr.push(newData);
state.allVisitedTags = newArr;
}
else{ //如果大于10条tab,则删除前面的,然后再添加
newArr.splice(0, 1);
newArr.push(newData);
state.allVisitedTags = newArr;
}
}else{ //如果有,则替换
newArr.splice(item_index, 1, newData);
}
},
// 删除一个tab标签
delVisitedTags (state, newData) {
const newArr = state.allVisitedTags;
const item_index = newArr.findIndex(e => e.path == newData.path);
newArr.splice(item_index, 1);
state.allVisitedTags = newArr;
},
// 清空所有tab标签
clearVisitedTags (state, newData) {
const newArr = [];
state.allVisitedTags = newArr;
},
}
}
2. 将 tagView 引入并放到vuex中
3. 在App.vue 或者 main.vue中(看自己项目而定,主要是放在一个全局的位置)
created() {
// 刚进来的页面
this.dealRoute(this.$route)
// 路由守卫,每次跳转页面时
this.$router.beforeEach((to, from, next) => {
//有menuId,表示为一级菜单,即直接配置的菜单,切换时需要清空tab标签 并且点击的不是当前页面的一级菜单
if(to.meta.menuId && to.meta.menuId !== this.allTagsArr[0].meta.menuId){
this.$store.commit('tagView/clearVisitedTags')
this.dealRoute(to)
}else{ //没有menuId,表示为二级有了
this.dealRoute(to)
}
next();
});
},
3.1 dealRoute 函数的实现
dealRoute(itemObj){
const cutOutIndex = itemObj.path.indexOf("{"); //正常进入是一个字符串的 { ==> 不是对象的情况不用管
const cutOutIndex_02 = itemObj.path.indexOf("%"); //再一次进入就变成了转义字符 ==> 不是对象的情况不用管
const index_finally = cutOutIndex > cutOutIndex_02 ? cutOutIndex : cutOutIndex_02; //肯定只有一种情况满足,另一个肯定找不到(取找得到的那个即可)
const routePath = index_finally == -1 ? itemObj.path : itemObj.path.substring(0,index_finally-1)
// itemObj.path = routePath; //把路由信息的path字段改一下(不带参数)
const routerObj = {
// path: itemObj.path,
path: routePath,
params: itemObj.params,
query: itemObj.query,
meta: itemObj.meta,
name: itemObj.name
}
this.$store.commit('tagView/addVisitedTags', routerObj)
// this.currentTagView = this.allTagsArr.findIndex(e => e.path == itemObj.path)
this.currentTagView = this.allTagsArr.findIndex(e => e.path == routePath)
},
3.2 allTagsArr 所有Tab页的数组是从vuex中获取的,写在computed中
allTagsArr: {
get() { return this.$store.state.tagView.allVisitedTags }
},
3.2 currentTagView表示当前在哪个Tab页,默认为0
data() {
return {
currentTagView: 0, //当前在哪一个标签
}
},
3.4 页面实现
<div class="tagViewBox">
<p v-for="(item,index) in allTagsArr" @click="skipTagView(item)" :class="{ active: currentTagView == index }">
<span :title='item.meta.title'>{{item.meta.title}}</span>
<i class="el-icon-close" @click.stop="delTag(item)"></i>
</p>
</div>
//样式如下(仅做参考哦)
.tagViewBox{
display: flex;
padding: 10px 20px;
& > p{
margin: 0;
padding: 8px 10px;
border: solid 1px #B777C0;
border-left: none;
color: #ccc;
cursor: pointer;
max-width: 9%;
display: flex;
& > span:nth-child(1){
display: block;
width: 90%;
margin-right: 5px;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
}
& > p:nth-child(1){
border-left: solid 1px #B777C0;
}
& > p.active{
color: #B777C0;
border-bottom: none;
}
}
3.5 删除一个Tab标签 ===>> delTag方法
delTag(item){ //删除一个标签页
if(this.allTagsArr.length >= 2){
this.$store.commit('tagView/delVisitedTags', item)
this.currentTagView--; //删除一个tab后,将当前tab页设置成前一个
this.skipTagView(this.allTagsArr[this.currentTagView]); //跳转到前一个tab页面内容
}else{
this.$message({
message: '这是当前最后一个Tab页,请在侧边栏切换模块。',
type: 'warning'
});
}
},
3.6 跳转到指定标签页 ===>> skipTagView方法
skipTagView(item){ //跳转到一个标签页
if(JSON.stringify(item.params) != '{}'){ //把以前的参数传过去,为了不去拼接path,直接使用name修改路由
this.$router.push({ name: item.name, params: item.params })
}
else if(JSON.stringify(item.query) != '{}'){
this.$router.push({ name: item.name, query: item.query })
}
else{
this.$router.push(item.path)
}
}
三、总结
1. 在跳转到指定标签页的时候,用 {name: ' ', params/query: ' ' } 方式,可以避免拼接路由的path
2. 在路由参数是 Object 时,只能传字符串,但是其第一次是正确的,第二次就被浏览器用 UrlEncode编码/解码 (可在线解码)方式转义了,所以在截取路由的时候要判断两种情况
第一次:
第二次:
文章仅为本人学习过程的一个记录,仅供参考,如有问题,欢迎指出!