vue如何实现el-menu与el-tabs联动,通过点击el-menu导航中的选项动态添加el-tabs页面

2022-07-22 13:29:47

Vue如何实现el-menu与el-tabs联动,通过点击el-menu导航中的选项动态添加tab页面

老规矩,先上效果图!
在这里插入图片描述
达成这个效果,首先我们先了解下原理

在el-menu中有一个属性router,开发文档中写的非常清晰,选择该属性后即开启路由跳转,即点击el-menu中的子选项后会进行页面跳转,但是你必须将需要跳转的路由地址写为跟组件的子路由地址,否则点击跳转后会直接跳向路由地址对应的页面,这样就失去了我们想要实现的效果

接下来说下el-tabs,它的构成规则大家可以去看一下饿了么ui(element-ui)开发文档中的模板说明,它里面的子元素都是通过遍历数组出来的,我给大家看下模板
在这里插入图片描述
所以明确这个,大家就应该有了思路

下面我讲下原理,首先我们需要一个全局变量用来存储即将要跳转的路由地址是什么,将其构建成一个数组,这里可以用BUS总线机制但更为简洁高效的方式是使用Vuex,关于Vuex的开发文档大家可以简单了解下,其实很简单,不要觉得麻烦

在这里插入图片描述

这是它的构造图,我们就将Vuex简单的理解为一个全局变量,可以看到他的整体走向,首先从State开始,State的作用就是一个仓库,用来存储你想要存取的数据,通过Dispatch方法将数据派遣到Actions进行一些操作,之后Actions再向Mutations提交完成转变

原理很简单,这里我们可以省区中间Actions的步骤,直接从State仓库向Mutations提交完成一系列的操作

好了,有了这个全局变量后,接下来的操作就一切清晰明了了,下面是整个demo的设计思路

点击el-menu中的子选项(将每个子选项的index值改为要跳转页面的路由地址,例:/page1) ==> 将这些地址存入Vuex中的State仓库 ==> el-tabs中el-tab-pane的循环数组变为当前的State仓库(当你引用vuex后,State仓库中的数据会逐一派发给各个组件) ==> 在Mutaition中写明方法,将要跳转的路由地址对应的页面设为激活项(即el-tab-pane中激活的页面)

这样一说大家是不是思路就很清晰了! 下面开始上代码

首先我将整个页面拆分成了两大组件,分别是左侧的LeftMenu,和右侧主体页面TabInner(其中包含了顶部的导航栏和下面el-tabs展示的页面)
先看LeftMenu的代码

<el-menu:default-active="$route.path"class="el-menu-vertical-demo":collapse="isCollapse"
      background-color="#1F2D3D"
      text-color="#ffffff"
      router><el-menu-item
        index="/page1"class="homePage"
        style="margin: 0 0 30px 0;"><iclass="iconfont" style="margin: 0 8px 0 0;">&#xe653;</i><span slot="title">首页</span></el-menu-item><el-menu-item
        v-for="item of MenuList":key="item.id":index="item.index"><iclass="iconfont" style="margin: 0 8px 0 0;">{{item.icon}}</i><span slot="title">{{item.content}}</span></el-menu-item></el-menu>

大家可以看到el-menu中添加了router选项,即开启了路由跳转地址,:default-active为什么要等于$route.path呢,是因为这样可以根据你跳转的地址来动态的切换激活选项,如果你设为定值,大家可以自行看下控制台的报错信息

里面循环的data数据

MenuList:[{
        index:'/page2',
        content:'数据目录管理',
        icon:'\ue619'},{
        index:'/page3',
        content:'数据产品管理',
        icon:'\ue625'}]

icon是iconfont中的,若想使用请翻看我博客中关于iconfont如何加入在v-for循环的数据中

首页我单独放在了一个el-menu-item中,剩下的子页面用循环展示

<el-menu-item
        index="/page1"class="homePage"
        style="margin: 0 0 30px 0;"><iclass="iconfont" style="margin: 0 8px 0 0;">&#xe653;</i><span slot="title">首页</span></el-menu-item>

然后配置下路由地址,找到router.js或是模块化开发router文件夹下的index.js

{
      path:'/',
      component: Home,
      redirect:'/page1',
      children:[{
        path:'/page1',
        name:'首页',
        component: page1,
        meta:{ title:'首页'}},{
        path:'/page2',
        name:'数据目录管理',
        component: page2,
        meta:{ title:'数据目录管理'}},{
        path:'/page3',
        name:'数据产品管理',
        component: page3,
        meta:{ title:'数据产品管理'}}]}

将这些子页面设为根路径下的子路由,并将页面重定向设置为/page1(即redirect: ‘/page1’),这样可以在打开项目的时候直接展示首页

下一步安装Vuex

npm i vuex--save

安装好后,我们开始配置
首先在src目录下新建一个store文件夹,在里面创建一个index.js
在这里插入图片描述
在里面配置,这里我就不过多叙述了,大家按着我的来就可以

import Vuefrom'vue'import Vuexfrom'vuex'/* eslint-disable */

Vue.use(Vuex)exportdefaultnewVuex.Store({
  state:{
    openTab:[],
    activeIndex:''},
  mutations:{add_tabs(state, data){this.state.openTab.push(data)},delete_tabs(state, route){let index=0for(let gohhof state.openTab){if(gohh.route=== route){break}
        index++}this.state.openTab.splice(index,1)},set_active_index(state, index){this.state.activeIndex= index}}})

写完后在main.js中引入vuex,这样就可以将数据派发到各个组件上

import Vuefrom'vue'import Appfrom'./App'import routerfrom'./router'import ElementUIfrom'element-ui'import'element-ui/lib/theme-chalk/index.css'import'./assets/iconfont/iconfont.css'import storefrom'./store/index.js'

Vue.use(ElementUI)

Vue.config.productionTip=false/* eslint-disable no-new */newVue({
  el:'#app',
  router,
  store,
  components:{ App},
  template:'<App/>'})

即把store引入,并在下面注册

做好这些后我简单说下store里面的index.js中文件的内容大概是什么意思

首先state仓库中分别存储了两个信息,一个是存放所有跳转路由地址的数组openTab,另一个是el-tab-pane哟弄个来展示当前激活页面的activeindex

那么mutations中存放的add_tabs是点击左侧el-menu中选项时触发的方法,即接受当前跳转的路由地址并将这一信息推入openTab这个数组中, 而delete_tabs则是将开启的tab标签关掉并设置下一激活项,set_active_index是设置激活项的方法

配置好这些以后,我们在tabInner组件中(即右侧主题内容组件)开始编写代码

首先书写计算属性computed,将el-tab-pane需要循环使用的openTab数组和展示激活项的activeIndex引入过来

computed:{openTab(){returnthis.$store.state.openTab},
    activeIndex:{get(){returnthis.$store.state.activeIndex},set(val){this.$store.commit('set_active_index', val)}}}

之后在el-tabs中写入

<el-tabs
          v-model="activeIndex"
          type="card"
          @tab-click="clickTab"
          @tab-remove="removeTab"
          closable><el-tab-pane
            v-for="item of openTab"
            v-if="openTab.length":key="item.name":label="item.name":name="item.route"></el-tab-pane></el-tabs>

双向绑定activeIndex即可展示对应激活项

之后通过监听方法watch监听路由变化做事件处理

watch:{'$route'(to,from){let flag=falsefor(let itemofthis.openTab){if(item.name=== to.name){this.$store.commit('set_active_index', to.path)
          flag=truebreak}}if(!flag){this.$store.commit('add_tabs',{route: to.path, name: to.name})this.$store.commit('set_active_index', to.path)}}}

这里为什么会定义一个值为布尔属性的变量flag呢,大家可以阅读下代码,意为,首先进行for循环,若此时openTab并未推入任何数据,是一个空数组,那么下面的if判断就不会成立,固flag不能变为true,从而进行下面的if(!flag)判断,若里面值为真才可进行其中的操作事件,此时flag仍未false,!flag即为true,我们提交两个方法,一个是当前路由地址推入state仓库中的openTab数组,另一个是设置el-tab-pane的激活项,即打开对应的页面

上面的话简单的可以理解为,若openTab中含有数据,那么我进行下面的判断,如果成立(意思就是左侧点击的导航项已经在el-tabs中打开了一个标签了,已经存在的页面就不会再打开了,直接进行页面的切换就可以了)然后flag变为true,并跳出循环,那么!flag就变成假了,下面的if判断也不会在做了。 但是如果openTab中并没有当前路由地址对应的页面信息,那么我就把这个信息存进去,并把el-tab-pane的激活项设置为他

mounted(){// 刷新时以当前路由做为tab加入tabs// 当前路由不是首页时,添加首页以及另一页到store里,并设置激活状态// 当当前路由是首页时,添加首页到store,并设置激活状态if(this.$route.path!=='/'&&this.$route.path!=='/page1'){this.$store.commit('add_tabs',{route:'/page1', name:'首页'})this.$store.commit('add_tabs',{route:this.$route.path, name:this.$route.name})this.$store.commit('set_active_index',this.$route.path)}else{this.$store.commit('add_tabs',{route:'/page1', name:'首页'})this.$store.commit('set_active_index','/page1')}}

同时我们也要再mounted中加入以下代码,这里写的很详细了,大家自行阅读下

做完这些后,我们需要把tab-click和tab-remove两个点击事件书写一下
首先是tab-click对应的方法clickTab

clickTab(tab){this.$router.push({path:this.activeIndex})
      console.log(this.$route.path)}

点击事件后,直接推向当前对应的激活项

接下来是tab-remove对应的方法removeTab

removeTab(target){if(target=='/'||target=='/page1'){return}this.$store.commit('delete_tabs', target)if(this.activeIndex=== target){// 设置当前激活的路由if(this.openTab&&this.openTab.length>=1){
          console.log('=============',this.openTab[this.openTab.length-1].route)this.$store.commit('set_active_index',this.openTab[this.openTab.length-1].route)this.$router.push({path:this.activeIndex})}}}

第一个判断是保证首页不关,如果当前路径是根路径或者是我首页的路径,那么直接return结束当前函数

讲解了上面的代码后大家应该可以理解明白后边的代码了
自己阅读理解能更多的提升自己
做好这些操作就可以实现效果啦!样式大家可以自己去定义,只要按照这个思路走一遍就没问题啦

希望我们共同进步,互相提升! 有更好更高效的方法还望大佬不吝赐教!

  • 作者:YodgeGGG
  • 原文链接:https://blog.csdn.net/RoonaTh/article/details/110133062
    更新时间:2022-07-22 13:29:47