点击多个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)) }) },
以上为个人经验,希望能给大家一个参考,也希望大家多多支持阿兔在线工具。