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

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

老规矩,先上效果图!

达成这个效果,首先我们先了解下原理

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

接下来说下el-tabs,它的构成规则大家可以去看一下饿了么ui(element-ui)开发文档中的模板说明,它里面的子元素都是通过遍历数组出来的,我给大家看下模板
vue如何实现el-menu与el-tabs联动,通过点击el-menu导航中的选项动态添加el-tabs页面_第1张图片
所以明确这个,大家就应该有了思路

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

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

这是它的构造图,我们就将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;"
      >
        <i class="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"
      >
        <i class="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;"
      >
        <i class="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 Vue from 'vue'
import Vuex from 'vuex'
/* eslint-disable */

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    openTab: [],
    activeIndex: ''
  },
  mutations: {
    add_tabs (state, data) {
      this.state.openTab.push(data)
    },
    delete_tabs (state, route) {
      let index = 0
      for (let gohh of 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 Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import './assets/iconfont/iconfont.css'
import store from './store/index.js'

Vue.use(ElementUI)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: ''
})

即把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 () {
      return this.$store.state.openTab
    },
    activeIndex: {
      get () {
        return this.$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 = false
      for (let item of this.openTab) {
        if (item.name === to.name) {
          this.$store.commit('set_active_index', to.path)
          flag = true
          break
        }
      }

      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结束当前函数

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

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

你可能感兴趣的:(前端,element-ui,vue,vue,vue.js,javascript,css3)