代码下载
一般购物车如上图所示。具体结算时,有满减、优惠卷、免单等各种方式。
在管理页面添加【活动】入口。
点击活动将会进入【活动】管理页面:
添加【活动】时,将从底部弹出actionsheet供选择活动类型:
在该页面的json文件中引入控件:
"mp-actionSheet": "/miniprogram_npm/weui-miniprogram/actionsheet/actionsheet",
<mp-actionSheet bindactiontap="btnClick" show="{{showDialog}}" actions="{{groups}}" title="请选择活动种类">
mp-actionSheet>
该groups变量的值:
groups: [
{ text: '满减(一次)', value: 1 },
{ text: '满减(叠加)', value: 2 },
{ text: '免单', value: 3 },
]
对于每一个【活动】,都可以选择其适用的商品,以及设定时间限制(下图为新建满减活动的页面):
购物车功能涉及客户端两个页面。对于原有的商品页面,需要增加【添加至购物车】的功能。而购物车页面属于新增加的页面。
需要添加一个固定位置的按钮,当点击这个按钮时,会将选中的商品添加到购物车中。
这里有个问题,就是当切换商品类型时,用不用保存已选中商品的列表以便点击添加按钮后统一加入到购物车。目前demo是简单处理了,切换选项卡时清空保存选中商品的列表。
<view wx:if="{{pageIndex==0}}" class="add-wrap">
<image src="../images/customer_user_add.png" bindtap="onAddToCard">image>
view>
cart-op云函数:
处理添加时要考虑重复添加商品的问题。我的做法是,对于重复添加的商品,将其在购物车中的数量递增,而且将其置于选中状态。
const user = await collection.where({
openid: wxContext.OPENID,
cart: _.exists(true)
}).get()
//登陆的时候会创建空记录,此时只需要合并记录
var old_items = user.data.length > 0 ? user.data[0].cart : []
console.log("old cart:", old_items)
var add_items = []
request.data.ids.forEach(id => {
var has = false
for (var index =0; index < old_items.length; index++){
if( id == old_items[index].id){//递增其数量
old_items[index].count != null ? old_items[index].count +=1 : old_items[index].count = 1
old_items[index].selected = true
has = true
}
}
if (!has) {
add_items.push({
id: id,
selected: true,
count: 1
})
}
})
var new_items = old_items
await collection.where({
openid: wxContext.OPENID
}).update({
data: {
cart: new_items
}
}).then(res => {
success = true
data = res.stats.updated
}).catch(console.error)
return {
success: false,
}
index.js:
onAddToCard: function (e) {
let self = this
console.debug(this.data._selectedItems)
wx.cloud.callFunction({
name: "cart-op",
data: {
cmd: "add",
data: {
ids: this.data._selectedItems
}
}
}).then(res => {
console.debug(res)
if (res.result.success) {
this.data._selectedItems = []
for (var index = 0; index < self.data.goodsItems.length; index++) {
if (self.data.goodsItems[index].isChecked) {
self.setData({//已经添加到购物车,取消这些商品的选中状态
['goodsItems[' + index + '].isChecked']: false
})
}
}
if (res.result.data == 0) {
wx.showToast({
title: '已加入',
})
} else {//如果有新加入的商品,在购物车图标上显示红点
self.data.list[1].dot = true
self.setData({
['list[1]']: self.data.list[1]
})
}
}
}).catch(err => {
console.error(err)
})
},
该功能至少应该考虑下述因素:
cart-op云函数:
数据库的聚合查询:
购物车中只存放商品的id以及数量、是否选中等即时状态,并没有保存商品展示时需要的信息。如果对购物车中的每一种商品都依据id查找,将会非常耗时,能明显感觉到。如果将这些信息与购物车保存在一起,数据冗余是次要的,以后每次更新商品信息,都需要连带更新购物车,这是无法想象的。可以利用云数据库的聚合功能,将购物车中的cart列表字段展开,然后与goods数据表做连表查询(对于我这个多年来只会简单的select * form xxx的人来说,找这些资料并利用起来有些难度)。
case "get": {
const res = await collection.aggregate()
.unwind('$cart') //展开
.lookup({
from: 'goods',
localField: 'cart.id',
foreignField: '_id',
as: 'goodsInfo'
})
.project({
addressBook: 0,
info: 0,
orders: 0,
_id: 0
})
.end()
return {
success: true,
data: res
}
}
我们需要在cart.json引入几个控件:
"usingComponents": {
"van-stepper": "../../components/vant/dist/stepper/index",
"van-submit-bar" : "../../components/vant/dist/submit-bar/index",
"mp-cells": "/miniprogram_npm/weui-miniprogram/cells/cells",
"mp-cell": "/miniprogram_npm/weui-miniprogram/cell/cell",
"mp-slideview": "/miniprogram_npm/weui-miniprogram/slideview/slideview",
"mp-checkbox" : "/miniprogram_npm/weui-miniprogram/checkbox/checkbox",
"mp-badge": "/miniprogram_npm/weui-miniprogram/badge/badge"
}
submit-bar 挡住商品列表情况的处理:
这个透明的提交条不占据页面空间,所以滚动的时候它不会被作为可是元素处理,但会阻挡用户的输入信息。
就像上面的情况下,点击checkbox是无法触发事件的,作为一个在界面上不愿意多花时间成本的门外汉,处理方式就是塞给它一个没有内容的view元素,这样商品列表起码能滚动到submit-bar的上方:
关于活动的计价规则:
实际上的活动计价比demo中要复杂,比如满减这项,可区分整个购物车的商品总量满减还是单独的商品可单独计价,而且是否可与其他活动联合(活动是否具有排他性)。demo中是先将商品做一个单价由低到高的排序,然后遍历活动规则,每一次遍历都将参加活动的商品总额和数量统计出来,然后根据活动类型判断处理(比如,免单的时候,是先免掉单价低的商品)。
calculate: function (goodsList, discounts = []) {
var totalPrice = 0
var selectedGoods = []
goodsList.forEach(item => {
if (item.selected) {
item.order_price = Number((item.price * item.count).toFixed(2)) //单位为元
totalPrice += item.order_price * 100 //单位为分
selectedGoods.push(item)
}
})
var sorted = selectedGoods.sort(function (a, b) {
return a.price - b.price
})
console.debug("sorted selected goods:", sorted)
var cutPriceList = []
for (var index = 0; index < discounts.length; index++) {
var discountGoods = []
var totalCount = 0
var total_Price = 0
sorted.forEach(item => {
if (discounts[index].goods.indexOf(item._id) != -1) {
discountGoods.push(item)
totalCount += item.count
total_Price += item.order_price
}
})
var cutPrice = 0;
switch (discounts[index].type) {
case "1": {//1 满N减M
if (total_Price >= discounts[index].total) {
cutPriceList.push({
title: discounts[index].title,
cut: discounts[index].cut*100 //分
})
}
}
break;
case "2": {//2 每满N减M
var number = Number.parseInt(total_Price / discounts[index].total)
if(number > 0){
cutPriceList.push({
title : discounts[index].title,
cut : discounts[index].title.cut*number*100 //分
})
}
}
break;
case "3": {//3 N免M
if (totalCount < discounts[index].total) {
break
}
var cutCount = discounts[index].cut
var start = 0
while (cutCount > 0) {
var count = 1
if (cutCount - discountGoods[start].count > 0) {
cutCount -= discountGoods[start].count
count = discountGoods[start].count
} else {
count = cutCount
cutCount = 0
}
cutPrice += Number((discountGoods[start].price * count).toFixed(2)) * 100
start++
}
cutPriceList.push({
title : discounts[index].title,
cut : cutPrice
})
}
break;
}
}
var sortedCutPriceList = cutPriceList.sort(function (a, b) {
return a.cut - b.cut
})
sortedCutPriceList.forEach(item =>{
totalPrice -= item.cut
})
return totalPrice
}