使用vue+element-ui实现侧边栏菜单el-menuel-tabs标签页联动效果

先看实现效果
在这里插入图片描述

实现思路

  1. el-menu使用vue-router的模式,以index进行路由跳转,当前激活菜单:default-active="$route.path",这样就会默认以当前路由路径高亮菜单选项,解决刷新等情况的高亮问题,主要代码
    <el-menu :default-active="$route.path"
      class="el-menu-vertical-demo"
      background-color="#2f3640"
      text-color="#fff"
      :style="{width:asideBarWidth}"
      :width="asideBarWidth"
      :collapse="$store.state.isCollapse"
      :unique-opened="true"
      :router="true"
      active-text-color="#409EFF">
      <menu-item v-for="item in menuData"
        :key="item.path"
        :item="item" />
    </el-menu>
  1. el-tabs:value(选中选项卡的 name)也是由$route.path进行控制,el-tabs的数据结构是这样的:[{title: '首页',path: '/home',name: 'Home'}](vuex管理),当点击的通过path进行路由跳转,删除时,如果是删除当前页,则需要获取下一个标签页的路径,再进行跳转。
<template>
  <el-tabs type="card"
    :value="$route.path"
    @tab-click="tabClick"
    closable
    class="my-tabs"
    @edit="handleTabsEdit">
    <el-tab-pane :key="item.path"
      v-for="(item) in tabsValue"
      :label="item.title"
      :name="item.path">
    </el-tab-pane>
  </el-tabs>
</template>

<script>
import { mapState } from 'vuex'
import { setSessionTabsValue } from '@/utils/storage'
export default {
  name: 'TabsNav',
  data () {
    return {
    }
  },
  created () {

  },
  watch: {

  },
  mounted () {
    // 监听刷新,将数据保存到sessionStorage里
    window.addEventListener('beforeunload', this.setStorage)
  },
  destroyed () {
    window.removeEventListener('beforeunload', this.setStorage)
  },
  computed: {
    ...mapState({
      tabsValue: state => state.tabsNav.tabsValue
    })
  },
  methods: {
    setStorage () {
      console.log('setStorage')
      setSessionTabsValue(this.tabsValue)
    },
    handleTabsEdit (targetPath, action) {
      // 删除选项卡
      if (action === 'remove') {
        if (targetPath === '/home') return
        let nextTab = {}
        // 找到下一个路由
        this.tabsValue.forEach((item, index) => {
          if (item.path === targetPath) {
            nextTab = this.tabsValue[index + 1] || this.tabsValue[index - 1]
          }
        })
        this.$store.commit('REMOVE_TABS_VALUE', targetPath)
        // 如果删除的是当前页,则进行跳转
        targetPath === this.$route.path && this.$router.push(nextTab.path)
      }
    },
    tabClick ({ name }) {
      if (name === this.$route.path) return
      this.$router.push(name)
    }
  }
}
</script>
  1. 使用vuex管理标签页的状态
import {
  getSessionTabsValue
} from '@/utils/storage'

// 初始化
const initTabValue = getSessionTabsValue() || {
  tabsValue: [{
    title: '首页',
    path: '/home',
    name: 'Home'
  }]
}

const tabsNav = {
  state: () => (initTabValue),
  mutations: {
    /**
     * 删除标签页
     * @param {*} state
     * @param {*} targetPath
     */
    REMOVE_TABS_VALUE(state, targetPath) {
      state.tabsValue = state.tabsValue.filter(item => item.path !== targetPath)
    },
    /**
     * 添加标签页,在MenuItem组件触发
     * @param {*} state
     * @param {*} data
     */
    ADD_TABS_VALUE(state, data) {
      // 判断是否已经存在
      const isExist = state.tabsValue.some(item => item.path === data.path)
      // 如果不存在添加
      if (!isExist) {
        state.tabsValue.push(data)
      }
    },
    /**
     * 初始化数据,在注销时调用,不然切换用户后还是旧标签数据
     * @param {*} state
     */
    RESET_TABS_VALUE(state) {
      state = initTabValue
    }

  },
  actions: {},
  getters: {}
}

export default tabsNav
  1. 监听路由跳转,即路由跳转时,判断是否使用标签页管理,如果是,再判断当前路由是否已经加入tabsValue(在mutations的ADD_TABS_VALUE判断)
// 路由对象数据结构
{
    path: '/home',
    name: 'Home',
    component: Home,
    meta: {
      title: '首页',
      isTabsPage: true
    }
  }

router.beforeEach((to, from, next) => {
  if (to.meta.title) { // 如果设置标题,拦截后设置标题
    document.title = to.meta.title
  }

  if (to.path !== '/login') {
    if (!store.getters.isLogin) {
      Message.error('请先登录')
      next('/login')
      return
    }
    // 判断是否需要添加到tabs标签
    if (to.meta.isTabsPage) {
      const tabsItem = {
        name: to.name,
        title: to.meta.title,
        path: to.path
      }
      store.commit('ADD_TABS_VALUE', tabsItem)
    }
  }
  next()
})
  1. 使用keep-alive对在标签页的页面进行缓存
 <el-main>
    <transition name="fade-transform" mode="out-in">
       <keep-alive :include="cachedViews">
         	<router-view></router-view>
       </keep-alive>
    </transition>
</el-main>

cachedViews () {
     return this.$store.state.tabsNav.tabsValue.map(item => item.name)
}

这样就实现了上面的效果,但是还有许多地方可以优化,并且耦合性有点高…

Logo

前往低代码交流专区

更多推荐