对于左右菜单联动的需求是很常见的在小程序里,主要表现为:
- 点击左侧的菜单栏,右侧会切换到对应的内容区域
- 滑动右侧的内容,左侧会自动切换到对应的菜单项
主要利用的是 scroll-view标签,以及相关的一些API,可参考:uniapp.dcloud.net.cn/api/ui/node… 去获取当前的所有节点集合,再配合 scroll-view 的 scroll-top 属性,使其在点击左侧菜单栏的时候动态赋值右侧scroll-view 的 scroll-top 属性,从而实现点击左侧菜单栏时右侧内容区域进行滚动
基本UI结构:
<template>
<view class="d-flex border-top border-light-secondary" style="height: 100%; box-sizing: border-box;">
<!-- 左侧菜单栏 -->
<scroll-view scroll-y style="flex: 1; height: 100%;" class="border-right border-light-secondary">
<view class="border-bottom border-light-secondary py-1" hover-class="bg-light-secondary"
v-for="(item,index) in cate" :key="item.id" @click="changeCate(index)">
<view class="py-1 font-md text-muted text-center" :class="activeIndex == index ? 'class-active' : ''">
{{item.name}}
</view>
</view>
</scroll-view>
<!-- 右侧数据 -->
<scroll-view scroll-y style="flex: 3.5; height: 100%;">
<view class="row" v-for="(item,index) in list" :key="index">
<view class="span-8 text-center py-2" v-for="(item2,index2) in item.list" :key="index2">
<image :src="item2.src" mode="" style="width: 120upx;height: 120upx;"></image>
<text class="d-block">{{item2.name}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
// 左侧菜单栏当前选中的分类
activeIndex: 0,
// 左侧菜单栏分类数据
cate: [],
// 右侧内容
list: [],
//
}
},
onl oad() {
// 模拟左侧菜单栏分类数据
for (let i = 0; i < 20; i++) {
this.cate.push({
name: "分类" + i,
id: i
})
}
// 模拟右侧内容数据
for (let i = 0; i < 15; i++) {
this.list.push({
list: [{
src: '/static/images/demo/cate_01.png',
name: '商品一'
},
{
src: '/static/images/demo/cate_02.png',
name: '商品一'
},
{
src: '/static/images/demo/cate_06.png',
name: '商品一'
},
{
src: '/static/images/demo/cate_05.png',
name: '商品一'
}
]
})
}
},
methods: {
// 点击左侧菜单栏,当前选中项高亮--切换
changeCate(index) {
this.activeIndex = index;
},
//
}
}
</script>
<style lang="scss" scoped>
.class-active {
border-left: 8upx solid #FD6801;
color: #FD6801 !important;
}
</style>
点击左侧菜单栏-右侧内容滚动到对应区域
思路:
拿到左右两边每个dom距离屏幕顶端的距离(top),放入cate[]和list[]中备用,当我们点击左边导航菜单项时,我们改变右边相同索引dom在竖直方向 向上的滚动距离(其滚动距离就是该索引对应dom距离顶部的距离)
在uniapp的scroll-view组件有个scroll-top属性可以很方便的改变top值。
<template>
<view class="d-flex border-top border-light-secondary" style="height: 100%; box-sizing: border-box;">
<!-- 左侧菜单栏 -->
<scroll-view scroll-y style="flex: 1; height: 100%;" class="border-right border-light-secondary">
<view class="border-bottom border-light-secondary py-1 left-scroll-item" hover-class="bg-light-secondary"
v-for="(item,index) in cate" :key="item.id" @click="changeCate(index)">
<view class="py-1 font-md text-muted text-center" :class="activeIndex == index ? 'class-active' : ''">
{{item.name}}
</view>
</view>
</scroll-view>
<!-- 右侧数据 -->
<scroll-view scroll-y style="flex: 3.5; height: 100%;" :scroll-top="rightScrollTop"
:scroll-with-animation="true">
<view class="row right-scroll-item" v-for="(item,index) in list" :key="index">
<view class="span-8 text-center py-2" v-for="(item2,index2) in item.list" :key="index2">
<image :src="item2.src" mode="" style="width: 120upx;height: 120upx;"></image>
<text class="d-block">{{item2.name}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
// 左侧菜单栏当前选中的分类
activeIndex: 0,
// 左侧菜单栏分类数据
cate: [],
// 右侧内容
list: [],
// 记录左侧导航里的每一个导航栏距离顶部的距离
leftDomsTop: [],
// 记录右侧菜单距离顶部的距离
rightDomsTop: [],
// 右侧内容区块滚动的距离
rightScrollTop: 0,
}
},
// 页面加载中类似于created--获取不到DOM节点
onl oad() {
// 模拟右侧内容数据
this.getData();
},
// 页面渲染完成-可获取DOM节点,相当于mounted
onReady() {
const query = uni.createSelectorQuery().in(this);
// 左侧导航栏中的每一个导航栏距离顶部距离
query.selectAll('.left-scroll-item').boundingClientRect(data => {
this.leftDomsTop = data.map(v => v.top);
}).exec();
// 右侧内容中的每一个距离顶部距离
query.selectAll('.right-scroll-item').boundingClientRect(data => {
this.rightDomsTop = data.map(v => v.top);
}).exec();
},
methods: {
// 获取数据
getData() {
// 模拟左侧菜单栏分类数据
for (let i = 0; i < 20; i++) {
// 左侧导航
this.cate.push({
name: "分类" + i,
id: i
})
// 右侧内容
this.list.push({
list: []
})
for (let i = 0; i < this.list.length; i++) {
for (let j = 0; j < 24; j++) {
this.list[i].list.push({
src: '/static/images/demo/cate_01.png',
name: '分类' + i + '-商品' + j
})
}
}
}
},
// 点击左侧菜单栏,当前选中项高亮--切换
changeCate(index) {
this.activeIndex = index;
// 右边内容scroll-view滚动到对应的区块
this.rightScrollTop = this.rightDomsTop[index];
},
//
}
}
</script>
<style lang="scss" scoped>
.class-active {
border-left: 8upx solid #FD6801;
color: #FD6801 !important;
}
</style>
滚动右侧内容-左侧菜单栏跟着联动到对应菜单栏项
原理
- 监听右边菜单的滚动事件(@scroll="onRightScroll"),可以拿到右边dom滚动的top值数组,拿到后去匹配左边相同索引的dom,将该索引值赋值给activeIndex即可。
- 监听左边菜单项activeIndex变化的时候,当左边状态为active的dom的leftDomsTop[activeIndex] + 该dom本身高度(cateItemHeight)> 左边scroll-view的高度(H)+ 其本身的top值(ST)时,我们让该dom节点向上滚动其本身高度距离(cateItemHeight),向上滚动也是同样的道理
<template>
<view class="d-flex border-top border-light-secondary" style="height: 100%; box-sizing: border-box;">
<!-- 左侧菜单栏 -->
<scroll-view scroll-y style="flex: 1; height: 100%;" class="border-right border-light-secondary" id="leftScroll" :scroll-top="leftScrollTop">
<view class="border-bottom border-light-secondary py-1 left-scroll-item" hover-class="bg-light-secondary"
v-for="(item,index) in cate" :key="item.id" @click="changeCate(index)">
<view class="py-1 font-md text-muted text-center" :class="activeIndex == index ? 'class-active' : ''">
{{item.name}}
</view>
</view>
</scroll-view>
<!-- 右侧数据 -->
<scroll-view scroll-y style="flex: 3.5; height: 100%;" :scroll-top="rightScrollTop" :scroll-with-animation="true" @scroll="onRightScroll">
<view class="row right-scroll-item" v-for="(item,index) in list" :key="index">
<view class="span-8 text-center py-2" v-for="(item2,index2) in item.list" :key="index2">
<image :src="item2.src" mode="" style="width: 120upx;height: 120upx;"></image>
<text class="d-block">{{item2.name}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
// 加载效果
showLoading:true,
// 左侧菜单栏当前选中的分类
activeIndex: 0,
// 左侧菜单栏分类数据
cate: [],
// 右侧内容
list: [],
// 记录左侧导航里的每一个导航栏距离顶部的距离
leftDomsTop: [],
// 记录右侧菜单距离顶部的距离
rightDomsTop: [],
// 右侧内容区块滚动的距离
rightScrollTop: 0,
leftScrollTop:0,
cateItemHeight: 0,
}
},
// 页面加载中类似于created--获取不到DOM节点
onl oad() {
// 模拟右侧内容数据
this.getData();
},
watch: {
async activeIndex(newValue, oldValue) {
// 获取scroll-view高度以及scrollTop
const query = uni.createSelectorQuery().in(this);
// 左侧导航栏中的每一个导航栏距离顶部距离
query.select('#leftScroll').fields({
size: true,
scrollOffset: true
}, data => {
let H = data.height;
let ST = data.scrollTop;
// 下边
if((this.leftDomsTop[newValue] + this.cateItemHeight) > (H+ST)){
return this.leftScrollTop = this.leftDomsTop[newValue] + this.cateItemHeight - H;
}
// 上边
if(ST > this.cateItemHeight){
this.leftScrollTop = this.leftDomsTop[newValue];
}
}).exec();
},
},
// 页面渲染完成-可获取DOM节点,相当于mounted
onReady() {
const query = uni.createSelectorQuery().in(this);
// 左侧导航栏中的每一个导航栏距离顶部距离
query.selectAll('.left-scroll-item').fields({
size: true,
rect: true
}, data => {
this.leftDomsTop = data.map(v => {
this.cateItemHeight = v.height;
return v.top;
});
}).exec();
// 右侧内容中的每一个距离顶部距离
query.selectAll('.right-scroll-item').boundingClientRect(data => {
this.rightDomsTop = data.map(v => v.top);
}).exec();
},
methods: {
// 获取数据
getData() {
// 模拟左侧菜单栏分类数据
for (let i = 0; i < 20; i++) {
// 左侧导航
this.cate.push({
name: "分类" + i,
id: i
})
// 右侧内容
this.list.push({
list: []
})
for(let i = 0; i < this.list.length; i++){
for(let j = 0; j < 24; j++){
this.list[i].list.push({
src: '/static/images/demo/cate_01.png',
name: '分类'+i+'-商品'+j
})
}
}
}
},
// 点击左侧菜单栏,当前选中项高亮--切换
changeCate(index) {
this.activeIndex = index;
// 右边内容scroll-view滚动到对应的区块
this.rightScrollTop = this.rightDomsTop[index];
},
// 监听右侧内容滚动事件
async onRightScroll(e) {
// console.log(e.detail.scrollTop);
// 匹配当前scrollTop所处的索引
this.rightDomsTop.forEach((v,k) => {
if(v < e.detail.scrollTop + 3){
this.activeIndex = k;
return false;
}
})
},
//
}
}
</script>
<style lang="scss" scoped>
.class-active {
border-left: 8upx solid #FD6801;
color: #FD6801 !important;
}
</style>
优化
对上面的代码进行优化重构--因为有一些代码是重复使用的比如 const query = uni.createSelectorQuery().in(this);
<template>
<view class="d-flex border-top border-light-secondary" style="height: 100%; box-sizing: border-box;">
<!-- 左侧菜单栏 -->
<scroll-view scroll-y style="flex: 1; height: 100%;" class="border-right border-light-secondary" id="leftScroll"
:scroll-top="leftScrollTop">
<view class="border-bottom border-light-secondary py-1 left-scroll-item" hover-class="bg-light-secondary"
v-for="(item,index) in cate" :key="item.id" @click="changeCate(index)">
<view class="py-1 font-md text-muted text-center" :class="activeIndex == index ? 'class-active' : ''">
{{item.name}}
</view>
</view>
</scroll-view>
<!-- 右侧数据 -->
<scroll-view scroll-y style="flex: 3.5; height: 100%;" :scroll-top="rightScrollTop" :scroll-with-animation="true"
@scroll="onRightScroll">
<view class="row right-scroll-item" v-for="(item,index) in list" :key="index">
<view class="span-8 text-center py-2" v-for="(item2,index2) in item.list" :key="index2">
<image :src="item2.src" mode="" style="width: 120upx;height: 120upx;"></image>
<text class="d-block">{{item2.name}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
// 加载效果
showLoading: true,
// 左侧菜单栏当前选中的分类
activeIndex: 0,
// 左侧菜单栏分类数据
cate: [],
// 右侧内容
list: [],
// 记录左侧导航里的每一个导航栏距离顶部的距离
leftDomsTop: [],
// 记录右侧菜单距离顶部的距离
rightDomsTop: [],
// 右侧内容区块滚动的距离
rightScrollTop: 0,
leftScrollTop: 0,
cateItemHeight: 0,
}
},
// 页面加载中类似于created--获取不到DOM节点
onl oad() {
// 模拟右侧内容数据
this.getData();
},
watch: {
async activeIndex(newValue, oldValue) {
// 获取scroll-view高度以及scrollTop
let data = await this.getElInfo({
size:true,
scrollOffset:true
})
let H = data.height;
let ST = data.scrollTop;
// 下边
if ((this.leftDomsTop[newValue] + this.cateItemHeight) > (H + ST)) {
return this.leftScrollTop = this.leftDomsTop[newValue] + this.cateItemHeight - H;
}
// 上边
if (ST > this.cateItemHeight) {
this.leftScrollTop = this.leftDomsTop[newValue];
}
},
},
// 页面渲染完成-可获取DOM节点,相当于mounted
onReady() {
this.getElInfo({
all: 'left',
size: true,
rect: true
}).then(data => {
this.leftDomsTop = data.map(v => {
this.cateItemHeight = v.height;
return v.top;
});
})
this.getElInfo({
all: 'right',
size: false,
rect: true
}).then(data => {
this.rightDomsTop = data.map(v => v.top);
})
},
methods: {
// 获取节点信息
getElInfo(obj = {}) {
return new Promise((res, rej) => {
let option = {
size: obj.size ? true : false,
rect: obj.rect ? true : false,
scrollOffset: obj.scrollOffset ? true : false
};
const query = uni.createSelectorQuery().in(this);
let q = obj.all ? query.selectAll(`.${obj.all}-scroll-item`) : query.select('#leftScroll');
q.fields(option, data => {
res(data);
}).exec();
})
},
// 获取数据
getData() {
// 模拟左侧菜单栏分类数据
for (let i = 0; i < 20; i++) {
// 左侧导航
this.cate.push({
name: "分类" + i,
id: i
})
// 右侧内容
this.list.push({
list: []
})
for (let i = 0; i < this.list.length; i++) {
for (let j = 0; j < 24; j++) {
this.list[i].list.push({
src: '/static/images/demo/cate_01.png',
name: '分类' + i + '-商品' + j
})
}
}
}
},
// 点击左侧菜单栏,当前选中项高亮--切换
changeCate(index) {
this.activeIndex = index;
// 右边内容scroll-view滚动到对应的区块
this.rightScrollTop = this.rightDomsTop[index];
},
// 监听右侧内容滚动事件
async onRightScroll(e) {
// 匹配当前scrollTop所处的索引
this.rightDomsTop.forEach((v, k) => {
if (v < e.detail.scrollTop + 3) {
this.activeIndex = k;
return false;
}
})
},
//
}
}
</script>
<style lang="scss" scoped>
.class-active {
border-left: 8upx solid #FD6801;
color: #FD6801 !important;
}
</style>
给分类页匹配加载动画效果
components/common/loading/loading.vue
<template>
<view class="position-fixed top-0 left-0 right-0 bottom-0 loading-model"
v-if="show">
<view class="spinner">
<view class="double-bounce1"></view>
<view class="double-bounce2"></view>
</view>
</view>
</template>
<script>
export default {
props:{
show:{
type:Boolean,
default:false
}
}
}
</script>
<style scoped>
.loading-model{
background: rgba(255, 255, 255, 0.6);
z-index: 1000;
}
.spinner {
width: 60px;
height: 60px;
position: relative;
margin: 300upx auto;
z-index: 1000;
}
.double-bounce1, .double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #FD6801;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;
animation: bounce 2.0s infinite ease-in-out;
z-index: 1000;
}
.double-bounce2 {
animation-delay: -1.0s;
}
@keyframes bounce {
0%, 100% {
transform: scale(0.0);
} 50% {
transform: scale(1.0);
}
}
</style>
main.js
// 引入全局加载动画
import loading from '@/components/common/loading/loading.vue';
Vue.component('loading', loading)
<template>
<view class="d-flex border-top border-light-secondary" style="height: 100%; box-sizing: border-box;">
<loading :show="showLoading"></loading>
<!-- 左侧菜单栏 -->
<scroll-view scroll-y style="flex: 1; height: 100%;" class="border-right border-light-secondary" id="leftScroll"
:scroll-top="leftScrollTop">
<view class="border-bottom border-light-secondary py-1 left-scroll-item" hover-class="bg-light-secondary"
v-for="(item,index) in cate" :key="item.id" @click="changeCate(index)">
<view class="py-1 font-md text-muted text-center" :class="activeIndex == index ? 'class-active' : ''">
{{item.name}}
</view>
</view>
</scroll-view>
<!-- 右侧数据 -->
<scroll-view scroll-y style="flex: 3.5; height: 100%;" :scroll-top="rightScrollTop"
:scroll-with-animation="true" @scroll="onRightScroll">
<view class="row right-scroll-item" v-for="(item,index) in list" :key="index">
<view class="span-8 text-center py-2" v-for="(item2,index2) in item.list" :key="index2">
<image :src="item2.src" mode="" style="width: 120upx;height: 120upx;"></image>
<text class="d-block">{{item2.name}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
// 加载效果
showLoading: true,
// 左侧菜单栏当前选中的分类
activeIndex: 0,
// 左侧菜单栏分类数据
cate: [],
// 右侧内容
list: [],
// 记录左侧导航里的每一个导航栏距离顶部的距离
leftDomsTop: [],
// 记录右侧菜单距离顶部的距离
rightDomsTop: [],
// 右侧内容区块滚动的距离
rightScrollTop: 0,
leftScrollTop: 0,
cateItemHeight: 0,
}
},
// 页面加载中类似于created--获取不到DOM节点
onl oad() {
// 模拟右侧内容数据
this.getData();
},
watch: {
async activeIndex(newValue, oldValue) {
// 获取scroll-view高度以及scrollTop
let data = await this.getElInfo({
size: true,
scrollOffset: true
})
let H = data.height;
let ST = data.scrollTop;
// 下边
if ((this.leftDomsTop[newValue] + this.cateItemHeight) > (H + ST)) {
return this.leftScrollTop = this.leftDomsTop[newValue] + this.cateItemHeight - H;
}
// 上边
if (ST > this.cateItemHeight) {
this.leftScrollTop = this.leftDomsTop[newValue];
}
},
},
// 页面渲染完成-可获取DOM节点,相当于mounted
onReady() {
this.getElInfo({
all: 'left',
size: true,
rect: true
}).then(data => {
this.leftDomsTop = data.map(v => {
this.cateItemHeight = v.height;
return v.top;
});
})
this.getElInfo({
all: 'right',
size: false,
rect: true
}).then(data => {
this.rightDomsTop = data.map(v => v.top);
})
},
methods: {
// 获取节点信息
getElInfo(obj = {}) {
return new Promise((res, rej) => {
let option = {
size: obj.size ? true : false,
rect: obj.rect ? true : false,
scrollOffset: obj.scrollOffset ? true : false
};
const query = uni.createSelectorQuery().in(this);
let q = obj.all ? query.selectAll(`.${obj.all}-scroll-item`) : query.select('#leftScroll');
q.fields(option, data => {
res(data);
}).exec();
})
},
// 获取数据
getData() {
// 模拟左侧菜单栏分类数据
for (let i = 0; i < 20; i++) {
// 左侧导航
this.cate.push({
name: "分类" + i,
id: i
})
this.$nextTick(() => {
this.showLoading = false;
})
// 右侧内容
this.list.push({
list: []
})
for (let i = 0; i < this.list.length; i++) {
for (let j = 0; j < 24; j++) {
this.list[i].list.push({
src: '/static/images/demo/cate_01.png',
name: '分类' + i + '-商品' + j
})
}
}
}
},
// 点击左侧菜单栏,当前选中项高亮--切换
changeCate(index) {
this.activeIndex = index;
// 右边内容scroll-view滚动到对应的区块
this.rightScrollTop = this.rightDomsTop[index];
},
// 监听右侧内容滚动事件
async onRightScroll(e) {
// 匹配当前scrollTop所处的索引
this.rightDomsTop.forEach((v, k) => {
if (v < e.detail.scrollTop + 3) {
this.activeIndex = k;
return false;
}
})
},
//
}
}
</script>
<style lang="scss" scoped>
.class-active {
border-left: 8upx solid #FD6801;
color: #FD6801 !important;
}
</style>
实际应用
<template>
<view style="height: 100vh;" class="d-flex flex-column">
<!-- #ifdef MP -->
<!-- 自定义导航 -->
<view class="d-flex a-center" style="height: 90rpx;">
<!-- 左边 -->
<view style="width: 85rpx;" class="d-flex a-center j-center">
<text class="iconfont icon-xiaoxi"></text>
</view>
<!-- 中间 -->
<view class="flex-1 bg-light rounded d-flex a-center text-light-muted" style="height: 65rpx;" @click="openSearch">
<text class="iconfont icon-sousuo mx-2"></text>
智能积木
</view>
<!-- 右边 -->
<view style="width: 85rpx;" class="d-flex a-center j-center">
<text class="iconfont icon-richscan_icon"></text>
</view>
</view>
<!-- #endif -->
<view class="d-flex border-top border-light-secondary animated fadeIn faster" style="height: 100%;box-sizing: border-box;">
<loading-plus v-if="beforeReady"></loading-plus>
<!-- <loading :show="showLoading"></loading> -->
<scroll-view id="leftScroll" scroll-y style="flex: 1;height: 100%;"
class="border-right border-light-secondary" :scroll-top="leftScrollTop">
<view class="border-bottom border-light-secondary py-1 left-scroll-item"
hover-class="bg-light-secondary"
v-for="(item,index) in cate" :key="index"
@tap="changeCate(index)">
<view class="py-1 font-md text-muted text-center"
:class="activeIndex === index ? 'class-active' : ''">
{{item.name}}</view>
</view>
</scroll-view>
<scroll-view scroll-y style="flex: 3.5;height: 100%;"
:scroll-top="rightScrollTop" :scroll-with-animation="true"
@scroll="onRightScroll">
<view class="row right-scroll-item"
v-for="(item,index) in list"
:key="index">
<view class="span24-8 text-center py-2"
v-for="(item2,index2) in item.list" :key="index2"
@click="openDetail(item2)">
<image :src="item2.cover"
style="width: 120upx;height: 120upx;"></image>
<text class="d-block">{{item2.name}}</text>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import loading from "@/common/mixin/loading.js"
export default {
mixins:[loading],
data() {
return {
showLoading:true,
// 当前选中的分类
activeIndex:0,
cate:[],
list:[],
leftDomsTop:[],
rightDomsTop:[],
rightScrollTop:0,
leftScrollTop:0,
cateItemHeight:0
}
},
watch: {
async activeIndex(newValue, oldValue) {
// 获取scroll-view高度,scrollTop
let data = await this.getElInfo({
size:true,
scrollOffset:true
})
let H = data.height
let ST = data.scrollTop
// 下边
if ((this.leftDomsTop[newValue]+this.cateItemHeight) > (H+ST) ) {
return this.leftScrollTop = this.leftDomsTop[newValue]+this.cateItemHeight - H
}
// 上边
if (ST > this.cateItemHeight) {
this.leftScrollTop = this.leftDomsTop[newValue]
}
}
},
onl oad() {
this.getData()
},
methods: {
openSearch(){
uni.navigateTo({
url: '../search/search',
});
},
// 获取节点信息
getElInfo(obj = {}){
return new Promise((res,rej)=>{
let option = {
size:obj.size ? true : false,
rect:obj.rect ? true : false,
scrollOffset:obj.scrollOffset ? true : false,
}
const query = uni.createSelectorQuery().in(this);
let q = obj.all ? query.selectAll(`.${obj.all}-scroll-item`):query.select('#leftScroll')
q.fields(option,data => {
res(data)
}).exec();
})
},
getData(){
/*
cate:[{
name:"分类1"
},{
name:"分类2"
}]
list:[{
list:[...]
},{
list:[...]
}]
*/
this.$H.get('/category/app_category').then(res=>{
var cate = []
var list = []
res.forEach(v=>{
cate.push({
id:v.id,
name:v.name
})
list.push({
list:v.app_category_items
})
})
this.cate = cate
this.list = list
this.$nextTick(()=>{
this.getElInfo({
all:'left',
size:true,
rect:true
}).then(data=>{
this.leftDomsTop = data.map(v=>{
this.cateItemHeight = v.height
return v.top
})
})
this.getElInfo({
all:'right',
size:false,
rect:true
}).then(data=>{
this.rightDomsTop = data.map(v=> v.top)
})
this.showLoading = false
})
})
},
// 点击左边分类
changeCate(index){
this.activeIndex = index
// 右边scroll-view滚动到对应区块
this.rightScrollTop = this.rightDomsTop[index]
},
// 监听右边滚动事件
async onRightScroll(e){
// 匹配当前scrollTop所处的索引
this.rightDomsTop.forEach((v,k)=>{
if (v < e.detail.scrollTop + 3) {
this.activeIndex = k
return false
}
})
},
// 打开详情页
openDetail(item){
/*
{
"id":1,
"name":"新品",
"cover":"https://res.vmallres.com/pimages/product/6901443331376/428_428_FAF5BBAB67C16D7426B5B1A2A38F9001DED6D011A0EE9977mp.png",
"category_id":1,
"goods_id":25,
"order":50,
"create_time":"2019-08-17 00:57:12",
"update_time":"2019-08-17 00:57:12"
}
*/
uni.navigateTo({
url: '../detail/detail?detail='+JSON.stringify({
id:item.goods_id,
title:item.name
}),
});
}
}
}
</script>
<style>
.class-active{
border-left: 8upx solid #FD6801;color: #FD6801!important;
}
</style>
common/mixin/loading-plus.vue
<template>
<view class="position-fixed top-0 left-0 right-0 bottom-0 bg-white font-md d-flex a-center j-center main-text-color" style="z-index: 10000;">
加载中...
</view>
</template>
<script>
</script>
<style>
</style>
common/mixin/loading.js
export default {
data(){
return {
beforeReady:true,
}
},
onReady() {
this.$nextTick(()=>{
setTimeout(()=>{
this.beforeReady = false
},500)
})
},
}
标签:uniapp,菜单,name,data,list,菜单栏,let,手摸,true
From: https://blog.csdn.net/weixin_43285360/article/details/142179133