小组刚完成一个小程序项目,第一版正式发布了,过程中也遇到了很多问题,这里记录一下我负责的模块中的购物车功能的实现过程。后期再把其他小伙伴的模块也一并贴上来分析一下,自己也学习一下他们的独门技能!效果图如下:
在这里,计数器、购物篮做成组件用于复用,由于左右联动的功能在另一个模块也有用到,所以就把这个功能也做成了一个组件,传入不通的数据即可,左右联动的实现方法在之前一片博客有写到
https://blog.csdn.net/dongguan_123/article/details/80598107
这篇博客主要讲一下选择商品的功能,以及支持手动输入的功能实现
数量选择器
结构上采用左、中、右结构,加减号分别绑定两个事件,中间输入框绑定获取焦点、失去焦点和change事件,用于支持手动输入数量
作为组件,事件必须传导到最上层,在那里对数据进行处理
methods: {
plusHandle(e) { // 增加事件
let {totalNum, typeOneIndex, typeTwoIndex, goodsIndex, totalStock} = this.data
let goodsId = e.currentTarget.dataset.goodsid;
// let pageX = e.touches[0].pageX;
// let pageY = e.touches[0].pageY;
totalNum++;
if (totalStock === 0) {
wx.showToast({
title: '请增加库存!',
icon: 'none',
duration: 1500
})
return
}
if (totalNum > totalStock) { // 超过库存
wx.showToast({
title: '库存不足,请增加库存!',
icon: 'none',
duration: 1500
})
return
}
this.setData({
totalNum,
change: false
});
let myEventDetail = {
goodsId: goodsId,
// pageX: pageX,
// pageY: pageY,
totalNum: totalNum,
typeOneIndex: typeOneIndex,
typeTwoIndex: typeTwoIndex,
goodsIndex: goodsIndex,
action: 'plus' // 增加事件标记
}; // detail对象,提供给事件监听函数
this.triggerEvent('StepperEvent', myEventDetail) // 触发父级事件
},
focusNum(e) {
// 手动输入时获取基础数据
this.triggerEvent('StepperEvent', {
action: 'getNum',
num: Number(e.detail.value)
});
},
blurNum(e) {
// 失去焦点,改变状态
this.setData({
change: false
});
// 购物篮失去焦点,商品数为0则清空
let myEventDetail = {}
let goodsId = e.currentTarget.dataset.goodsid;
myEventDetail = {
goodsId: goodsId,
action: 'blur'
};
this.triggerEvent('StepperEvent', myEventDetail)
},
changeNum(e) {
let num = Number(e.detail.value)
let {typeOneIndex, typeTwoIndex, goodsIndex, totalStock} = this.data
let goodsId = e.currentTarget.dataset.goodsid;
let myEventDetail = {}
let totalNum = 0;
if (num > totalStock) { // 超过库存
wx.showToast({
title: '库存不足,请增加库存!',
icon: 'none',
duration: 1500
})
this.setData({
totalNum: num,
change: false
});
totalNum = totalStock;
} else {
totalNum = num;
}
myEventDetail = {
goodsId: goodsId,
totalNum: totalNum,
typeOneIndex: typeOneIndex,
typeTwoIndex: typeTwoIndex,
goodsIndex: goodsIndex,
action: 'change'
};
this.setData({
totalNum: totalNum,
change: true
});
this.triggerEvent('StepperEvent', myEventDetail)
},
minusHandle(e) {
let num = this.data.totalNum;
let {typeOneIndex, typeTwoIndex, goodsIndex} = this.data
let goodsId = e.currentTarget.dataset.goodsid;
if (num <= 0) {
return
}
num--;
let myEventDetail = {
goodsId: goodsId,
totalNum: num,
typeOneIndex: typeOneIndex,
typeTwoIndex: typeTwoIndex,
goodsIndex: goodsIndex,
action: 'minus'
};
this.setData({
totalNum: num,
change: false
});
this.triggerEvent('StepperEvent', myEventDetail);
}
}
stemper组件是linkPage(商品左右联动)组件的子组件,需要在linkPage里过渡一下
linkPage.wxml
linkPage.js
stepperEvent(e) {
let myEventDetail = e.detail;
if(myEventDetail.action === 'blur') return
this.triggerEvent('ContentEvent', myEventDetail)
}
购物篮和列表栏的触发事件要有所区分
choose-goods.wxml
最后所有数据汇集到主页面进行处理
choose-goods.js 根据事件标记区分是哪一类事件,进行不同的处理
/*商品列表添加事件*/
contentEvent(e) {
let {totalNum, action, typeOneIndex, typeTwoIndex, goodsIndex} = e.detail;
if (action === 'getNum' || action === 'blur') {
this.setData({ // 获取总基数
basePageTotalNum: this.data.pageTotalNum - e.detail.num
});
return
}
if (action === 'overStock') {
Dialog({
message: '该商品库存不够啦~',
confirmButtonText: '我知道了',
selector: '#tty-dialog-msg'
}).then(() => {});
return
}
this.addGoods(totalNum, action, typeOneIndex, typeTwoIndex, goodsIndex);
},
/*添加商品*/
addGoods(totalNum, action, typeOneIndex, typeTwoIndex, goodsIndex) {
let {typeData, goodsData, pageTotalNum} = this.data;
let goods = typeData[typeOneIndex].goods_category_two[typeTwoIndex].goods[goodsIndex];
typeData[typeOneIndex].goods_category_two[typeTwoIndex].goods[goodsIndex].count = totalNum;
if (action === 'plus') {
pageTotalNum++; // 商品总数
} else if (action === 'minus') {
pageTotalNum--;
} else if (action === 'change') {
pageTotalNum = this.data.basePageTotalNum + totalNum;
}
if (pageTotalNum < 0) {
pageTotalNum = 0;
return
}
goods.count = totalNum;
goods.typeOneIndex = typeOneIndex;
goods.typeTwoIndex = typeTwoIndex;
goods.goodsIndex = goodsIndex;
if (totalNum === 1 && action === 'plus') { // 购物篮里没有,则新增
goodsData.unshift(goods);
} else {
if(goodsData.includes(goods)) { // 如果购物篮中已有商品,在原有基础上加1
let index = goodsData.indexOf(goods)
totalNum === 0 ? goodsData.splice(index, 1) : goodsData[index].count = totalNum;
}else { // 如果购物篮中没有,则添加(针对手动修改)
goodsData.unshift(goods);
}
}
this.setData({
goodsData,
typeData,
pageTotalNum,
basePageTotalNum: pageTotalNum - totalNum
});
this.calculateMoney(goodsData);
}
/*购物框添加事件*/
basketEvent(e) {
if (e.detail.action === 'getNum') {
this.setData({ // 获取计算基数
basePageTotalNum: this.data.pageTotalNum - e.detail.num
});
return
}
let {
totalNum, // 商品选择的数量
goodsId, // 修改商品时对应的商品id
action // 操作行为
} = e.detail;
let {
typeData, // 列表数据
goodsData, // 购物篮数据
pageTotalNum // 页面商品总数
} = this.data;
if (action === 'plus') {
pageTotalNum++;
} else if (action === 'minus') {
pageTotalNum--;
} else if (action === 'change') {
pageTotalNum = this.data.basePageTotalNum + totalNum;
}
if (pageTotalNum < 0) {
pageTotalNum = 0;
}
for (let i = 0; i < goodsData.length; i++) {
if (goodsData[i].goods_id === goodsId) {
// 获取索引,处理列表商品数量
let typeOneIndex = goodsData[i].typeOneIndex;
let typeTwoIndex = goodsData[i].typeTwoIndex;
let goodsIndex = goodsData[i].goodsIndex;
let count = 0;
if (action === 'plus') {
goodsData[i].count++;
count = goodsData[i].count
} else if (action === 'minus') {
goodsData[i].count--;
count = goodsData[i].count
if (goodsData[i].count <= 0) {
goodsData[i].count = 0;
goodsData.splice(i, 1);
}
} else if (action === 'change') {
if(totalNum <= 0) {
goodsData[i].count = 0;
}else {
goodsData[i].count = totalNum;
}
count = goodsData[i].count
// 失去焦点,数量为0,删除
} else if (action === 'blur' && goodsData[i].count === 0) {
count = goodsData[i].count
goodsData.splice(i, 1);
}
typeData[typeOneIndex].goods_category_two[typeTwoIndex].goods[goodsIndex].count = count;
}
}
this.setData({
goodsData,
typeData,
pageTotalNum,
basePageTotalNum: pageTotalNum - totalNum
});
this.calculateMoney(goodsData);// 计算总金额
}
列表栏中添加商品时,购物篮中没有商品,跟随列表栏变化而变化,属于正向计算,比较简单,有则加1,无则新增
反过来,购物篮中修改数据时就麻烦些了,需要拿到对应的商品id,反向查找列表栏中的商品,并做修改
由上面的效果图可以知道,数据的格式是三层嵌套的数组,这就导致数据的查找比较麻烦,必须循环遍历才能找到最底层的数据
"data": [{
goods_category_one_id: "221310651891449856",
goods_category_one_name: "农药",
goods_category_two: [
{
goods_category_two_id: "221310651891449858",
goods_category_two_name: "杀虫剂",
goods: [{
amount: 0,
base_unit_id: "221310649857212419",
base_unit_name: "桶",
count: 0,
goods_id: "258169085668364288",
goods_name: "联苯·虫螨腈",
goods_spec: "",
price: 3.5,
stock: 24,
url: ""
}]
},
{
goods_category_two_id: "221310651891449859",
goods_category_two_name: "杀菌剂",
goods: [{
amount: 0,
base_unit_id: "221310649857212422",
base_unit_name: "克",
count: 0,
goods_id: "258168407638147072",
goods_name: "联苯·虫螨腈",
goods_spec: "",
price: 3.5,
stock: 24,
url: ""
},
{
amount: 0,
base_unit_id: "221310649857212421",
base_unit_name: "克",
count: 0,
goods_id: "259282725129682944",
goods_name: "速克灵100g",
goods_spec: "",
price: 5.6,
stock: 0,
url: ""
},
{
amount: 0,
base_unit_id: "221310649857212420",
base_unit_name: "盒",
count: 0,
goods_id: "259285646445645824",
goods_name: "解决",
goods_spec: "",
price: 56,
stock: 12,
url: ""
}
]
}
]
}
项目地址https://github.com/LandQ123/littleProgressChooseGoods