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