记录–左右菜单联动

  • 记录–左右菜单联动已关闭评论
  • 58 次浏览
  • A+
所属分类:Web前端
摘要

对于左右菜单联动的需求是很常见的在小程序里,主要表现为:主要利用的是 scroll-view标签,以及相关的一些API,可参考:uniapp.dcloud.net.cn/api/ui/node… 去获取当前的所有节点集合,再配合 scroll-view 的 scroll-top 属性,使其在点击左侧菜单栏的时候动态赋值右侧scroll-view 的 scroll-top 属性,从而实现点击左侧菜单栏时右侧内容区域进行滚动


这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录--左右菜单联动

对于左右菜单联动的需求是很常见的在小程序里,主要表现为:

  • 点击左侧的菜单栏,右侧会切换到对应的内容区域
  • 滑动右侧的内容,左侧会自动切换到对应的菜单项

主要利用的是 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: [], 				//  			} 		}, 		onLoad() { 			// 模拟左侧菜单栏分类数据 			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>

记录--左右菜单联动

点击左侧菜单栏-右侧内容滚动到对应区域:

<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节点 		onLoad() { 			// 模拟右侧内容数据 			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>

记录--左右菜单联动

记录--左右菜单联动

滚动右侧内容-左侧菜单栏跟着联动到对应菜单栏项

<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节点     onLoad() {       // 模拟右侧内容数据       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节点     onLoad() {       // 模拟右侧内容数据       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节点 		onLoad() { 			// 模拟右侧内容数据 			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] 				} 			} 		}, 		onLoad() { 			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" alt="记录--左右菜单联动", 					"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) 		}) 	}, }

本文转载于:

https://juejin.cn/post/7295310885635719195

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--左右菜单联动