目录:
1.第一部分(http://www.pblcode.com/view/article/details?articleId=550)
2.第二部分
1.先分析主要的购物车的逻辑
(1)单组件之间的逻辑。
(2)多组件之间的数据计算。
2.购物车布局的实现
(1)checked-group的使用以及bindchange:“checkedchange”在checked-group上的绑定。
(2)wxss文件的编写。
(3)index.js的编写。
一.定义。
二.函数中的()=>{}的用法。
三.全选逻辑。
四.单个选择逻辑。即 bindchange:“checkedchange”在checked-group上的用法。
3.难点------组件数据的交互
(1)bind:change=“onQuantityChange”组件数据的交互。
(2)点缀。
(3)wx.showToast() API的使用。
4.总结
其实要实现购物车逻辑,那么最主要的就是理顺思路
1. 首先
(1)涉及到了每一个组件之间的数据传递
(2)每一个单一的数据在进行总的数据计算
我认为要是这两步搞清楚了那么这个购物车实际上还是蛮简单的。
下面开始。。。先来几张图吧。
2.布局
<!--index.wxml-->
<view class="goods-list">
<view class="group-title">{{shopName}}</view><!--菜篮子旗县店-->
<!--checkbox-group多项选择器,内部有多个checkbox构成-->
<checkbox-group class="group-list" bindchange="checkboxChange">
<view class="group-item" wx:for="{{cartGoodsList}}" wx:key="{{index}}">
<view class="checkbox">
<checkbox checked="{{item.checked}}" value="{{index}}"></checkbox>
</view>
<navigator url="/subPages/goods/detail/index" class="goods-item" hover-class="none">
<view class="pic">
<c-image src="{{item.url}}" mode="scaleToFill"></c-image>
</view>
<view class="info">
<view class="title">{{item.name}}</view>
<view class="content">
<view class="con-left">
<view class="sub-title">单位:千克</view>
<view class="price">¥{{item.price}}</view>
</view>
<view class="con-right">
<c-quantity bind:change="onQuantityChange" data-index="{{index}}" quantity="{{item.num}}"></c-quantity>
</view>
</view>
</view>
</navigator>
</view>
</checkbox-group>
</view>
其实这就是最简单的布局了,不过这个布局还是有一些要说的地方。
(1)在上面的代码中它用到了checkbox-group来当最外层,为什么呢,其实就是像按钮一样,你要是用一个的话还行,你要是同时使用了两个及以上的话,那么就要组将它包围起来。这里这个checkbox-group就是起的这个作用。
然后你还是会发现在checkbox-group上面还挂载了一个函数,那么这个函数为什么放在checkbox-group上呢,我们之前说了这个不是就像按钮组一样吗?如果在按钮组上挂载了函数,那么不是就可以监控到组里面你要操控的按钮了嘛。这里的这个bindchange="checkboxChange"起的就是这个作用。
然后接下来就是我们遍历工作了。一定要记住,实在组里面遍历,所以遍历之后wx:key=“{{index}}”就是指的遍历的次数,即每一次遍历的类似的号码一样。这个后面会用到。
剩下的就没有什么好说的了,比较难的像自定义组件啊,组件之间的交互啊之前说过了。
<view class="footer flex-between">
<label class="selectAll" catchtap="onSelectAll">
<checkbox checked="{{allChoose}}" />
<text class="title">全选</text>
</label>
<view class="flex-item totalPrice">
<text>合计:</text>
<text class="num">¥{{totalPrice}}</text>
</view>
<button class="flex-none u-btn btn-checkout checked" bindtap="toBuyNow">结算</button>
</view>
(2) 下面就是wxss文件的说明了,简单说一下
<!--index.wxss-->
.goods-list {
height: calc(100vh - 50px);/*当前窗口的高度减去50px*/
overflow: auto;/*内容会出现滚动条*/
}
.group-title {
padding: 20rpx;
border-bottom: 3rpx solid #eee;
border-top: 3rpx solid #eee;
margin-top: 10rpx;
margin-bottom: 20rpx;
}
.group-list .group-item {
display: flex;
align-items: center;
}
.group-list .group-item .checkbox {
padding: 10px;
}
.goods-item {
display: flex;
margin: 0 10px 10px 0;
background: #fff;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
flex:1;
}
.goods-item .pic {
width: 100px;
height: 100px;
flex: none;
}
.goods-item .info {
flex: 1;
display: flex;
flex-direction: column;
padding: 15rpx 10rpx;
}
.goods-item .info .title {
font-size: 28rpx;
flex: 1;
font-weight: 600;
}
.goods-item .info .content {
display: flex;
justify-content: space-between;
align-items: center;
}
.goods-item .info .content .con-left{
display: flex;
flex-direction: column;
}
.goods-item .info .content .con-left .sub-title {
font-size: 24rpx;
margin-bottom: 8rpx;
color: #969aa3;
}
.goods-item .info .content .con-left .price {
font-size: 28rpx;
color: #ef3325;
}
.goods-item .info .content .con-right{
padding-right: 20rpx;
}
/*底部*/
.footer {
height: 100rpx;
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
border-top: 1px solid #f0f0f0;
z-index: 2;
}
.footer .selectAll {
padding-left: 20rpx;
}
.footer .selectAll checkbox{
padding-bottom: 5rpx;
}
.footer .selectAll .title {
color: #000;
margin-left: 10rpx;
}
.footer .totalPrice {
margin: 0 20rpx;
text-align: right;
}
.footer .totalPrice .num {
color: #fd3d37;
}
.footer .btn-checkout {
height: 100%;
border-radius: 0;
}
(3)接下来就到我们最难的部分了,也就是购物车的灵魂--index.js
一.首先第一步,先定义
data: {
totalPrice: 0,//总价格
cartGoodsList: [],//商品列表
allChoose: false,//全选
shopName: app.store.shopName//该页的名称
},
onLoad(options) {},
onShow() {
//在这里执行获取购物车列表信息的方法
this.getCartGoodsList();
}
二.定义好了之后呢,就是上面执行了一个函数
//获取购物车列表信息
getCartGoodsList() {
//这里获取数据之后需要进行处理
let list = [{
id: 1,
url: '//img14.360buyimg.com/mobilecms/s372x372_jfs/t27259/211/1654431032/271251/fdcccf0f/5bea2c6dNa52b08d6.jpg!q70.dpg',
name: "海南香蕉",
price: 22.2,
num: 2,
checked: false
}, {
id: 2,
url: '//img14.360buyimg.com/mobilecms/s372x372_jfs/t27259/211/1654431032/271251/fdcccf0f/5bea2c6dNa52b08d6.jpg!q70.dpg',
name: "海南香蕉2",
price: 11.1,
num: 2,
checked: false
}, {
id: 3,
url: '//img14.360buyimg.com/mobilecms/s372x372_jfs/t27259/211/1654431032/271251/fdcccf0f/5bea2c6dNa52b08d6.jpg!q70.dpg',
name: "海南香蕉1",
price: 3.35,
num: 3,
checked: false
}, {
id: 4,
url: '//img14.360buyimg.com/mobilecms/s372x372_jfs/t27259/211/1654431032/271251/fdcccf0f/5bea2c6dNa52b08d6.jpg!q70.dpg',
name: "海南香蕉",
price: 22.2,
num: 3,
checked: false
}];
this.setData({
cartGoodsList: list,
totalPrice: 0,
allChoose: false
}, () => {
this.handlePrice()
})
}
在最后有一处()=>{
this.handlePrice();
}这个是什么意思呢,其实这个吧,就是让this起作用,不过按照我的理解吧,这里这样写主要为了让我们的操作最终影响到这个函数,这个函数是计算最后的金额的,那么也就是说我们每执行一步操作函数最后都要在执行一遍这个handlePrice函数以便于去显示我们执行之后的金额。
//处理 数据更改后的价格
handlePrice() {
let l = this.data.cartGoodsList;
let price = 0;
for (var i = 0; i < l.length; i++) {
if (l[i].checked) {
price += l[i].price * l[i].num
}
}
this.setData({
totalPrice: price.toFixed(2)
})
}
三.接下来就好办了其实。先写全选操作吧。
onSelectAll() {
let flag = this.data.allChoose;
let l = this.data.cartGoodsList;
for (var i = 0; i < l.length; i++) {
l[i].checked = !flag
}
this.setData({
cartGoodsList: l,
allChoose: !flag
}, () => {
this.handlePrice()
})
}
上面的代码仅仅是控制全选而已,就是点击上面图的全选就全部选中的逻辑,比较简单。
四.接下来既然全选搞定了,那么就解决单个选择呗。
这个单个选择吧我上面还特别的强调过,上面不是说bindchange:"checkboxChange"是控制里面的每一个按钮的吗?
那么很明显这个函数就是控制每一个单个选择的。
checkboxChange(e) {
let l = this.data.cartGoodsList;
let v = e.detail.value;//这个v实际上是指你点击的这个元素时数组里的第几个元素
let flag = false;
for (var i = 0; i < l.length; i++) {
l[i].checked = false;
for (var j = 0; j < v.length; j++){
l[v[j]].checked = true;
}
}
console.log(v);
if (v.length == l.length){
flag = true;
}
this.setData({
cartGoodsList: l,
allChoose: flag
}, () => {
this.handlePrice()
})
}
下面来详细的解读一下上面的这个函数
可以很明显的看到,这个函数和前面的函数由很大不一样,这个函数是有参数的。那么是谁传的呢,其实就是我们点击改变的时候这个binchange:“checkboxChange”传给我们的。
这个传给我们什么呢。在上面我们定义了一个 let v = e.detail.value;传给我们的这个e就是在这里用到的。我们将e.detail.value赋给了v,其实这个很好解释,其实就是我们将点击改变传过来的值的且是checked为true的值放入到了v这个数组里,不分先后的放入,先点谁就放谁的那种。
这样就能彻底解释了其实上面的代码无非就是这么个意思,先把所有的checked设置为false,然后把点击的按钮设置为checked:true,然后如果你把所有的都点了,那么就是全选了。那么这个函数就解释完了。
3.然后又到了我们这个购物车的第二大难点了。
在上一个微信小程序的案例(http://www.pblcode.com/view/article/details?articleId=550)里我们说到了子组件传递修改后的值给父组件吧,但是上一个案例没有应用到啊,那么在这里用到了。
(1)在<c-quantity bind:change="onQuantityChange" data-index="{{index}}" quantity="{{item.num}}"></c-quantity>里面首先我们先分析啊,无非就是按照组件交互的格式定义了函数,设置了自定义属性index,并且修改了这个自定义组件里面的quantity,在上一个案例中,显示这个c-quantity的值的就是这个{{quantity}},那么也就是说我们父组件调用子组件的时候可以用相同的赋值去修改子组件里面的数值。
onQuantityChange(e){
this.setData({
["cartGoodsList["+e.currentTarget.dataset.index+"].num"]: e.detail
},()=>{
this.handlePrice()
})
}
这个函数又有了一个参数传进来了。这个参数是你子组件修改过后的值。
接下来来理解最重要的一句话
["cartGoodsList["+e.currentTarget.dataset.index+"].num"]: e.detail;
就是这句话,是什么意思呢,我们之前不是定义了for循环吗?还记得wx:key={{index}}上面我说的是准确的说事每一个的顺序号码,在上面的哪一行代码里也是一样的意思。
这个e.currentTarget.dataset.index其实就是就像我们点击操作的那个c-quantity组件的号码,就像我们上面的图片那样,一共有四个,比如吧,你点击第一个,那么这个其实就是将第一个你修改的c-quantity里面的值赋值给了cartGoodsList里面的第一个值的num了。
理解了上面的那些我们这个微信小程序的购物车就基本上结束了。
(2)下面在点缀一下吧。
//前往 立即购买(产生订单)的页面
toBuyNow(){
let l = this.data.cartGoodsList;
let price = this.data.totalPrice;
if(price > 0){
let list = [];
for (var i = 0; i < l.length; i++) {
if (l[i].checked) {
list.push(l[i]);
}
}
let object = {
allPrice:price,
goodsList:list
}
wx.navigateTo({
url: 'buy-now/index?object=' + JSON.stringify(object),
})
}else{
wx.showToast({
title: "您还未选择结算商品",
icon: 'none',
duration: 2000,
mask:true
})
}
}
上面的代码就是你已经操作完了,你点击结算的时候吧,将最后的金额和你点击的商品放入数组中,然后创建对象传递给需要它的地方,然后设置一下,如果您一个商品都没有选择,那么弹出弹窗来提醒用户。
(3)下面简单的说一下wx.showToast();这个API吧。
就以上面用到的属性简单介绍:
1.title:提示的内容
2.icon:图标
3.duration:提示的延迟时间
4.mask:是否显示透明蒙层,防止触摸穿透
这就是所谓组件交互吧,就是复杂了一些,不过还是很好理解的
{{ cmt.username }}
{{ cmt.content }}
{{ cmt.commentDate | formatDate('YYYY.MM.DD hh:mm') }}