<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,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> 
 | 
          <li @click.stop="cleartags()">关闭其他</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) { 
 | 
          let 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 
 | 
      } 
 | 
      let index = this.tags.indexOf(this.selectedTag) 
 | 
      this.handleClose(this.selectedTag, index) 
 | 
    }, 
 | 
    handleClose(item, index) { 
 | 
      if (this.tags.length == 1) { 
 | 
        return 
 | 
      } 
 | 
      //先把长度保存下来后面用来比较做判断条件 
 | 
      let 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 }); 
 | 
        } 
 | 
      } else { 
 | 
        // 如果你点击不是最后一位标签,点的前面的,那就往右边跳转 
 | 
        this.$router.push({ path: this.tags[index].url }); 
 | 
      } 
 | 
    }, 
 | 
    //点击跳转路由 
 | 
    tagsmenu(item, index) { 
 | 
      console.log('tagsmenu'); 
 | 
      //判断:当前路由不等于当前选中项的url,也就代表你点击的不是现在选中的标签,是另一个标签就跳转过去,如果你点击的是现在已经选中的标签就不用跳转了,因为你已经在这个路由了还跳什么呢。 
 | 
      if (this.$route.path !== item.url) { 
 | 
        //用path的跳转方法把当前项的url当作地址跳转。 
 | 
        this.$router.push({ path: item.url }); 
 | 
        let tagsDiv = document.getElementById('tags-box') 
 | 
        if (index) { 
 | 
          tagsDiv.scrollTo(index * 110, 0) 
 | 
        } 
 | 
      } 
 | 
    }, 
 | 
    //通过判断路由一致返回布尔值添加class,添加高亮效果 
 | 
    isActive(route, index) { 
 | 
      let res = route === this.$route.path 
 | 
      return res 
 | 
    }, 
 | 
    scrollToStart() { 
 | 
  
 | 
      let tagsDiv = document.getElementById('tags-box') 
 | 
      tagsDiv.scrollTo(0, 0) 
 | 
    }, 
 | 
    scrollToEnd() { 
 | 
      let tagsDiv = document.getElementById('tags-box') 
 | 
      tagsDiv.scrollTo(tagsDiv.scrollWidth, 0) 
 | 
    }, 
 | 
    //右键事件,显示右键菜单,并固定好位置。 
 | 
    openMenu(tag, e) { 
 | 
      if(this.tags.length ==1){ 
 | 
        return 
 | 
      } 
 | 
      this.visible = true 
 | 
      this.selectedTag = tag 
 | 
      const offsetLeft = this.$el.getBoundingClientRect().left 
 | 
      console.log(tag, e,e.clientX,e.clientY,offsetLeft); 
 | 
      this.left = e.clientX - offsetLeft  + 200  //右键菜单距离左边的距离 
 | 
      this.top =  50  //右键菜单距离上面的距离  这两个可以更改,看看自己的右键菜单在什么位置,自己调 
 | 
      // this.left = e.clientX  + 60  //右键菜单距离左边的距离 
 | 
      // this.top = e.clientY +20  //右键菜单距离上面的距离  这两个可以更改,看看自己的右键菜单在什么位置,自己调 
 | 
    }, 
 | 
    //隐藏右键菜单 
 | 
    closeMenu() { 
 | 
      this.visible = false 
 | 
    }, 
 | 
    //右键菜单关闭所有选项,触发vuex中的方法,把当前路由当参数传过去用于判断 
 | 
    cleartags(val){ 
 | 
      this.visible = false 
 | 
      this.cleartagsview(this.selectedTag) 
 | 
    } 
 | 
  } 
 | 
}; 
 | 
</script> 
 | 
  
 | 
<style lang="scss" scoped> 
 | 
.btn { 
 | 
  font-size: 20px; 
 | 
  line-height: 48px; 
 | 
  height: 48px; 
 | 
} 
 | 
.nor-btn { 
 | 
  color: #666; 
 | 
  cursor: pointer; 
 | 
  &:hover { 
 | 
    color: #2E68EC; 
 | 
  } 
 | 
} 
 | 
  
 | 
.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: #2E68EC; 
 | 
  border-bottom: 2px solid #2E68EC; 
 | 
} 
 | 
//右键菜单样式 
 | 
.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, .3); 
 | 
  li { 
 | 
    margin: 0; 
 | 
    padding: 0 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> 
 |