微信小程序——购物车面向对象逻辑编写
业务逻辑:
我们要在微信小程序客户端中构造购物车,实现对购物车的增加商品、删除商品、修改商品数量、查询购物车商品等操作,即对应增删改查操作。
逻辑架构分析
缓存(前端数据库)
我们需要一个数据库来储存购物车里的数据,在微信小程序中缓存提供了API接口,我们可以调用微信小程序的缓存来存储购物车中的数据;
模型层(与缓存交互)
模型层将用于与购物车有关的读取、更新、插入缓存的业务逻辑编写,还有一些计算总数、总价等等方法;
控制器层(控制数据驱动wxml)
调用模型层的方法,将数据打印打wxml
业务需求分析
业务代码编写
接下来将一一分析每个业务逻辑的代码编写
添加商品
用户在商品详情界面时,会将商品加入到购物车,然后进行购买。
控制层
onAddingToCartTap:function(){
this.addToCart();
var counts=this.data.cartTotalCounts+this.data.productCounts;
this.setData({
cartTotalCounts: counts
})
},
addToCart:function(){
var tempObj = {}, keys = ['id', 'name', 'main_img_url', 'price'];
for (var key in this.data.product) {
if (keys.indexOf(key) >= 0) {
tempObj[key] = this.data.product[key];
}
}
cart.add(tempObj, this.data.productCounts);
},
在控制中调用
addToCart方法,将商品的信息组装成新的字典:先定义我们需要的key和新的字典对象tempObject,然后遍历product中的key,判断key是否存在于keys中,如果存在,我们就重新在tempObject中组装一个键值对。然后调用cart.add方法,该方法将在后面详述;
因为本业务还需要显示购物车的实时商品数量,所以这里还需要将购物车的商品数量加上刚刚添加的商品数量,重新绑定数据。
模型层
cart.add方法:
/*
* 加入到购物车
* 如果之前没有样的商品,则直接添加一条新的记录, 数量为 counts
* 如果有,则只将相应数量 + counts
* @params:
* item - {obj} 商品对象,
* counts - {int} 商品数目,
* */
add(item, counts) {
var cartData = this.getCartDataFromLocal();
if (!cartData) {
cartData = [];
}
var isHadInfo = this._isHasThatOne(item.id, cartData);
//新商品
if (isHadInfo.index == -1) {
item.counts = counts;
item.selectStatus = true; //默认在购物车中为选中状态
cartData.push(item);
}
//已有商品
else {
cartData[isHadInfo.index].counts += counts;
}
this.execSetStorageSync(cartData); //更新本地缓存
return cartData;
};
在该方法中,我们要将控制层接收的商品信息item存入到缓存中:
首先,我们得读取原本缓存中的商品信息,调用方法getCartDataFromLocal();
如果缓存中没有购物车的数据就重新新建数组,给传入的字典对象设置新的键值对数目和选择状态,然后插入到购物车数组中。最后再将购物车数组同步到缓存中execSetStorageSync(cartData);
如果缓存中有购物车的数据,就读出然后再调用_isHasThatOne方法传入id和购物车数组来判断该id的商品是否已经存在该数据库中,如果存在,会得到index,然后根据index就直接在数组中索引对应的index下标下的商品数目上更新即可,如果不存在就得设置新的键值对数目和选择状态,然后插入到购物车数组中。最后再将购物车数组同步到缓存中execSetStorageSync(cartData);
/*
* 获取购物车
* param
* flag - {bool} 是否过滤掉不下单的商品
*/
getCartDataFromLocal(flag) {
var res = wx.getStorageSync(this._storageKeyName);
if (!res) {
res = [];
}
//在下单的时候过滤不下单的商品,
if (flag) {
var newRes = [];
for (let i = 0; i < res.length; i++) {
if (res[i].selectStatus) {
newRes.push(res[i]);
}
}
res = newRes;
}
return res;
};
/*购物车中是否已经存在该商品*/
_isHasThatOne(id, arr) {
var item,
result = { index: -1 };
for (let i = 0; i < arr.length; i++) {
item = arr[i];
if (item.id == id) {
result = {
index: i,
data: item
};
break;
}
}
return result;
};
/*本地缓存 保存/更新*/
execSetStorageSync(data) {
wx.setStorageSync(this._storageKeyName, data);
};
getCartDataFromLocal(flag)方法分两种情况获得购物车数据,一种是获得选中商品的数据,另一种是获得全部商品的数据。
以上代码不再赘述,自行理解;
查看所有商品
onShow: function () {
var cartData = cart.getCartDataFromLocal(),
countsInfo = cart.getCartTotalCounts(true);
this.setData({
selectedCounts: countsInfo.counts1,
selectedTypeCounts: countsInfo.counts2,
account: this._calcTotalAccountAndCounts(cartData).account,
cartData: cartData
});
},
我们需要利用getCartDataFromLocal方法将购物车的数据读出
同时计算选中商品的数目cart.getCartTotalCounts(true);
然后再计算其价格_calcTotalAccountAndCounts(cartData);
最后完成绑定数据
/* 计算购物车商品数据*/
_calcTotalAccountAndCounts: function (data) {
var len = data.length,
account = 0,
selectedCounts = 0,
selectedTypeCounts = 0;
let multiple = 100;
for (let i = 0; i < len; i++) {
//避免 0.05 + 0.01 = 0.060 000 000 000 000 005 的问题,乘以 100 *100
if (data[i].selectStatus) {
account += data[i].counts * multiple * Number(data[i].price) * multiple;
selectedCounts += data[i].counts;
selectedTypeCounts++;
}
}
return {
selectedCounts: selectedCounts,
selectedTypeCounts: selectedTypeCounts,
account: account / (multiple * multiple)
}
},
_calcTotalAccountAndCounts方法完成对选中商品数据的计算,首先还是对商品的状态进行判定,如果是选中的情况,那么就累加其价格和数目,还有商品种类。最后进行返回。计算价格前先乘以100是为了防止js的浮点运算的误差。
修改商品数目
前端代码
<view class="btns {{item.counts==1?'disabled':''}}" bindtap="changeCounts"
data-id="{{item.id}}" data-type="cut">-</view>
<view class="counts">{{item.counts}}</view>
<view class="btns" bindtap="changeCounts" data-id="{{item.id}}" data-type="add">+</view>
/*调整商品数目*/
changeCounts: function (event) {
var id = cart.getDataSet(event, 'id'),
type = cart.getDataSet(event, 'type'),
index = this._getProductIndexById(id),
counts = 1;
if (type == 'add') {
cart.addCounts(id);
} else {
counts = -1;
cart.cutCounts(id);
}
//更新商品页面
this.data.cartData[index].counts += counts;
this._resetCartData();
},
在这里我们需要更新购物车商品的数量,首先我们得获得商品的id和商品是加是减,然后根据商品id找到该id在购物车数组中的下标**_getProductIndexById**(),并返回,然后根据下标和加减type来进行操作cart.addCounts(id);,更新缓存中的数据和页面数据,最后更新购物车的商品数据**_resetCartData**()。
/*根据商品id得到 商品所在下标*/
_getProductIndexById: function (id) {
var data = this.data.cartData,
len = data.length;
for (let i = 0; i < len; i++) {
if (data[i].id == id) {
return i;
}
}
},
/*更新购物车商品数据*/
_resetCartData: function () {
var newData = this._calcTotalAccountAndCounts(this.data.cartData); /*重新计算总金额和商品总数*/
this.setData({
account: newData.account,
selectedCounts: newData.selectedCounts,
selectedTypeCounts: newData.selectedTypeCounts,
cartData: this.data.cartData
});
},
cart.addCounts(id);
addCounts(id) {
this._changeCounts(id,1);
};
cutCounts(id) {
this._changeCounts(id, -1);
};
/*
* 修改商品数目
* params:
* id - {int} 商品id
* counts -{int} 数目
* */
_changeCounts(id, counts) {
var cartData = this.getCartDataFromLocal(),
hasInfo = this._isHasThatOne(id, cartData);
if (hasInfo.index != -1) {
if (hasInfo.data.counts > 1) {
cartData[hasInfo.index].counts += counts;
}
}
this.execSetStorageSync(cartData); //更新本地缓存
};
这里的业务逻辑就是获得控制层得到的商品id还有加减指令,然后传入到_changeCounts()中,在该方法中获得购物车所有商品数据,然后根据id查找出在购物车商品数组中的下标,如果下标存在,就利用数组索引完成加减操作,最后更新到缓存中。
删除商品
/*删除商品*/
delete: function (event) {
var id = cart.getDataSet(event, 'id'),
index = this._getProductIndexById(id);
this.data.cartData.splice(index, 1);//删除某一项商品
this._resetCartData();
//this.toggleSelectAll();
cart.delete(id); //内存中删除该商品
},
获得商品的id,然后根据id获得购物车数组下标,最后先在控制器数据中删去,更新购物车数据,然后再调用
cart.delete(id); 实现缓存中同步删除。
delete(ids) {
if (!(ids instanceof Array)) {
ids = [ids];
}
var cartData = this.getCartDataFromLocal();
for (let i = 0; i < ids.length; i++) {
var hasInfo = this._isHasThatOne(ids[i], cartData);
if (hasInfo.index != -1) {
cartData.splice(hasInfo.index, 1); //删除数组某一项
}
}
this.execSetStorageSync(cartData);
}
在model层中对缓存中指定的商品数据进行删除。
商品选择
前端
<view class="cart-item-checkbox" ontap="toggleSelect" data-id="{{item.id}}" data-status="{{item.selectStatus}}">
<image wx:if="{{item.selectStatus}}" src="../../imgs/icon/circle@selected.png"></image>
<image wx:else src="../../imgs/icon/circle@noselected.png"></image>
</view>
控制器
/*选择商品*/
toggleSelect: function (event) {
var id = cart.getDataSet(event, 'id'),
status = cart.getDataSet(event, 'status'),
index = this._getProductIndexById(id);
this.data.cartData[index].selectStatus = !status;
this._resetCartData();
},
/*全选*/
toggleSelectAll: function (event) {
var status = cart.getDataSet(event, 'status') == 'true';
var data = this.data.cartData,
len = data.length;
for (let i = 0; i < len; i++) {
data[i].selectStatus = !status;
}
this._resetCartData();
},
保存商品选择状态到缓存中
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
cart.execSetStorageSync(this.data.cartData);
},
tus = cart.getDataSet(event, ‘status’) == ‘true’;
var data = this.data.cartData,
len = data.length;
for (let i = 0; i < len; i++) {
data[i].selectStatus = !status;
}
this._resetCartData();
},
保存商品选择状态到缓存中
```js
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
cart.execSetStorageSync(this.data.cartData);
},
总结:购物车的业务逻辑有点像后端逻辑,需要缓存作为数据库,而任何逻辑必须要做的几步就是,从缓存中读出数据,然后根据商品id找出缓存数组中的下标,此外进行任何修改时,都需要保证控制层的数据和缓存同步。