UniApp 商品分类左右联动技术文档
·
一、功能说明
实现 商品分类左右联动 效果:
-
点击左侧分类 → 右侧自动滚动到对应分类内容;
-
滚动右侧内容 → 左侧自动高亮当前分类。
二、核心结构
<scroll-view class="menuLeft" scroll-y>
<view v-for="(item,index) in menuList"
@click="changeCurrent(index)"
:class="{currentMenu: currentIndex==index}">
{{item.name}}
</view>
</scroll-view>
<scroll-view class="menuRight" scroll-y
:scroll-top="scrollTop"
@scroll="scrollRight">
<view class="menuDetailBox"
v-for="(item,index) in menuList" :key="index">
<view class="title">{{item.name}}</view>
<view v-for="menu in item.children" class="menu">
{{menu.name}}
</view>
</view>
</scroll-view>
三、主要逻辑
1️⃣ 初始化高度计算(onLoad)
获取右侧每个分类区块高度,并计算累计值:
query.selectAll('.menuDetailBox').boundingClientRect(data => {
this.newData = data.map((item, i) => {
item.offsetHeight = data.slice(0, i + 1)
.reduce((sum, cur) => sum + cur.height, 0)
return item
})
}).exec()
2️⃣ 点击左侧分类
根据预计算的高度滚动右侧内容:
changeCurrent(index) {
this.currentIndex = index
this.scrollTop = this.newData[index - 1]?.offsetHeight || 0
}
3️⃣ 右侧滚动时联动左侧
监听滚动距离,动态更新左侧高亮:
scrollRight(e) {
const top = e.detail.scrollTop
for (let i = 0; i < this.newData.length; i++) {
if (top < this.newData[i].offsetHeight) {
this.currentIndex = i
break
}
}
}
四、样式要点
-
左侧固定宽度
200rpx,右侧自适应。 -
当前分类高亮(加粗 / 红条标识)。
-
右侧滚动视图需开启
scroll-y="true"。
.currentMenu { font-weight: 700; }
.line::before { background-color: red; }
五、总结
-
使用
scrollTop+ 高度累计实现左右同步滚动。 -
左右两栏通过
currentIndex实现联动高亮。 -
简单可靠,适用于电商、分类导航场景。
完整代码
<template>
<view class="menuBox">
<view class="menuContent">
<scroll-view scroll-y="true" class="menuLeft">
<view class="menuType" @click="changeCurrent(index)" v-for="(item,index) in menuList"
:class="{currentMenu:currentIndex==index}">
{{item.name}}
</view>
<view class="line" :style="{top:currentIndex*90 +'rpx'}"></view>
</scroll-view>
<scroll-view :scroll-top="scrollTop" @scroll="scrollRight" class="menuRight" scroll-y="true">
<div class="menuDetailBox" v-for="(item,index) in menuList" :key="index">
<div class="title">{{item.name}}</div>
<div class="menu" v-for="(menu,index) in item.children" :key="index">
<div class="d2">
123
</div>
<div class="menuName">
{{menu.name}}
</div>
</div>
</div>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
newData:[], // 拿到右侧所有的商品列表元素
scrollTop: 0,
currentIndex: 0,
menuList: [{
"id": 1,
"name": "手机数码",
"children": [{
"id": 101,
"name": "手机"
},
{
"id": 105,
"name": "数码相机"
}
]
},
{
"id": 2,
"name": "家用电器",
"children": [{
"id": 201,
"name": "电视机"
},
{
"id": 204,
"name": "冰箱"
},
{
"id": 205,
"name": "吸尘器"
}
]
},
{
"id": 3,
"name": "服饰鞋包",
"children": [{
"id": 301,
"name": "男装"
},
{
"id": 302,
"name": "女装"
},
{
"id": 303,
"name": "运动鞋"
},
{
"id": 304,
"name": "箱包"
},
{
"id": 305,
"name": "配饰"
}
]
},
{
"id": 4,
"name": "美妆个护",
"children": [{
"id": 401,
"name": "面部护肤"
},
{
"id": 402,
"name": "彩妆"
},
{
"id": 403,
"name": "洗发护发"
},
{
"id": 404,
"name": "身体护理"
},
{
"id": 405,
"name": "香水"
}
]
},
{
"id": 5,
"name": "母婴玩具",
"children": [{
"id": 501,
"name": "奶粉"
},
{
"id": 502,
"name": "尿不湿"
},
{
"id": 503,
"name": "婴儿用品"
},
{
"id": 504,
"name": "儿童玩具"
},
{
"id": 505,
"name": "孕妇用品"
}
]
},
{
"id": 6,
"name": "食品饮料",
"children": [{
"id": 601,
"name": "休闲零食"
},
{
"id": 602,
"name": "粮油调味"
},
{
"id": 603,
"name": "饮料酒水"
},
{
"id": 604,
"name": "冲调速食"
},
{
"id": 605,
"name": "生鲜水果"
}
]
},
{
"id": 7,
"name": "家居家装",
"children": [{
"id": 701,
"name": "家具"
},
{
"id": 702,
"name": "灯具"
},
{
"id": 703,
"name": "床上用品"
},
{
"id": 704,
"name": "厨房用品"
},
{
"id": 705,
"name": "装饰摆件"
}
]
},
{
"id": 8,
"name": "运动户外",
"children": [{
"id": 801,
"name": "跑步装备"
},
{
"id": 802,
"name": "健身器材"
},
{
"id": 803,
"name": "户外鞋服"
},
{
"id": 804,
"name": "骑行装备"
},
{
"id": 805,
"name": "垂钓用品"
}
]
},
{
"id": 9,
"name": "汽车用品",
"children": [{
"id": 901,
"name": "机油"
},
{
"id": 902,
"name": "轮胎"
},
{
"id": 903,
"name": "车载电器"
},
{
"id": 904,
"name": "清洁用品"
},
{
"id": 905,
"name": "维修配件"
}
]
},
{
"id": 10,
"name": "图书文娱",
"children": [{
"id": 1001,
"name": "文学小说"
},
{
"id": 1002,
"name": "少儿读物"
},
{
"id": 1003,
"name": "教育考试"
},
{
"id": 1004,
"name": "艺术设计"
},
{
"id": 1005,
"name": "音像制品"
}
]
}
]
}
},
methods: {
changeCurrent(index) {
this.currentIndex = index
this.scrollTop = this.newData[index-1]?.offsetHeight || 0
},
scrollRight(e) {
// console.log(e.detail.scrollTop,'scrollRight');
const query = uni.createSelectorQuery().in(this);
for (let i = 0; i < this.newData.length; i++) {
let el = this.newData[i]
if (e.detail.scrollTop < el.offsetHeight) {
this.currentIndex = i
return true
}
}
}
},
onLoad() {
uni.setNavigationBarTitle({
title: '你好'
})
const query = uni.createSelectorQuery().in(this);
query.selectAll('.menuDetailBox').boundingClientRect(data => {
console.log('所有分类项:', data);
this.newData = data.map((item, index) => {
function fgfg(total) {
let sum = 0
for (var i = 0; i <= total; i++) {
sum += data[i].height
}
return sum
}
item.offsetHeight = fgfg(index)
return item
})
}).exec();
}
}
</script>
<style lang="scss" scoped>
.menuBox {
height: calc(100vh);
display: flex;
flex-direction: column;
border: 1px solid #ccc;
margin: 0 10rpx;
.menuContent {
height: calc(100vh);
overflow: auto;
position: relative;
display: flex;
position: sticky;
top: 0rpx;
.menuLeft {
float: left;
position: relative;
width: 200rpx;
background-color: #f5f5f5;
.menuType {
transition: all 0.4s;
position: relative;
z-index: 999999999;
height: 90rpx;
display: flex;
align-items: center;
padding-left: 20rpx;
&.currentMenu {
font-weight: 700;
}
}
}
.menuRight {
flex: 1;
background-color: #fff;
.menuDetailBox {
.title {
height: 60rpx;
display: flex;
align-items: center;
}
.menu {
background-color: skyblue;
height: 100rpx;
display: flex;
justify-content: center;
&:not(:nth-last-of-type(1)) {
margin-bottom: 20rpx;
}
.d2 {
width: 60rpx;
height: 60rpx;
}
.menuName {
flex: 1;
margin-left: 20rpx;
}
}
}
}
}
}
.line {
position: absolute;
left: 0;
top: 0;
height: 90rpx;
width: 100%;
background-color: #fff;
z-index: 999;
transition: all 0.4s;
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 90rpx;
width: 4rpx;
background-color: red;
z-index: 999;
transition: all 0.4s
}
}
</style>
更多推荐



所有评论(0)