实现效果
页面布局这里就不一一介绍,自行看看代码
<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
这是我在参考这位博主的方法后,加入自己的想法,加工改造之后的实现方式,以及自己对代码的理解,如果有不对的地方,欢迎各位同行指教学习~~~~