vue如何點擊多個tab標簽打開關(guān)閉多個頁面
點擊多個tab標簽打開關(guān)閉多個頁面
需求
現(xiàn)將頁面分為Header LeftSideBar Main三大模塊 左側(cè)LeftSideBar為menu菜單,點擊菜單每一項,在Main中出現(xiàn)上部為tag標簽,下部為內(nèi)容 可打開多個tag標簽 ,可內(nèi)容切換 ,可關(guān)閉
效果圖

1.router.js中(在LeftSideBar組件中現(xiàn)在有兩個菜單項icons和tabs)
{
path:'/addtab',
redirect:'/addtab/index',//重定向 輸入/addtab 地址會變?yōu)?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")
}
]
}請戳--動態(tài)組件嵌套路由
2.this.$router.push({name:"TabContent",params:{}}) 實現(xiàn)點擊左側(cè)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是存放路由信息的數(shù)組
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
);//打開標簽后,判斷數(shù)組中是否已經(jīng)存在該路由
if (!flag) {
state.visitedTags.push(
Object.assign(
{},
{
path: payload.route.path,
name: payload.route.name,
params: payload.route.params
}
)
);
} //數(shù)組中路由存在不push ,單擊左側(cè)路由變化,點擊標簽路由變化均觸發(fā)
} //添加標簽第一次點擊是在mountd中觸發(fā)addTags方法,后來的每次點擊路由均會變化 ,使用watch監(jiān)聽觸發(fā)
watch:{
$route(){
this.addTags();
}//地址欄變化了就觸發(fā)這個添加方法
}b.關(guān)閉標簽
在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;
? ? ? ? }
? ? ? }
? ? } //如果要關(guān)閉的標簽在路由中存在則刪除 ? ??在tagviews中
? ? ?isActive(route) {
? ? ? ? return route.path === this.$route.path
? ? ? },//當前地址欄路徑是否與渲染的路徑相同 樣式匹配
? ? ? closeSelectedTag(view){
? ? ? ? ?this.$store.dispatch({
? ? ? ? ? ?type:"closeTags",
? ? ? ? ? ?view
? ? ? ? ?}).then((views)=>{
? ? ? ? ? ? 此時的views是指的被刪除后的visitedViews數(shù)組中存在的元素
? ? ? ? ? ?if (this.isActive(view)) {
? ? ? ? ? ? 當前關(guān)閉的標簽是否是被選中的標簽
? ? ? ? ? ? ?const latestView = views.slice(-1)[0];
? ? ? ? ? ? ?if (latestView) {
? ? ? ? ? ? ? ?this.$router.push(latestView);//如果數(shù)組不為空則讓選中的標簽為緊鄰關(guān)閉標簽的那一個
? ? ? ? ? ? ?} else {
? ? ? ? ? ? ? ?this.$router.push('/') ;//如果為空則頁面跳轉(zhuǎn)到/
? ? ? ? ? ? ?}
? ? ? ? ? ?}
? ? ? ? ?})
? ? ? }說一下思路
點擊左側(cè)的每一項都會打開一個組件(對應一個路由) 第一次點擊時將路由信息push到visitedViews中 后來的每次點擊都是通過watch $route執(zhí)行添加標簽方法
刪除時要考慮是否是對激活項進行關(guān)閉 若是則先刪除數(shù)組中要關(guān)閉的標簽的那個路由,然后獲取剩余visitedViews中的路由,讓最后一個路由作為激活項
vue tab頁多頁面切換
實現(xiàn)路由發(fā)生變化時,新增一個tab標簽頁,點擊其他標簽時切換到對應的頁面,刷新網(wǎng)頁同時保留狀態(tài)

這里就直接說它實現(xiàn)的代碼就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中狀態(tài)的唯一方法 - 同步操作
mutations: {
// 路由導航start
//清空vuex數(shù)據(jù)
clearVUEX(state) {
state.catch_components = []
state.activePath = 'index'
state.tabList = [{
path: '/idnex',
label: '首頁',
name: 'index',
fullPath: "/index"
}]
},
// 跳轉(zhuǎn)頁面執(zhí)行
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)
// 有該路由標簽是否為多次點擊(相當于查看同路由下的詳情,該過程只改變了參數(shù))
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
},
//關(guān)閉菜單
closeTab(state, val) {
// 重新賦值
state.activePath = val.activePath
state.tabList = val.tabList
},
// 點擊標簽選擇菜單
changeMenu(state, val) {
state.activePath = val
},
// 路由導航end
},
actions: {
}
})
根據(jù)自己的需求定義一個展示路由標簽組件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數(shù)據(jù)
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)
},
// 點擊標簽跳轉(zhuǎn)路由
changeMenu(item) {
// 歷史選中菜單
var oldActivePath = this.$store.state.activePath
// 首先判斷點擊的是否是自己,如果是自己則return
if (oldActivePath === item.name) {
return
}
// 存儲菜單
this.$store.commit('changeMenu', item.name)
// 頁面跳轉(zhuǎn)
this.$router.push({
path: item.fullPath
})
},
// 關(guān)閉tab標簽
handleClose(tab, index) {
// 歷史選中菜單
var oldActivePath = this.$store.state.activePath
// 歷史已選中菜單列表
var oldTabList = this.$store.state.tabList
// 計算標簽個數(shù)
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)
// 如果關(guān)閉的標簽不是當前路由的話,就不跳轉(zhuǎn)
if (tab.name !== oldActivePath) {
return
}
// 如果length為1,必然只剩下首頁標簽,此時關(guān)閉后,更新到首頁
if (length === 1) {
// 同時存儲菜單
this.$store.commit('closeTab', {
activePath: 'home',
tabList: oldTabList
})
// tab頁向左跳轉(zhuǎn)
this.$router.push({
name: oldTabList[index - 1].name
})
// 不再向下執(zhí)行
return
}
// 關(guān)閉的標簽是最右邊的話,往左邊跳轉(zhuǎn)一個
if (index === length) {
// 同時更新路徑
oldActivePath = oldTabList[index - 1].name
// 同時存儲菜單
this.$store.commit('closeTab', {
activePath: oldActivePath,
tabList: oldTabList
})
// tab頁向左跳轉(zhuǎn)
this.$router.push({
name: oldTabList[index - 1].name
})
} else {
// 同時更新路徑
oldActivePath = oldTabList[index].name
// 同時存儲菜單
this.$store.commit('closeTab', {
activePath: oldActivePath,
tabList: oldTabList
})
// tab頁向右跳轉(zhuǎn)
this.$router.push({
name: oldTabList[index].name
})
}
},
},
watch: {
// 路由發(fā)生變化時調(diào)用更新tab標簽數(shù)據(jù)
'$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路由數(shù)據(jù),在App.vue中插入代碼
created() {
//在頁面刷新時將vuex里的信息保存到sessionStorage里
window.addEventListener("beforeunload", () => {
sessionStorage.setItem("store", JSON.stringify(this.$store.state))
})
},
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
安裝vue3開發(fā)者工具但控制臺沒有顯示出vue選項的解決
這篇文章主要介紹了安裝vue3開發(fā)者工具但控制臺沒有顯示出vue選項的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
Vue結(jié)合Element-Plus封裝遞歸組件實現(xiàn)目錄示例
本文主要介紹了Vue結(jié)合Element-Plus封裝遞歸組件實現(xiàn)目錄示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04
vue實現(xiàn)頁面內(nèi)容禁止選中功能,僅輸入框和文本域可選
今天小編就為大家分享一篇vue實現(xiàn)頁面內(nèi)容禁止選中功能,僅輸入框和文本域可選,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11
vue中路由跳轉(zhuǎn)的多種方式(和$router下路由跳轉(zhuǎn)的那幾個方法的區(qū)別)
Vue.js是一款流行的前端JavaScript框架,它提供了多種方式來實現(xiàn)路由跳轉(zhuǎn),本文給大家分享vue中路由跳轉(zhuǎn)的幾種方式(和$router下路由跳轉(zhuǎn)的那幾個方法的區(qū)別),感興趣的朋友一起看看吧2023-11-11
vue?watch中如何獲取this.$refs.xxx節(jié)點
這篇文章主要介紹了vue?watch中獲取this.$refs.xxx節(jié)點的方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08

