vue点击菜单实现多标签页tab,打开关闭多个页面

在这里插入图片描述

1.Test.vue代码
1.1

<template>
  <div>
    <div>
      <div class="m-tab-container">
        <div>
          <router-link
            class="m-tab-item"
            :to="item"
            :key="item.path"
            :class="isActive(item) ? 'active' : ''"
            v-for="item in Array.from(visitedMenus)"
          >
            {{ item.titleText }}
            <span
              class="m-icon-close el-icon-close"
              @click.prevent.stop="closeSelectedMenu(item)"
            ></span>
          </router-link>
        </div>
      </div>
    </div>
  </div>
</template>

1.2js

<script>
export default {
  name: "",
  data() {
    return {
      visitedMenus: [],
    };
  },
  created() {
    this.visitedMenus = this.$store.state.visitedMenus;
  },
  methods: {
    isActive(route) {
      // 判断当前地址栏地址,并激活样式
      return route.path == this.$route.path;
    },
    closeSelectedMenu(page) {
      this.$store.dispatch({ type: "closeMenu", page }).then((pages) => {
        let deletekey = pages.deletekey;
        let visitedMenus = pages.visitedMenus;
        if (this.isActive(page)) {
          // slice() 方法可从已有的数组中返回选定的元素。
          // slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分。
          // 注意: slice() 方法不会改变原始数组。
          let latestPage;
          if (visitedMenus.length != 0) {
            // 删除最后一个,默认前一个。不是最后一个,默认后一个
            if (deletekey == visitedMenus.length) {
              latestPage = visitedMenus.slice(-1)[0];
            } else {
              latestPage = visitedMenus.slice(deletekey, deletekey + 1)[0];
            }
            this.$router.push(latestPage);
          } else {
            // 已删除完,默认登陆成功后的页面。由于动态设置的接口返回导航数据,没有中文名称,不做限制
            this.$message.warning("请至少保留一个!");
          }
        }
      });
    },
  },
};
</script>

1.3scss

<style rel="stylesheet/scss" lang="scss">
.m-tab-container {
  background: #fff;
  height: 45px;
  border-top: 1px solid #d8dce5;
  border-bottom: 1px solid #d8dce5;
  overflow: hidden;
  white-space: nowrap;
  & > div {
    overflow: auto;
    overflow-y: hidden;
  }
  a.m-tab-item {
    text-decoration: none;
  }
  .m-tab-item {
    display: inline-block;
    position: relative;
    height: 32px;
    line-height: 32px;
    border: 1px solid #d8dce5;
    color: #495060;
    background: #fff;
    padding: 0 8px;
    font-size: 14px;
    margin-left: 5px;
    margin-top: 6px;
    &:first-of-type {
      margin-left: 18px;
    }
    &.active {
      background-color: #1c5ffd;
      color: #fff;
      border-color: #1c5ffd;
      &::before {
        content: "";
        background: #fff;
        display: inline-block;
        width: 8px;
        height: 8px;
        border-radius: 50%;
        position: relative;
        margin-right: 2px;
      }
    }
    .m-icon-close {
      width: 20px;
      height: 20px;
      vertical-align: 2px;
      border-radius: 50%;
      text-align: center;
      transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
      transform-origin: 100% 50%;
      &:before {
        display: inline-block;
        vertical-align: -3px;
      }
      &:hover {
        background-color: #b4bccc;
        color: #fff;
      }
    }
  }
}
</style>

2.路由导航页面里面举例。可根据需要修改格式
{
path: ‘test’,
name: ‘T’,
component: Test,
meta: { titleText: ‘测试’ }
}
3.vuex文件。该文件需要在main.js中引用,才能生效

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
    state: {
        visitedMenus: []
    },
    mutations: {
        setVisitedMenus(state, value) {
            state.visitedMenus = value
        },
        addRouteMenus(state, routeMenu) {
            if (routeMenu.route.path != '/login' && routeMenu.route.path != '/register') {
                let hasMenu = state.visitedMenus.some(item => item.path == routeMenu.route.path)//判断此路由是否在已存储的路由数据中
                if (!hasMenu) {
                    // 如果存储的路由数据中,没有此路由就添加
                    state.visitedMenus.push(Object.assign({}, {
                        path: routeMenu.route.path,
                        titleText: routeMenu.route.meta.titleText,
                        query: routeMenu.route.query
                    }))
                }
            }
        },
    },
    actions: {
        closeMenu({ commit, state }, routeMenu) {
            let deletekey;
            for (const [key, item] of state.visitedMenus.entries()) {
                if (item.path == routeMenu.page.path) {
                    deletekey = key;
                    state.visitedMenus.splice(key, 1);
                    break;
                }
            }
            return Promise.resolve({
                visitedMenus: state.visitedMenus,
                deletekey: deletekey
            })
        }
    }

})
export default store

4.App.vue

<script>
export default {
  name: "",
  data() {
    return {};
  },
  created() {
    // 防止页面刷新,vuex里面的菜单数据消失
    if (sessionStorage.getItem("visitedMenus")) {
      this.$store.commit(
        "setVisitedMenus",
        JSON.parse(sessionStorage.getItem("visitedMenus"))
      );
    }

    window.addEventListener("beforeunload", () => {
      sessionStorage.setItem(
        "visitedMenus",
        JSON.stringify(this.$store.state.visitedMenus)
      );
    });
  },
  watch: {
    $route() {
      this.addRouteMenu();
    },
  },
  methods: {
    addRouteMenu() {
      const route = this.$route;
      this.$store.commit({
        type: "addRouteMenus",
        route,
      });
    },
  },
};
</script>
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值