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

这篇具有很好参考价值的文章主要介绍了vue实现多个tab标签页的切换与关闭。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.实现效果

vue 关闭页签,javascript,vue,vue.js,javascript,前端

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

页面代码文章来源地址https://www.toymoban.com/news/detail-613196.html

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

到了这里,关于vue实现多个tab标签页的切换与关闭的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue项目关于iframe嵌套的页面,在切换tab标签时会被重新刷新的问题处理方案

    这两天工作中遇到一个这样的需求,切换tab标签时,要求对应的tab页面不刷新,但是项目中加入了一部分含有iframe的页面,在切换路由的过程中,如果使用keep-alive是达不到缓存ifram嵌套的页面效果的。 vue中的keep-alive 1.原理:vue 的缓存机制并 不是直接存储 DOM 结构 ,而是将

    2024年02月04日
    浏览(80)
  • 前端Vue自定义验证码密码登录切换tabs选项卡标签栏标题栏 验证码登录模版 密码登录模版

    前端Vue自定义验证码密码登录切换tabs选项卡标签栏标题栏 验证码登录模版 密码登录模版, 请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=13221 效果图如下: 实现代码如下: 使用方法 HTML代码实现部分 组件实现代码

    2024年02月11日
    浏览(57)
  • 5.6.1 Ext JS之标签页的关闭和批零关闭

    Tab Panel 是包含多个标签页的面板, 这是一种很常用的组件, 类似于浏览器的标签页。关于 Ext JS的Tab Panel的基本使用可以参考: [Ext JS3.9] 标签面板(TabPanel )介绍与开发, 本篇介绍如何关闭单个标签页和批量关闭标签页。 默认状况下,标签页是无法关闭的, 通过配置某个

    2024年02月08日
    浏览(47)
  • 28.HarmonyOS App(JAVA)多页签的实现(Tab)

     HarmonyOS App(JAVA)多页签的实现(Tab) 页面可左右滑动,点击界面1,2,3切换到对应界面   在layout目录下的xml文件中创建PageSlider。 PageSlider ohos:id=\\\"$+id:page_slider\\\" ohos:height=\\\"300vp\\\" ohos:width=\\\"300vp\\\" ohos:layout_alignment=\\\"horizontal_center\\\" / 每个页面可能需要呈现不同的数据,因此需要适配不同的

    2024年03月09日
    浏览(22)
  • Vue3实现带动画效果的tab栏切换

    效果图如下所示: 实现思路: 其实很简单 1、首先切换tab栏时tab标签激活下标与对应显示内容下标要一致。 2、其次点击tab栏切换时更新下标 3、最后就是css添加动画效果 这样就 了!!! 上全部代码

    2024年02月22日
    浏览(39)
  • element-ui tab标签禁止切换

    给el-tabs标签设置style=“pointer-events: none;” 注意:每个el-tab-pane标签也要加上style=\\\"pointer-events: auto;\\\"否则各个页面的所有鼠标事件都会失效 HTML JS

    2024年02月11日
    浏览(47)
  • 三种方法实现tab栏切换(CSS方法、JS方法、Vue方法)

    给下图的静态页面添加tab栏切换效果  

    2024年02月13日
    浏览(47)
  • vue实现弹出框内嵌页面展示,添加tab切换展示实时加载

    最近做业务的时候,发现产品的原型图上有一个弹出框,上面包含了两个窗口要进行切换。 每个窗口都有分页列表展示、搜索、添加和删除,感觉就是两个完整的页面,如果全写在一个页面会很麻烦,还可能会出现一系列的问题,后期改起来比较麻烦,所以我就准备分开来写

    2024年02月16日
    浏览(41)
  • 解决element ui中el-tabs标签点击切换闪屏问题

    现象:点击切换 element ui中el-tabs时候,table会出现闪一下的状况; 初始element ui中el-tabs组件代码如下: 使用v-if=\\\"activeName===\\\'first\\\'\\\" 解决闪屏   改造后代码如下:

    2024年02月07日
    浏览(48)
  • Windows 10 修改 Alt+Tab 键 切换 新版 Microsoft Edge 单个标签页窗口

    解决方案:在 Windows 设置 的搜索框中 搜索 alt ,选择 选择按下 Alt+Tab 时显示的窗口和选项卡 ,将 按 Alt +Tab 将显示 选为 仅打开的窗口 我的小站、Github 详细过程: 在 Windows 10 Build 20161 中,微软更新了 Alt+Tab 的默认行为,切换界面中包括了 Microsoft Edge 的标签页面(Chromium版本

    2024年02月06日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包