当前位置 : 主页 > 网络编程 > JavaScript >

vue如何点击多个tab标签打开关闭多个页面

来源:互联网 收集:自由互联 发布时间:2023-02-08
目录 点击多个tab标签打开关闭多个页面 需求 效果图 说一下思路 vue tab页多页面切换 点击多个tab标签打开关闭多个页面 需求 现将页面分为Header LeftSideBar Main三大模块 左侧LeftSideBar为m
目录
  • 点击多个tab标签打开关闭多个页面
    • 需求
    • 效果图
    • 说一下思路
  • vue tab页多页面切换

    点击多个tab标签打开关闭多个页面

    需求

    现将页面分为Header LeftSideBar Main三大模块 左侧LeftSideBar为menu菜单,点击菜单每一项,在Main中出现上部为tag标签,下部为内容 可打开多个tag标签 ,可内容切换 ,可关闭

    效果图

    1.router.js中(在LeftSideBar组件中现在有两个菜单项icons和tabs)

    {
          path:'/addtab',
          redirect:'/addtab/index',//重定向 输入/addtab 地址会变为/addtab/index
          component: Main,
          children:[
            {
              path:'index',//当addtab/index匹配成功时 TabContent会被渲染在Main中的router-view中
              name:'TabContent',
              component:()=>import("@/components/TabContent")
            }
          ]
    },
    {
          path:'/addicon',
          redirect:'/addicon/index',
          component: Main,
          children:[
            {
              path:'index',
              name:'IconContent',
              component:()=>import("@/components/IconContent")
            }
          ]
    }

    请戳--动态组件嵌套路由

    2.this.$router.push({name:"TabContent",params:{}})  实现点击左侧tab  打开组件main  

    3.在main中

    <template>
      <div>
        <TagsView/>
        <router-view></router-view>
      </div>
    </template>

    4.在TagsView中

    <template>
      <div class="tags-view-wrapper">
        <router-link class="tags-view-item" :to="item" :key="item.path" :class="isActive(item)?'active':''" v-for="(item) in Array.from(visitedViews)">
          {{item.params.name}}
          <span class='el-icon-close' @click.prevent.stop='closeSelectedTag(item)'></span>
        </router-link>
      </div>
    </template>

    a.添加标签的方法

    visitedViews是存放路由信息的数组

     
    addTags(){
         const route=this.$route;//获取地址栏路由
         this.$store.commit({
         type:'addTags',
         route
     })
    }
    在store.js中
    addTags(state, payload) {
          let flag = state.visitedTags.some(
            item => item.path === payload.route.path
          );//打开标签后,判断数组中是否已经存在该路由
          if (!flag) {
            state.visitedTags.push(
              Object.assign(
                {},
                {
                  path: payload.route.path,
                  name: payload.route.name,
                  params: payload.route.params
                }
              )
            );
          } //数组中路由存在不push ,单击左侧路由变化,点击标签路由变化均触发
        } //添加标签

    第一次点击是在mountd中触发addTags方法,后来的每次点击路由均会变化 ,使用watch监听触发

     watch:{
          $route(){
            this.addTags();
          }//地址栏变化了就触发这个添加方法
        }

    b.关闭标签

     在store.js中   

     closeTags(state, payload) {
          for (const [key, item] of state.visitedTags.entries()) {
            if (item.path === payload.view.path) {
              state.visitedTags.splice(key, 1);
              break;
            }
          }
        } //如果要关闭的标签在路由中存在则删除     

     在tagviews中

         isActive(route) {
            return route.path === this.$route.path
          },//当前地址栏路径是否与渲染的路径相同 样式匹配
          closeSelectedTag(view){
             this.$store.dispatch({
               type:"closeTags",
               view
             }).then((views)=>{
                此时的views是指的被删除后的visitedViews数组中存在的元素
               if (this.isActive(view)) {
                当前关闭的标签是否是被选中的标签
                 const latestView = views.slice(-1)[0];
                 if (latestView) {
                   this.$router.push(latestView);//如果数组不为空则让选中的标签为紧邻关闭标签的那一个
                 } else {
                   this.$router.push('/') ;//如果为空则页面跳转到/
                 }
               }
             })
          }

    说一下思路

    点击左侧的每一项都会打开一个组件(对应一个路由)  第一次点击时将路由信息push到visitedViews中 后来的每次点击都是通过watch $route执行添加标签方法 

    删除时要考虑是否是对激活项进行关闭 若是则先删除数组中要关闭的标签的那个路由,然后获取剩余visitedViews中的路由,让最后一个路由作为激活项

    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))
          })
        
        },
    

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持易盾网络。

    上一篇:简单谈一谈Vue中render函数
    下一篇:没有了
    网友评论