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
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>
|