点击多个tab标签打开关闭多个页面
需求
效果图
说一下思路
vue tab页多页面切换
点击多个tab标签打开关闭多个页面 需求现将页面分为Header LeftSideBar Main三大模块 左侧LeftSideBar为menu菜单,点击菜单每一项,在Main中出现上部为tag标签,下部为内容 可打开多个tag标签 ,可内容切换 ,可关闭
效果图1.router.js中(在LeftSideBar组件中现在有两个菜单项icons和tabs)
{
path:'/addtab',
redirect:'/addtab/index',//重定向 输入/addtab 地址会变为/addtab/index
component: Main,
children:[
{
path:'index',//当addtab/index匹配成功时 TabContent会被渲染在Main中的router-view中
name:'TabContent',
component:()=>import("@/components/TabContent")
}
]
},
{
path:'/addicon',
redirect:'/addicon/index',
component: Main,
children:[
{
path:'index',
name:'IconContent',
component:()=>import("@/components/IconContent")
}
]
}
请戳--动态组件嵌套路由
2.this.$router.push({name:"TabContent",params:{}}) 实现点击左侧tab 打开组件main
3.在main中
<template>
<div>
<TagsView/>
<router-view></router-view>
</div>
</template>
4.在TagsView中
<template>
<div class="tags-view-wrapper">
<router-link class="tags-view-item" :to="item" :key="item.path" :class="isActive(item)?'active':''" v-for="(item) in Array.from(visitedViews)">
{{item.params.name}}
<span class='el-icon-close' @click.prevent.stop='closeSelectedTag(item)'></span>
</router-link>
</div>
</template>
a.添加标签的方法
visitedViews是存放路由信息的数组
addTags(){
const route=this.$route;//获取地址栏路由
this.$store.commit({
type:'addTags',
route
})
}
在store.js中
addTags(state, payload) {
let flag = state.visitedTags.some(
item => item.path === payload.route.path
);//打开标签后,判断数组中是否已经存在该路由
if (!flag) {
state.visitedTags.push(
Object.assign(
{},
{
path: payload.route.path,
name: payload.route.name,
params: payload.route.params
}
)
);
} //数组中路由存在不push ,单击左侧路由变化,点击标签路由变化均触发
} //添加标签
第一次点击是在mountd中触发addTags方法,后来的每次点击路由均会变化 ,使用watch监听触发
watch:{
$route(){
this.addTags();
}//地址栏变化了就触发这个添加方法
}
b.关闭标签
在store.js中
closeTags(state, payload) {
for (const [key, item] of state.visitedTags.entries()) {
if (item.path === payload.view.path) {
state.visitedTags.splice(key, 1);
break;
}
}
} //如果要关闭的标签在路由中存在则删除
在tagviews中
isActive(route) {
return route.path === this.$route.path
},//当前地址栏路径是否与渲染的路径相同 样式匹配
closeSelectedTag(view){
this.$store.dispatch({
type:"closeTags",
view
}).then((views)=>{
此时的views是指的被删除后的visitedViews数组中存在的元素
if (this.isActive(view)) {
当前关闭的标签是否是被选中的标签
const latestView = views.slice(-1)[0];
if (latestView) {
this.$router.push(latestView);//如果数组不为空则让选中的标签为紧邻关闭标签的那一个
} else {
this.$router.push('/') ;//如果为空则页面跳转到/
}
}
})
}
说一下思路
点击左侧的每一项都会打开一个组件(对应一个路由) 第一次点击时将路由信息push到visitedViews中 后来的每次点击都是通过watch $route执行添加标签方法
删除时要考虑是否是对激活项进行关闭 若是则先删除数组中要关闭的标签的那个路由,然后获取剩余visitedViews中的路由,让最后一个路由作为激活项
vue tab页多页面切换实现路由发生变化时,新增一个tab标签页,点击其他标签时切换到对应的页面,刷新网页同时保留状态
这里就直接说它实现的代码就OK!!!
VueX记录下每次新增后的tab标签页路由
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 路由导航start
// 缓存组件页面
catch_components: [],
// 当前选中的菜单 - 默认选择首页
activePath: '/index',
// 菜单项 - 默认需要展示的页面()
tabList: [{
path: '/index',
label: '首页',
name: 'index',
fullPath: "/index"
}],
// 路由导航end
},
// 更改vuex的store中状态的唯一方法 - 同步操作
mutations: {
// 路由导航start
//清空vuex数据
clearVUEX(state) {
state.catch_components = []
state.activePath = 'index'
state.tabList = [{
path: '/idnex',
label: '首页',
name: 'index',
fullPath: "/index"
}]
},
// 跳转页面执行
selectMenu(state, submenu) {
// 首页就是 wellcome 也就是 home
if (submenu.name === 'index') {
submenu.name = 'index'
label.path = '首页'
submenu.path = '/index'
submenu.fullPath= '/index'
}
// 当前选中菜单
var activePath = submenu.name
// 历史已选中菜单列表
var oldTabList = state.tabList
// 将菜单信息添加到tablist - 添加时判断是否已有该路由标签
var result = oldTabList.some(item => {
if (item.name === activePath) {
// console.log('--------', item.fullPath != submenu.fullPath)
// 有该路由标签是否为多次点击(相当于查看同路由下的详情,该过程只改变了参数)
if (!item.fullPath != submenu.fullPath) {
item.fullPath = submenu.fullPath
}
return true
}
})
// 如果不包含该对象,则添加
if (!result) {
oldTabList.push({
path: submenu.name,
name: submenu.name,
label: submenu.label,
fullPath: submenu.fullPath
})
}
// 重新赋值标签路由和当前选中菜单
state.activePath = activePath
state.tabList = oldTabList
},
// 添加keepalive缓存
addKeepAliveCache(state, val) {
// 如果是首页不缓存
if (val === 'index') {
return
}
// console.log(state.catch_components)
// 添加时判断,如果该组件已存在,便不添加
if (state.catch_components.indexOf(val) === -1) {
// 不存在,缓存页面
state.catch_components.push(val)
}
},
// 删除keepalive缓存
removeKeepAliveCache(state, val) {
let cache = state.catch_components
for (let i = 0; i < cache.length; i++) {
if (cache[i] === val) {
cache.splice(i, 1);
}
}
state.catch_components = cache
},
//关闭菜单
closeTab(state, val) {
// 重新赋值
state.activePath = val.activePath
state.tabList = val.tabList
},
// 点击标签选择菜单
changeMenu(state, val) {
state.activePath = val
},
// 路由导航end
},
actions: {
}
})
根据自己的需求定义一个展示路由标签组件vue文件
BScroll :当路由标签过多时,用于横向滚动标签页
<!-- crumbs.vue -->
<template>
<div class="tags">
<div class="horizontal-container">
<div class="scroll-wrapper" ref="scroll">
<div class="scroll-content">
<el-tag size="medium" v-for="(tab, index) in tabList" :key="tab.path" @close="handleClose(tab, index)"
@click="changeMenu(tab)" :closable="tab.name !== 'index'"
:effect="activePath === tab.name ? 'dark' : 'plain'">
{{tab.label}}
</el-tag>
</div>
</div>
</div>
</div>
</template>
<script>
import {
mapState
} from 'vuex';
import BScroll from '@better-scroll/core'
export default {
data() {
return {
//菜单列表
menuList: [],
}
},
computed: {
...mapState({ // 从 state 中的到的计算属性
activePath: state => state.activePath, // 已选中菜单
tabList: state => state.tabList, // tags菜单列表
catch_components: state => state.catch_components, // keepalive缓存
})
},
mounted() {
// this.handleCommand()
this.init()
},
methods: {
init() {
this.bs = new BScroll(this.$refs.scroll, {
scrollX: true,
probeType: 3 // listening scroll event
})
},
// 清空当前vuex数据
handleCommand() {
this.$store.commit('clearVUEX')
},
// 点击菜单 - 传入name,添加到keepalive缓存页面
selectMenu(item) {
// console.log(item.name)
// 加入keepalive缓存
this.$store.commit('addKeepAliveCache', item.name)
//添加tags标签
//访问wellcome 就代表home
var name = item.name === 'index' ? 'index' : item.name
var submenu = {
path: item.path,
name: name,
label: item.meta.title,
fullPath: item.fullPath
}
// console.log(submenu)
//更新选中菜单
this.$store.commit('selectMenu', submenu)
console.log(this.$store.state.tabList)
},
// 点击标签跳转路由
changeMenu(item) {
// 历史选中菜单
var oldActivePath = this.$store.state.activePath
// 首先判断点击的是否是自己,如果是自己则return
if (oldActivePath === item.name) {
return
}
// 存储菜单
this.$store.commit('changeMenu', item.name)
// 页面跳转
this.$router.push({
path: item.fullPath
})
},
// 关闭tab标签
handleClose(tab, index) {
// 历史选中菜单
var oldActivePath = this.$store.state.activePath
// 历史已选中菜单列表
var oldTabList = this.$store.state.tabList
// 计算标签个数
let length = oldTabList.length - 1
// 删除tabList中的该对象
for (let i = 0; i < oldTabList.length; i++) {
let item = oldTabList[i]
if (item.name === tab.name) {
oldTabList.splice(i, 1);
}
}
// 删除keepAlive缓存
this.$store.commit('removeKeepAliveCache', tab.name)
// 如果关闭的标签不是当前路由的话,就不跳转
if (tab.name !== oldActivePath) {
return
}
// 如果length为1,必然只剩下首页标签,此时关闭后,更新到首页
if (length === 1) {
// 同时存储菜单
this.$store.commit('closeTab', {
activePath: 'home',
tabList: oldTabList
})
// tab页向左跳转
this.$router.push({
name: oldTabList[index - 1].name
})
// 不再向下执行
return
}
// 关闭的标签是最右边的话,往左边跳转一个
if (index === length) {
// 同时更新路径
oldActivePath = oldTabList[index - 1].name
// 同时存储菜单
this.$store.commit('closeTab', {
activePath: oldActivePath,
tabList: oldTabList
})
// tab页向左跳转
this.$router.push({
name: oldTabList[index - 1].name
})
} else {
// 同时更新路径
oldActivePath = oldTabList[index].name
// 同时存储菜单
this.$store.commit('closeTab', {
activePath: oldActivePath,
tabList: oldTabList
})
// tab页向右跳转
this.$router.push({
name: oldTabList[index].name
})
}
},
},
watch: {
// 路由发生变化时调用更新tab标签数据
'$route': {
handler(newValue) {
// console.log(newValue, oldValue)
this.selectMenu(newValue);
},
immediate: true
}
},
}
</script>
<style lang="less" scoped="scoped">
/deep/ .el-tag--medium {
margin-right: 10px;
}
.horizontal-container {
.scroll-wrapper {
position: relative;
width: 100%;
// margin: 80px auto;
margin: 0 auto;
white-space: nowrap;
// border: 3px solid #42b983;
border-radius: 5px;
overflow: hidden;
.scroll-content {
display: inline-block;
}
.scroll-item {
height: 40px;
line-height: 40px;
// font-size: 24px;
display: inline-block;
text-align: center;
padding: 0 10px;
}
}
}
/deep/.el-tabs__nav-scroll {
background: #fff;
}
.el-tag {
cursor: pointer;
margin-left: 10px;
border-radius: 2px;
font-size: 12px;
color: #1890FF;
border-color: #1890FF;
}
.el-tag--dark {
color: #fff;
background-color: #1890FF;
}
.el-dropdown-link {
cursor: pointer;
}
.el-icon-arrow-down {
font-size: 12px;
}
.submit-row {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
</style>
若F5或者强刷新页面时需要保留当前tab路由数据,在App.vue中插入代码
created() {
//在页面刷新时将vuex里的信息保存到sessionStorage里
window.addEventListener("beforeunload", () => {
sessionStorage.setItem("store", JSON.stringify(this.$store.state))
})
},
以上为个人经验,希望能给大家一个参考,也希望大家多多支持软件开发网。