效果:增加数量不做限制,减少数量到0的时候从购物车中删除商品。

在这里插入图片描述

购物车数据结构:
在这里插入图片描述
html

<!-- 单向联动菜单 -->
<view class="scroll-box" :style="{ 'height': scrollHeight +'px' }">
	<!-- 左边栏 -->
	<view class="left-tab">
		<scroll-view class="tab" scroll-y="true" scroll-with-animation >
			<block v-for="(item,index) in tabData" :key="index" >
				<!-- 标签动态样式 -->
				<view class="tab-item" :class=" [currentTab==index ? 'active' : 'none'] " @click="clickTab"
				:data-current="index" :data-title="[item.title]" >
					{{item.title}} 
				</view>
			</block>		
		</scroll-view>
	</view>			
	<!-- 右边页面 -->			
	<scroll-view class="right-box" scroll-y="true" :scroll-top="scrollTop" @scroll="scroll" >
		<view class="page-view">						
			<view class="class-item">						
				<view class="item-title">
					<text>{{goodsTitle}}</text>
				</view>
				
				<view class="item-container">
					<view class="thumb-box" v-for="(item, index) in menuData" :key="index">
						<view class="left-image">
							<image class="item-menu-image" :src="item.imgUrl" mode=""></image>
						</view>
						<view class="right-text">
							<view class="item-menu-name">{{item.name}}</view>
							<view class="item-menu-explain">{{item.explain}}</view>
							<view class="item-menu-ps">
								<view class="item-menu-price">¥{{item.price}}</view>
								<!-- Animate动画库使用 https://blog.csdn.net/qq_40976321/article/details/107379659 -->
								<view class="item-menu-select" hover-class="animated rotateOut" @click="addCar" :data-item="[item]">加购</view>
							</view>
						</view>								
					</view>
				</view>
			</view>					
		</view>
	</scroll-view>				
</view>

<!-- 底部购物车 -->
<view class="car" v-if="isShowCar">
	<view class="car-left">
		<view class="car-pice">¥{{totalPrice}}</view>
	</view>
	<view class="car-right">
		<view class="car-text">选好了</view>	
	</view>			 
</view>
<!-- 凸起图标 -->
<view class="car-img-back" v-if="isShowCar" @click="clickCar">
	<image class="car-img" src="../../../static/icon/png/bag.png" mode=""></image>
	<!-- 角标 -->
	<view class="car-num">
		<text class="car-num-text">{{totalNum}}</text>
	</view>
</view>				
<!-- 购物车列表 -->
<view class="car-list" v-if="isShowList">
	<scroll-view scroll-y="true" style="height: 500rpx;">				
		<view class="car-box" v-for="(item, index) in carData" :key="index">
			<view class="car-left-image" style="width: 23%;">
				<image class="car-menu-image" :src="item.imgUrl" mode=""></image>
			</view>
			<view class="car-right-text" style="width: 73%;">
				<view class="car-menu-name">{{item.name}}</view>
				<view class="car-menu-explain">{{item.explain}}</view>
				<view class="car-menu-ps">
					<view class="car-menu-price" >¥{{item.price}}</view>
					<view class="car-num-select" >
						<view @click="clickMinus(index)"> 
							<image style="width: 48rpx;height: 48rpx;" src="../../../static/icon/png/jian.png" ></image> 
						</view>
						<view style="margin: 0rpx 15rpx;font-size: 28rpx;"> {{item.num}} </view>
						<view @click="clickAdd(index)"> 
							<image style="width: 48rpx;height: 48rpx;" src="../../../static/icon/png/jia.png" ></image> 
						</view>
					</view>
				</view>
			</view>								
		</view>				
	</scroll-view>
</view>
<!-- 遮罩层 -->
<view class="car-mark" v-if="isShowCarMark" @click="clickCarMark"></view>

js

//点击左边类别栏事件
clickTab(e) {
	let clickIndex = e.target.dataset.current //当前标签的索引	
	this.currentTab = clickIndex	
	
	let tapTitle = e.target.dataset.title //获得点击的标题
	this.goodsTitle = tapTitle[0]				
	
	//对数组进行筛选,让右边的列表只显示对应类别的数据
	this.menuData = this.tempData.filter(item => item.title == this.goodsTitle)				
	//滚动条返回顶部
	this.scrollTop = 0
},	

//记录滚动条位置
scroll : function(e) {
	this.scrollTop = e.detail.scrollTop
},

//显示、隐藏购物车清单
clickCar(){
	this.isShowList = !this.isShowList
	this.isShowCarMark = !this.isShowCarMark
},			
clickCarMark(){
	this.isShowList = !this.isShowList
	this.isShowCarMark = !this.isShowCarMark
},

//**********商品加入购物车 ***************
addCar(e){
	//获得点击加购的商品数据
	let item = e.target.dataset.item[0]					
	
	//如果购物车里的商品name = 点击数据商品name 则返回此元素下标
	let index = this.carData.findIndex(ev => ev.name === item.name)
	
	//如果购物车不存在相同的商品
	if(index === -1) {  
	    item.num = 1  //添加数量属性num ,默认为 1【原数据没有数量字段】			    
	    this.carData.push(item)    //把商品追加进购物车					
	} else { 
	    this.carData[index].num++   //存在相同的商品则数量叠加
	}
	
	//更新总数与总价
	this.allNum()
	this.allPrice()
	//显示购物车
	this.isShowCar = true					
},

//增加数量
clickAdd(index){					
	this.carData[index].num = this.carData[index].num + 1
	this.allNum()
	this.allPrice()
},

//减少数量,小于1时删除元素
clickMinus(index){
	if(this.carData[index].num > 1){
		this.carData[index].num = this.carData[index].num - 1					
	} else {
		this.carData.splice(index,1) //从数组删除元素					
	}	
	this.allNum()
	this.allPrice()
},

// 计算商品总数量
allNum() {				
	let count = 0;
	this.carData.forEach(item=>{
		count+=item.num					
	})
	this.totalNum = count
	
	//购物车有商品的时候,滚动栏高度减少,让出位置给购物车
	if(this.totalNum === 1 && this.isAddHeight){
		this.scrollHeight = this.scrollHeight - 50
		this.isAddHeight = false
	} 
	//购物车没有商品的时候,隐藏组件,还原滚动栏高度
	if (this.totalNum === 0){
		this.scrollHeight = this.scrollHeight + 50
		this.isAddHeight = true
		this.isShowCar = false
		this.clickCar()					
	}
},	

// 计算商品总价格
allPrice() {				
	let Price = 0;
	this.carData.forEach(item=>{
		//总价格=数量 X 单价  ,由于数组的单价是字符串类型,所以要先转换成浮点数字类型
		Price+=item.num * parseFloat(item.price)				
	})
	//Number类型的数据调用toFixed方法,指定保留几位小数,返回的数据是一个string类型
	this.totalPrice = Price.toFixed(2)
},

css

.scroll-box{
	display: flex;
	flex-direction: row;
	/* 需要定义高度,否则不能分栏滚动 */
	/* height: calc(78.2vh); */
}
.left-tab{
	flex: 1; /*均匀分配元素*/
	display: flex;
	overflow: hidden;
	background: #f6f6f6;
}
.tab{  
	width: 150rpx;
	height: 100%;

}
.tab-item{  
	height: 100rpx;
	background: #f6f6f6;
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 22rpx;
	line-height: 1;
}
.active{  /* 选项卡活动时的样式 */
	color:#A7D500;	
	font-weight: bolder;
	background: #ffffff;
}	

/* 右边页面样式 */
.right-box {
	left: 15%;
	width: 80%;
	background: #f6f6f6;		
}

.page-view {
	padding: 10rpx;
}

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

.item-title {
	font-size: 26rpx;
	color: $u-main-color;
	font-weight: bold;
}

.item-container {
	display: flex;
	flex-direction: column;	
}
.thumb-box {
	width: 98%;
	height: 150rpx;
	display: flex;
	flex-direction: row;
	align-items: center;
	/* justify-content: center;	 */
	margin-top: 50rpx;
}
.item-menu-image {
	width: 150rpx;
	height: 150rpx;
}
.item-menu-name {
	font-weight: normal;
	font-size: 28rpx;
	margin-bottom: 10rpx;
}
.item-menu-explain{
	font-size: 20rpx;
	margin-bottom: 10rpx;
	/* 下方四项配合使用,超出范围的显示... */
	width: 400rpx;
	overflow: hidden;
	text-overflow: ellipsis;  
	white-space: nowrap;
}
.item-menu-ps{
	display: flex;
	flex-direction: row;
	justify-content: space-between; /*两边对齐*/	
}
.item-menu-price{
	font-size: 26rpx;
	font-weight: 600;
}
.item-menu-select{	
	width: 100rpx;
	height: 36rpx;
	/* 文字垂直居中 (line-height)和(height)设置一样就可以 */
	line-height: 36rpx;
	/* 文字水平居中 */	
	text-align:center;
	font-size: 22rpx;
	background-color: #A7D500;
	border-radius: 50rpx;	
}

/* 购物车 */
.car{
	display: flex;	
	flex-direction: row;
	align-items: center;	
	height: 100rpx;
	width: 100%;
	position: fixed;	
	bottom: 0rpx;
	background-color:#ffffff ;
	z-index: 79;
}
.car-img-back{
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;	
	height: 100rpx;
	width: 100rpx;
	position: fixed;
	bottom: 35rpx;
	left: 30rpx;
	background-color: #A7D500;
	border-radius: 50%;
	z-index: 79;
}
.car-img{
	height: 60rpx;
	width: 60rpx;
}
.car-left{
	/* 平均分布元素,父元素display: flex; 子元素flex:1; */
	flex: 1;
}
.car-pice{	
	padding-left: 160rpx;
	font-size: 34rpx;
	font-weight: 700;
}
.car-num{
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	height: 30rpx;
	width: 30rpx;
	position: fixed;
	bottom: 100rpx;
	left: 110rpx;
	background-color: #ffea49;
	border-radius: 50%;
	z-index: 79;
}
.car-num-text{
	font-size: 20rpx;
}
.car-right{
	/* 平均分布元素,父元素display: flex; 子元素flex:1; */
	flex: 1;	
}
.car-text{	
	height: 100rpx;
	width: 200rpx;
	line-height: 100rpx;
	margin-left: 200rpx;	
	text-align: center;
	background-color: #A7D500;
}
.car-list{
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	height: 500rpx;
	width: 100%;
	position: fixed;
	bottom: 100rpx;
	background-color: #ffffff;	
	border-radius: 10px 10px 0px 0px;
	z-index: 78;
}

.car-container {
	display: flex;
	flex-direction: column;
	width: 100%;
}
.car-box {
	width: 100%;
	height: 120rpx;
	display: flex;
	flex-direction: row;
	align-items: center;
	padding: 20rpx 10rpx;
	border-bottom: solid #f4f4f4 1px;
}
.car-menu-image {
	width: 100rpx;
	height: 100rpx;
	margin-left: 35rpx;
}
.car-menu-name {
	font-weight: normal;
	font-size: 28rpx;
	margin-bottom: 10rpx;
}
.car-menu-explain{
	font-size: 20rpx;
	margin-bottom: 10rpx;
	/* 下方四项配合使用,超出范围的显示... */
	width: 400rpx;
	overflow: hidden;
	text-overflow: ellipsis;  
	white-space: nowrap;
}
.car-menu-ps{
	display: flex;
	flex-direction: row;
	justify-content: space-between; /*两边对齐*/	
}
.car-menu-price{
	font-size: 26rpx;
	font-weight: 600;
}
.car-num-select{	
	display: flex;
	flex-direction: row;
	width: 160rpx;
	height: 36rpx;	
}

/* 遮罩层 */
.car-mark{
	position: fixed;
	left:0;
	top:0;
	width:100%;
	height:100%;
	z-index:77;
	background-color: rgba(0, 0, 0, 0.4);
	transition: all 0.3s ease-in 0s;
}
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐