【Vue实用功能】Vue实现tab页多页面切换

  • Post author:
  • Post category:vue



Vue实现tab页多页面切换


实现路由发生变化时,新增一个tab标签页,点击其他标签时切换到对应的页面,刷新网页同时保留状态

在这里插入图片描述

这里就直接说它实现的代码就OK!!!

VueX记录下每次新增后的tab标签页路由


store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    // 路由导航start
    // 缓存组件页面
    catch_components: [],
    // 当前选中的菜单 - 默认选择首页
    activePath: '/index',
    // 菜单项 - 默认需要展示的页面()
    tabList: [{
      path: '/index',
      label: '首页',
      name: 'index',
      fullPath: "/index"
    }],
    // 路由导航end
  },
  // 更改vuex的store中状态的唯一方法 - 同步操作
  mutations: {
    // 路由导航start
    //清空vuex数据
    clearVUEX(state) {
      state.catch_components = []
      state.activePath = 'index'
      state.tabList = [{
        path: '/idnex',
        label: '首页',
        name: 'index',
        fullPath: "/index"
      }]
    },
    // 跳转页面执行
    selectMenu(state, submenu) {
      // 首页就是 wellcome   也就是 home
      if (submenu.name === 'index') {
        submenu.name = 'index'
        label.path = '首页'
        submenu.path = '/index'
        submenu.fullPath= '/index'
      }
      // 当前选中菜单
      var activePath = submenu.name
      // 历史已选中菜单列表
      var oldTabList = state.tabList

      // 将菜单信息添加到tablist - 添加时判断是否已有该路由标签
      var result = oldTabList.some(item => {
        if (item.name === activePath) {
          // console.log('--------', item.fullPath != submenu.fullPath)
          // 有该路由标签是否为多次点击(相当于查看同路由下的详情,该过程只改变了参数)
          if (!item.fullPath != submenu.fullPath) {
            item.fullPath = submenu.fullPath
          }
          return true
        }
      })
      // 如果不包含该对象,则添加
      if (!result) {
        oldTabList.push({
          path: submenu.name,
          name: submenu.name,
          label: submenu.label,
          fullPath: submenu.fullPath
        })
      }
      // 重新赋值标签路由和当前选中菜单
      state.activePath = activePath
      state.tabList = oldTabList
    },
    // 添加keepalive缓存
    addKeepAliveCache(state, val) {
      // 如果是首页不缓存
      if (val === 'index') {
        return
      }
      // console.log(state.catch_components)

      // 添加时判断,如果该组件已存在,便不添加
      if (state.catch_components.indexOf(val) === -1) {
        // 不存在,缓存页面
        state.catch_components.push(val)
      }
    },
    // 删除keepalive缓存
    removeKeepAliveCache(state, val) {
      let cache = state.catch_components
      for (let i = 0; i < cache.length; i++) {
        if (cache[i] === val) {
          cache.splice(i, 1);
        }
      }
      state.catch_components = cache
    },
    //关闭菜单
    closeTab(state, val) {
      // 重新赋值
      state.activePath = val.activePath
      state.tabList = val.tabList
    },
    // 点击标签选择菜单
    changeMenu(state, val) {
      state.activePath = val
    },
    // 路由导航end

  },
  actions: {

  }
})


根据自己的需求定义一个展示路由标签组件vue文件


BScroll :

当路由标签过多时,用于横向滚动标签页

 <!-- crumbs.vue -->
<template>
  <div class="tags">
    <div class="horizontal-container">
      <div class="scroll-wrapper" ref="scroll">
        <div class="scroll-content">
          <el-tag size="medium" v-for="(tab, index) in tabList" :key="tab.path" @close="handleClose(tab, index)"
            @click="changeMenu(tab)" :closable="tab.name !== 'index'"
            :effect="activePath === tab.name ? 'dark' : 'plain'">
            {{tab.label}}
          </el-tag>
        </div>
      </div>
    </div>

  </div>
</template>

<script>
  import {
    mapState
  } from 'vuex';

  import BScroll from '@better-scroll/core'

  export default {
    data() {
      return {
        //菜单列表
        menuList: [],

      }
    },
    computed: {
      ...mapState({ // 从 state 中的到的计算属性
        activePath: state => state.activePath, // 已选中菜单
        tabList: state => state.tabList, // tags菜单列表
        catch_components: state => state.catch_components, // keepalive缓存
      })
    },
    mounted() {
      // this.handleCommand()
      this.init()

    },
    methods: {
      init() {
        this.bs = new BScroll(this.$refs.scroll, {
          scrollX: true,
          probeType: 3 // listening scroll event
        })
      },
      // 清空当前vuex数据
      handleCommand() {
        this.$store.commit('clearVUEX')

      },
      // 点击菜单 - 传入name,添加到keepalive缓存页面
      selectMenu(item) {
        // console.log(item.name)
        // 加入keepalive缓存
        this.$store.commit('addKeepAliveCache', item.name)
        //添加tags标签
        //访问wellcome 就代表home
        var name = item.name === 'index' ? 'index' : item.name
        var submenu = {
          path: item.path,
          name: name,
          label: item.meta.title,
          fullPath: item.fullPath
        }
        // console.log(submenu)
        //更新选中菜单
        this.$store.commit('selectMenu', submenu)
        console.log(this.$store.state.tabList)
      },
      // 点击标签跳转路由
      changeMenu(item) {
        // 历史选中菜单
        var oldActivePath = this.$store.state.activePath
        // 首先判断点击的是否是自己,如果是自己则return
        if (oldActivePath === item.name) {
          return
        }
        // 存储菜单
        this.$store.commit('changeMenu', item.name)
        // 页面跳转
        this.$router.push({
          path: item.fullPath
        })
      },
      // 关闭tab标签
      handleClose(tab, index) {


        // 历史选中菜单
        var oldActivePath = this.$store.state.activePath
        // 历史已选中菜单列表
        var oldTabList = this.$store.state.tabList
        // 计算标签个数
        let length = oldTabList.length - 1
        // 删除tabList中的该对象
        for (let i = 0; i < oldTabList.length; i++) {
          let item = oldTabList[i]
          if (item.name === tab.name) {
            oldTabList.splice(i, 1);
          }
        }
        // 删除keepAlive缓存
        this.$store.commit('removeKeepAliveCache', tab.name)
        // 如果关闭的标签不是当前路由的话,就不跳转
        if (tab.name !== oldActivePath) {
          return
        }
        // 如果length为1,必然只剩下首页标签,此时关闭后,更新到首页
        if (length === 1) {
          // 同时存储菜单
          this.$store.commit('closeTab', {
            activePath: 'home',
            tabList: oldTabList
          })
          // tab页向左跳转
          this.$router.push({
            name: oldTabList[index - 1].name
          })
          // 不再向下执行
          return
        }
        // 关闭的标签是最右边的话,往左边跳转一个
        if (index === length) {
          // 同时更新路径
          oldActivePath = oldTabList[index - 1].name
          // 同时存储菜单
          this.$store.commit('closeTab', {
            activePath: oldActivePath,
            tabList: oldTabList
          })
          // tab页向左跳转
          this.$router.push({
            name: oldTabList[index - 1].name
          })
        } else {
          // 同时更新路径
          oldActivePath = oldTabList[index].name
          // 同时存储菜单
          this.$store.commit('closeTab', {
            activePath: oldActivePath,
            tabList: oldTabList
          })
          // tab页向右跳转
          this.$router.push({
            name: oldTabList[index].name
          })
        }
      },
    },


    watch: {
      // 路由发生变化时调用更新tab标签数据
      '$route': {
        handler(newValue) {
          // console.log(newValue, oldValue)
          this.selectMenu(newValue);
        },
        immediate: true
      }



    },


  }
</script>


<style lang="less" scoped="scoped">
  /deep/ .el-tag--medium {
    margin-right: 10px;
  }

  .horizontal-container {
    .scroll-wrapper {
      position: relative;
      width: 100%;
      // margin: 80px auto;
      margin: 0 auto;
      white-space: nowrap;
      // border: 3px solid #42b983;
      border-radius: 5px;
      overflow: hidden;

      .scroll-content {

        display: inline-block;
      }

      .scroll-item {
        height: 40px;
        line-height: 40px;
        // font-size: 24px;
        display: inline-block;
        text-align: center;
        padding: 0 10px;
      }

    }
  }

  /deep/.el-tabs__nav-scroll {
    background: #fff;
  }


  .el-tag {
    cursor: pointer;
    margin-left: 10px;
    border-radius: 2px;
    font-size: 12px;
    color: #1890FF;
    border-color: #1890FF;
  }

  .el-tag--dark {
    color: #fff;
    background-color: #1890FF;
  }

  .el-dropdown-link {
    cursor: pointer;
  }

  .el-icon-arrow-down {
    font-size: 12px;
  }

  .submit-row {
    display: flex;
    flex-direction: row;
    justify-content: flex-end;
    align-items: center;
  }
</style>


若F5或者强刷新页面时需要保留当前tab路由数据,在App.vue中插入代码

    created() {
      //在页面刷新时将vuex里的信息保存到sessionStorage里
      window.addEventListener("beforeunload", () => {
        sessionStorage.setItem("store", JSON.stringify(this.$store.state))
      })
    

    },



版权声明:本文为weixin_44590591原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。