首页前端开发JavaScriptVue实现多页签组件

Vue实现多页签组件

时间2024-01-31 14:51:02发布访客分类JavaScript浏览899
导读:收集整理的这篇文章主要介绍了Vue实现多页签组件,觉得挺不错的,现在分享给大家,也给大家做个参考。 直接看效果,增加了右键菜单,分别有重新加载、关闭左边、关闭右边、关闭其他功能。也可以到...
收集整理的这篇文章主要介绍了Vue实现多页签组件,觉得挺不错的,现在分享给大家,也给大家做个参考。

直接看效果,增加了右键菜单,分别有重新加载、关闭左边、关闭右边、关闭其他功能。

也可以到我的gIThub上看看代码(如果觉得这个组件有用的话,别忘了顺手给个小星星)

代码:https://github.com/Caijt/VuePageTab

演示:https://caijt.github.io/VuePageTab/

我这个多页签组件里面的删除缓存的方法不是使用keep-alive组件自带的include、exculde结合的效果,而是使用暴力删除缓存的方法,这个在上个博客中也有提到,用这种方法的话,可以实现更完整的多页签功能,例如同个路由可以根据参数的不同同时打开不同的页签,也能不用去写那些路由的name值。

先直接看组件代码(里面用了一些element-ui的组件,如果你们不用element-ui的话。可以去掉,自己实现)

template>
     div class="__common-layout-pageTabs">
      el-scrollbar>
       div class="__tabs">
    div     class="__tab-item"     v-for="item in oPEnedPageRouters"     :class="{
      '__is-active': item.fullPath == $route.fullPath,     }
    "     :key="item.fullPath"     @click="onClick(item)"     @contextmenu.prevent="showContextMenu($event, item)"    >
     {
{
 item.meta.title }
}
         span      class="el-icon-close"      @click.stop="onClose(item)"      @contextmenu.PRevent.stop=""      :style="openedPageRouters.length = 1 ? 'width:0;
    ' : ''"     >
    /span>
        /div>
       /div>
      /el-scrollbar>
      div v-show="contextMenuVisible">
   ul    :style="{
 left: contextMenuLeft + 'px', top: contextMenuTop + 'px' }
    "    class="__contextmenu"   >
        li>
         el-button type="text" @click="reload()" size="mini">
          重新加载     /el-button>
        /li>
        li>
         el-button      type="text"      @click="closeOtherLeft"      :disabled="false"      size="mini"      >
    关闭左边/el-button     >
        /li>
        li>
         el-button      type="text"      @click="closeOtherRight"      :disabled="false"      size="mini"      >
    关闭右边/el-button     >
        /li>
        li>
         el-button type="text" @click="closeOther" size="mini"      >
    关闭其他/el-button     >
        /li>
       /ul>
      /div>
     /div>
    /template>
    script>
export default {
 props: {
  keepAlivecomponentInstance: {
}
, //keep-alive控件实例对象  blankRouteName: {
   type: String,   default: "blank",  }
, //空白路由的name值 }
, data() {
  return {
   contextMenuVisible: false, //右键菜单是否显示   contextMenuLeft: 0, //右键菜单显示位置   contextMenuTop: 0, //右键菜单显示位置   contextMenuTargetPageRoute: null, //右键所指向的菜单路由   openedPageRouters: [], //已打开的路由页面  }
    ;
 }
, watch: {
  //当路由变更时,执行打开页面的方法  $route: {
   handler(v) {
        this.openPage(v);
   }
,   immediate: true,  }
, }
, mounted() {
      //添加点击关闭右键菜单  window.addEventListener("click", this.closeContextMenu);
 }
, destroyed() {
      window.removeEventListener("click", this.closeContextMenu);
 }
, methods: {
  //打开页面  openPage(route) {
   if (route.name == this.blankRouteName) {
        return;
   }
       let isExist = this.openedPageRouters.some(    (item) =>
     item.fullPath == route.fullPath   );
   if (!isExist) {
        let openedPageRoute = this.openedPageRouters.find(     (item) =>
     item.path == route.path    );
        //判断页面是否支持不同参数多开页面功能,如果不支持且已存在Path值一样的页面路由,那就替换它    if (!route.meta.canMultipleOpen &
    &
 openedPageRoute != null) {
         this.delRouteCache(openedPageRoute.fullPath);
         this.openedPageRouters.splice(      this.openedPageRouters.indexOf(openedPageRoute),      1,      route     );
    }
 else {
         this.openedPageRouters.push(route);
    }
   }
  }
,  //点击页面标签卡时  onClick(route) {
   if (route.fullPath !== this.$route.fullPath) {
        this.$router.push(route.fullPath);
   }
  }
,  //关闭页面标签时  onClose(route) {
       let index = this.openedPageRouters.indexOf(route);
       this.delPageRoute(route);
   if (route.fullPath === this.$route.fullPath) {
        //删除页面后,跳转到上一页面    this.$router.replace(     this.openedPageRouters[index == 0 ? 0 : index - 1]    );
   }
  }
,  //右键显示菜单  showContextMenu(e, route) {
       this.contextMenuTargetPageRoute = route;
       this.contextMenuLeft = e.layerX;
       this.contextMenuTop = e.layerY;
       this.contextMenuVisible = true;
  }
,  //隐藏右键菜单  closeContextMenu() {
       this.contextMenuVisible = false;
       this.contextMenuTargetPageRoute = null;
  }
,  //重载页面  reload() {
       this.delRouteCache(this.contextMenuTargetPageRoute.fullPath);
   if (this.contextMenuTargetPageRoute.fullPath === this.$route.fullPath) {
    this.$router.replace({
 name: this.blankRouteName }
    ).then(() =>
 {
         this.$router.replace(this.contextMenuTargetPageRoute);
    }
    );
   }
  }
,  //关闭其他页面  closeOther() {
       for (let i = 0;
     i  this.openedPageRouters.length;
 i++) {
        let r = this.openedPageRouters[i];
    if (r !== this.contextMenuTargetPageRoute) {
         this.delPageRoute(r);
         i--;
    }
   }
   if (this.contextMenuTargetPageRoute.fullPath != this.$route.fullPath) {
        this.$router.replace(this.contextMenuTargetPageRoute);
   }
  }
,  //根据路径获取索引  getPageRouteindex(fullPath) {
       for (let i = 0;
     i  this.openedPageRouters.length;
 i++) {
    if (this.openedPageRouters[i].fullPath === fullPath) {
         return i;
    }
   }
  }
,  //关闭左边页面  closeOtherLeft() {
       let index = this.openedPageRouters.indexOf(    this.contextMenuTargetPageRoute   );
       let currentIndex = this.getPageRouteIndex(this.$route.fullPath);
       if (index >
 currentIndex) {
        this.$router.replace(this.contextMenuTargetPageRoute);
   }
       for (let i = 0;
     i  index;
 i++) {
        let r = this.openedPageRouters[i];
        this.delPageRoute(r);
        i--;
        index--;
   }
  }
,  //关闭右边页面  closeOtherRight() {
       let index = this.openedPageRouters.indexOf(    this.contextMenuTargetPageRoute   );
       let currentIndex = this.getPageRouteIndex(this.$route.fullPath);
       for (let i = index + 1;
     i  this.openedPageRouters.length;
 i++) {
        let r = this.openedPageRouters[i];
        this.delPageRoute(r);
        i--;
   }
   if (index  currentIndex) {
        this.$router.replace(this.contextMenuTargetPageRoute);
   }
  }
,  //删除页面  delPageRoute(route) {
       let routeIndex = this.openedPageRouters.indexOf(route);
       if (routeIndex >
= 0) {
        this.openedPageRouters.splice(routeIndex, 1);
   }
       this.delRouteCache(route.fullPath);
  }
,  //删除页面缓存  delRouteCache(key) {
       let cache = this.keepAliveComponentInstance.cache;
       let keys = this.keepAliveComponentInstance.keys;
       for (let i = 0;
     i  keys.length;
 i++) {
    if (keys[i] == key) {
         keys.splice(i, 1);
     if (cache[key] != null) {
          delete cache[key];
     }
         break;
    }
   }
  }
, }
,}
    ;
    /script>
    style lang="scss">
.__common-layout-pageTabs {
 .__contextmenu {
      // width: 100px;
      margin: 0;
      border: 1px solid #e4e7ed;
      background: #fff;
      z-index: 3000;
      position: absolute;
      list-style-type: none;
      padding: 5px 0;
      border-radius: 4px;
      font-Size: 14px;
      color: #333;
      box-shadow: 1px 1px 3px 0 rgba(0, 0, 0, 0.1);
  li {
       margin: 0;
       padding: 0px 15px;
       &
:hover {
        background: #f2f2f2;
        cursor: pointer;
   }
   button {
        color: #2c3e50;
   }
  }
 }
     $c-tab-border-color: #dcDFe6;
     position: relative;
     &
::before {
      content: "";
      border-bottom: 1px solid $c-tab-border-color;
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      height: 100%;
 }
 .__tabs {
      display: flex;
  .__tab-item {
       white-space: nowrap;
       padding: 8px 6px 8px 18px;
       font-size: 12px;
       border: 1px solid $c-tab-border-color;
       border-left: none;
       border-bottom: 0px;
       line-height: 14px;
       cursor: pointer;
       transition: color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),    padding 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
       &
:First-child {
        border-left: 1px solid $c-tab-border-color;
        border-top-left-radius: 2px;
        margin-left: 10px;
   }
       &
:last-child {
        border-top-right-radius: 2px;
        margin-right: 10px;
   }
       &
:not(.__is-active):hover {
        color: #409eff;
    .el-icon-close {
         width: 12px;
         margin-right: 0px;
    }
   }
       &
.__is-active {
        padding-right: 12px;
        border-bottom: 1px solid #fff;
        color: #409eff;
    .el-icon-close {
         width: 12px;
         margin-right: 0px;
         margin-left: 2px;
    }
   }
   .el-icon-close {
        width: 0px;
        height: 12px;
        overflow: hidden;
        border-radius: 50%;
        font-size: 12px;
        margin-right: 12px;
        transform-origin: 100% 50%;
        transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
        vertical-align: text-top;
        &
:hover {
         background-color: #c0c4cc;
         color: #fff;
    }
   }
  }
 }
}
    /style>
    

这个组件它需要两个属性,一个是keepAliveComponentInstance(keep-alive的控件实例对象),blankRouteName(空白路由的名称)

为什么我需要keep-alive的控件实例对象呢,因为这个对象里面有两个属性,一个是cache,一个是keys,存储着keep-alive的缓存的数据,有了这个对象,我就能在页签关闭时手动删除缓存。那这个对象怎么获取呢,如下所示,在keep-alive所在的父页面上的mounted事件上进行获取(如果keep-alive跟多页签组件不在同一个父页面,那可能就得借用vuex来传值了)

template>
     div id="app">
      page-tabs :keep-alive-component-instance="keepAliveComponentInstance" />
      div ref="keepAliveContainer">
       keep-alive>
        router-view :key="$route.fullPath" />
       /keep-alive>
      /div>
     /div>
    /template>
    script>
    import pageTabs From "./components/pageTabs.vue";
export default {
 name: "App", components: {
  pageTabs, }
, mounted() {
  if (this.$refs.keepAliveContainer) {
       this.keepAliveComponentInstance = this.$refs.keepAliveContainer.childNodes[0].__vue__;
//获取keep-alive的控件实例对象  }
 }
, data() {
  return {
   keepAliveComponentInstance: null,  }
    ;
 }
}
    ;
    /script>
    

而空白路由的名称,是干什么,主要我要实现刷新当前页面的功能,我们知道vue是不允许跳转到当前页面,那么我就想我先跳转到别的页面,再跳转回回来的页面,不就也实现刷新的效果了。(当然我用的是relpace,所以不会产生历史记录)

注:这个空白路由并不是固定定义在根路由上,需根据多页签组件所在位置,假如你有一个根router-view,还有一个布局组件,这个组件里面也有一个子router-view,多页签组件就在这个布局组件里,那么空白路由就需定义在布局组件对应的路由的children里面了

还有这个组件会根据路由对象的meta对象进行不同的配置,如下所示

let router = new Router({
 routes: [  //这个是空白页面,重新加载当前页面会用到  {
   name: "blank",   path: "/blank",  }
,  {
   path: "/a",   component: A,   meta: {
    title: "A页面", //页面标题    canMultipleOpen: true //支持根据参数不同多开不同页签,如果你需要/a跟/a?v=123都分别打开两个页签,请设置为true,否则就只会显示一个页签,后打开的会替换到前打开的页签   }
  }
}
    

以上就是Vue实现多页签组件的详细内容,更多关于Vue实现多页签组件的资料请关注其它相关文章!

您可能感兴趣的文章:
  • Vue多选列表组件深入详解
  • vue 使用 v-model 双向绑定父子组件的值遇见的问题及解决方案
  • vue使用transition组件动画效果的实例代码
  • vue 组件基础知识总结
  • 深入了解Vue动态组件和异步组件
  • Vue使用Ref跨层级获取组件的步骤
  • vue3中轻松实现switch功能组件的全过程
  • vue3自定义dialog、modal组件的方法
  • Vue中强制组件重新渲染的正确方法
  • vue 动态创建组件的两种方法
  • 详解vue3中组件的非兼容变更

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

vue

若转载请注明出处: Vue实现多页签组件
本文地址: https://pptw.com/jishu/594103.html
c语言计算1~n的阶乘之和 vue element el-transfer增加拖拽功能

游客 回复需填写必要信息