美文网首页苏苏的vue之旅~
vue实现多个tab标签页的切换与关闭

vue实现多个tab标签页的切换与关闭

作者: 苏苏哇哈哈 | 来源:发表于2022-03-06 23:40 被阅读0次

    1.实现效果

    tab切换.gif

    2.实现原理

    • vuex,实现对当前激活项,当前tab列表,当前tab的translateX,当前缓存页,当前路由的状态管理。

    • 将vuex中的数据保存到sessionStorage中,避免页面刷新丢失,当浏览器关闭时,清空数据。

    • 通过ref定位,拿到当前窗口宽度与当前所在路由的tab标签的所有宽度,判断两者,实现对多tab超出窗口宽度的处理。

    • 当点击tab标签页的时候,获取相应的激活项,动态的实现左侧菜单栏的选中状态,用watch监听,updateActiveName和updateOpened。

    • 当关闭tab标签的时候,splice删除当前标签,若是删除的最后一项,跳转到该项的前一项页面。当长度为1时,跳转到首页。

    3.主要代码

    store.js

    import Vue from 'vue';
    import Vuex from 'vuex';
    Vue.use(Vuex);
    export default new Vuex.Store({
        state: {
            catch_components: [],
            activePath: '/index',
            openNames: [],
            activeName: "",
            tranx: "-0",
            tabList: [
                { path: '/index', label: '首页', name: '首页' }
            ]
        },
        mutations: {
     
            //清空vuex数据
            clearTabs(state) {
                state.catch_components = []
                state.activePath = '/homepage'
                state.openNames = []
                state.activeName = ""
                state.tranx = "-0"
                state.tabList = [
                    { path: '/homepage', label: '首页', name: 'home' }
                ]
            },
            // 跳转页面执行
            selectMenu(state, submenu) {
                var activePath = submenu.path
                var oldTabList = state.tabList
                var result = oldTabList.some(item => {
                    if (item.path === activePath) {
                        return true
                    }
                })
                if (!result) {
                    oldTabList.push({
                        path: submenu.path,
                        name: submenu.name,
                        label: submenu.label,
                        index: submenu.index,
                        subName: submenu.subName
                    })
                }
                state.activePath = activePath
                state.tabList = oldTabList
                state.activeName = submenu.subName + "-" + submenu.index
                state.openNames = [submenu.subName]
            },
            // 添加keepalive缓存
            addKeepAliveCache(state, val) {
                if (val === '/homepage') {
                    return
                }
                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
            },
            setTranx(state, val) {
                console.log(val)
                state.tranx = val
            },
            //关闭菜单
            closeTab(state, val) {
                state.activePath = val.activePath
                state.tabList = val.tabList
                state.openNames = val.openNames
                state.activeName = val.activeName
            },
            // 点击标签选择菜单
            changeMenu(state, val) {
                state.activePath = val.path
                state.activeName = val.subName + "-" + val.index
                state.openNames = [val.subName]
            }
        },
    })
    

    页面代码

    <Menu
      ref="asideMenu"
      :active-name="activeName"
      :open-names="openNames"
      accordion
      theme="light"
      :style="{ width: 'auto' }"
      :class="isCollapsed ? 'collapsed-menu' : 'menu-item'"
    >
      <MenuItem
        @click.native="selectMenu({ path: '/index', title: '首页' })"
        name="index"
        key="index"
      >
        <Icon type="ios-paw"></Icon>
        <span class="menuTitle">首页</span>
      </MenuItem>
      <Submenu
        v-for="(item, index) in menuMap"
        :name="index"
        :key="index"
        class="sub_title"
      >
        <template slot="title">
          <svg class="icon" aria-hidden="true" v-if="item.fonticon">
            <use :xlink:href="item.fonticon"></use>
          </svg>
          <Icon :type="item.icon" v-else />
          <span class="menuTitle">{{ item.title }}</span>
        </template>
        <template v-if="item.children">
          <MenuItem
            v-for="(each, i) in item.children"
            :name="index + '-' + i"
            :key="index + '-' + i"
            @click.native="selectMenu(each, i, index)"
            ><span class="menuTitle">{{ each.title }}</span>
          </MenuItem>
        </template>
      </Submenu>
    </Menu>
    
    <Row class="head-tags">
      <div class="head-left left" @click="setTranx(0)">
        <Icon type="ios-rewind" size="30" color="#ffc0cb" />
      </div>
      <div class="tags-box">
        <div
          ref="tags"
          class="tags-box-scroll"
          :style="{ transform: `translateX(${tranx}px)` }"
        >
          <Tag
            :ref="'tag' + index"
            class="tags-item"
            :class="{ 'tags-item-active': activePath === item.path }"
            v-for="(item, index) in tabList"
            :key="index"
            :name="item.path"
            :closable="item.path !== '/index'"
            @click.native="changeMenu(item)"
            @on-close="handleClose(item, index)"
            >{{ item.label }}</Tag
          >
        </div>
      </div>
      <div class="head-left right" @click="getTrans(1)">
        <Icon type="ios-fastforward" size="30" color="#ffc0cb" />
      </div>
    </Row>
    
    computed: {
      ...mapState({
      activePath: (state) => state.activePath, // 已选中菜单
        tabList: (state) => state.tabList, // tags菜单列表
        catch_components: (state) => state.catch_components, // keepalive缓存
        openNames: (state) => state.openNames,
        activeName: (state) => state.activeName,
        tranx: (state) => state.tranx,
      }),
    },
    watch: {
       openNames() {
         this.$nextTick(() => {
           this.$refs.asideMenu.updateOpened();
         });
       },
       activeName() {
         this.$nextTick(() => {
           this.$refs.asideMenu.updateActiveName();
         });
       },
     },
    handleClose(tab, index) {
      var oldOpenNames = this.$store.state.openNames,
        oldActiveName = this.$store.state.activeName,
        oldActivePath = this.$store.state.activePath,
        oldTabList = this.$store.state.tabList;
      let length = oldTabList.length - 1;
      for (let i = 0; i < oldTabList.length; i++) {
        let item = oldTabList[i];
        if (item.path === tab.path) {
          oldTabList.splice(i, 1);
        }
      }
      // 删除keepAlive缓存
      this.$store.commit("removeKeepAliveCache", tab.path);
      if (tab.path !== oldActivePath) {
        return;
      }
      if (length === 1) {
        this.$store.commit("closeTab", {
          activePath: "/index",
          tabList: oldTabList,
        });
        this.$router.push({ path: oldTabList[index - 1].path });
        return;
      }
      if (index === length) {
        oldActivePath = oldTabList[index - 1].path;
        oldOpenNames = [oldTabList[index - 1].subName];
        oldActiveName =
          oldTabList[index - 1].subName + "-" + oldTabList[index - 1].index;
        this.$store.commit("closeTab", {
          activePath: oldActivePath,
          tabList: oldTabList,
          openNames: oldOpenNames,
          activeName: oldActiveName,
        });
        this.$router.push({ path: oldTabList[index - 1].path });
      } else {
        oldActivePath = oldTabList[index].path;
        oldOpenNames = [oldTabList[index].subName];
        oldActiveName =
          oldTabList[index].subName + "-" + oldTabList[index].index;
        this.$store.commit("closeTab", {
          activePath: oldActivePath,
          tabList: oldTabList,
          openNames: oldOpenNames,
          activeName: oldActiveName,
        });
        this.$router.push({ path: oldTabList[index].path });
      }
      this.getTrans(2);
    },
    changeMenu(item) {
      var oldActivePath = this.$store.state.activePath;
      if (oldActivePath === item.path) {
        return;
      }
      this.$store.commit("changeMenu", item);
      this.$router.push({ path: item.path });
      this.$nextTick(() => {
        this.getTrans(0);
      });
    },
    selectMenu(item, i, subName) {
      // 加入keepalive缓存
      this.$store.commit("addKeepAliveCache", item.path);
      var submenu = {
        path: item.path,
        name: item.title,
        label: item.title,
        index: i,
        subName: subName,
      };
      this.$store.commit("selectMenu", submenu);
      this.$router.push({ path: item.path });
      this.$nextTick(() => {
        this.getTrans(0);
      });
    },
    getTrans(e) {
      let width = 0;
      if (this.$refs.tags) {
        width = this.$refs.tags.clientWidth;
      }
      this.tabList.map((item, index) => {
        if (item.path === this.activePath) {
          this.currentIndex = index;
        }
        if (this.$refs[`tag${index}`] && this.$refs[`tag${index}`][0]) {
          this.$set(
            this.tabList[index],
            "width",
            this.$refs[`tag${index}`][0].$el.clientWidth + 4
          );
        }
      });
      let list = this.tabList.filter((item, index) => {
        return index <= this.currentIndex;
      });
      let totalWidth = list.reduce((total, currentValue) => {
        return total + Number(currentValue.width);
      }, 0);
      let totalAllWidth = this.tabList.reduce((total, currentValue) => {
        return total + Number(currentValue.width);
      }, 0);
    
      if (e == 0) {
        if (Number(width) > Number(totalWidth) || Number(width) == 0) {
          this.setTranx(-0);
          return false;
        }
        this.setTranx(Number(width) - Number(totalWidth) - 60);
      } else if (e == 1) {
        if (Number(width) > Number(totalAllWidth)) {
          return false;
        }
        this.setTranx(Number(width) - Number(totalAllWidth) - 60);
      } else {
        if (
          Number(width) > Number(totalAllWidth) &&
          this.$store.state.tranx < 0
        ) {
          this.setTranx(-0);
        }
      }
    },
    setTranx(val) {
      this.$store.commit("setTranx", val);
    },
    

    4.完整代码,关注公众号 苏苏的bug,更多vue相关,尽在苏苏的码云如果对你有帮助,欢迎你的star+订阅!

    相关文章

      网友评论

        本文标题:vue实现多个tab标签页的切换与关闭

        本文链接:https://www.haomeiwen.com/subject/jzrgrrtx.html