|
@@ -0,0 +1,209 @@
|
|
|
+<template>
|
|
|
+ <div class="tags-nav">
|
|
|
+ <div class="close-con">
|
|
|
+ <Dropdown transfer @on-click="handleTagsOption" style="margin-top:7px;">
|
|
|
+ <Button size="small" type="text">
|
|
|
+ <Icon :size="18" type="ios-close-circle-outline" />
|
|
|
+ </Button>
|
|
|
+ <DropdownMenu slot="list">
|
|
|
+ <DropdownItem name="close-all">关闭所有</DropdownItem>
|
|
|
+ <DropdownItem name="close-others">关闭其他</DropdownItem>
|
|
|
+ </DropdownMenu>
|
|
|
+ </Dropdown>
|
|
|
+ </div>
|
|
|
+ <ul v-show="visible" :style="{left: contextMenuLeft + 'px', top: contextMenuTop + 'px'}" class="contextmenu">
|
|
|
+ <li v-for="(item, key) of menuList" @click="handleTagsOption(key)" :key="key">{{item}}</li>
|
|
|
+ </ul>
|
|
|
+ <div class="btn-con left-btn">
|
|
|
+ <Button type="text" @click="handleScroll(240)">
|
|
|
+ <Icon :size="18" type="ios-arrow-back" />
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ <div class="btn-con right-btn">
|
|
|
+ <Button type="text" @click="handleScroll(-240)">
|
|
|
+ <Icon :size="18" type="ios-arrow-forward" />
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ <div class="scroll-outer" ref="scrollOuter" @DOMMouseScroll="handlescroll" @mousewheel="handlescroll">
|
|
|
+ <div ref="scrollBody" class="scroll-body" :style="{left: tagBodyLeft + 'px'}">
|
|
|
+ <transition-group name="taglist-moving-animation">
|
|
|
+ <Tag
|
|
|
+ type="dot"
|
|
|
+ v-for="(item, index) in list"
|
|
|
+ ref="tagsPageOpened"
|
|
|
+ :key="`tag-nav-${index}`"
|
|
|
+ :name="item.name"
|
|
|
+ :data-route-item="item"
|
|
|
+ @on-close="handleClose(item)"
|
|
|
+ @click.native="handleClick(item)"
|
|
|
+ :closable="item.name !== $config.homeName"
|
|
|
+ :color="isCurrentTag(item) ? 'primary' : 'default'"
|
|
|
+ @contextmenu.prevent.native="contextMenu(item, $event)"
|
|
|
+ >{{ showTitleInside(item) }}</Tag>
|
|
|
+ </transition-group>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { showTitle, routeEqual } from '@/libs/util'
|
|
|
+import beforeClose from '@/router/before-close'
|
|
|
+export default {
|
|
|
+ name: 'TagsNav',
|
|
|
+ props: {
|
|
|
+ value: Object,
|
|
|
+ list: {
|
|
|
+ type: Array,
|
|
|
+ default () {
|
|
|
+ return []
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data () {
|
|
|
+ return {
|
|
|
+ tagBodyLeft: 0,
|
|
|
+ rightOffset: 40,
|
|
|
+ outerPadding: 4,
|
|
|
+ contextMenuLeft: 0,
|
|
|
+ contextMenuTop: 0,
|
|
|
+ visible: false,
|
|
|
+ menuList: {
|
|
|
+ others: '关闭其他',
|
|
|
+ all: '关闭所有'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ currentRouteObj () {
|
|
|
+ const { name, params, query } = this.value
|
|
|
+ return { name, params, query }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ handlescroll (e) {
|
|
|
+ var type = e.type
|
|
|
+ let delta = 0
|
|
|
+ if (type === 'DOMMouseScroll' || type === 'mousewheel') {
|
|
|
+ delta = (e.wheelDelta) ? e.wheelDelta : -(e.detail || 0) * 40
|
|
|
+ }
|
|
|
+ this.handleScroll(delta)
|
|
|
+ },
|
|
|
+ handleScroll (offset) {
|
|
|
+ const outerWidth = this.$refs.scrollOuter.offsetWidth
|
|
|
+ const bodyWidth = this.$refs.scrollBody.offsetWidth
|
|
|
+ if (offset > 0) {
|
|
|
+ this.tagBodyLeft = Math.min(0, this.tagBodyLeft + offset)
|
|
|
+ } else {
|
|
|
+ if (outerWidth < bodyWidth) {
|
|
|
+ if (this.tagBodyLeft < -(bodyWidth - outerWidth)) {
|
|
|
+ this.tagBodyLeft = this.tagBodyLeft
|
|
|
+ } else {
|
|
|
+ this.tagBodyLeft = Math.max(this.tagBodyLeft + offset, outerWidth - bodyWidth)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.tagBodyLeft = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleTagsOption (type) {
|
|
|
+ if (type.includes('all')) {
|
|
|
+ // 关闭所有,除了home
|
|
|
+ let res = this.list.filter(item => item.name === this.$config.homeName)
|
|
|
+ this.$emit('on-close', res, 'all')
|
|
|
+ } else if (type.includes('others')) {
|
|
|
+ // 关闭除当前页和home页的其他页
|
|
|
+ let res = this.list.filter(item => routeEqual(this.currentRouteObj, item) || item.name === this.$config.homeName)
|
|
|
+ this.$emit('on-close', res, 'others', this.currentRouteObj)
|
|
|
+ setTimeout(() => {
|
|
|
+ this.getTagElementByRoute(this.currentRouteObj)
|
|
|
+ }, 100)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleClose (current) {
|
|
|
+ if (current.meta && current.meta.beforeCloseName && current.meta.beforeCloseName in beforeClose) {
|
|
|
+ new Promise(beforeClose[current.meta.beforeCloseName]).then(close => {
|
|
|
+ if (close) {
|
|
|
+ this.close(current)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ this.close(current)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ close (route) {
|
|
|
+ let res = this.list.filter(item => !routeEqual(route, item))
|
|
|
+ this.$emit('on-close', res, undefined, route)
|
|
|
+ },
|
|
|
+ handleClick (item) {
|
|
|
+ this.$emit('input', item)
|
|
|
+ },
|
|
|
+ showTitleInside (item) {
|
|
|
+ return showTitle(item, this)
|
|
|
+ },
|
|
|
+ isCurrentTag (item) {
|
|
|
+ return routeEqual(this.currentRouteObj, item)
|
|
|
+ },
|
|
|
+ moveToView (tag) {
|
|
|
+ const outerWidth = this.$refs.scrollOuter.offsetWidth
|
|
|
+ const bodyWidth = this.$refs.scrollBody.offsetWidth
|
|
|
+ if (bodyWidth < outerWidth) {
|
|
|
+ this.tagBodyLeft = 0
|
|
|
+ } else if (tag.offsetLeft < -this.tagBodyLeft) {
|
|
|
+ // 标签在可视区域左侧
|
|
|
+ this.tagBodyLeft = -tag.offsetLeft + this.outerPadding
|
|
|
+ } else if (tag.offsetLeft > -this.tagBodyLeft && tag.offsetLeft + tag.offsetWidth < -this.tagBodyLeft + outerWidth) {
|
|
|
+ // 标签在可视区域
|
|
|
+ this.tagBodyLeft = Math.min(0, outerWidth - tag.offsetWidth - tag.offsetLeft - this.outerPadding)
|
|
|
+ } else {
|
|
|
+ // 标签在可视区域右侧
|
|
|
+ this.tagBodyLeft = -(tag.offsetLeft - (outerWidth - this.outerPadding - tag.offsetWidth))
|
|
|
+ }
|
|
|
+ },
|
|
|
+ getTagElementByRoute (route) {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.refsTag = this.$refs.tagsPageOpened
|
|
|
+ this.refsTag.forEach((item, index) => {
|
|
|
+ if (routeEqual(route, item.$attrs['data-route-item'])) {
|
|
|
+ let tag = this.refsTag[index].$el
|
|
|
+ this.moveToView(tag)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ contextMenu (item, e) {
|
|
|
+ if (item.name === this.$config.homeName) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.visible = true
|
|
|
+ const offsetLeft = this.$el.getBoundingClientRect().left
|
|
|
+ this.contextMenuLeft = e.clientX - offsetLeft + 10
|
|
|
+ this.contextMenuTop = e.clientY - 64
|
|
|
+ },
|
|
|
+ closeMenu () {
|
|
|
+ this.visible = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ '$route' (to) {
|
|
|
+ this.getTagElementByRoute(to)
|
|
|
+ },
|
|
|
+ visible (value) {
|
|
|
+ if (value) {
|
|
|
+ document.body.addEventListener('click', this.closeMenu)
|
|
|
+ } else {
|
|
|
+ document.body.removeEventListener('click', this.closeMenu)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted () {
|
|
|
+ setTimeout(() => {
|
|
|
+ this.getTagElementByRoute(this.$route)
|
|
|
+ }, 200)
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less">
|
|
|
+@import './tags-nav.less';
|
|
|
+</style>
|