效果
点击左侧边栏,右侧会定位到对应内容;
右侧滑动,左侧也会显示对应的高亮;
也就是左右联动的效果
项目场景
tocc-app 应急巡检
传入数据:
左侧点击,右侧滚动到对应位置
点击左侧导航条,就计算出右侧要的
toggleActive(index) {
this.isSidebarClick = true
this.scActiveIndex = index
// .map返回数组,里面的元素是每个对象中scContentData的长度 [6,6,6,6,6]
// 然后 .reduce 来求和 计算总的长度
this.scContentScrollTop =
this.scDataList
.slice(0, index)
.map((item) => item.scContentData.length)
.reduce((total, length) => total + length, 0) *
41 +
// 因为右边除了数据,还要展示小标题,所以要再加上前面的标题
this.scDataList.slice(0, index).length * 41
},
完整代码
<template>
<view class="sc-tabs">
<!-- 左侧侧边栏 -->
<view class="menu-aside-tab-box">
<scroll-view class="menu-aside-tab" scroll-y>
<view
v-for="(item, index) in scDataList"
:class="scActiveIndex === index ? 'aside-active' : 'aside-line'"
:key="item.barId"
class="aside-bar"
@click="toggleActive(index)">
<!-- :style="{ 'border-left': scActiveIndex === index ? '3px #3c9cff solid' : 'none' }" -->
<view
:style="{
'border-left': scActiveIndex === index ? '3px #3c9cff solid' : 'none',
'font-size': scActiveIndex === index ? '15px' : '13px',
width: '100%',
height: '12px',
'line-height': '12px'
}">
{{ item.barTitle }}
</view>
</view>
</scroll-view>
</view>
<!-- 右侧内容 -->
<view class="scContent-box">
<scroll-view class="scContent" scroll-y :scroll-top="scContentScrollTop" @scroll="ScHandleContentScroll">
<view v-for="(item, index) in scDataList" :key="item.barId" class="content-bar">
<view class="bar-title">
<view class="br-title-line"></view>
<text>{{ item.barTitle }}</text>
</view>
<view class="line"></view>
<!-- 此处为内容栏每行,可以按需将view修改为card或list -->
<view v-for="subItem in item.scContentData" :key="subItem.id" class="content-bar-item">
<view style="height: 41px; line-height: 29px">{{ subItem.name }}</view>
<view class="stateBox">
<!-- 正常按钮 -->
<button
class="state"
v-show="!subItem.isShowAddButton"
:style="{ backgroundColor: buttonBgcMap[subItem.state[0]] }">
{{ subItem.state[0] }}
</button>
<!-- 异常按钮 -->
<button
class="state"
@click="errorButtonClick(item.barId, subItem.id)"
:disabled="subItem.isShowAddButton"
:style="{ backgroundColor: buttonBgcMap[subItem.state[1]] }">
{{ subItem.state[1] }}
</button>
<!-- 添加按钮 -->
<button
class="state"
v-show="subItem.isShowAddButton"
@click="addClick"
:style="{ backgroundColor: buttonBgcMap['添加'] }">
添加
</button>
</view>
</view>
<!-- -->
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
name: 'yhuNavigation',
props: {
scPassingArray: {
type: Array,
required: true
}
},
data() {
return {
scActiveIndex: 0,
scDataList: [], //数据
scContentScrollTop: 0, // 内容栏滚动位置
ScContentHeight: 0, // 内容栏高度
isSidebarClick: false, // 是否通过点击侧边栏触发滚动事件
buttonBgcMap: {
异常: '#FF5252',
添加: '#1484E8',
正常: '#25BE00'
}
}
},
created() {
this.scDataList = [...this.scPassingArray]
},
mounted() {
this.calculateContentHeight()
},
methods: {
calculateContentHeight() {
this.ScContentHeight =
this.scDataList.map((item) => item.scContentData.length).reduce((total, length) => total + length, 0) * 30 +
this.scDataList.length * 40
},
toggleActive(index) {
this.isSidebarClick = true
this.scActiveIndex = index
console.log('this.scDataList.slice(0, index)', this.scDataList.slice(0, index))
console.log(
'this.scDataList.slice(0, index).map((item) => item.scContentData.length)',
this.scDataList.slice(0, index).map((item) => item.scContentData.length)
)
this.scContentScrollTop =
this.scDataList
.slice(0, index)
.map((item) => item.scContentData.length)
.reduce((total, length) => total + length, 0) *
41 +
this.scDataList.slice(0, index).length * 41
},
ScHandleContentScroll(event) {
// 通过点击侧边栏触发的滚动事件,不执行后续的处理
if (this.isSidebarClick) {
this.isSidebarClick = false
return
}
const scrollTop = event.detail.scrollTop
let ScAccumulatedHeight = 0
let index = 0
for (let i = 0; i < this.scDataList.length; i++) {
const ScitemHeight = this.scDataList[i].scContentData.length * 30 + 40
if (scrollTop >= ScAccumulatedHeight && scrollTop < ScAccumulatedHeight + ScitemHeight) {
index = i
break
}
ScAccumulatedHeight += ScitemHeight
}
this.scActiveIndex = index
},
// 异常按钮点击
errorButtonClick(itemBarId, subItemId) {
this.$emit('changeIsShowAddButton', { itemBarId, subItemId })
// this.isShowAddButton = true
// console.log('isShowAddButton', this.isShowAddButton)
},
// 点击添加按钮
addClick() {
console.log('点击了添加按钮!!')
}
}
}
</script>
<style>
/deep/.uni-scroll-view::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
overflow: auto !important;
appearance: auto !important;
background: transparent;
}
.sc-tabs {
display: flex;
width: 100%;
height: 100vh;
/* margin-top: -5px; */
border-top: 1px solid #eeeeee;
}
.menu-aside-tab-box {
width: 22%;
/* width: 88px; */
height: 100%;
}
.menu-aside-tab {
/* width: 20vh; */
width: 88px;
height: 100vh;
background-color: transparent;
}
.aside-bar {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 40px;
font-family: Alibaba-PuHuiTi, Alibaba-PuHuiTi;
font-size: 13px;
font-weight: normal;
line-height: 18px;
text-align: center;
}
.aside-active {
position: relative;
width: 100%;
height: 40px;
font-size: 15px;
font-weight: 600;
line-height: 40px;
color: #3c9cff;
background-color: #ffffff;
}
.aside-active-beforecontent {
position: absolute;
width: 3px;
background-color: #008bff;
}
.aside-line {
font-family: Alibaba-PuHuiTi, Alibaba-PuHuiTi;
/* width: 95%; */
/* height: 18px; */
font-size: 13px;
font-weight: normal;
line-height: 18px;
color: #333333;
}
.scContent-box {
/* width: 80vh; */
flex: 1;
height: 100vh;
}
.scContent {
width: 100%;
height: 100%;
}
.content-bar {
/* padding: 0px 60px; */
padding-left: 12px;
/* padding-bottom: 10px; */
background: #ffffff;
}
.content-bar-item {
display: flex;
justify-content: space-between;
width: 96%;
height: 41px;
padding-top: 5px;
/* position: relative; */
line-height: 30px;
border-bottom: 1px solid #eeeeee;
/* border: 1px solid red; */
}
.bar-title {
display: flex;
align-items: center;
/* width: 300px; */
width: 100%;
height: 40px;
font-family: Alibaba-PuHuiTi, Alibaba-PuHuiTi;
font-size: 14px;
font-weight: 600;
line-height: 10px;
color: #333333;
}
.br-title-line {
width: 8px;
height: 8px;
margin-right: 6px;
border: 2px solid #3c9cff;
border-radius: 4px;
}
.line {
width: calc(100% - 11px);
height: 1px;
background: #eeeeee;
}
.stateBox {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 90px;
height: 41px;
padding-top: 3px;
/* border: 1px solid red; */
overflow: hidden;
}
.state {
width: 40px;
height: 22px;
padding: 0 6px;
font-family: Alibaba-PuHuiTi, Alibaba-PuHuiTi;
font-size: 14px;
font-weight: normal;
line-height: 22px;
color: #ffffff;
text-align: center;
border-radius: 2px;
}
</style>