| <template> | 
|   <div class="tags-view-style" style="display: flex; overflow-x: scroll"> | 
|     <i | 
|       class="el-icon-arrow-left btn" | 
|       v-if="leftStatus" | 
|       :class="leftStatus ? 'nor-btn' : 'ban-btn'" | 
|       @click="scrollToStart()" | 
|     ></i> | 
|     <div id="tags-box" ref="tags"> | 
|       <div | 
|         v-for="(item, index) in tags" | 
|         :key="index" | 
|         :id="'tags-box-' + index" | 
|         @contextmenu.prevent="openMenu(item, $event)" | 
|         :class="isActive(item.url, item.params,index) ? 'active' : ''" | 
|         class="tagsview" | 
|         @click="tagsmenu(item, index)" | 
|       > | 
|         {{ item.label }} | 
|         <!-- 这个地方一定要click加个stop阻止,不然会因为事件冒泡一直触发父元素的点击事件,无法跳转另一个路由 --> | 
|         <span | 
|           v-if="tags.length > 1" | 
|           class="el-icon-close tagsicon" | 
|           @click.stop="handleClose(item, index)" | 
|         ></span> | 
|         <!-- <ul v-show="visible" class="contextmenu" :style="{left:left+'px',top:top+'px'}"> | 
|           <li @click.stop="rightClose()">关闭</li> | 
|           <li @click.stop="cleartags($route.path)">关闭其他</li> | 
|         </ul> --> | 
|       </div> | 
|     </div> | 
|     <i | 
|       class="el-icon-arrow-right btn" | 
|       v-if="rightStatus" | 
|       :class="rightStatus ? 'nor-btn' : 'ban-btn'" | 
|       @click="scrollToEnd()" | 
|     ></i> | 
|   </div> | 
| </template> | 
|   | 
| <script> | 
| // 这个就是导入vuex的数据,配合下面...map用 | 
| import { mapState, mapMutations } from 'vuex' | 
| export default { | 
|   data() { | 
|     return { | 
|       // 右键菜单隐藏对应布尔值 | 
|       visible: false, | 
|       // 右键菜单对应位置 | 
|       top: 0, | 
|       left: 0, | 
|       leftStatus: false, | 
|       rightStatus: false | 
|     } | 
|   }, | 
|   computed: { | 
|     // 引入vuex中state中的tags数据,一样this调用就行 | 
|     ...mapState(['tags']) | 
|   }, | 
|   watch: { | 
|     // 监听右键菜单的值是否为true,如果是就创建全局监听点击事件,触发closeMenu事件隐藏菜单,如果是false就删除监听 | 
|     visible(value) { | 
|       if (value) { | 
|         document.body.addEventListener('click', this.closeMenu) | 
|       } else { | 
|         document.body.removeEventListener('click', this.closeMenu) | 
|       } | 
|     }, | 
|     $route(to, from) { | 
|       this.tags.forEach((item, index) => { | 
|         if (item.url === to.path && item.index === to.query.index) { | 
|           const tagsDiv = document.getElementById('tags-box') | 
|           if (index) { | 
|             tagsDiv.scrollTo(index * 110, 0) | 
|           } else { | 
|             tagsDiv.scrollTo(0, 0) | 
|           } | 
|         } | 
|       }) | 
|     } | 
|   | 
|   }, | 
|   mounted() { | 
|     this.$refs.tags.addEventListener('scroll', e => { | 
|       if (this.$refs.tags.scrollLeft > 0) { | 
|         this.leftStatus = true | 
|       } else { | 
|         this.leftStatus = false | 
|       } | 
|       if (this.$refs.tags.scrollLeft + this.$refs.tags.clientWidth < this.$refs.tags.scrollWidth) { | 
|         this.rightStatus = true | 
|       } else { | 
|         this.rightStatus = false | 
|       } | 
|     }, false) | 
|   }, | 
|   methods: { | 
|     // 引入vuex中mutation方法,可以直接this.xxx调用他 | 
|     ...mapMutations(['closeTab', 'cleartagsview']), | 
|     // 点击叉叉删除的事件 | 
|     rightClose() { | 
|       this.visible = false | 
|       if (this.tags.length == 1) { | 
|         return | 
|       } | 
|       const index = this.tags.indexOf(this.selectedTag) | 
|       this.handleClose(this.selectedTag, index) | 
|     }, | 
|     handleClose(item, index) { | 
|       if (this.tags.length == 1) { | 
|         return | 
|       } | 
|       // 先把长度保存下来后面用来比较做判断条件 | 
|       const length = this.tags.length - 1 | 
|       // vuex调方法,上面...map引入的vuex方法,不会这种方法的看vue官网文档 | 
|       this.closeTab(item) | 
|       // 如果关闭的标签不是当前路由的话,就不跳转 | 
|       if (item.url !== this.$route.path) { | 
|         return | 
|       } | 
|       // 判断:如果index和length是一样的,那就代表都是一样的长度,就是最后一位,那就往左跳转一个 | 
|       if (index === length) { | 
|         // 再判断:如果length=0,也就是说你删完了所有标签 | 
|         if (length === 0) { | 
|           // 那么再判断:如果当前路由不等于index,也就是我首页的路由 | 
|           if (this.$route.path !== '/index') { | 
|             // 那么就跳转首页。这一步的意思是:如果删除的最后一个标签不是首页就统一跳转首页,如果你删除的最后一个标签是首页标签,已经在这个首页路由上了,你还跳个什么呢。这不重复操作了吗。 | 
|             this.$router.push({ path: '/index' }) | 
|           } | 
|         } else { | 
|           // 那么,如果上面的条件都不成立,没有length=0.也就是说你还有好几个标签,并且你删除的是最后一位标签,那么就往左边挪一位跳转路由 | 
|           this.$router.push({ path: this.tags[index - 1].url,query:{param: this.tags[index - 1].params} }) | 
|         } | 
|       } else { | 
|         // 如果你点击不是最后一位标签,点的前面的,那就往右边跳转 | 
|         this.$router.push({ path: this.tags[index].url ,query:{param: this.tags[index].params}}) | 
|       } | 
|     }, | 
|     // 点击跳转路由 | 
|     tagsmenu(item, index) { | 
|       console.log('tagsmenu') | 
|       // 判断:当前路由不等于当前选中项的url,也就代表你点击的不是现在选中的标签,是另一个标签就跳转过去,如果你点击的是现在已经选中的标签就不用跳转了,因为你已经在这个路由了还跳什么呢。 | 
|       if (this.$route.path !== item.url) { | 
|         // 用path的跳转方法把当前项的url当作地址跳转。 | 
|         this.$router.push({ path: item.url ,query:{param: this.tags[index].params}}) | 
|         const tagsDiv = document.getElementById('tags-box') | 
|         if (index) { | 
|           tagsDiv.scrollTo(index * 110, 0) | 
|         } | 
|       } | 
|     }, | 
|     // 通过判断路由一致返回布尔值添加class,添加高亮效果 | 
|     isActive(route,params, index) { | 
|       const res =(route === this.$route.path && params== this.$route.query.param) | 
|       return res | 
|     }, | 
|     scrollToStart() { | 
|       const tagsDiv = document.getElementById('tags-box') | 
|       tagsDiv.scrollTo(0, 0) | 
|     }, | 
|     scrollToEnd() { | 
|       const tagsDiv = document.getElementById('tags-box') | 
|       tagsDiv.scrollTo(tagsDiv.scrollWidth, 0) | 
|     }, | 
|     // 右键事件,显示右键菜单,并固定好位置。 | 
|     openMenu(tag, e) { | 
|       this.visible = true | 
|       this.selectedTag = tag | 
|       const offsetLeft = this.$el.getBoundingClientRect().left | 
|       console.log(tag, e) | 
|       this.left = e.clientX - offsetLeft + 60 // 右键菜单距离左边的距离 | 
|       this.top = e.clientY + 20 // 右键菜单距离上面的距离           这两个可以更改,看看自己的右键菜单在什么位置,自己调 | 
|     }, | 
|     // 隐藏右键菜单 | 
|     closeMenu() { | 
|       this.visible = false | 
|     }, | 
|     // 右键菜单关闭所有选项,触发vuex中的方法,把当前路由当参数传过去用于判断 | 
|     cleartags(val) { | 
|       this.visible = false | 
|       this.cleartagsview(val) | 
|     } | 
|   } | 
| } | 
| </script> | 
|   | 
| <style lang="scss" scoped> | 
| .btn { | 
|   font-size: 20px; | 
|   line-height: 48px; | 
|   height: 48px; | 
| } | 
| .nor-btn { | 
|   color: #666; | 
|   cursor: pointer; | 
|   &:hover { | 
|     color: #2080f7; | 
|   } | 
| } | 
|   | 
| .ban-btn { | 
|   color: #999; | 
|   cursor: not-allowed; | 
| } | 
|   | 
| #tags-box { | 
|   overflow-x: hidden; | 
|   white-space: nowrap; | 
|   flex: 1; | 
|   // width: 240px; | 
|   scrollbar-width: none; /* firefox */ | 
|   -ms-overflow-style: none; /* IE 10+ */ | 
|   &::-webkit-scrollbar { | 
|     display: none; /* Chrome Safari */ | 
|   } | 
| } | 
| #tags-box::-webkit-scrollbar { | 
|   display: none; /* Chrome Safari */ | 
| } | 
| //标签导航样式 | 
| .tagsview { | 
|   cursor: pointer; | 
|   // margin-left: 4px; | 
|   height: 48px; | 
|   line-height: 48px; | 
|   padding: 0 8px 0 24px; | 
|   // border: 1px solid #d8dce5; | 
|   // border-radius: 5px; | 
|   color: #000; | 
|   font-size: 14px; | 
|   display: inline-block; | 
| } | 
| //叉号鼠标经过样式 | 
| .tagsicon:hover { | 
|   color: #f56c6c; | 
| } | 
| //标签高亮 | 
| .active { | 
|   color: #2080f7; | 
|   border-bottom: 2px solid #2080f7; | 
| } | 
| //右键菜单样式 | 
| .contextmenu { | 
|   margin: 0; | 
|   background: #fff; | 
|   z-index: 100; | 
|   position: absolute; | 
|   list-style-type: none; | 
|   padding: 5px 0; | 
|   border-radius: 4px; | 
|   font-size: 12px; | 
|   font-weight: 400; | 
|   color: #333; | 
|   box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); | 
|   li { | 
|     margin: 0; | 
|     padding: 7px 16px; | 
|     cursor: pointer; | 
|     &:hover { | 
|       background: #eee; | 
|     } | 
|   } | 
| } | 
| .tags-view-style { | 
|   scrollbar-width: none; /* firefox */ | 
|   -ms-overflow-style: none; /* IE 10+ */ | 
|   &::-webkit-scrollbar { | 
|     display: none; /* Chrome Safari */ | 
|   } | 
| } | 
| </style> |