首页 > 其他分享 >记录--左右菜单联动

记录--左右菜单联动

时间:2024-01-27 17:58:20浏览次数:33  
标签:菜单 name -- data list 菜单栏 let 联动 true

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

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

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

主要利用的是 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节点
    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节点
		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",
					"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

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

 

标签:菜单,name,--,data,list,菜单栏,let,联动,true
From: https://www.cnblogs.com/smileZAZ/p/17991721

相关文章

  • 【第6个渗透靶机项目】 LordOfTheRoot_1.0.1
    每天都要加油啊!    ------2024-01-27 11:10:510x00  信息搜集靶场提示信息,[email protected]扫描靶机,发现只开通了ssh服务那么全面扫描一下吧!那只能从靶场提示信息入手了!登陆一下[email protected]吧。意思是让敲震端口3次!让我们了解一下端口碰撞:端口......
  • .NetCore开发人员首选框架---Bridge(Abp-VNext + Vue3)
    bridge系统是基于Abp-VNext+Vue3开发的一套前后端分离的通用权限管理系统,不论是单体服务,还是微服务都可在此基础上自由扩展,此框架组合可以说是集成了.netcore在BS架构领域最前沿的技术,框架简介如下:##......
  • 医学研究的新里程碑:人体组织展示可视化模型
    在医学领域,人体组织是研究疾病、生理机制和药物作用的关键。然而,传统的组织学研究方法往往局限于切片观察,难以全面、直观地展示组织结构和功能。随着科技的发展,人体组织展示可视化模型为医学研究带来了革命性的变革。 使用山海鲸可视化搭建的人体组织展示可视化系统,利用现代科......
  • 前缀和与差分典题
    includeusingnamespacestd;constintN=1010;inta[N][N],b[N][N];voidinsert(intx1,inty1,intx2,inty2,intc){b[x1][y1]+=c;b[x2+1][y1]-=c;b[x1][y2+1]-=c;b[x2+1][y2+1]+=c;}intmain(){intn,m,q,w;scanf("%d%d%d",&n,&m,&......
  • PowerShell编写Windows服务器的DDNS-ipv6客户端
    这个代码保存成ddnsv6.fm20.cn.ps1就能正常运行了functionGet-GUID(){#指定注册表路径$path="HKLM:\Software\Firadio\ddnsv6.fm20.cn"#如果注册表路径不存在则创建if(-not(Test-Path$path)){New-Item-Force-Path$path>$null}......
  • C# 简单的 HTTP 静态文件服务 NS (Netnr.Serve)
    NS(Netnr.Serve)简单的HTTP静态文件服务SimpleHTTPstaticfileservingStart(启动)启动逐个参数设置--urls(default:http://*:713/):--root(default:D:/site):#根目录,默认命令行启动位置--index(default:index.html):--404(default:404.html):--suffix(d......
  • Node.js笔记
    第一篇 一、Node.js模块:模块使用npm运行管理。events:事件模块,提供事件触发和事件监听功能。util:核心功能模块,用于弥补核心JS功能的不足。fs:文件操作模块,提供文件操作APIhttp:Web协议模块,提供Web协议交互功能express:Web框架,用于快速构建Web应用服务vm:沙箱模块,用于提......
  • CSAPP学习笔记——Chapter10,11 系统级I/O与网络编程
    CSAPP学习笔记——Chapter10,11系统级I/O与网络编程Chapter10系统级I/O系统级I/O这一章的内容,主要可以通过这张图概括:UnixI/O模型是在操作系统内核中实现的。应用程序可以通过诸如open、close、lseek、read、write和stat这样的函数来访UnixI/O。较高级别的RIO和标......
  • 国旗计划
    SCOI2015省选]国旗计划手搓模拟的数据这题做重要的首先是有化圆为链的思想,讲给的区间化为链式,也就是说讲l>r的地方的r+m,相当于多走了一圈,所以需要将环复制一遍,在加倍身长为一条链;#include<bits/stdc++.h>usingnamespacestd;constintN=1e6+10;int......
  • 计算机介绍
    什么是计算机全称电子计算机,俗称电脑按照程序运行,能够自动高效处理海量数据由硬件和软件组成常见形式:台式、笔记本、大型计算机等广泛应用:科学计算、数据处理、自动控制、计算机辅助设计、人工智能、网络等领域计算机硬件一些物理装置按系统结构的要求构成有机整体——......