6 changed files with 326 additions and 31 deletions
-
243src/components/common/CommonTab.vue
-
8src/components/common/LocalConfig.vue
-
10src/main.js
-
36src/router/index.js
-
30src/store/index.js
-
24src/views/Home.vue
@ -0,0 +1,243 @@ |
|||||
|
<template> |
||||
|
<div class="tags-list" ref="totalLists"> |
||||
|
<div |
||||
|
class="arrow-icon" |
||||
|
@click="arrowBack" |
||||
|
style="position: fixed; left: 4px" |
||||
|
> |
||||
|
<el-button |
||||
|
size="medium" |
||||
|
icon="el-icon-arrow-left" |
||||
|
:disabled="isLeftDisabled" |
||||
|
></el-button> |
||||
|
</div> |
||||
|
<div class="tag-style" ref="tagBox"> |
||||
|
<div class="scrollWrapper" ref="scrollWrapper" id="nav"> |
||||
|
<el-tag |
||||
|
:key="tag.displayName" |
||||
|
size="medium" |
||||
|
v-for="(tag, index) in tags" |
||||
|
:closable="true" |
||||
|
:disable-transitions="false" |
||||
|
@close="handleClose(tag, index)" |
||||
|
@click="changeMenu(tag)" |
||||
|
:effect="$route.name === tag.displayName ? 'dark' : 'plain'" |
||||
|
> |
||||
|
{{ tag.displayName }} |
||||
|
</el-tag> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div |
||||
|
class="arrow-icon" |
||||
|
@click="arrowForward" |
||||
|
style="position: fixed; right: 4px" |
||||
|
> |
||||
|
<el-button |
||||
|
size="medium" |
||||
|
icon="el-icon-arrow-right" |
||||
|
:disabled="isRightDisabled" |
||||
|
></el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapState, mapMutations } from "vuex"; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
scrollWrapper: null, |
||||
|
isLeftDisabled: false, |
||||
|
isRightDisabled: false, |
||||
|
}; |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapState({ |
||||
|
tags: (state) => state.tabsList, |
||||
|
}), |
||||
|
}, |
||||
|
watch: { |
||||
|
$route: { |
||||
|
handler(val) { |
||||
|
this.updateButtonStates(); |
||||
|
this.scrollToActiveTag(); |
||||
|
}, |
||||
|
// 深度观察监听 |
||||
|
deep: true, |
||||
|
}, |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.scrollWrapper = this.$refs.scrollWrapper; |
||||
|
this.$nextTick(() => { |
||||
|
this.updateButtonStates(); |
||||
|
}); |
||||
|
this.scrollWrapper.addEventListener("scroll", this.handleScroll); |
||||
|
window.addEventListener("resize", this.updateButtonStates); |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
// 移除事件监听器 |
||||
|
if (this.scrollWrapper) { |
||||
|
this.scrollWrapper.removeEventListener("scroll", this.handleScroll); |
||||
|
} |
||||
|
window.removeEventListener("resize", this.updateButtonStates); |
||||
|
}, |
||||
|
methods: { |
||||
|
...mapMutations({ |
||||
|
close: "closeTab", |
||||
|
}), |
||||
|
handleScroll() { |
||||
|
// 使用 setTimeout 确保获取到最新的滚动位置 |
||||
|
setTimeout(() => { |
||||
|
this.updateButtonStates(); |
||||
|
}, 50); |
||||
|
}, |
||||
|
// 标签向左切换 |
||||
|
arrowBack() { |
||||
|
this.$refs.scrollWrapper.scrollBy({ left: -300, behavior: "smooth" }); |
||||
|
}, |
||||
|
// 标签向右切换 |
||||
|
arrowForward() { |
||||
|
this.$refs.scrollWrapper.scrollBy({ left: 300, behavior: "smooth" }); |
||||
|
}, |
||||
|
updateButtonStates() { |
||||
|
const { scrollLeft, scrollWidth, clientWidth } = this.scrollWrapper; |
||||
|
this.isLeftDisabled = scrollLeft <= 0; |
||||
|
this.isRightDisabled = scrollLeft + clientWidth >= scrollWidth - 1; |
||||
|
}, |
||||
|
scrollToActiveTag() { |
||||
|
this.$nextTick(() => { |
||||
|
// 查找当前激活的标签(effect为'dark'的标签) |
||||
|
const scrollWrapper = this.$refs.scrollWrapper; |
||||
|
const tagElements = scrollWrapper.querySelectorAll('.el-tag'); |
||||
|
|
||||
|
// 遍历标签元素,找到与当前路由匹配的标签 |
||||
|
let activeTag = null; |
||||
|
for (let i = 0; i < tagElements.length; i++) { |
||||
|
const tagElement = tagElements[i]; |
||||
|
// 获取标签内的文本内容 |
||||
|
const tagText = tagElement.textContent ? tagElement.textContent.trim() : ''; |
||||
|
|
||||
|
// 检查标签文本是否与当前路由名称匹配 |
||||
|
if (tagText === this.$route.name) { |
||||
|
activeTag = tagElement; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (activeTag) { |
||||
|
const tagPosition = activeTag.offsetLeft; |
||||
|
const tagWidth = activeTag.offsetWidth; |
||||
|
|
||||
|
// 无论如何都滚动到使标签居中的位置 |
||||
|
const scrollTo = tagPosition - (scrollWrapper.clientWidth - tagWidth) / 2; |
||||
|
scrollWrapper.scrollTo({ |
||||
|
left: scrollTo, |
||||
|
behavior: 'smooth' |
||||
|
}); |
||||
|
|
||||
|
// 滚动后更新按钮状态 |
||||
|
setTimeout(() => { |
||||
|
this.updateButtonStates(); |
||||
|
}, 150); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
handleClose(tag, index) { |
||||
|
let length = this.tags.length - 1; |
||||
|
this.close(tag); |
||||
|
// 如果关闭的标签不是当前路由的话,就不跳转 |
||||
|
if (tag.displayName !== this.$route.name) { |
||||
|
this.handleScroll(); |
||||
|
return; |
||||
|
} |
||||
|
// 关闭的标签是最右边的话,往左边跳转一个 |
||||
|
if (index === length) { |
||||
|
if (this.tags[index - 1]) { |
||||
|
this.$router.push({ path: this.tags[index - 1].routeUrl }); |
||||
|
} else { |
||||
|
this.$router.push({ path: "/home" }); |
||||
|
} |
||||
|
} else { |
||||
|
// 否则往右边跳转 |
||||
|
this.$router.push({ path: this.tags[index].routeUrl }); |
||||
|
} |
||||
|
this.handleScroll(); |
||||
|
}, |
||||
|
changeMenu(item) { |
||||
|
if (item.displayName !== this.$route.name) { |
||||
|
this.$router.push({ path: item.routeUrl }); |
||||
|
} else { |
||||
|
// this.$message({ |
||||
|
// message: '请不要重复选择', |
||||
|
// type: 'error' |
||||
|
// }); |
||||
|
} |
||||
|
this.$nextTick(() => { |
||||
|
this.scrollToActiveTag(); |
||||
|
}); |
||||
|
}, |
||||
|
async clearControlLabel() { |
||||
|
await this.$store.commit("clearMenuTab"); |
||||
|
this.$router.push({ path: "/home" }); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
@import '../../assets/css/global_button.css'; |
||||
|
.tags-list { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
position: fixed; |
||||
|
top: 50px; |
||||
|
width: 100%; |
||||
|
height: 36px; |
||||
|
background: #fff; |
||||
|
border-bottom: 1px solid #d8dce5; |
||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04); |
||||
|
padding-bottom: 0; |
||||
|
} |
||||
|
.tag-style { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
overflow: hidden; |
||||
|
pointer-events: all; |
||||
|
cursor: pointer; |
||||
|
position: relative; |
||||
|
margin: 0 64px; |
||||
|
} |
||||
|
.scrollWrapper { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
overflow-x: auto; |
||||
|
transition: all 500ms linear; |
||||
|
} |
||||
|
.scrollWrapper::-webkit-scrollbar { |
||||
|
height: 0; |
||||
|
} |
||||
|
.el-tag { |
||||
|
margin: 0 5px; |
||||
|
cursor: pointer; |
||||
|
padding: 0 0px 0 10px; |
||||
|
} |
||||
|
.el-tag .el-tag--info { |
||||
|
background: #fff; |
||||
|
} |
||||
|
::v-deep .el-tag .el-icon-close { |
||||
|
right: 0px; |
||||
|
} |
||||
|
.arrow-icon { |
||||
|
pointer-events: all; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.arrow-style { |
||||
|
font-size: 16px; |
||||
|
padding: 0 8px; |
||||
|
position: relative; |
||||
|
top: 8px; |
||||
|
} |
||||
|
.arrow-icon .el-button { |
||||
|
padding: 0px 5px; |
||||
|
height: 28px; |
||||
|
} |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue