uniapp实现左右分类联动
仅供参考,根据个人所需调整
效果图
分类内容数据格式效果图
rectInfoList
数据内容
template
<template>
<view class="">
<view class="slot-content category-wrap">
<view class="category-box">
<view class="cate-left">
<scroll-view class="hFull" :scroll-top="scrollTop" scroll-y="true" show-scrollbar="false">
<view class="cate-item ft24" :class="{active: current == index}" v-for="(item, index) in categoryList" @click="menuTab(index)" :key="index">
{{item.name}}
</view>
</scroll-view>
</view>
<view class="cate-right">
<scroll-view class="hFull" :scroll-into-view="`cate${intoCurrent}`" scroll-y="true" show-scrollbar="false" @scroll="getCurrentHeight">
<view class="cate-right-item" :id="`cate${index}`" v-for="(item, index) in categoryList" :key="item.id">
<view class="fcb" :class="current == index ? '' : 'pdt20'">
<view class="ft32 bold cate-title" :class="{active: current == index}">{{item.name}}</view>
<view class="fc fold fc46" :class="item.isShow ? 'isFold' : ''" v-if="item.productList.length > 9" @click.stop="isShowItemTap(index)">
<text class="ft24 pdr10">{{item.isShow ? '收起' : '展开'}}</text>
<text class="iconfont iconright ft22"></text>
</view>
</view>
<view class="grid-list" :class="[item.productList.length > 9 ? 'maxH' : '', item.isShow ? 'allItem' : '']">
<view class="grid-item ft24 tc" :class="el.isCheck ? 'actives' : ''" v-for="(el, idx) in item.productList" :key="idx" @click="filtrateTap(item, idx)">
<text>{{el.good_name}}</text>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
<view class="fixed-btn">
<view class="fcb">
<button class="fil-btn filtrate-reset" type="default" @click="resetFilTap">重 置</button>
<button class="fil-btn filtrate-confirm" type="primary" @click="confirmFilTap">完 成</button>
</view>
</view>
</view>
</view>
</template>
js
<script>
export default {
data() {
return {
categoryList: [ // 测试数据
{id: 1, name: '配件', isShow: false, productList: [
{good_name: 'mi', isCheck: false},
]},
{id: 2, name: '手表', isShow: false, productList: [
{good_name: 'mi1', isCheck: false},
]}
// ...
],
current: 0, // 当前点击项
intoCurrent:0, // 当前into-view
rectInfoList:[], // 储存右侧内容元素的top bottom
scrollTop:0, // 左侧导航栏距离顶部的位置
}
},
onShow() {
this.$nextTick(() => {
// 获取右侧内容数据
this.getRectInfo();
})
},
methods: {
/**
* @param {Object} index
* 切换菜单
*/
menuTab(index) {
this.current = index;
this.intoCurrent = `${index}`
},
/**
* @param {Object} index 下标
* 收起 or 展开
*/
isShowItemTap(index) {
this.categoryList[index].isShow = !this.categoryList[index].isShow;
},
/**
* 获取元素与顶部之间的距离
*/
getRectInfo() {
let top = 0;
let bottom = 0;
let temp = 0;
for (let i = 0; i < this.categoryList.length; i++) {
let view = uni.createSelectorQuery().in(this).select(`#cate${i}`);
view.fields({
size: true,
rect: true
}, data => {
top = temp;
bottom = top + data.height;
temp = bottom;
this.rectInfoList.push({ 'top': top, 'bottom': bottom })
}).exec();
}
},
/**
* @param {Object} e
* 滑动右侧内容联动左分类
*/
getCurrentHeight(e){
let currentHeight = e.detail.scrollTop;
this.rectInfoList.forEach((item, index)=>{
// 当前获取的盒子高度大于 top && 小于 bottom,联动选中左分类名称
if(currentHeight >= item.top && currentHeight <= item.bottom){
this.current = index;
// 每次切换左菜单分类,让滚动条与顶部保持一定的距离
this.scrollTop = index * uni.upx2px(88);
}
})
},
filtrateTap(item, cIdx) {
item.productList[cIdx].isCheck = !item.productList[cIdx].isCheck;
},
resetFilTap() {
},
confirmFilTap() {
},
}
}
</script>
css
- 部分样式
<style lang="scss" scoped>
$aux1: #4685F7;
.fc46 {
color: $aux1;
}
.grid-list {
display: flex;
flex-wrap: wrap;
padding: 12rpx 0;
overflow: hidden;
.grid-item {
@include wh(168rpx,76rpx);
line-height: 76rpx;
@include borderRadius(10rpx);
@include bg(#F4F6F9);
margin-bottom: 12rpx;
&:not(:nth-child(3n)) {
margin-right: 10rpx;
}
}
.actives {
@include bg(#FF6B00);
color: $white;
}
}
.fixed-btn {
position: absolute;
bottom: 0;
width: 100%;
z-index: 1;
padding: 10rpx 32rpx;
@include bg
box-shadow: 0px -3px 8px rgba(0,0,0,0.0500);
.fil-btn {
margin: 0;
width: 48.5%;
@include borderRadius(10rpx);
}
.filtrate-confirm {
@include bg($aux1);
}
}
.fold {
padding-right: 32rpx;
.iconright {
transform: rotate(90deg);
}
}
.isFold {
.iconright {
transform: rotate(270deg);
margin-left: 0;
transform: all 5s;
}
}
.category-wrap {
height: calc(100vh - 300rpx);
@include bg;
position: relative;
.category-box {
display: flex;
height: 100%;
}
.active {
@include bg;
color: $aux1;
}
.hFull {
height: 100%;
}
.cate-left {
@include wh(186rpx, 100%);
@include bg(#F3F3F8);
overflow-y: auto;
-webkit-overflow-scrolling: touch;
.cate-item {
line-height: 88rpx;
padding-left: 44rpx;
}
}
.cate-right {
flex: 1;
height: calc(100% - 100rpx);
padding-left: 28rpx;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.cate-right-item {
&:not(:last-of-type) {
border-bottom: 1rpx solid #E0E0E0;
}
}
.maxH {
max-height: 278rpx;
.grid-item {
&:nth-child(n+7):nth-child(-n+9) {
margin-bottom: 25rpx;
}
}
}
.allItem {
max-height: none;
transition: all .1s;
.grid-item {
margin-bottom: 12rpx !important;
}
}
}
</style>