设计样式如图:
设计规格描述:
-
当某个路由被第一次打开时,在标签栏最后插入新标签,标签内容显示为 页面名称 + 关键参数值
-
点击某个标签时,显示该标签的路由页面
-
点击标签上的 × ,
-
如果关闭的不是当前显示的标签页,直接删除这个标签就可以了
-
如果关闭的是当前显示标签页,移除标签,并且后退到当前标签左边一个标签页
-
-
打开同一个路由页面时,但是传的关键参数值不同,要插入新标签页
(标签的唯一性key判断 = 路由元素数据meta.title(即路由页面标签名) + 路由跳转时携带的关键参数flag的值)
-
浏览器刷新时,要自动将路由对应的标签添加到标签列表
代码主要实现在App.vue中
1、创建一个用于显示标签栏的组件 syClick.vue
<template>
<div class="nav-click" v-if="$route.meta.showClick">
<ul :style="{'left':left + 'px'}">
<li class="nav-click-body" v-for="(item,index) in titles" :key="index" :title="item.title + item.flag"
:class="{'nav-click-show':item.curr,'nav-click-default':!item.curr}" >
<div class="nav-click-body-title hidden" @click="changeTag(index)">{{item.title}} {{item.flag}}</div>
<div v-if="index!=0" class="nav-click-body-close" @click="removeTag(index)" :class="{'nav-click-body-show':item.curr,'nav-click-body-default':!item.curr}">×</div>
</li>
<div class="nav-click-right">
<sy-icon type="ellipsis-v" @click="UnNavClick" v-show="downIcon"></sy-icon>
<sy-icon type="remove" @click="CloseNavClick" v-show="!downIcon"></sy-icon>
</div>
</ul>
</div>
</template>
<script>
export default {
name:'syClick',
props:{
// 路由组件页标签列表
titles:{
type: Array,
default: ()=>{
return []
}
}
},
data() {
return {
left: 0,
clickHeight:50,
downIcon: true,
}
},
methods: {
UnNavClick(){
this.downIcon = !this.downIcon;
this.clickHeight = 100
},
// 关闭所有的标签
CloseNavClick(){
this.downIcon = !this.downIcon;
this.clickHeight = 50
this.$emit('removeAll')
},
// 删除标签页
removeTag(index) {
this.$emit('removeTag', index);
},
// 切换标签页
changeTag(index) {
this.$emit('changeTag', index);
}
}
}
</script>
2、在 App.vue 中挂载 syClick
App.vue 添加的相关代码
<template>
<div>
<div v-if="showApp == true">
<!-- 头部 -->
<sy-Head @on-refrash="reload"></sy-Head>
<!-- 页标签显示栏 -->
<sy-Click :titles="history_list" @removeAll="removeAll" @removeTag="removeTab" @changeTag="changeTag"></sy-Click>
<!-- 导航 -->
<sy-Nav @showPage="showPage"></sy-Nav>
<!-- 页面主体内容 -->
<keep-alive :include="cachedViews">
<router-view v-if="$route.meta.keepAlive" :key="key"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
</div>
</template>
<script>
import syHead from './pages/syHead.vue'
import syNav from './pages/syNav.vue'
import home from './pages/home.vue'
import syClick from './pages/syClick.vue'
var _self;
export default {
name: 'app',
components: {
home,
syNav,
syHead,
syClick
},
data() {
return {
// 标签列表
history_list: [{
"title": "系统首页",
"path": "/home",
"curr": true,
"name": "home",
"flag": "",
_route: 'home', // 用来存储对应的路由只读实例对象
"route": {
"name": "home",
"meta": {
"title": "系统首页",
"requireAuth": false,
"showHeader": true,
"showNav": true,
"showtag": true,
"showPosition": true,
"showClick": true
},
"path": "/home",
"query": {},
"params": {},
"fullPath": "/home"
}
}],
// 标签点击标识,用来控制切换路由时的缓存处理方式,如果是标签点击切换的路由,直接用缓存不需要处理,并且也不需要执行新增标签方法,
// 如果不是标签点击的,那么要判断当前缓存中是否已存在将要跳转的路由,如果存在要先清除缓存
isTabClick: false,
// 键盘control是否被按下,用来控制F5刷新组件
isUpKey_Control: false,
}
},
created() {
_self = this;
},
mounted: function() {
},
methods: {
// 重置缓存:先删除再添加
resetKeepAive(route) {},
// 删除缓存
deleteKeepAive(route) {},
// 添加缓存
addKeepAive(route) {},
// 导航菜单点击
showPage: function(router) {
this.isTabClick = false;
this.$router.push(router);
},
// 切换标签
changeTag(index) {
this.isTabClick = true;
for (let i = 0; i < this.history_list.length; i++) {
this.history_list[i].curr = false;
}
this.history_list[index].curr = true;
this.$router.push(this.history_list[index]._route);
},
// 不是标签点击的 (isTabClick == false)
addTab(to) {
// 判断是否要显示tab标签
if (!to.meta || !to.meta.showtag) {
return
}
let haved = false;
// 查找要转向的路由是否在已打开的标签列表里
for (let i = 0; i < this.history_list.length; i++) {
this.history_list[i].curr = false;
// 如果当前转向的路由,已经在标签列表中,则直接设置对应的标签焦点状态 (如果路由有参数的,需要判断关键参数flag)
if (this.history_list[i].title == to.meta.title &&
(!to.query || !to.query.flag || this.history_list[i].flag == to.query.flag)) {
haved = true;
this.history_list[i].curr = true;
// 如果当前点击的路由已经在缓存列表中,则先清除缓存列表,再添加;
this.resetKeepAive(to)
}
}
if (!haved) {
this.history_list.push({
title: to.meta.title,
path: to.path,
curr: true,
name: to.name,
flag: (to.query && to.query.flag) ? to.query.flag : "",
_route: to,
route: {
"name": to.name,
"meta": to.meta || {},
"path": to.name || '',
"query": to.query || {},
"params": to.params || {},
"fullPath": to.fullPath || ""
}
})
this.addKeepAive(to)
}
},
// 关闭标签-清除标签页组件缓存(清除缓存时是根据组件name来清除的,所以组件name相同的多个标签页缓存会同时被清除)
removeTab(index) {
this.isTabClick = true;
let name = this.history_list[index].name;
// 清除缓存
this.deleteKeepAive(this.history_list[index]);
// 如果关闭的是当前活动标签页
if (this.history_list[index].curr) {
this.history_list.splice(index, 1)
if (this.history_list.length == 1) {
this.$router.push('/home');
} else {
this.$router.push(this.history_list[index - 1]._route);
}
} else {
this.history_list.splice(index, 1);
}
},
// 关闭所有标签
removeAll() {
for (let i = this.history_list.length - 1; i > 0; i--) {
this.history_list.splice(i, 1);
}
this.$store.commit('CLEAR_KEEP_ALIVE');
this.$router.push('/home');
},
// 刷新当前标签页组件
reload() {
let _time = new Date().getTime()
let index = this.curr_index;
// 先清除当前的路由缓存
this.deleteKeepAive(this.history_list[index]);
// 路由查询参数中将_VERSION时间戳参数值刷新
let query = this.history_list[index].route.query || {};
if (query._VERSION) {
this.history_list[index].route.query._VERSION = _time;
} else {
this.$set(this.history_list[index].route.query, '_VERSION', _time);
}
// 将参数数组转成地址参数字符串
let queryString = '';
this.$XEUtils.each(query, function(value, key) {
queryString += key + '=' + value + '&'
})
queryString = queryString.substring(0, queryString.length - 1);
let fullPath = this.history_list[index].route.fullPath || '';
this.history_list[index].route.fullPath = this.history_list[index].path + "?" + queryString
this.$router.push(this.history_list[index].path + "?" + queryString)
}
},
computed: {
// 当前活动状态的标签页序号
curr_index() {
let index = -1;
for (let i = 0; i < this.history_list.length; i++) {
if (this.history_list[i].curr == true) {
index = i;
break;
}
}
return index ;
},
// 需要缓存的路由组件列表
cachedViews() {
return this.$store.state.keepAliveList
},
// 路由组件的key,用于keepAlive缓存
key() {
return this.history_list[this.curr_index].route.fullPath
},
},
watch: {
// 监听路由变化,自动新增标签
'$route': {
handler(to, from) {
// 如果不是标签头点击跳转的路由,要新增标签头
if (this.isTabClick == false) {
this.addTab(to);
}
if (!to.query._VERSION) {
to.query._VERSION = new Date().getTime()
}
this.isTabClick = false
},
immediate: true
}
}
}
</script>