uni-app实现微信小程序购物车功能

实现效果

页面布局这里就不一一介绍,自行看看代码

<template>
	<view>
		<view class="big">
			<view class="headCss" v-if="!flag">
				购物车空空如也,请先<span class="choise" @tap="gotoChoise">选购</span>
			</view>
			<view v-else>
				<view class="cartitem" v-for="(item,index) of myData" :key="index">
					<view class="top">
						<view class="twoleft">
							<checkbox-group @change="selected(item)">
								<checkbox color="orange" style="transform:scale(0.7);" :checked="item.flag" />
							</checkbox-group>
							<span>商品:{{item.name}}</span>
						</view>
						<view class="del">
							<uni-icons class="del" color="orange" type="trash" size="18" @tap="del(item,index)"></uni-icons>
						</view>
					</view>
					
					<view class="center">
						<view class="mainContain">
							<view>
								<image :src="'http://image.nssnail.com/'+item.goods.fileList[0].relativePath"></image>
							</view>
							<view class="main">
								<view class="descript">{{item.goods.description}}
									<view class="twoBtn">
										<view style="color: red;">¥{{item.myPrice}}</view>
										<view class="buybtn">
											<uni-icons color="orange" type="minus" size="15" @tap="reduce(item)"></uni-icons>
											<span class="myqtys">{{item.qty}}</span>
											<uni-icons color="orange" type="plus" size="15" @tap="add(item)"></uni-icons>
										</view>
									</view>
								</view>
							</view>
						</view>
					</view>
				</view>
				
				<!-- 底部导航 -->
				<view class="three">
					<view class="choose">
						<checkbox-group @change="selectedall()">
							<checkbox :checked="allchecked" color="orange" style="transform:scale(0.7);" />全选
						</checkbox-group>
					</view>
					<view class="two">
						<view class="total">
							合计:{{totalPrice}}
						</view>
						<button @tap="submit" :disabled="disabled" class="allNumber">
							结算:( {{totalNum}} )
						</button>
						
					</view>
				</view>
				
			</view>
		
		</view>
	</view>
</template>

css部分

.cartitem {
	width: 92%;
	background-color: white;
	margin: 20rpx auto;
	border-radius: 10rpx;
	padding-bottom: 10rpx;
}
.del {
	transform: translateX(30rpx);
}
.headCss{
	width: 92%;
	background-color: white;
	height: 200rpx;
	line-height: 200rpx;
	text-align: center;
	font-weight: 600;
	margin: 20rpx auto;
	border-radius: 10rpx;
	font-size: 30rpx
}
.choise{
	color: orange;
	margin: 0 20rpx;
}
.three{
	display: flex;
	flex-wrap: nowrap;
	width: 100%;
	background-color: white;
	position: fixed;
	z-index: 1;
	height: 100rpx;
	line-height: 100rpx;
	bottom: 0;
	justify-content: space-between;
	font-size: 30rpx;
}
.two{
	display: flex;
	flex-wrap: nowrap;
	font-size: 30rpx;
	margin-top: 10rpx;
	margin-right: 20rpx;
}
.choose{
	margin-top: 10rpx;
	margin-left: 20rpx;
	line-height: 80rpx;
	text-align: center;
	height: 80rpx;
	width: 200rpx;
}
.top{
	width: 92%;
	display: flex;
	flex-wrap: nowrap;
	justify-content: space-between;
}
.twoleft{
	display: flex;
	flex-wrap: nowrap;
	margin: 17rpx;
}
.twoleft span{
	display: inline-block;
	width: 400rpx;
	transform: translateX(-180rpx);
	margin-top: 5rpx;
}
.center{
	width: 100%;
	margin-top: 10rpx;
}
.center image{
	width: 200rpx;
	height: 200rpx;
	margin-left: 25rpx;
}
.mainContain{
	display: flex;
	flex-wrap: nowrap;
}
.main{
	margin-left: 20rpx;
}
.descript{
	height: 200rpx;
	width: 420rpx;
	margin-left: 20rpx;
	position: relative;
}
.twoBtn{
	width: 100%;
	position: absolute;
	bottom: 10rpx;
	display: flex;
	flex-wrap: nowrap;
	justify-content: space-between;
}
.buybtn{
	margin-right: 20rpx;
	transform: translateY(-6rpx);
}
.myqtys{
	margin: 0 30rpx;
	display: inline-block;
	transform: translateY(-2rpx);
	font-size: 30rpx;
}
.onePrice{
	width: 400rpx;
	margin-left: 25rpx;
	color: red;
}
.big{
	padding-bottom: 100rpx;
}
button::after{ 
	border: none;
}
.total,.allNumber{
	width: 300rpx;
	background-color: orange;
	color: white;
	line-height: 80rpx;
	text-align: center;
	border-radius: 40rpx;
	height: 80rpx;
}
.total{
	background-color: white;
	margin-right: 20rpx;
	color: black;
	width: 200rpx;
}
.allNumber{
	font-size: 30rpx;
}
.all{
	width: 100%;
	height: 500rpx;
	background-color: white;
}
.btn{
	background-color: red;
	font-size: 28rpx;
	width: 180rpx;
	height: 70rpx;
	line-height: 70rpx;
	margin-top: 40rpx;
	color: white;
	font-weight: 600;
}

 script部分

<script>
	import uniGoodsNav from '@/components/uni-goods-nav/uni-goods-nav.vue'
	import {mapState,mapMutations} from 'vuex'
	export default {
		data() {
			return {
				flag: true,//用于判断用户购物车是否有商品,没有商品为false,有商品为true
				myData: [],
				allchecked: false ,
				sessionId:'',
				cartList:[],//结算时的购物车
				disabled:true,//按钮默认为禁用状态
				submitPrice:'',//结算时的总价
			}
		},
		onLoad() {
            //页面渲染
			this.init()
		},
		computed: {
			...mapState(['hasLogin','uerInfo']), 
			
			// 计算选中商品数量
			totalNum() {
				let totalNum = 0;
				this.myData.map(item => {
					item.flag ? totalNum += 1 : totalNum += 0
				})
				return totalNum
			},
			
			//计算选中商品的总价
			totalPrice() {
				let totalPrice = 0;
				this.myData.map(item => {
                    //这里的totalprice计算是数量乘以价格
					item.flag ? totalPrice += item.qty * item.myPrice : totalPrice += 0
				})
				return totalPrice
			}
		},
		watch:{
            // 监听结算的订单数,当订单数小于0的时候,结算按钮为禁用状态,用户不能提交订单
			totalNum(val){
				if(val != 0){
					this.disabled = false
				}else{
					this.disabled = true
				}
			},
			
			// 监听总价
            // 监听totalprice的值,再将值赋给submitPrice,提交订单的时候传的参数是submitPrice
			totalPrice(val){
				this.submitPrice = val
			}
		},
		methods: {
			...mapMutations(['login']),
			// 页面初始化
			init(){
                // 获取本地存储的sessionId,作为入参请求购物车信息
				uni.getStorage({
					key: 'userInfo',
					success:(res) => {
						this.login(res.data);
						this.sessionId = res.data.sessionId
						uni.request({
							url:'/getCart',
							data:{
								sessionId:this.sessionId
							},
							success: (res) => {
								if(res.data.stat == 1){
									res.data.data.map(item =>{
										item.flag = false
									})
									this.myData = res.data.data
									if(this.myData.length == 0){
										this.flag = false
									}
								}
							}
						})
					} 
				})

			},

			// 单个商品前的勾选
			selected(item) {
                // 反选
				item.flag = !item.flag
				if (!item.flag) {
					this.allchecked = false
				} else {
					const test = this.myData.every(item => {
						return item.flag === true
					})
					if (test) {
						this.allchecked = true
					} else {
						this.allchecked = false
					}
				}
				
			},
			// 全选按钮
			selectedall() {
				this.allchecked = !this.allchecked
				if (this.allchecked) {
					this.myData.map(item => {
						item.flag = true
					})
				} else {
					this.myData.map(item => {
						item.flag = false
					})
				}
			},
			
			// 结算
			submit(){
				this.myData.forEach(e=>{
				    if(e.flag){
						var data={
						    id:e.id,
							goodId:e.goods.id,
							userId:e.userId,
							qty:e.qty,
							price:e.price,
							name:e.name
							}
						this.cartList.push(data)
						}
					})
					uni.request({
						url:'cartSettlement',
						method:'POST',
						data:{
							cartList:this.cartList,
							sessionId:this.sessionId,
							totalPrice:this.submitPrice,
							payPwd:this.number
						},
						success: (res) => {
							if(res.data.stat == 1){
								uni.showToast({
									title:res.data.message,
									icon:'success',
									duration:3000
								})
								this.init()
							}else if(res.data.stat == 0){
								uni.showToast({
									title:res.data.message,
									icon:'none',
									duration:3000
								})
							}
						}
					})
			},
			
			// 减号操作
			reduce(item) {
				var id = item.id
				var goodsPrice = item.myPrice
				if (item.qty > 1) {
					var myqty = --item.qty
					this.updateQty(myqty,goodsPrice,id)
				} else {
					item.qty = 1
					var myqty = 1
					this.updateQty(myqty,goodsPrice,id)
					return
				}
			},
			
			// 加号操作
			add(item) {
				var id = item.id
				var goodsPrice = item.myPrice
				var myqty = ++item.qty
				this.updateQty(myqty,goodsPrice,id)
			},
			
			// 修改商品数量
			updateQty(myqty,goodsPrice,id){
				var price = myqty * goodsPrice
				uni.request({
					url:'updateCart',
					method:'POST',
					data:{
						id:id,
						qty:myqty,
						price:price,
						sessionId:this.sessionId
					},
					success: (res) => {
						if(res.data.stat == 1){
							return this.myData
						}
					}
				})
			},
			
			// 删除单挑购物车商品
			del(item, index) {
				var id = item.id
				console.log(id)
				uni.showModal({
				    title: '提示',
				    content: '是否删除购物车?',
				    success: (res) => {
				        if (res.confirm) {
				            uni.request({
				            	url:'deleteCart',
								method:'POST',
				            	data:{
				            		id:id,
				            		sessionId:this.sessionId
				            	},
				            	success: (res) => {
				            		if(res.data.stat == 1){
				            			uni.showToast({
				            				title:'删除成功',
				            				icon:'success',
				            				duration:2000,
				            				success: () => {
				            					this.init()
				            				}
				            			})
				            		}
				            	}
				            })
				        }
				    }
				});
			},

		},
		components:{
			uniGoodsNav
		}
	}
</script>

 代码解析+思路

定义一个flag标志位,当用户购物车为空的时候,为false;配合复选框一起用的时候,追加到myData中,当其中一条购物车信息被选中的时候,flag为true。


页面初始化的时候,将后台返回的信息进行map数组处理,新增一个flag字段,状态为false,然后再将处理后的数据赋给myData。所以页面刚进来的时候,所有购物车信息都处于未选中状态。

res.data.data.map(item =>{
	item.flag = false
})
this.myData = res.data.data

 

单选/全选

对单选选中的购物车标志位进行判断,假如标志位的状态为false,则全选按钮没有选中;假如为true,则继续往下判断,遍历myData中的其他数据,是否存在flag为false,有则不选中全选,没有则表示myData中的所有数据的状态都为true,即为全选。全选按钮的判断原理相似就不再赘述。

selected(item) {
	item.flag = !item.flag
		if (!item.flag) {
			this.allchecked = false
		} else {
			const test = this.myData.every(item => {
			return item.flag === true
		    })
		    if (test) {
			this.allchecked = true
		    }else {
			    this.allchecked = false
		}
	}
},

computed计算商品的总价

在computed中计算totalprice,初始值默认为0,当totalprice的值发生变化的时候,都会执行这条语句,遍历myData,这里是一个三目运算,当item.flag为true的时候,totalprice等于(每条数据的价格乘以数量)的总和,为false的时候加0。商品数的做法一样,只是将价格总和改为1,每选中一条数据总数加一。

this.myData.map(item => {
	item.flag ? totalPrice += item.qty * item.myPrice : totalPrice += 0
	})
return totalPrice

 

watch监听

通过监听结算总数来判断按钮的状态;因为点击结算的时候,总价格totalprice不能直接引用,所以将它的值赋给定义的一个submitPrice变量,提交的时候传参传submitPrice。

// 监听总价
totalPrice(val){
	this.submitPrice = val
}

加/减数量

减少数量的时候要加一个判断,不能低于0;加号不做限制;点击加/减号的时候,将选中的购物车的信息赋值给变量,再作为入参,调用修改数量的方法;这里的修改数量方法时updataQty,入参为myqty,goodsPrice,id


最后就是结算的功能,这里我卡了很久,不过最后还是做出来了。先说思路:点击结算的时候,遍历myData这个数组,判断数组中标志位,为true的时候,定义一个对象data,每遍历出一条flag为true的数据,都将它的值赋给data,再将data对象push到定义的一个cartList数组中,最后提交结算的时候,cartList作为入参。

if(e.flag){
	var data={
			id:e.id,
			goodId:e.goods.id,
			userId:e.userId,
			qty:e.qty,
			price:e.price,
			name:e.name
		}
	this.cartList.push(data)
}

整个购物车的思路及做法就是这样。

 


参考博客:https://blog.csdn.net/qq_42292879/article/details/104465533

这是我在参考这位博主的方法后,加入自己的想法,加工改造之后的实现方式,以及自己对代码的理解,如果有不对的地方,欢迎各位同行指教学习~~~~

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值