目前一個商城小程序項目正在進行中,然后有個購物車的功能值得注意下,因為暫時是在構(gòu)建前端頁面上,所以暫時是沒有后臺部分的,需求如下:

購物車功能示意
首先給大家講一下思路:
進入本頁面的時候,從接口獲取購物車數(shù)據(jù),然后渲染到頁面的商品列表里面,然后根據(jù)返回的單價和數(shù)量計算總價,然后頁面功能主要是根據(jù)所選的商品計算總價。差不多就是這些了。
下面我們一步一步的來實現(xiàn):
一.循環(huán)接收接口返回的商品信息(我這里是模擬的接口數(shù)據(jù))
/*這里是模擬的接口返回數(shù)據(jù)*/
orderinfo: [{
imgGood: "../../images/02fenlei_pic1.jpg",//商品圖片地址
nameGood: "新鮮圣女果小番茄包郵批發(fā)時令 應(yīng)季摘果小西紅柿",//商品名稱和描述
npriceGood: 23.9,//商品最新價格
opriceGood: 29.8,//商品以往價格
count: 10,//商品數(shù)量
id: 0,//商品id
selected: true,//商品是否為被選中狀態(tài),購物車首頁默認(rèn)全選
specifications: "500g"http://商品規(guī)格
},
{
imgGood: "../../images/02fenlei_pic2.jpg",
nameGood: "水果胡蘿卜3袋 新鮮枝純小 紅蘿卜甜脆",
npriceGood: 9.9,
opriceGood: 19.8,
count: 5,
id: 1,
selected: true,
specifications: "3袋"
},
{
imgGood: "../../images/02fenlei_pic3.jpg",
nameGood: "貝貝南瓜板栗味小南瓜糯面 粉日本南瓜新鮮10斤裝",
npriceGood: 36.9,
opriceGood: 39.9,
count: 3,
id: 2,
selected: true,
specifications: "10斤"
}]
有了模擬的接口數(shù)據(jù)之后,然后我們開始寫wxml頁面去接收這些數(shù)據(jù)
<view class="container">
<view class='no_shop' wx:if="{{carisShow}}">
<view class='no_shop_only'>
<image class='shop_show_only' src='../../images/03gouwuche_kong.png'></image>
<text class='on_shop_txt'>購物車空空如也</text>
<navigator url="/pages/index/index" open-type='switchTab' hover-class="none">
<view class='btn_return'>
<text class='txt_btn_return'>去首頁逛逛吧</text>
</view>
</navigator>
</view>
</view>
<view class='has_shop' wx:else>
<!-- 第一排 -->
<view class='has_shop_title'>
<view class='position_title'>
<view class='has_shop_circleunchecked' wx:if="{{!isChecked}}" bindtap='checkAll'></view>
<view class='position_shop_circlechecked' wx:else bindtap='checkAll'>
<image class='has_shop_circlechecked1' src='../../images/03gouwuche_gou.png'></image>
</view>
<image class='has_shop_icon1' src='../../images/03gouwuche_icon_dianpu.png'></image>
<text class='has_shop_smalltxt'>長智超市(配送/自提)</text>
</view>
<text class='btn_shop_change' wx:if="{{isEdit}}" bindtap='editGood'>編輯</text>
<text class='btn_shop_change' wx:else bindtap='editComplete'>完成</text>
</view>
<!-- 循環(huán)的商品列表 -->
<view class='has_shop_list'>
<!-- 循環(huán)商品列表 -->
<block wx:key="key{{goods_car_index}}" wx:for="{{goodsCar}}">
<view class='has_shop_item'>
<view class='btn_ischeck'>
<!-- 判斷是否為選中狀態(tài) -->
<view class='img_icon_ischeck' wx:if="{{!item.selected}}" bindtap='selectShop' data-index='{{index}}'></view>
<view class='position_shop_circlechecked' wx:else data-index='{{index}}' bindtap='selectShop'>
<image class='has_shop_circlechecked1' src='../../images/03gouwuche_gou.png'></image>
</view>
<view class='position_hasshop_item'>
<view class='position_hasshop_img'>
<!-- 商品圖片 -->
<image class='shop_img' src='{{item.imgGood}}'></image>
<image class='icon_vip' src='../../images/vip.png'></image>
</view>
<view class='menu_right_txt'>
<!-- 商品名稱和描述 -->
<text class='menu_right_name'>{{item.nameGood}}</text>
<view class='menu_right_down'>
<!-- 商品最新價格 -->
<text class='menu_right_nprice'>¥{{item.npriceGood}}</text>
<!-- 商品以往價格 -->
<text class='menu_right_oprice'>{{item.opriceGood}}</text>
<view class='has_shop_num'>
<!-- 商品數(shù)量減少按鈕 -->
<image class='btn_sub' src='../../images/jian.png' bindtap='subNum' data-index='{{index}}'></image>
<text class='goods_num'>{{item.count}}</text>
<!-- 增加商品數(shù)量按鈕 -->
<image class='btn_add' src='../../images/jia.png' bindtap='addNum' data-index='{{index}}'></image>
</view>
</view>
</view>
<view class='btn_delete_shop' wx:if="{{!isEdit}}" bindtap='deteleGood' data-index='{{index}}'>刪除</view>
</view>
</view>
</view>
</block>
</view>
<!-- 底部選擇欄 -->
<view class='shop_car_total'>
<view class='car_total_left'>
<view class='has_shop_circleunchecked' wx:if="{{!isChecked}}" bindtap='checkAll'></view>
<image class='has_shop_circlechecked' src='../../images/03gouwuche_gou.png' wx:else bindtap='checkAll'></image>
<text class='total_txt'>全選</text>
</view>
<view class='shop_total_right'>
<view class='shop_total_freight'>
<view class='position_total'>
<text class='total_name'>合計:</text>
<text class='total_name_num'>¥{{totalPrice}}</text>
</view>
<view class='position_total_freight'>
<text class='total_freight'>不含運費</text>
</view>
</view>
<!-- 一個都沒選擇,展示灰色結(jié)算按鈕 -->
<view class='btn_detele_all' wx:if="{{isSettlement}}">結(jié)算</view>
<view class='btn_detele_all_red' wx:if="{{isSettlementRed}}" bindtap='goOrder'>結(jié)算</view>
<view class='btn_detele_all_red' wx:if="{{idDeteleRed}}" bindtap='deteleMore'>刪除</view>
<view class='btn_detele_all' wx:if="{{idDetel}}">刪除</view>
</view>
</view>
</view>
</view>
頁面接收這些數(shù)據(jù),這里下面的刪除按鈕,是需要點擊編輯按鈕才會出現(xiàn)的,效果圖如下

購物車編輯功能示意
樣式部分我也發(fā)一下,然后后面再主要講一下js文件
wxss:
.container{
background:rgba(248,248,248,1);
}
.no_shop{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
margin-top: 50%;
}
.no_shop_only{
width: 380rpx;
height: 333rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.shop_show_only{
width: 380rpx;
height: 191rpx;
}
.on_shop_txt{
font-size:28rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(127,131,137,1);
line-height:40rpx;
margin-top: 18rpx;
}
.btn_return{
width: 250rpx;
height: 60rpx;
margin-top: 24rpx;
border-radius:30rpx;
border:1px solid rgba(214,70,60,1);
display: flex;
justify-content: center;
align-items: center;
}
.txt_btn_return{
font-size:30rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(214,70,60,1);
line-height:42px;
}
.has_shop{
width: 100%;
min-height: 100%;
display: flex;
flex-direction: column;
background:rgba(255,255,255,1);
}
.has_shop_title{
position: fixed;
width: 100%;
height: 100rpx;
display: flex;
align-items: center;
border-bottom: 3rpx solid rgba(248,248,248,1);
z-index: 99;
}
.position_title{
width: 650rpx;
height: 100rpx;
display: flex;
flex-direction: row;
align-items: center;
}
.has_shop_circleunchecked{
width:29rpx;
height:29rpx;
border:1rpx solid rgba(151,151,151,1);
border-radius: 50%;
margin: 0 30rpx 0 30rpx;
}
.has_shop_circlechecked1{
width:32rpx;
height:32rpx;
}
.has_shop_circlechecked{
width:32rpx;
height:32rpx;
margin: 0 30rpx 0 30rpx;
}
.has_shop_icon1{
width: 32rpx;
height: 29rpx;
}
.has_shop_smalltxt{
font-size:28rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(51,51,51,1);
line-height:40rpx;
}
.btn_shop_change{
font-size:24rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(85,141,247,1);
line-height:33rpx;
}
.has_shop_list{
width: 100%;
min-height: 100%;
margin-top: 100rpx;
display: flex;
flex-direction: column;
}
.has_shop_item{
width: 100%;
height: 230rpx;
border-bottom: 3rpx solid rgba(248,248,248,1);
display: flex;
align-items: center;
justify-content: center;
}
.btn_ischeck{
width: 100%;
display: flex;
height: 100%;
align-items: center;
}
.img_icon_ischeck{
width:29rpx;
height:29rpx;
border:1rpx solid rgba(151,151,151,1);
border-radius: 50%;
margin: 0 30rpx 0 30rpx;
}
.shop_img{
width: 200rpx;
height: 200rpx;
}
.icon_vip{
position: absolute;
width: 38rpx;
height: 30rpx;
top: 115rpx;
left: 92rpx;
}
.menu_right_txt{
flex: 1;
height: 200rpx;
padding-top: 15rpx;
line-height:30rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
margin-right: 40rpx;
}
.menu_right_name{
font-size:28rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(51,51,51,1);
}
.menu_right_lable{
width: 50rpx;
height: 50rpx;
background:rgba(214,70,60,1);
border-radius: 25rpx;
font-size:20rpx;
color:rgba(255,255,255,1);
text-align: center;
line-height: 50rpx;
}
.menu_right_down{
display: flex;
align-items: center;
line-height:42rpx;
justify-content: space-between;
}
.menu_right_nprice{
font-size:30rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(214,70,60,1);
}
.menu_right_oprice{
font-size:20rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(185,189,185,1);
/* margin-left: 22rpx; */
text-decoration:line-through;
padding-top: 14rpx;
}
.menu_right_shopcar{
width: 38rpx;
height: 38rpx;
float: right;
}
.position_hasshop_item{
width: 673rpx;
display: flex;
flex-direction: row;
}
.btn_delete_shop{
top: 100rpx;
width: 100rpx;
height: 230rpx;
background:rgba(235,84,77,1);
font-size:24rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(255,255,255,1);
display: flex;
justify-content: center;
align-items: center;
line-height:33rpx;
}
.position_hasshop_img{
width: 200rpx;
height: 200rpx;
padding: 15rpx;
}
.has_shop_num{
display: flex;
flex-direction: row;
align-items: center;
}
.btn_sub{
width: 28rpx;
height: 28rpx;
}
.btn_add{
width: 28rpx;
height: 28rpx;
}
.goods_num{
font-size:32rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(51,51,51,1);
margin: 0 16rpx 0 16rpx;
}
.shop_car_total{
width: 100%;
height: 100rpx;
position: fixed;
bottom: 0;
display: flex;
align-items: center;
justify-content: space-between;
background:rgba(255,255,255,1);
}
.car_total_left{
display: flex;
align-items: center;
height: 100rpx;
}
.total_txt{
font-size:28rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(51,51,51,1);
line-height:40rpx;
}
.shop_total_right{
height: 100rpx;
display: flex;
justify-content: flex-end;
}
.btn_detele_all{
width: 220rpx;
height: 100rpx;
background:rgba(229,229,229,1);
display: flex;
justify-content: center;
align-items: center;
font-size:30rpx;
font-family:PingFangSC-Medium;
font-weight:500;
color:rgba(153,153,153,1);
line-height:42rpx;
}
.shop_total_freight{
height: 100rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
.total_name{
font-size: 28rpx;
color:rgba(51,51,51,1);
}
.total_name_num{
font-size:28rpx;
font-family:PingFangSC-Medium;
font-weight:500;
line-height:40rpx;
color: #D6463C;
}
.total_freight{
font-size:24rpx;
font-family:PingFangSC-Regular;
font-weight:400;
color:rgba(153,153,153,1);
line-height:33rpx;
}
.position_total{
display: flex;
flex-direction: row;
}
.position_total_freight{
display: flex;
}
.shop_total_freight{
margin-right: 30rpx;
}
.btn_detele_all_red{
width: 220rpx;
height: 100rpx;
background:rgba(235,84,77,1);
display: flex;
justify-content: center;
align-items: center;
font-size:30rpx;
font-family:PingFangSC-Medium;
font-weight:500;
color:rgba(255,255,255,1);
line-height:42rpx;
}
.position_shop_circlechecked{
width: 32rpx;
height: 32rpx;
margin:0 30rpx 0 30rpx;
}
樣式這里我就不多講了,主要來說一下js部分哈
js:
// pages/shopcar/shopcar.js
import { API } from '../../API/API.js';
Page({
/**
* 頁面的初始數(shù)據(jù)
*/
data: {
carisShow: false, //購物車是否有商品
isChecked: true, //全選狀態(tài)設(shè)置
isEdit: true, //是否編輯狀態(tài)
isSettlementRed: true, //紅色結(jié)算按鈕狀態(tài)
isSettlement: false, //紅色結(jié)算按鈕狀態(tài)
idDeteleRed: false, //紅色刪除按鈕
idDetel: false, //灰色刪除按鈕
isSelect: false, //是否為編輯狀態(tài)
goodsCar: []//用來接收接口返回數(shù)據(jù)
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面加載
*/
onLoad: function(options) {
},
// 編輯事件
editGood: function() {
this.setData({
isEdit: false,
isSelect: true,
isSettlementRed: false,
isSettlement: false,
idDeteleRed: true,
idDetel: false
});
},
// 完成事件
editComplete: function() {
this.setData({
isEdit: true,
isSettlementRed: true,
isSettlement: false,
idDeteleRed: false,
idDetel: false
});
},
// 全選事件
checkAll: function() {
let isChecked = this.data.isChecked; //獲取全選狀態(tài)
let isSettlementRed = this.data.isSettlementRed; //獲取紅色結(jié)算按鈕的狀態(tài)
let isSettlement = this.data.isSettlement; //獲取灰色結(jié)算按鈕的狀態(tài)
isChecked = !isChecked;
isSettlementRed = !isSettlementRed;
isSettlement = !isSettlement;
let list = this.data.goodsCar;
if(this.data.isSelect){
// 設(shè)置全選狀態(tài)
for (let i = 0; i < list.length; i++) {
list[i].selected = isChecked;
// 判斷是否全選中
if (list[i].selected) {
console.log(1)
this.data.isChecked = false;
isSettlementRed = false;
isSettlement = false;
}
}
this.setData({
isChecked: isChecked,
goodsCar: list,
isSettlementRed: isSettlementRed, //隱藏紅色結(jié)算
isSettlement: isSettlement, //顯示灰色結(jié)算
idDeteleRed: true,
idDetel: false
});
}else{
// 設(shè)置全選狀態(tài)
for (let i = 0; i < list.length; i++) {
list[i].selected = isChecked;
// 判斷是否全選中
if (list[i].selected) {
console.log(1)
this.data.isChecked = false;
isSettlementRed = true;
isSettlement = false;
}
}
this.setData({
isChecked: isChecked,
goodsCar: list,
isSettlementRed: isSettlementRed, //隱藏紅色結(jié)算
isSettlement: isSettlement, //顯示灰色結(jié)算
idDeteleRed: false,
idDetel: false
});
}
this.totalPrice();
},
//單選事件
selectShop: function(e) {
let _this = this;
// 獲取當(dāng)前選項的索引
let index = e.currentTarget.dataset.index;
// 獲取商品列表
let list = this.data.goodsCar;
// 默認(rèn)全選
this.data.isChecked = true;
// 操作當(dāng)前選項
list[index].selected = !list[index].selected;
var isUncheck = true;
// 當(dāng)前為刪除操作狀態(tài)時
if (this.data.isSelect){
for (var i = list.length - 1; i >= 0; i--) {
// 判斷是否全選中
if (!list[i].selected) {
this.data.isChecked = false;
}
//判斷是否全沒選
else if (list[i].selected) {
isUncheck = false;
}
}
this.setData({
goodsCar: list,
isChecked: false,
isSettlement: false,
isSettlementRed: false,
idDeteleRed: !isUncheck,
idDetel: isUncheck
})
}else{
for (var i = list.length - 1; i >= 0; i--) {
// 判斷是否全選中
if (!list[i].selected) {
this.data.isChecked = false;
}
//判斷是否全沒選
else if (list[i].selected) {
this.data.isSettlementRed = true; //紅色結(jié)算按鈕狀態(tài)
this.data.isSettlement = false; //灰色結(jié)算按鈕狀態(tài)
this.data.idDeteleRed = false; //紅色刪除按鈕
this.data.idDetel = false; //灰色刪除按鈕
isUncheck = false;
}
}
// 重新渲染數(shù)據(jù)
this.setData({
goodsCar: list,
isChecked: this.data.isChecked,
isSettlement: isUncheck,
isSettlementRed: !isUncheck
})
}
this.totalPrice();
},
//減少數(shù)量
subNum: function(e) {
// 獲取點擊的索引
const index = e.currentTarget.dataset.index;
// 獲取商品數(shù)據(jù)
let list = this.data.goodsCar;
// 獲取商品數(shù)量
let num = list[index].count;
// 點擊遞減
num = num - 1;
list[index].count = num;
console.log(list);
// 重新渲染 ---顯示新的數(shù)量
this.setData({
goodsCar: list
});
this.totalPrice();
},
//增加數(shù)量
addNum: function(e) {
// 獲取點擊的索引
const index = e.currentTarget.dataset.index;
// 獲取商品數(shù)據(jù)
let list = this.data.goodsCar;
// 獲取商品數(shù)量
let num = list[index].count;
// 點擊遞增
num = num + 1;
list[index].count = num;
console.log(list);
// 重新渲染 ---顯示新的數(shù)量
this.setData({
goodsCar: list
});
this.totalPrice();
},
// 計算金額
totalPrice: function() {
let list = this.data.goodsCar;
let total = 0;
// 循環(huán)列表得到每個數(shù)據(jù)
for (let i = 0; i < list.length; i++) {
// 判斷選中計算價格
if (list[i].selected) {
// 所有價格加起來 count_money
total += list[i].count * list[i].npriceGood;
}
}
// 最后賦值到data中渲染到頁面
this.setData({
goodsCar: list,
totalPrice: total.toFixed(2)
});
},
// 批量刪除
deteleMore: function() {
var _this = this;
let list = this.data.goodsCar;
wx.showModal({
title: '提示',
content: '確認(rèn)刪除這些商品嗎',
success: function(res) {
if (res.confirm) {
for (let i = list.length-1; i >= 0; i--) {
if (list[i].selected) {
list.splice(i, 1);
_this.setData({
goodsCar: list
});
// 如果數(shù)據(jù)為空
if (!list.length) {
_this.setData({
carisShow: true
});
} else {
// 調(diào)用金額渲染數(shù)據(jù)
_this.totalPrice();
}
} else {
console.log(res);
}
}
}
}
})
},
//刪除單個商品
deteleGood: function(e) {
var that = this;
// 獲取索引
const index = e.currentTarget.dataset.index;
// 獲取商品列表數(shù)據(jù)
let list = this.data.goodsCar;
wx.showModal({
title: '提示',
content: '確認(rèn)刪除嗎',
success: function(res) {
if (res.confirm) {
// 刪除索引從1
list.splice(index, 1);
// 頁面渲染數(shù)據(jù)
that.setData({
goodsCar: list
});
// 如果數(shù)據(jù)為空
if (!list.length) {
that.setData({
carisShow: true
});
} else {
// 調(diào)用金額渲染數(shù)據(jù)
that.totalPrice();
}
} else {
console.log(res);
}
},
fail: function(res) {
console.log(res);
}
})
},
// 結(jié)算生成訂單
goOrder:function(){
let _this = this;
wx.showModal({
title: '提示',
content: '確認(rèn)生成訂單?',
success: function(res){
if(res.confirm){
// 攜帶訂單信息生成訂單
let list = _this.data.goodsCar;
let nlist = [];
for(let i=0;i<list.length;i++){
if(list[i].selected){
nlist.push(list[i]);
}
}
API.orderinfo = nlist;//將訂單的信息傳給API.js
wx.navigateTo({
url: '../order/order'
})
}else{
console.log(res);
}
}
})
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面初次渲染完成
*/
onReady: function() {
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面顯示
*/
onShow: function() {
this.data.goodsCar = API.orderinfo;
this.totalPrice();
},
大概就是這些了,頁面注釋寫也比較詳細(xì)了,有什么問題可以私信我
我這里是將接口返回的數(shù)據(jù)先存放到全局的一個js里面去進行模擬的,也就是頁面最后的
API文件里面的,大家如果不會用這個的話,可以直接將模擬的接口數(shù)據(jù)放到data里面,效果是一樣的,我這樣做只是為了方便購物車提交訂單后生成訂單的時候,可以繼續(xù)模擬