温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

利用Vue怎么实现一个多页签组件

发布时间:2021-01-14 14:27:02 来源:亿速云 阅读:277 作者:Leah 栏目:开发技术

今天就跟大家聊聊有关利用Vue怎么实现一个多页签组件,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

<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=""       :      ></span>     </div>    </div>   </el-scrollbar>   <div v-show="contextMenuVisible">    <ul     :     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怎么实现一个多页签组件有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

vue
AI