移动端商品分类左右联动

news2024/12/23 20:07:51

代码:

<template>
	<view class="u-wrap">
		<view class="u-menu-wrap">
			<scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop"
				:scroll-into-view="itemId">
				<!--  -->
				<view v-for="(item,index) in tabbar" :key="index" class="u-tab-item"
					:class="[current == index ? 'u-tab-item-active' : '']" :id="'item' + item.id" @click="swichMenu(index)">
					<text class="u-line-1">{{item.name}}</text>
				</view>
			</scroll-view>
			<scroll-view :scroll-top="scrollRightTop" scroll-y scroll-with-animation class="right-box"
				@scroll="rightScroll">
				<view class="page-view">
					<view class="class-item" :id="'item' + index" v-for="(item , index) in tabbar" :key="index" style="margin-bottom: 20rpx;">
						<view v-if="item.children.length==0" style="display: flex;justify-content: center;">
							<image src="../../static/zwsj.png" style="width: 200rpx;height: 200rpx;" mode=""></image>
						</view>
						<block v-for="(item1 , index1) in item.children" :key="index1">
							<view class="item-title">
								<text>{{item1.name}}</text>
							</view>
							<view v-if="item1.children.length==0" style="display: flex;justify-content: center;">
								<image src="../../static/zwsj.png" style="width: 200rpx;height: 200rpx;" mode=""></image>
							</view>
							<view class="item-container">
								<view class="thumb-box" v-for="(item2, index2) in item1.children" :key="index2" @click="goGoodsList(item2.parentId,item2.name,index2)">
									<image class="item-menu-image" :src="item2.imgPath" mode=""></image>
									<view class="item-menu-name">{{item2.name}}</view>
								</view>
							</view>
						</block>
					</view>
				</view>
			</scroll-view>
		</view>

		<tabBar :status="1"></tabBar>
	</view>
</template>

<script>
	import {handleTree} from "@/utils/common.js"
	export default {
		data() {
			return {
				scrollTop: 0, //tab标题的滚动条位置
				oldScrollTop: 0,
				current: 0, // 预设当前项的值
				menuHeight: 0, // 左边菜单的高度
				menuItemHeight: 0, // 左边菜单item的高度
				itemId: '', // 栏目右边scroll-view用于滚动的id
				tabbar: [],
				menuItemPos: [],
				arr: [],
				scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
				timer: null, // 定时器

			}
		},
		onShow() {
			// 获取商品分类
			this.getCategoryList()
		},
		onReady() {
			this.getMenuItemTop()
		},
		methods: {
			// 商品列表
			goGoodsList(id,title,index) {
				uni.navigateTo({
					url: `/pages/goodsList/goodsList?id=${id}&title=${title}&index=${index}`
				})
			},
			
			// 获取商品分类
			getCategoryList(){
				this.$request({
					"url":`goods/getCategoryList`,
					"method":"POST"
				}).then(res=>{
					if(res.code === 200){
						this.tabbar = handleTree(res.data);
					}else{
						uni.showToast({
							title:"查询商品分类失败",
							icon:'none'
						})
					}
				})
			},
			// 点击左边的栏目切换
			async swichMenu(index) {
				if (this.arr.length == 0) {
					await this.getMenuItemTop();
				}
				if (index == this.current) return;
				this.scrollRightTop = this.oldScrollTop;
				this.$nextTick(()=> {
					this.current = index;
					this.scrollRightTop = this.arr[index];
					this.leftMenuStatus(index);
				})
			},
			// 获取一个目标元素的高度
			getElRect(elClass, dataVal) {
				new Promise((resolve, reject) => {
					const query = uni.createSelectorQuery().in(this);
					query.select('.' + elClass).fields({
						size: true
					}, res => {
						// 如果节点尚未生成,res值为null,循环调用执行
						if (!res) {
							setTimeout(() => {
								this.getElRect(elClass);
							}, 10);
							return;
						}
						this[dataVal] = res.height;
						resolve();
					}).exec();
				})
			},
			// 观测元素相交状态
			async observer() {
				this.tabbar.map((val, index) => {
					let observer = uni.createIntersectionObserver(this);
					// 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
					// 如果跟.right-box底部相交,就动态设置左边栏目的活动状态
					observer.relativeTo('.right-box', {
						top: 0
					}).observe('#item' + index, res => {
						if (res.intersectionRatio > 0) {
							let id = res.id.substring(4);
							this.leftMenuStatus(id);
						}
					})
				})
			},
			// 设置左边菜单的滚动状态
			async leftMenuStatus(index) {
				this.current = index;
				// 如果为0,意味着尚未初始化
				if (this.menuHeight == 0 || this.menuItemHeight == 0) {
					await this.getElRect('menu-scroll-view', 'menuHeight');
					await this.getElRect('u-tab-item', 'menuItemHeight');
				}
				// 将菜单活动item垂直居中
				this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
			},
			// 获取右边菜单每个item到顶部的距离
			getMenuItemTop() {
				new Promise(resolve => {
					let selectorQuery = uni.createSelectorQuery();
					selectorQuery.selectAll('.class-item').boundingClientRect((rects) => {
						// 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
						if (!rects.length) {
							setTimeout(() => {
								this.getMenuItemTop();
							}, 10);
							return;
						}
						rects.forEach((rect) => {
							// 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
							this.arr.push(rect.top - rects[0].top);
							resolve();
						})
					}).exec()
				})
			},
			// 右边菜单滚动
			async rightScroll(e) {
				this.oldScrollTop = e.detail.scrollTop;
				if (this.arr.length == 0) {
					await this.getMenuItemTop();
				}
				if (this.timer) return;
				if (!this.menuHeight) {
					await this.getElRect('menu-scroll-view', 'menuHeight');
				}
				setTimeout(() => { // 节流
					this.timer = null;
					// scrollHeight为右边菜单垂直中点位置
					let scrollHeight = e.detail.scrollTop + this.menuHeight / 2 - 231;
					for (let i = 0; i < this.arr.length; i++) {
						let height1 = this.arr[i];
						let height2 = this.arr[i + 1];
						// 如果不存在height2,意味着数据循环已经到了最后一个,设置左边菜单为最后一项即可
						if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
							this.leftMenuStatus(i);
							return;
						}
						if (scrollHeight >= height1 && scrollHeight < height2) {
							this.leftMenuStatus(i);
							return;
						}
					}
				}, 10)
			}
		}
	}
</script>

<style lang="scss" scoped>
	.u-wrap {
		height: calc(100vh - 100rpx);
		/* #ifdef H5 */
		height: calc(100vh - var(--window-top));
		/* #endif */
		display: flex;
		flex-direction: column;
	}

	.u-menu-wrap {
		flex: 1;
		display: flex;
		overflow: hidden;
	}

	.u-tab-view {
		width: 200rpx;
		height: 100%;
	}

	.u-tab-item {
		height: 110rpx;
		background: #f6f6f6;
		box-sizing: border-box;
		display: flex;
		align-items: center;
		justify-content: center;
		font-size: 26rpx;
		color: #444;
		font-weight: 400;
		line-height: 1;
	}

	.u-tab-item-active {
		position: relative;
		color: #000;
		font-size: 30rpx;
		font-weight: 600;
		background: #fff;
	}

	.u-tab-item-active::before {
		content: "";
		position: absolute;
		border-left: 4px solid #eee;
		height: 32rpx;
		left: 0;
		top: 39rpx;
	}

	.u-tab-view {
		height: 100%;
	}

	.right-box {
		height: 100%;
		background-color: rgb(250, 250, 250);
	}

	.page-view {
		padding: 16rpx;
		padding-top: 0;
		height: 100%;
	}

	.class-item {
		margin-bottom: 30rpx;
		background-color: #fff;
		padding: 16rpx;
		border-radius: 8rpx;
	}

	.class-item:last-child {
		min-height: 100vh;
	}

	.item-title {
		font-size: 26rpx;
		color: #101010;
		font-weight: bold;
	}

	.item-menu-name {
		font-weight: normal;
		font-size: 24rpx;
		color: #101010;
	}

	.item-container {
		display: flex;
		flex-wrap: wrap;
	}

	.thumb-box {
		width: 33.333333%;
		display: flex;
		align-items: center;
		justify-content: center;
		flex-direction: column;
		margin-top: 20rpx;
	}

	.item-menu-image {
		width: 120rpx;
		height: 120rpx;
	}
</style>

运行效果: 

 

数据结构:

[
    {
        name:'个护清洁',
        children:[
            {
                name:'纸品湿巾',
                children:[
                    {
                        name:'卷纸',
                        imgPath:"http://zxgj.sxgokit.com/profile/upload/2023/04/24/2726d124-7de4-41fe-8424-27c3f0c1e148.jpg"
                    },
                ]
            },
        ]
    },
]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/826136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

blender的下载安装和配置中文环境

引言 在3D建模和动画设计领域&#xff0c;Blender 作为一款强大且免费的开源软件&#xff0c;一直以优秀的性能和对众多技术的支持赢得了大批用户的喜爱。然而&#xff0c;对于刚接触这款软件的用户而言&#xff0c;其安装和配置过程可能会带来一定困扰&#xff0c;尤其是在设…

Maven安装(3.8.4版本)

下载maven 官方下载链接&#xff1a;Maven – Download Apache Maven 下载完成后进行解压到自己要安装的目录下 如果下载不成功可以在以下百度云盘获取(3.8.4版本) 链接: 百度网盘 请输入提取码 提取码: t9pc maven环境配置 新建系统变量&#xff1a;MAVEN_HOMEF:\maven…

opencv 30 -图像平滑处理01-均值滤波 cv2.blur()

什么是图像平滑处理? 图像平滑处理&#xff08;Image Smoothing&#xff09;是一种图像处理技术&#xff0c;旨在减少图像中的噪声、去除细节并平滑图像的过渡部分。这种处理常用于预处理图像&#xff0c;以便在后续图像处理任务中获得更好的结果。 常用的图像平滑处理方法包括…

[Linux]详解环境基础开发工具的使用

[Linux]环境基础开发工具的使用 文章目录 [Linux]环境基础开发工具的使用0. 前言1. Linux 软件包管理器 yumyum介绍yum的使用yum源 2. Linux编辑器-vimvim介绍vim基本模式底行模式下的命令汇总命令模式下的命令汇总vim简单配置 3. Linux编译器gcc/g4. Linux项目自动化构建工具-…

mysql重置和修改密码 Ubuntu系统

忘记密码要重置密码 cat /etc/mysql/debian.cnf/etc/mysql/debian.cnf这个只有Debian或者Ubuntu服务器才有&#xff0c;里面有mysql安装后自带的用户&#xff0c;作用就是重启及运行mysql服务。我们用这个用户登录来达到重置密码的操作 使用上面的那个文件中的用户名和密码登…

6.8 稀疏数组

6.8 稀疏数组 稀疏数组是一种数据结构&#xff0c;在程序中数据结构的思想&#xff0c;是非常重要的。例如 需求&#xff1a;编写五子棋游戏中&#xff0c;有存盘退出和续上盘的功能。分析问题&#xff1a;因为该二维数组的很多值是默认值0&#xff0c;因此记录了很多没有意义…

Meta押宝人工智能聊天机器人以留住用户

Meta计划发布具有人类个性的AI聊天机器人&#xff0c;这一举措旨在增强用户留存率。知情人士透露&#xff0c;该聊天机器人的原型已经在开发中&#xff0c;并有望在下个月月初推出。最终产品可以达到与用户进行正常讨论的人类水平。Meta员工将这些聊天机器人称为“personas”&a…

针对高可靠性和高性能优化的1200V硅碳化物沟道MOSFET

目录 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance摘要信息解释研究了什么文章创新点文章的研究方法文章的结论 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance 摘要 本文详…

【Java从入门到大牛】集合进阶下篇

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;Java从入门到大牛 &#x1f320; 首发时间&#xff1a;2023年8月2日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e…

windows脚本 批量删除指定文件夹、指定文件

前言 用于批量删除项目中的测试数据&#xff0c;提供用户纯净的软件。 使用说明&#xff1a; 修改file_list和folder_list对应的数据&#xff0c;来自定义删除的内容 效果图 源码 echo off chcp 65001 > nul 2>&1REM 设置文件列表&#xff0c;可以包含多个文件路…

MySQL安装详细教程!!!

安装之前&#xff0c;先卸载你之前安装过的数据库程序&#xff0c;否则会造成端口号占用的情况。 1.首先下载MySQL:MySQL :: Download MySQL Community Server(下载路径) 2.下载版本不一样&#xff0c;安装方法略有不同&#xff1b;&#xff08;版本5的安装基本一致&#xff0…

STL 之 list接口的简单使用【C++】

文章目录 push_front &&pop_frontpush_back&&pop_backinserterase迭代器begin&& endrbegin和rend front&&backsizeresizeemptyclearsortspliceuniquemergereverse ![在这里插入图片描述](https://img-blog.csdnimg.cn/717807397d8d499d840aae2…

万宾智慧排水监测系统,实现城市排水“靶向治疗”

随着城市化进程的加速&#xff0c;排水系统面临着越来越多的挑战。传统排水系统在应对城市发展带来的水资源管理和环境保护问题时&#xff0c;往往显得力不从心。近年来&#xff0c;城市内涝、河水倒灌、雨污分流不到位、河道黑臭杂乱、水体污染、井盖异常、污水溢流发臭等民生…

cmake使用笔记

vim CMakeLists.txt mkdir build cd build cmake ..创建 CMakeLists.txt&#xff0c;添加内容 cmake_minimum_required(VERSION 3.26) #工程名称 project(hello) #宏定义 add_definitions(-D宏名称) #头文件路径 include_directories(${PROJECT_SOURCE_DIR}/inc) #搜索源文件…

前端Vue入门-day07-Vuex入门

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 自定义创建项目 vuex概述 构建 vuex [多组件数据共享] 环境 创建一个空仓库 state 状态 1. 提供数据&…

Android Gradle 骚操作,将两个项目合并到一个项目中

1. 前言 在工作中&#xff0c;由于各种原因&#xff0c;导致需要将两个可单独运行的App项目&#xff0c;合并到一个git仓库里&#xff0c;且单独的App项目里还有其他Module模块。 如果只是将两个项目复制到同一个文件夹下&#xff0c;还是得单独打开各个项目&#xff0c;是很不…

基础篇:多线程所需知识:RAII接口模式对生产者和消费者封装以及多batch实现

我们先来弄一个最基础的infer类&#xff1a; class Infer{ public: bool load_model(const string &file){context_ file;return true;} void forward(){if(context_.empty()){printf("加载模型异常\n");return;}printf("使用%s进行推理\n " , contex…

图解:订单系统的设计

目录 订单系统简介 1. 订单系统在企业中的角色 2. 订单系统与各业务系统的关系 3. 订单系统上下游关系 4. 订单系统的业务架构 订单系统核心功能 1. 订单中所包含的内容信息 2. 流程引擎 订单系统的发展 最后 本文主要讲述了在传统电商企业中&#xff0c;订单系统应承…

SpringCloud入门Day01-服务注册与发现、服务通信、负载均衡与算法

SpringCloudNetflix入门 一、应用架构的演变 伴随互联网的发展&#xff0c;使用互联网的人群越来越多&#xff0c;软件应用的体量越来越大和复杂。而传统单体应用 可能不足以支撑大数据量以及发哦并发场景应用的框架也随之进行演变从最开始的单体应用架构到分布式&#xff08…

RB-Heparin罗丹明B-肝素偶联物的合成【星戈瑞】

罗丹明B标记肝素是一种荧光标记的肝素探针。在生物医学和生物化学研究中&#xff0c;研究人员常常需要将特定的生物分子或化合物进行标记&#xff0c;以便能够在实验中可视化或追踪它们的位置和相互作用。 合成Rhodamine B-Heparin罗丹明B-肝素偶联物需要将罗丹明B染料与肝素进…