vue 点击菜单动态生成Tab
- 2020 年 2 月 20 日
- 笔记
UI 组件采用element NavMenu点击左侧的菜单列表生成Tab,如下图

主要思路
(1)点击菜单列表的时生成tab数据
(2)点击tab 展示当前激活tab的信息
(3)点击关闭按钮移除tab的数据,如果删除的是当前激活的tab,激活的tab前移或后移(删除tab的前一个或者后一个)
(4)采用动态组件展示每个tab的具体内容
这个例子中菜单列表没有采用路由跳转,采用路由与不采用路由跳转动态生成Tab 的原理都是一样的。
Home.vue
<template> <div> <el-container :style="{height: containerHeight, border: '1px solid #eee'}" id="con"> <el-header style="background:#3c8dbc;"><i class="fa fa-bars collapseBtn" @click="handleCollapseClick"></i> </el-header> <el-container> <el-aside :width="asideWidth"> <el-menu :default-active="activeMenuItem" :collapse="isCollapse" :collapse-transition="false" > <el-submenu v-for="item in menuData" :index="item.id+''" :key="item.id"> <template slot="title"> <i class="el-icon-location"></i> <span>{{item.name}}</span> </template> <el-menu-item v-for="innerItem in item.children" :index="innerItem.id.toString()" :key="innerItem.id" @click="handleItemClick(innerItem)" >{{innerItem.name}}</el-menu-item> </el-submenu> </el-menu> </el-aside> <el-container> <el-main> <el-tabs :value="activeTabItem" @tab-remove="closeTab" @tab-click="tabClick"> <el-tab-pane v-for="item in tabs" :label="item.label" :key="item.id" :name="item.id" :closable="item.closable" > <!-- <tab-content :tabData = "item.label"></tab-content> --> <async-component :componentPath="item.component"></async-component> </el-tab-pane> </el-tabs> </el-main> <el-footer> footer </el-footer> </el-container> </el-container> </el-container> </div> </template> <script> import TabContent from "@/components/TabContent.vue" import AsyncComponent from "./AsyncComponent"; //import AdminIndex from "@/components/AdminIndex.vue"; export default { data() { return { containerHeight: "", asideWidth:"230px", isCollapse:false, menuData: [ { name: "导航1", id: 1, children: [ { name: "导航1-选项1", id: 2, componentPath:"Item1.vue" }, { name: "导航1-选项2", id: 3, componentPath:"Item2.vue" } ] }, { name: "导航2", id: 4, children: [ { name: "导航2-选项1", id: 5, componentPath:"Item3.vue" }, { name: "导航2-选项2", id: 6, componentPath:"Item4.vue" } ] } ], activeMenuItem:"", tabs: [], activeTabItem: "", }; }, created() {}, computed: {}, components: {AsyncComponent}, methods: { handleCollapseClick(){ this.isCollapse = !this.isCollapse this.asideWidth = this.asideWidth=="230px"?"66px":"230px" }, handleItemClick(item) { let tab = this.tabs.find(tab => tab.id == item.id); if (!tab) { let newTab = { id: item.id + "", label: item.name, closable: true, component:item.componentPath || "" }; this.tabs.push(newTab); } // activeTabItem 是绑定的name 并且要求是字符串 this.activeTabItem = item.id + ""; }, tabClick(tab) { console.log(tab) this.activeMenuItem = tab.name this.activeTabItem = tab.name }, closeTab(targetName) { let tabs = this.tabs; let activeTabItem = this.activeTabItem; if(activeTabItem == targetName){ tabs.forEach((tab,index) => { if(tab.id == targetName){ let nextTab = tabs[index-1] || tabs[index+1] if(nextTab){ activeTabItem = nextTab.id } } }) } this.activeTabItem = activeTabItem this.tabs = tabs.filter(tab => tab.id != targetName) } }, mounted() { this.containerHeight = window.innerHeight + "px"; $(window).resize(function() { $("#con").height($(window).height() - 2); }); } }; </script> <style> .el-header { background-color: #377fa9; color: #fff; height: 50px !important; line-height: 50px !important; } .el-header .left img { width: 120px; vertical-align: middle; } .el-header .left span { font-size: 20px; color: #edf8ff; margin-left: 15px; } .el-header .collapseBtn:hover{cursor:pointer;color:bisque;} .el-header .right { float: right; } .el-header .right a { color: #fff; } .el-aside { /* color: #32acca !important; */ background: #fff !important; border-right:1px solid #ccc; } .el-menu { border-right: none !important; /* background: #1f3146 !important; */ } .el-main { padding-top: 0 !important; } .el-footer { height: 40px !important; line-height: 40px !important; border-top: 1px solid #ccc; background: #f8fafd; padding: 10px; margin-left: 0; } .el-footer img { vertical-align: middle; width: 65px; margin-right: 10px; } </style>
AsyncComponent.vue
<template> <div> <!-- <is-loading v-if='isLoading'></is-loading> <loading-error v-if='isError' @reload='load' :errorDetails='errorDetails'></loading-error> --> <component :is="nowComponent"></component> </div> </template> <script> export default { data() { return { nowComponent: null, }; }, props: { componentPath: String, }, mounted() { this.load(); }, methods: { load() { this.nowComponent = () => import(`@/components/${this.componentPath}`) } } }; </script>