9 changed files with 455 additions and 15 deletions
After Width: | Height: | Size: 711 B |
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,311 @@ |
|||
<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> |
Loading…
Reference in new issue