|
|
@@ -1,24 +1,25 @@
|
|
|
<template>
|
|
|
- <!-- <el-breadcrumb class="app-breadcrumb" separator="/">-->
|
|
|
- <!-- <transition-group name="breadcrumb">-->
|
|
|
- <!-- <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">-->
|
|
|
- <!-- <span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>-->
|
|
|
- <!-- <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>-->
|
|
|
- <!-- </el-breadcrumb-item>-->
|
|
|
- <!-- </transition-group>-->
|
|
|
- <!-- </el-breadcrumb>-->
|
|
|
- <div>
|
|
|
- <el-tag
|
|
|
- @click="toPath(tag)"
|
|
|
- @close="close(tag)"
|
|
|
- style="margin-top: 20px;margin-right: 10px;cursor: pointer"
|
|
|
- v-for="tag in keepList"
|
|
|
- :key="tag.path"
|
|
|
- closable
|
|
|
- :type="activeMenu(tag)"
|
|
|
- >
|
|
|
- {{ tag.name }}
|
|
|
- </el-tag>
|
|
|
+ <div class="tab-history">
|
|
|
+ <!-- 左翻页按钮(不可用时隐藏) -->
|
|
|
+ <button v-if="canScrollLeft" class="tab-nav-btn" @click="scrollBy(-1)">«</button>
|
|
|
+
|
|
|
+ <!-- 标签滚动容器 -->
|
|
|
+ <div ref="tabScroll" class="tab-scroll" @scroll="handleScroll">
|
|
|
+ <el-tag
|
|
|
+ class="tab-tag"
|
|
|
+ @click="toPath(tag)"
|
|
|
+ @close="close(tag)"
|
|
|
+ v-for="tag in keepList"
|
|
|
+ :key="tag.path"
|
|
|
+ closable
|
|
|
+ :type="activeMenu(tag)"
|
|
|
+ >
|
|
|
+ {{ tag.name }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右翻页按钮(不可用时隐藏) -->
|
|
|
+ <button v-if="canScrollRight" class="tab-nav-btn" @click="scrollBy(1)">»</button>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -27,7 +28,9 @@
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
- levelList: null
|
|
|
+ levelList: null,
|
|
|
+ canScrollLeft: false,
|
|
|
+ canScrollRight: false
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -43,24 +46,39 @@ export default {
|
|
|
}
|
|
|
|
|
|
},
|
|
|
+ mounted() {
|
|
|
+ this.updateNavState()
|
|
|
+ this.$nextTick(() => {
|
|
|
+ // 打开时自动滚动到最新(最右侧)标签
|
|
|
+ this.scrollToEnd()
|
|
|
+ })
|
|
|
+ window.addEventListener('resize', this.updateNavState)
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ window.removeEventListener('resize', this.updateNavState)
|
|
|
+ },
|
|
|
watch: {
|
|
|
- // 监听计算属性的变化
|
|
|
+ // 标签变化后:若新增标签则自动滚动到最右,并更新翻页按钮状态
|
|
|
keepList: {
|
|
|
handler(newVal, oldVal) {
|
|
|
- if (newVal && newVal.length >= 12) {
|
|
|
- this.$store.commit('user/REMOVE_TAB_BY_PATH', newVal[0].path)
|
|
|
- }
|
|
|
+ const isNewTab = !oldVal || newVal.length > oldVal.length
|
|
|
+ // 新增标签时自动滚动到最右(最新标签)
|
|
|
+ if (!isNewTab) return
|
|
|
+ // 双重 nextTick:确保新标签完全渲染后再滚动,避免 scrollWidth 未更新导致滚不到最右
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.scrollToEnd()
|
|
|
+ // 滚动可能不到位,再补偿一次确保到最右
|
|
|
+ setTimeout(this.scrollToEnd, 100)
|
|
|
+ })
|
|
|
+ })
|
|
|
},
|
|
|
deep: true,
|
|
|
immediate: true
|
|
|
}
|
|
|
},
|
|
|
- created() {
|
|
|
- // this.getBreadcrumb()
|
|
|
- },
|
|
|
methods: {
|
|
|
activeMenu(tag) {
|
|
|
- console.log(this.$route, tag, 'this.$route.name')
|
|
|
return this.$route.name === tag.path ? 'danger' : ''
|
|
|
},
|
|
|
close(tag) {
|
|
|
@@ -74,21 +92,91 @@ export default {
|
|
|
this.$router.push({
|
|
|
name: tag.path
|
|
|
})
|
|
|
+ },
|
|
|
+ // 更新左右翻页按钮可用状态
|
|
|
+ updateNavState() {
|
|
|
+ const el = this.$refs.tabScroll
|
|
|
+ if (!el) return
|
|
|
+ this.canScrollLeft = el.scrollLeft > 0
|
|
|
+ this.canScrollRight = el.scrollLeft + el.clientWidth < el.scrollWidth - 1
|
|
|
+ },
|
|
|
+ // 滚动到最右(最新标签),并更新按钮状态
|
|
|
+ scrollToEnd() {
|
|
|
+ const el = this.$refs.tabScroll
|
|
|
+ if (!el) return
|
|
|
+ el.scrollLeft = el.scrollWidth
|
|
|
+ this.updateNavState()
|
|
|
+ },
|
|
|
+ // 处理滚动事件
|
|
|
+ handleScroll() {
|
|
|
+ this.updateNavState()
|
|
|
+ },
|
|
|
+ // 翻页:左右都逐步滚动约一个标签宽度(即时滚动,避免 smooth 被打断)
|
|
|
+ scrollBy(direction) {
|
|
|
+ const el = this.$refs.tabScroll
|
|
|
+ if (!el) return
|
|
|
+ const step = 140 // 约一个标签的宽度
|
|
|
+ const maxScroll = el.scrollWidth - el.clientWidth
|
|
|
+ const target = Math.max(0, Math.min(el.scrollLeft + direction * step, maxScroll))
|
|
|
+ el.scrollLeft = target
|
|
|
+ this.updateNavState()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-.app-breadcrumb.el-breadcrumb {
|
|
|
- display: inline-block;
|
|
|
+.tab-history {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 100%;
|
|
|
+ width: 100%;
|
|
|
+ padding: 0 4px;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.tab-nav-btn {
|
|
|
+ flex-shrink: 0;
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ margin: 0 4px;
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.4);
|
|
|
+ border-radius: 4px;
|
|
|
+ background: transparent;
|
|
|
+ color: #fff;
|
|
|
font-size: 14px;
|
|
|
- line-height: 72px;
|
|
|
- margin-left: 8px;
|
|
|
+ cursor: pointer;
|
|
|
+ outline: none;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 0;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(255, 255, 255, 0.2);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.tab-scroll {
|
|
|
+ flex: 1;
|
|
|
+ overflow-x: auto;
|
|
|
+ white-space: nowrap;
|
|
|
+ height: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ min-width: 0;
|
|
|
+
|
|
|
+ /* 隐藏横向滚动条(保留滚动能力) */
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ height: 0;
|
|
|
+ }
|
|
|
+ scrollbar-width: none;
|
|
|
|
|
|
- .no-redirect {
|
|
|
- color: #97a8be;
|
|
|
- cursor: text;
|
|
|
+ .tab-tag {
|
|
|
+ margin-right: 10px;
|
|
|
+ cursor: pointer;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
}
|
|
|
</style>
|