vue實(shí)現(xiàn)多個(gè)tab標(biāo)簽頁(yè)的切換與關(guān)閉詳細(xì)代碼
1.實(shí)現(xiàn)效果

2.實(shí)現(xiàn)原理
vuex,實(shí)現(xiàn)對(duì)當(dāng)前激活項(xiàng),當(dāng)前tab列表,當(dāng)前tab的translateX,當(dāng)前緩存頁(yè),當(dāng)前路由的狀態(tài)管理。
將vuex中的數(shù)據(jù)保存到sessionStorage中,避免頁(yè)面刷新丟失,當(dāng)瀏覽器關(guān)閉時(shí),清空數(shù)據(jù)。
通過(guò)ref定位,拿到當(dāng)前窗口寬度與當(dāng)前所在路由的tab標(biāo)簽的所有寬度,判斷兩者,實(shí)現(xiàn)對(duì)多tab超出窗口寬度的處理。
當(dāng)點(diǎn)擊tab標(biāo)簽頁(yè)的時(shí)候,獲取相應(yīng)的激活項(xiàng),動(dòng)態(tài)的實(shí)現(xiàn)左側(cè)菜單欄的選中狀態(tài),用watch監(jiān)聽(tīng),updateActiveName和updateOpened。
當(dāng)關(guān)閉tab標(biāo)簽的時(shí)候,splice刪除當(dāng)前標(biāo)簽,若是刪除的最后一項(xiàng),跳轉(zhuǎn)到該項(xiàng)的前一項(xiàng)頁(yè)面。當(dāng)長(zhǎng)度為1時(shí),跳轉(zhuǎn)到首頁(yè)。
3.主要代碼
store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
catch_components: [],
activePath: '/index',
openNames: [],
activeName: "",
tranx: "-0",
tabList: [
{ path: '/index', label: '首頁(yè)', name: '首頁(yè)' }
]
},
mutations: {
//清空vuex數(shù)據(jù)
clearTabs(state) {
state.catch_components = []
state.activePath = '/homepage'
state.openNames = []
state.activeName = ""
state.tranx = "-0"
state.tabList = [
{ path: '/homepage', label: '首頁(yè)', name: 'home' }
]
},
// 跳轉(zhuǎn)頁(yè)面執(zhí)行
selectMenu(state, submenu) {
var activePath = submenu.path
var oldTabList = state.tabList
var result = oldTabList.some(item => {
if (item.path === activePath) {
return true
}
})
if (!result) {
oldTabList.push({
path: submenu.path,
name: submenu.name,
label: submenu.label,
index: submenu.index,
subName: submenu.subName
})
}
state.activePath = activePath
state.tabList = oldTabList
state.activeName = submenu.subName + "-" + submenu.index
state.openNames = [submenu.subName]
},
// 添加keepalive緩存
addKeepAliveCache(state, val) {
if (val === '/homepage') {
return
}
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
},
setTranx(state, val) {
console.log(val)
state.tranx = val
},
//關(guān)閉菜單
closeTab(state, val) {
state.activePath = val.activePath
state.tabList = val.tabList
state.openNames = val.openNames
state.activeName = val.activeName
},
// 點(diǎn)擊標(biāo)簽選擇菜單
changeMenu(state, val) {
state.activePath = val.path
state.activeName = val.subName + "-" + val.index
state.openNames = [val.subName]
}
},
})頁(yè)面代碼
computed: {
...mapState({
activePath: (state) => state.activePath, // 已選中菜單
tabList: (state) => state.tabList, // tags菜單列表
catch_components: (state) => state.catch_components, // keepalive緩存
openNames: (state) => state.openNames,
activeName: (state) => state.activeName,
tranx: (state) => state.tranx,
}),
},
watch: {
openNames() {
this.$nextTick(() => {
this.$refs.asideMenu.updateOpened();
});
},
activeName() {
this.$nextTick(() => {
this.$refs.asideMenu.updateActiveName();
});
},
},
handleClose(tab, index) {
var oldOpenNames = this.$store.state.openNames,
oldActiveName = this.$store.state.activeName,
oldActivePath = this.$store.state.activePath,
oldTabList = this.$store.state.tabList;
let length = oldTabList.length - 1;
for (let i = 0; i < oldTabList.length; i++) {
let item = oldTabList[i];
if (item.path === tab.path) {
oldTabList.splice(i, 1);
}
}
// 刪除keepAlive緩存
this.$store.commit("removeKeepAliveCache", tab.path);
if (tab.path !== oldActivePath) {
return;
}
if (length === 1) {
this.$store.commit("closeTab", {
activePath: "/index",
tabList: oldTabList,
});
this.$router.push({ path: oldTabList[index - 1].path });
return;
}
if (index === length) {
oldActivePath = oldTabList[index - 1].path;
oldOpenNames = [oldTabList[index - 1].subName];
oldActiveName =
oldTabList[index - 1].subName + "-" + oldTabList[index - 1].index;
this.$store.commit("closeTab", {
activePath: oldActivePath,
tabList: oldTabList,
openNames: oldOpenNames,
activeName: oldActiveName,
});
this.$router.push({ path: oldTabList[index - 1].path });
} else {
oldActivePath = oldTabList[index].path;
oldOpenNames = [oldTabList[index].subName];
oldActiveName =
oldTabList[index].subName + "-" + oldTabList[index].index;
this.$store.commit("closeTab", {
activePath: oldActivePath,
tabList: oldTabList,
openNames: oldOpenNames,
activeName: oldActiveName,
});
this.$router.push({ path: oldTabList[index].path });
}
this.getTrans(2);
},
changeMenu(item) {
var oldActivePath = this.$store.state.activePath;
if (oldActivePath === item.path) {
return;
}
this.$store.commit("changeMenu", item);
this.$router.push({ path: item.path });
this.$nextTick(() => {
this.getTrans(0);
});
},
selectMenu(item, i, subName) {
// 加入keepalive緩存
this.$store.commit("addKeepAliveCache", item.path);
var submenu = {
path: item.path,
name: item.title,
label: item.title,
index: i,
subName: subName,
};
this.$store.commit("selectMenu", submenu);
this.$router.push({ path: item.path });
this.$nextTick(() => {
this.getTrans(0);
});
},
getTrans(e) {
let width = 0;
if (this.$refs.tags) {
width = this.$refs.tags.clientWidth;
}
this.tabList.map((item, index) => {
if (item.path === this.activePath) {
this.currentIndex = index;
}
if (this.$refs[`tag${index}`] && this.$refs[`tag${index}`][0]) {
this.$set(
this.tabList[index],
"width",
this.$refs[`tag${index}`][0].$el.clientWidth + 4
);
}
});
let list = this.tabList.filter((item, index) => {
return index <= this.currentIndex;
});
let totalWidth = list.reduce((total, currentValue) => {
return total + Number(currentValue.width);
}, 0);
let totalAllWidth = this.tabList.reduce((total, currentValue) => {
return total + Number(currentValue.width);
}, 0);
if (e == 0) {
if (Number(width) > Number(totalWidth) || Number(width) == 0) {
this.setTranx(-0);
return false;
}
this.setTranx(Number(width) - Number(totalWidth) - 60);
} else if (e == 1) {
if (Number(width) > Number(totalAllWidth)) {
return false;
}
this.setTranx(Number(width) - Number(totalAllWidth) - 60);
} else {
if (
Number(width) > Number(totalAllWidth) &&
this.$store.state.tranx < 0
) {
this.setTranx(-0);
}
}
},
setTranx(val) {
this.$store.commit("setTranx", val);
},<Menu
ref="asideMenu"
:active-name="activeName"
:open-names="openNames"
accordion
theme="light"
:style="{ width: 'auto' }"
:class="isCollapsed ? 'collapsed-menu' : 'menu-item'"
>
<MenuItem
@click.native="selectMenu({ path: '/index', title: '首頁(yè)' })"
name="index"
key="index"
>
<Icon type="ios-paw"></Icon>
<span class="menuTitle">首頁(yè)</span>
</MenuItem>
<Submenu
v-for="(item, index) in menuMap"
:name="index"
:key="index"
class="sub_title"
>
<template slot="title">
<svg class="icon" aria-hidden="true" v-if="item.fonticon">
<use :xlink:href="item.fonticon" rel="external nofollow" ></use>
</svg>
<Icon :type="item.icon" v-else />
<span class="menuTitle">{{ item.title }}</span>
</template>
<template v-if="item.children">
<MenuItem
v-for="(each, i) in item.children"
:name="index + '-' + i"
:key="index + '-' + i"
@click.native="selectMenu(each, i, index)"
><span class="menuTitle">{{ each.title }}</span>
</MenuItem>
</template>
</Submenu>
</Menu>
<Row class="head-tags">
<div class="head-left left" @click="setTranx(0)">
<Icon type="ios-rewind" size="30" color="#ffc0cb" />
</div>
<div class="tags-box">
<div
ref="tags"
class="tags-box-scroll"
:style="{ transform: `translateX(${tranx}px)` }"
>
<Tag
:ref="'tag' + index"
class="tags-item"
:class="{ 'tags-item-active': activePath === item.path }"
v-for="(item, index) in tabList"
:key="index"
:name="item.path"
:closable="item.path !== '/index'"
@click.native="changeMenu(item)"
@on-close="handleClose(item, index)"
>{{ item.label }}</Tag
>
</div>
</div>
<div class="head-left right" @click="getTrans(1)">
<Icon type="ios-fastforward" size="30" color="#ffc0cb" />
</div>
</Row>總結(jié)
到此這篇關(guān)于vue實(shí)現(xiàn)多個(gè)tab標(biāo)簽頁(yè)的切換與關(guān)閉的文章就介紹到這了,更多相關(guān)vue多個(gè)tab標(biāo)簽頁(yè)切換關(guān)閉內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Vue中如何實(shí)現(xiàn)打字機(jī)的效果
這篇文章主要介紹了在Vue中如何實(shí)現(xiàn)打字機(jī)的效果,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
vue中使用$http.post請(qǐng)求傳參的錯(cuò)誤及解決
這篇文章主要介紹了vue中使用$http.post請(qǐng)求傳參的錯(cuò)誤及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
Vue2無(wú)法監(jiān)聽(tīng)數(shù)組下標(biāo)和對(duì)象新增屬性的原因和解決方法
文章詳細(xì)解釋了Vue2中無(wú)法監(jiān)聽(tīng)對(duì)象屬性新增/刪除和數(shù)組下標(biāo)變化的原因,并提供了解決方案,如使用Vue.set和重寫(xiě)數(shù)組方法,還對(duì)比了Vue3的Proxy,強(qiáng)調(diào)了Proxy在監(jiān)聽(tīng)機(jī)制和性能上的優(yōu)勢(shì),需要的朋友可以參考下2026-05-05
用vue的雙向綁定簡(jiǎn)單實(shí)現(xiàn)一個(gè)todo-list的示例代碼
本篇文章主要介紹了用vue的雙向綁定簡(jiǎn)單實(shí)現(xiàn)一個(gè)todo的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08
vue+element樹(shù)形選擇器組件封裝和使用方式
這篇文章主要介紹了vue+element樹(shù)形選擇器組件封裝和使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2020-04-04
vue.js中Vue-router 2.0基礎(chǔ)實(shí)踐教程
這篇文章主要給大家介紹了關(guān)于vue.js中Vue-router 2.0基礎(chǔ)實(shí)踐的相關(guān)資料,其中包括vue-router 2.0的基礎(chǔ)用法、動(dòng)態(tài)路由匹配、嵌套路由、編程式路由、命名路由以及命名視圖等相關(guān)知識(shí),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-05-05
Vue--Router動(dòng)態(tài)路由的用法示例詳解
這篇文章主要介紹了Vue--Router動(dòng)態(tài)路由的用法,很多時(shí)候,我們需要將給定匹配模式的路由映射到同一個(gè)組件,在?Vue?Router?中,我們可以在路徑中使用一個(gè)動(dòng)態(tài)字段來(lái)實(shí)現(xiàn),我們稱(chēng)之為路徑參數(shù),本文對(duì)Vue?Router動(dòng)態(tài)路由相關(guān)知識(shí)給大家介紹的非常詳細(xì),需要的朋友參考下吧2022-08-08
vue頁(yè)面跳轉(zhuǎn)過(guò)渡動(dòng)畫(huà)與防止抖動(dòng)方式
這篇文章主要介紹了vue頁(yè)面跳轉(zhuǎn)過(guò)渡動(dòng)畫(huà)與防止抖動(dòng)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2026-03-03
基于vue3實(shí)現(xiàn)一個(gè)簡(jiǎn)單的輸入框效果
這篇文章主要為大家詳細(xì)介紹了如何使用Vue3實(shí)現(xiàn)一個(gè)簡(jiǎn)單的輸入框,可以實(shí)現(xiàn)輸入文字,添加表情等功能,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03

