vue菜單欄聯(lián)動(dòng)內(nèi)容頁面tab的實(shí)現(xiàn)示例
一、需求
需要實(shí)現(xiàn)效果:左側(cè)菜單欄與右側(cè)內(nèi)容部分聯(lián)動(dòng),當(dāng)點(diǎn)擊左側(cè)的菜單,右側(cè)會(huì)展示對應(yīng)的tab,沒有點(diǎn)擊時(shí),不展示(如剛進(jìn)入頁面沒有點(diǎn)擊菜單,則沒有tab);點(diǎn)擊后沒有關(guān)閉tab再打開其他菜單(如測試項(xiàng)目2),則測試項(xiàng)目2的tab高亮為選中狀態(tài)。
實(shí)現(xiàn)效果:


二、整體實(shí)現(xiàn)思路
1.環(huán)境:vue、element-ui
2.首先,在el-tab-pane中,是展示的tab(如上圖的測試項(xiàng)目1、測試項(xiàng)目2)。因此我們創(chuàng)建了一個(gè)數(shù)組activeTabs來儲(chǔ)存tab的信息。
- :label="getTabTitle(route.path)" 對應(yīng)的則是tab展示的標(biāo)題內(nèi)容,我用了一個(gè)方法getTabTitle獲取路由對應(yīng)的標(biāo)題。
- :name="route.meta.title" 則是與el-tabs下的 v-model="activeName"相對應(yīng),如果name的值與v-model的值一樣,則表示當(dāng)前選中的tab。
- 代碼中,
v-model="activeName"用于控制el-tabs組件的當(dāng)前活動(dòng)標(biāo)簽,而:name="route.meta.title"用于為每個(gè)標(biāo)簽指定一個(gè)唯一的名稱,這樣就可以通過這個(gè)名稱來確定當(dāng)前選中的標(biāo)簽。通過這兩個(gè)指令影響到同一個(gè)數(shù)據(jù)(例如activeName和route.meta.title),以達(dá)到實(shí)現(xiàn)標(biāo)簽切換的效果。 - 當(dāng)前選中的狀態(tài)由
@tab-click="selectTab"事件處理器決定。當(dāng)點(diǎn)擊一個(gè)標(biāo)簽時(shí)(tab-click事件),會(huì)觸發(fā)selectTab方法。在selectTab方法中,this.activeName屬性根據(jù)點(diǎn)擊的標(biāo)簽的meta.title進(jìn)行更新。v-model="activeName"會(huì)自動(dòng)反映這個(gè)變化,使得el-tabs組件根據(jù)activeName的值來確定哪個(gè)標(biāo)簽是當(dāng)前選中的,從而產(chǎn)生高亮效果。 - @edit="handleTabsEdit"是為了對tab做一些操作,點(diǎn)擊 tabs 的新增按鈕或 tab 被關(guān)閉后觸發(fā)。
值得注意的是v-model="activeName"、selectTab、 :name="route.meta.title"幾個(gè)的對應(yīng)關(guān)系,以及其數(shù)據(jù)結(jié)構(gòu)。
<el-tabs
v-model="activeName"
editable
@edit="handleTabsEdit"
@tab-click="selectTab"
>
<el-tab-pane
v-for="(route, index) in activeTabs"
:key="index"
:label="getTabTitle(route.path)"
:name="route.meta.title"
>
</el-tab-pane>
</el-tabs>三、我遇到的問題
1.數(shù)據(jù)結(jié)構(gòu)問題,一開始拿到的只是name或label、path,這樣是不行的,最好還是拿到當(dāng)前對應(yīng)菜單欄的完整router,方便我們操作。
2.點(diǎn)擊菜單欄對應(yīng)的tab沒有高亮但內(nèi)容顯示了
3.點(diǎn)擊關(guān)閉tab,tab關(guān)閉了但下面的內(nèi)容沒有關(guān)閉,沒有跳轉(zhuǎn)到下一個(gè)剩余tab的內(nèi)容。
4.點(diǎn)擊tab之間相互切換,功能是正常的頁面內(nèi)容切換,但存在問題tab沒有高亮,有時(shí)候要點(diǎn)擊兩次才會(huì)高亮,判段問題是出在沒有更新調(diào)用selectTab。
四、具體代碼
<template>
<section class="app-main">
<el-tabs
v-model="activeName"
editable
@edit="handleTabsEdit"
@tab-click="selectTab"
>
<el-tab-pane
v-for="(route, index) in activeTabs"
:key="index"
:label="getTabTitle(route.path)"
:name="route.meta.title"
>
</el-tab-pane>
</el-tabs>
<transition name="fade-transform" mode="out-in"> //展示具體頁面內(nèi)容
<content //自定義組件
>
<div class="router-inner-container">
<router-view v-show="activeName" :key="key" /> //v-show="activeName"非常重要,注意不要用v-if,用來與當(dāng)前tab對應(yīng),即關(guān)閉tab也關(guān)閉當(dāng)前內(nèi)容。
</div>
</content>
</transition>
</section>
</template>
<script>
import { mapGetters } from 'vuex'; //引入vuex,來拿到我的所有菜單路由
export default {
name: 'AppMain',
props: {
noTag: {
type: Boolean,
default: false
},
noMap: {
type: Boolean,
default: false
}
},
data() {
return {
activeName: null, // 默認(rèn)選中第一個(gè) tab
activeTabs: [] // 存儲(chǔ)當(dāng)前顯示的 tab
};
},
computed: {
...mapGetters(['sidebar', 'sidebarRouters']), //菜單所有路由sidebarRouters
key() {
return this.$route.path;
},
isStatisticsView() {
if (this.$route.path == '/home/index') {
return true;
} else {
return false;
}
}
},
methods: {
selectTab(tab, event) {
if (Array.isArray(tab) && tab.length > 0) { //由于數(shù)據(jù)結(jié)構(gòu)問題做的判段,拿到activeName
this.activeName = tab[0].meta.title;
} else if (typeof tab === 'object' && tab.meta) {
this.activeName = tab.meta.title;
} else if (Array.isArray(tab) && !tab.length > 0) {
this.activeName = ''; //當(dāng)所有tab都關(guān)閉時(shí),關(guān)閉所有內(nèi)容,否則頁面會(huì)tab都關(guān)閉了,但還有最后一個(gè)關(guān)閉的tab的頁面內(nèi)容。
}
},
handleTabsEdit(targetName, action) {
// 處理標(biāo)簽頁編輯事件
if (action === 'remove') {
// 刪除標(biāo)簽頁
this.removeTab(targetName);
}
},
removeTab(targetName) {
const tabs = this.activeTabs;
const index = tabs.findIndex((tab) => tab.meta.title === targetName);
if (index !== -1) {
tabs.splice(index, 1);
this.selectTab(tabs, event); //很重要,更新activeName,否則刪除后不會(huì)切換下一個(gè)對應(yīng)tab也不會(huì)切換頁面內(nèi)容
}
},
updateActiveTabs() {
const currentPath = this.$route;
// 判斷對象是否在 activeTabs 中存在
const existsInTabs = this.activeTabs.some(
(tab) => tab.hasOwnProperty('path') && tab.path === currentPath.path
);
if (!existsInTabs) {
// 如果當(dāng)前路由不在 activeTabs 中,將其添加進(jìn)去
this.activeTabs.push(currentPath);
}
this.selectTab(currentPath, event); //很重要,更新activeName,否則切換菜單欄時(shí)對應(yīng)的tab不會(huì)高亮不會(huì)切換
},
findMatchingRoute(routes, targetPath) { //為了處理數(shù)組的
for (const route of routes) {
if (route.path === targetPath) {
return route;
}
if (route.children && route.children.length > 0) {
const matchingChild = this.findMatchingRoute(
route.children,
targetPath
);
if (matchingChild) {
return matchingChild;
}
}
}
return null;
},
getTabTitle(route) {
// 根據(jù)路由信息獲取對應(yīng)的標(biāo)題
const matchingRoute = this.findMatchingRoute(this.sidebarRouters, route);
return matchingRoute ? matchingRoute.meta.title : '';
},
findMatchingMenu(routes, targetTitle) {
// 遞歸查找匹配的菜單項(xiàng)
for (const route of routes) {
if (route.meta) {
if (route.meta.title === targetTitle) {
return route;
}
if (route.children && route.children.length > 0) {
const matchingChild = this.findMatchingMenu(
route.children,
targetTitle
);
if (matchingChild) {
return matchingChild;
}
}
}
}
return null;
}
},
mounted() {},
watch: {
$route(to, from) {
if (to && to.meta && !to.meta.noMap) {
this.$nextTick(() => {
this.$refs.jmap.resizeMap();
});
}
// 更新 activeTabs
this.updateActiveTabs();
},
activeName: {
immediate: true,
deep: true,
handler(val, oldVal) {
if (val && val !== oldVal) {
const matchingMenu = this.findMatchingMenu(this.sidebarRouters, val);
if (matchingMenu) {
this.$router.push({ path: matchingMenu.path }); //切換tab時(shí),切換路由來對應(yīng)想要頁面內(nèi)容
}
}
}
}
}
};
</script>
<style lang="scss">
.app-main {
/*50 = navbar */
height: calc(100vh - 50px);
width: 100%;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
background: #f3f3f3;
&.no-header {
height: 100vh;
}
}
.fixed-header + .app-main {
padding-top: 60px;
}
.single-layout {
.app-main {
padding: 0px;
}
}
.map-style + .router-inner-container {
position: absolute;
}
</style>
<style lang="scss">
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
.fixed-header {
padding-right: 15px;
}
}
.no-header {
.float-panel {
height: 100vh;
}
}
</style>到此這篇關(guān)于vue菜單欄聯(lián)動(dòng)內(nèi)容頁面tab的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)vue菜單欄聯(lián)動(dòng)頁面tab內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue實(shí)現(xiàn)圖片下載點(diǎn)擊按鈕彈出本地窗口選擇自定義保存路徑功能
vue前端實(shí)現(xiàn)前端下載,并實(shí)現(xiàn)點(diǎn)擊按鈕彈出本地窗口,選擇自定義保存路徑,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-12-12
關(guān)于vue中的時(shí)間格式轉(zhuǎn)化問題
這篇文章主要介紹了關(guān)于vue中的時(shí)間格式轉(zhuǎn)化問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
vue 每次渲染完頁面后div的滾動(dòng)條保持在最底部的方法
下面小編就為大家分享一篇vue 每次渲染完頁面后div的滾動(dòng)條保持在最底部的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03

