You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

311 lines
9.5 KiB

<template>
<el-collapse v-model="activeNames" @change="handleChange">
<el-collapse-item v-for="(item, index) in menuList" :key="index" :name="item[renderKeyNew.domName]">
<template #title>
<!-- <div class="flex gap8" @click.stop="handleTitleClick"> -->
<div class="flex-between gap8 collapse-item">
<div class="flex gap8 ">
<GuipToolTip :content="item[renderKeyNew.menuTitle]">
<span class="menu-name">{{ item[renderKeyNew.menuTitle] }}</span>
</GuipToolTip>
<span v-if="item[renderKeyNew.selected]" class="menu-select"></span>
</div>
<img class="arrow_img" :class="{ 'is-active': activeNames.includes(item[renderKeyNew.domName]) }"
src="@/assets/input_ex_ic.png" alt="">
</div>
</template>
<div v-for="(item1, index1) in item.list" @click="setActiveCur(item1, item, index)"
:class="['flex-between point', activeFloor == item1[renderKeyNew.subtitle] ? 'curActive' : '']"
:key="index1">
<span class="l-menu-name">{{ item1[renderKeyNew.subtitle] }}</span>
<img v-if="item1[renderKeyNew.SubSelected]" class="selected" src="../assets/menu/is_selected.png" alt="">
</div>
</el-collapse-item>
</el-collapse>
</template>
<script>
import store from '../store';
import GuipToolTip from "@/components/GuipToolTip.vue";
export default {
options: { styleIsolation: "shared" },
props: {
menuData: {
type: [Array, Object],
required: true
},
// 后续如果渲染的值不同可以通过调整这个修改取值
renderKey: {
type: [Object],
default: () => {}
}
},
data() {
return {
activeNames: [],//获取到的值 同 绑定的name属性
curIndex: 0,
activeFloor: '',
scrollLock: false,
componentsName: ''
};
},
computed: {
// 处理options为空的情况
menuList() {
let flag = Array.isArray(this.menuData || []);
let data = this.menuData || []
if (!flag) {
data = Object.values(this.menuData)
return data
}
return this.menuData;
},
renderKeyNew() {
return Object.assign({
menuTitle: 'name',//渲染标题
subtitle: 'name',//二级标题
selected: 'is_select',//大标题:是否展示绿色状态
SubSelected: 'is_select',//二级标题:是否展示绿色状态
domName: 'type',//未来获取dom绑定依据
domAppend: 'section_' //dom 前缀
// 后续获取dom =》 domAppend + domName 在元素上绑定的 id名称
}, this.renderKey);
}
},
components: {
GuipToolTip
},
watch: {
menuList(newVal) {
let subtitle = this.renderKeyNew.subtitle;
this.activeFloor = newVal[this.curIndex]?.list?.[0][subtitle];
this.componentsName = newVal[this.curIndex]?.list?.[0]?.componentsName || '';
}
},
mounted() {
this.$nextTick(() => {
// 更可靠的获取滚动容器方式
this.scrollContainer = document.querySelector('.main-content') ||
document.getElementById('main-content') ||
window;
if (this.scrollContainer) {
this.scrollContainer.addEventListener('scroll', this.handleScroll);
this.calculateFloorOffsets();
} else {
console.error('未找到滚动容器');
}
});
},
beforeDestroy() {
if (this.scrollContainer) {
this.scrollContainer.removeEventListener('scroll', this.handleScroll);
}
},
methods: {
handleChange(val) {
console.log("面板状态变化:", val);
},
handleTitleClick() {
console.log("标题点击");
},
random() {
var randomNumber = Math.random();
return randomNumber
},
calculateFloorOffsets() {
this.menuList.forEach(item => {
item.list?.forEach(every => {
const el = document.getElementById(every.desc);
if (el) {
// 获取元素相对于视口顶部的距离
every.offsetTop = el.getBoundingClientRect().top + window.pageYOffset;
}
});
});
},
handleScroll() {
if (this.scrollLock || !this.menuList[this.curIndex]?.list) return;
const scrollPosition = this.getScrollPosition();
let activeFloor = null;
console.log(this.curIndex, scrollPosition, 'this.curIndex===');
// 从下往上查找当前可见区域对应的楼层
// for (let i = this.menuList[this.curIndex].list.length - 1; i >= 0; i--) {
// const item = this.menuList[this.curIndex].list[i];
// if (scrollPosition + 100 >= (item.offsetTop || 0)) { // 100是提前量
// activeFloor = item.desc;
// break;
// }
// }
// 从上往下
for (let i = 0; i < this.menuList[this.curIndex].list.length; i++) {
const item = this.menuList[this.curIndex].list[i];
if (scrollPosition + 100 >= (item.offsetTop || 0)) { // 100是提前量
activeFloor = item.desc;
break;
}
}
if (activeFloor && this.activeFloor !== activeFloor) {
this.activeFloor = activeFloor;
}
},
getScrollPosition() {
if (this.scrollContainer === window) {
return window.pageYOffset || document.documentElement.scrollTop;
}
return this.scrollContainer.scrollTop;
},
// setActiveCur(dom, item) {
// if (this.$route.path != item.path) {
// this.$router.push(item.path)
// store.commit('SET_PAGETITLE', item.name);
// }
// setTimeout(() => {
// this.activeFloor = dom;
// this.setHighActive(dom)
// }, 500)
// },
// setHighActive(dom) {
// this.scrollLock = true;
// const ele = document.getElementById(dom)
// if (!ele) return
// ele.classList.add('ceshi')
// ele.scrollIntoView({ behavior: 'smooth', block: 'start' })
// setTimeout(() => {
// ele.classList.remove('ceshi')
// }, 1000)
// }
setActiveCur(item1, item, index) {
this.curIndex = index;
let subtitle = this.renderKeyNew.subtitle;
let domName = this.renderKeyNew.domName;
let componentsName = item1?.componentsName;
// 如果采用的是 组件切换显示的办法
if (componentsName) {
this.activeFloor = componentsName;
// 同步 当前切换的组件名称
store.commit('SET_COMPONENTS_NAME', componentsName);
return
}
this.scrollLock = true;
this.activeFloor = item1[subtitle];
this.$nextTick(() => {
this.setHighActive(item1[domName]);
setTimeout(() => {
this.scrollLock = false;
}, 1000);
});
},
setHighActive(dom) {
const ele = document.getElementById(this.renderKeyNew.domAppend + dom);
if (ele) {
ele.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
}
};
</script>
<style scoped lang="scss">
/* 自定义折叠图标 */
::v-deep .el-collapse-item__header .el-collapse-item__arrow {
display: none !important;
}
::v-deep .el-collapse-item__header {
max-width: 120px;
}
.arrow_img {
width: 14px;
height: 14px;
}
.arrow_img.is-active {
transform: rotate(90deg);
}
.collapse-item {
width: 100%;
}
.menu-name {
font-size: 14px;
font-weight: normal;
letter-spacing: 0.08em;
color: #1E2226;
padding: 11px 0;
height: 40px;
line-height: 18px;
max-width: 80px;
box-sizing: border-box;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.l-menu-name {
max-width: 90px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: #8A9099;
padding: 9px 0;
height: 36px;
box-sizing: border-box;
}
.curActive {
.l-menu-name {
color: #006AFF;
font-weight: bold;
}
}
.menu-select {
width: 8px;
height: 8px;
border-radius: 100%;
background: #00C261;
}
.el-collapse {
min-width: 158px;
max-width: 158px;
background-color: #fff;
height: 100%;
overflow-y: auto;
padding: 21px 22px 21px 16px;
box-sizing: border-box;
box-shadow: 0px 0px 11px 2px rgba(147, 147, 147, 0.11);
}
.selected {
width: 20px;
height: 14px;
}
::v-deep .el-collapse-item__header {
height: 40px;
border-bottom: none;
}
::v-deep .el-collapse-item__content {
padding-bottom: 0;
}
::v-deep .el-collapse-item__wrap {
border-bottom: none;
}
</style>