| | |
| | | <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 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':''" |
| | | @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> |
| | | <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> |
| | | <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"; |
| | | // 这个就是导入vuex的数据,配合下面...map用 |
| | | import { mapState, mapMutations } from 'vuex' |
| | | export default { |
| | | data() { |
| | | return { |
| | | //右键菜单隐藏对应布尔值 |
| | | // 右键菜单隐藏对应布尔值 |
| | | visible: false, |
| | | //右键菜单对应位置 |
| | | // 右键菜单对应位置 |
| | | top: 0, |
| | | left: 0, |
| | | leftStatus: false, |
| | | rightStatus: false, |
| | | rightStatus: false |
| | | } |
| | | }, |
| | | computed: { |
| | | //引入vuex中state中的tags数据,一样this调用就行 |
| | | ...mapState(["tags"]), |
| | | // 引入vuex中state中的tags数据,一样this调用就行 |
| | | ...mapState(['tags']) |
| | | }, |
| | | watch:{ |
| | | //监听右键菜单的值是否为true,如果是就创建全局监听点击事件,触发closeMenu事件隐藏菜单,如果是false就删除监听 |
| | | watch: { |
| | | // 监听右键菜单的值是否为true,如果是就创建全局监听点击事件,触发closeMenu事件隐藏菜单,如果是false就删除监听 |
| | | visible(value) { |
| | | if (value) { |
| | | document.body.addEventListener('click', this.closeMenu) |
| | |
| | | document.body.removeEventListener('click', this.closeMenu) |
| | | } |
| | | }, |
| | | $route(to,from){ |
| | | $route(to, from) { |
| | | this.tags.forEach((item, index) => { |
| | | if (item.url === to.path) { |
| | | let tagsDiv = document.getElementById('tags-box') |
| | | const tagsDiv = document.getElementById('tags-box') |
| | | if (index) { |
| | | tagsDiv.scrollTo(index * 110, 0) |
| | | } else { |
| | |
| | | }, |
| | | 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 |
| | | this.rightStatus = true |
| | | } else { |
| | | this.rightStatus = false |
| | | } |
| | | |
| | | }, false) |
| | | }, |
| | | methods: { |
| | | //引入vuex中mutation方法,可以直接this.xxx调用他 |
| | | ...mapMutations(["closeTab", "cleartagsview"]), |
| | | //点击叉叉删除的事件 |
| | | // 引入vuex中mutation方法,可以直接this.xxx调用他 |
| | | ...mapMutations(['closeTab', 'cleartagsview']), |
| | | // 点击叉叉删除的事件 |
| | | rightClose() { |
| | | this.visible = false |
| | | if (this.tags.length == 1) { |
| | | return |
| | | } |
| | | let index = this.tags.indexOf(this.selectedTag) |
| | | const 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); |
| | | // 先把长度保存下来后面用来比较做判断条件 |
| | | const length = this.tags.length - 1 |
| | | // vuex调方法,上面...map引入的vuex方法,不会这种方法的看vue官网文档 |
| | | this.closeTab(item) |
| | | // 如果关闭的标签不是当前路由的话,就不跳转 |
| | | if (item.url !== this.$route.path) { |
| | | return; |
| | | return |
| | | } |
| | | // 判断:如果index和length是一样的,那就代表都是一样的长度,就是最后一位,那就往左跳转一个 |
| | | if (index === length) { |
| | | //再判断:如果length=0,也就是说你删完了所有标签 |
| | | // 再判断:如果length=0,也就是说你删完了所有标签 |
| | | if (length === 0) { |
| | | //那么再判断:如果当前路由不等于index,也就是我首页的路由 |
| | | if (this.$route.path !== "/index") { |
| | | //那么就跳转首页。这一步的意思是:如果删除的最后一个标签不是首页就统一跳转首页,如果你删除的最后一个标签是首页标签,已经在这个首页路由上了,你还跳个什么呢。这不重复操作了吗。 |
| | | this.$router.push({ path: "/index" }); |
| | | // 那么再判断:如果当前路由不等于index,也就是我首页的路由 |
| | | if (this.$route.path !== '/index') { |
| | | // 那么就跳转首页。这一步的意思是:如果删除的最后一个标签不是首页就统一跳转首页,如果你删除的最后一个标签是首页标签,已经在这个首页路由上了,你还跳个什么呢。这不重复操作了吗。 |
| | | this.$router.push({ path: '/index' }) |
| | | } |
| | | } else { |
| | | //那么,如果上面的条件都不成立,没有length=0.也就是说你还有好几个标签,并且你删除的是最后一位标签,那么就往左边挪一位跳转路由 |
| | | this.$router.push({ path: this.tags[index - 1].url }); |
| | | // 那么,如果上面的条件都不成立,没有length=0.也就是说你还有好几个标签,并且你删除的是最后一位标签,那么就往左边挪一位跳转路由 |
| | | this.$router.push({ path: this.tags[index - 1].url }) |
| | | } |
| | | } else { |
| | | // 如果你点击不是最后一位标签,点的前面的,那就往右边跳转 |
| | | this.$router.push({ path: this.tags[index].url }); |
| | | this.$router.push({ path: this.tags[index].url }) |
| | | } |
| | | }, |
| | | //点击跳转路由 |
| | | // 点击跳转路由 |
| | | tagsmenu(item, index) { |
| | | console.log('tagsmenu'); |
| | | //判断:当前路由不等于当前选中项的url,也就代表你点击的不是现在选中的标签,是另一个标签就跳转过去,如果你点击的是现在已经选中的标签就不用跳转了,因为你已经在这个路由了还跳什么呢。 |
| | | console.log('tagsmenu') |
| | | // 判断:当前路由不等于当前选中项的url,也就代表你点击的不是现在选中的标签,是另一个标签就跳转过去,如果你点击的是现在已经选中的标签就不用跳转了,因为你已经在这个路由了还跳什么呢。 |
| | | if (this.$route.path !== item.url) { |
| | | //用path的跳转方法把当前项的url当作地址跳转。 |
| | | this.$router.push({ path: item.url }); |
| | | let tagsDiv = document.getElementById('tags-box') |
| | | // 用path的跳转方法把当前项的url当作地址跳转。 |
| | | this.$router.push({ path: item.url }) |
| | | const tagsDiv = document.getElementById('tags-box') |
| | | if (index) { |
| | | tagsDiv.scrollTo(index * 110, 0) |
| | | } |
| | | } |
| | | }, |
| | | //通过判断路由一致返回布尔值添加class,添加高亮效果 |
| | | // 通过判断路由一致返回布尔值添加class,添加高亮效果 |
| | | isActive(route, index) { |
| | | let res = route === this.$route.path |
| | | const res = route === this.$route.path |
| | | return res |
| | | }, |
| | | scrollToStart() { |
| | | |
| | | let tagsDiv = document.getElementById('tags-box') |
| | | const tagsDiv = document.getElementById('tags-box') |
| | | tagsDiv.scrollTo(0, 0) |
| | | }, |
| | | }, |
| | | scrollToEnd() { |
| | | let tagsDiv = document.getElementById('tags-box') |
| | | 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 //右键菜单距离上面的距离 这两个可以更改,看看自己的右键菜单在什么位置,自己调 |
| | | console.log(tag, e) |
| | | this.left = e.clientX - offsetLeft + 60 // 右键菜单距离左边的距离 |
| | | this.top = e.clientY + 20 // 右键菜单距离上面的距离 这两个可以更改,看看自己的右键菜单在什么位置,自己调 |
| | | }, |
| | | //隐藏右键菜单 |
| | | // 隐藏右键菜单 |
| | | closeMenu() { |
| | | this.visible = false |
| | | }, |
| | | //右键菜单关闭所有选项,触发vuex中的方法,把当前路由当参数传过去用于判断 |
| | | cleartags(val){ |
| | | // 右键菜单关闭所有选项,触发vuex中的方法,把当前路由当参数传过去用于判断 |
| | | cleartags(val) { |
| | | this.visible = false |
| | | this.cleartagsview(val) |
| | | } |
| | | }, |
| | | }; |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | |
| | | color: #666; |
| | | cursor: pointer; |
| | | &:hover { |
| | | color: #2E68EC; |
| | | color: #4d99a8; |
| | | } |
| | | } |
| | | |
| | | .ban-btn { |
| | | color: #999; |
| | | cursor:not-allowed |
| | | cursor: not-allowed; |
| | | } |
| | | |
| | | #tags-box { |
| | |
| | | scrollbar-width: none; /* firefox */ |
| | | -ms-overflow-style: none; /* IE 10+ */ |
| | | &::-webkit-scrollbar { |
| | | display: none; /* Chrome Safari */ |
| | | } |
| | | display: none; /* Chrome Safari */ |
| | | } |
| | | } |
| | | #tags-box::-webkit-scrollbar { |
| | | display: none; /* Chrome Safari */ |
| | | display: none; /* Chrome Safari */ |
| | | } |
| | | //标签导航样式 |
| | | .tagsview { |
| | |
| | | color: #000; |
| | | font-size: 14px; |
| | | display: inline-block; |
| | | |
| | | } |
| | | //叉号鼠标经过样式 |
| | | .tagsicon:hover{ |
| | | .tagsicon:hover { |
| | | color: #f56c6c; |
| | | } |
| | | //标签高亮 |
| | | .active{ |
| | | color: #2E68EC; |
| | | border-bottom: 2px solid #2E68EC; |
| | | .active { |
| | | color: #4d99a8; |
| | | border-bottom: 2px solid #4d99a8; |
| | | } |
| | | //右键菜单样式 |
| | | .contextmenu { |
| | |
| | | font-size: 12px; |
| | | font-weight: 400; |
| | | color: #333; |
| | | box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3); |
| | | box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); |
| | | li { |
| | | margin: 0; |
| | | padding: 7px 16px; |