目前一个商城小程序项目正在进行中,然后有个购物车的功能值得注意下,因为暂时是在构建前端页面上,所以暂时是没有后台部分的,需求如下:
购物车功能示意
首先给大家讲一下思路:
进入本页面的时候,从接口获取购物车数据,然后渲染到页面的商品列表里面,然后根据返回的单价和数量计算总价,然后页面功能主要是根据所选的商品计算总价。差不多就是这些了。
下面我们一步一步的来实现:
一.循环接收接口返回的商品信息(我这里是模拟的接口数据)
/*这里是模拟的接口返回数据*/
orderinfo: [{
imgGood: "../../images/02fenlei_pic1.jpg",//商品图片地址
nameGood: "新鲜圣女果小番茄包邮批发时令 应季摘果小西红柿",//商品名称和描述
npriceGood: 23.9,//商品最新价格
opriceGood: 29.8,//商品以往价格
count: 10,//商品数量
id: 0,//商品id
selected: true,//商品是否为被选中状态,购物车首页默认全选
specifications: "500g"//商品规格
},
{
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斤"
}]
有了模拟的接口数据之后,然后我们开始写wxml页面去接收这些数据
<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>
<!-- 循环的商品列表 -->
<view class='has_shop_list'>
<!-- 循环商品列表 -->
<block wx:key="key{{goods_car_index}}" wx:for="{{goodsCar}}">
<view class='has_shop_item'>
<view class='btn_ischeck'>
<!-- 判断是否为选中状态 -->
<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'>
<!-- 商品数量减少按钮 -->
<image class='btn_sub' src='../../images/jian.png' bindtap='subNum' data-index='{{index}}'></image>
<text class='goods_num'>{{item.count}}</text>
<!-- 增加商品数量按钮 -->
<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>
<!-- 一个都没选择,展示灰色结算按钮 -->
<view class='btn_detele_all' wx:if="{{isSettlement}}">结算</view>
<view class='btn_detele_all_red' wx:if="{{isSettlementRed}}" bindtap='goOrder'>结算</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>
页面接收这些数据,这里下面的删除按钮,是需要点击编辑按钮才会出现的,效果图如下
购物车编辑功能示意
样式部分我也发一下,然后后面再主要讲一下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({
/**
* 页面的初始数据
*/
data: {
carisShow: false, //购物车是否有商品
isChecked: true, //全选状态设置
isEdit: true, //是否编辑状态
isSettlementRed: true, //红色结算按钮状态
isSettlement: false, //红色结算按钮状态
idDeteleRed: false, //红色删除按钮
idDetel: false, //灰色删除按钮
isSelect: false, //是否为编辑状态
goodsCar: []//用来接收接口返回数据
},
/**
* 生命周期函数--监听页面加载
*/
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; //获取全选状态
let isSettlementRed = this.data.isSettlementRed; //获取红色结算按钮的状态
let isSettlement = this.data.isSettlement; //获取灰色结算按钮的状态
isChecked = !isChecked;
isSettlementRed = !isSettlementRed;
isSettlement = !isSettlement;
let list = this.data.goodsCar;
if(this.data.isSelect){
// 设置全选状态
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, //隐藏红色结算
isSettlement: isSettlement, //显示灰色结算
idDeteleRed: true,
idDetel: false
});
}else{
// 设置全选状态
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, //隐藏红色结算
isSettlement: isSettlement, //显示灰色结算
idDeteleRed: false,
idDetel: false
});
}
this.totalPrice();
},
//单选事件
selectShop: function(e) {
let _this = this;
// 获取当前选项的索引
let index = e.currentTarget.dataset.index;
// 获取商品列表
let list = this.data.goodsCar;
// 默认全选
this.data.isChecked = true;
// 操作当前选项
list[index].selected = !list[index].selected;
var isUncheck = true;
// 当前为删除操作状态时
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; //红色结算按钮状态
this.data.isSettlement = false; //灰色结算按钮状态
this.data.idDeteleRed = false; //红色删除按钮
this.data.idDetel = false; //灰色删除按钮
isUncheck = false;
}
}
// 重新渲染数据
this.setData({
goodsCar: list,
isChecked: this.data.isChecked,
isSettlement: isUncheck,
isSettlementRed: !isUncheck
})
}
this.totalPrice();
},
//减少数量
subNum: function(e) {
// 获取点击的索引
const index = e.currentTarget.dataset.index;
// 获取商品数据
let list = this.data.goodsCar;
// 获取商品数量
let num = list[index].count;
// 点击递减
num = num - 1;
list[index].count = num;
console.log(list);
// 重新渲染 ---显示新的数量
this.setData({
goodsCar: list
});
this.totalPrice();
},
//增加数量
addNum: function(e) {
// 获取点击的索引
const index = e.currentTarget.dataset.index;
// 获取商品数据
let list = this.data.goodsCar;
// 获取商品数量
let num = list[index].count;
// 点击递增
num = num + 1;
list[index].count = num;
console.log(list);
// 重新渲染 ---显示新的数量
this.setData({
goodsCar: list
});
this.totalPrice();
},
// 计算金额
totalPrice: function() {
let list = this.data.goodsCar;
let total = 0;
// 循环列表得到每个数据
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: '确认删除这些商品吗',
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
});
// 如果数据为空
if (!list.length) {
_this.setData({
carisShow: true
});
} else {
// 调用金额渲染数据
_this.totalPrice();
}
} else {
console.log(res);
}
}
}
}
})
},
//删除单个商品
deteleGood: function(e) {
var that = this;
// 获取索引
const index = e.currentTarget.dataset.index;
// 获取商品列表数据
let list = this.data.goodsCar;
wx.showModal({
title: '提示',
content: '确认删除吗',
success: function(res) {
if (res.confirm) {
// 删除索引从1
list.splice(index, 1);
// 页面渲染数据
that.setData({
goodsCar: list
});
// 如果数据为空
if (!list.length) {
that.setData({
carisShow: true
});
} else {
// 调用金额渲染数据
that.totalPrice();
}
} else {
console.log(res);
}
},
fail: function(res) {
console.log(res);
}
})
},
// 结算生成订单
goOrder:function(){
let _this = this;
wx.showModal({
title: '提示',
content: '确认生成订单?',
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);
}
}
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function() {
this.data.goodsCar = API.orderinfo;
this.totalPrice();
},
大概就是这些了,页面注释写也比较详细了,有什么问题可以私信我
我这里是将接口返回的数据先存放到全局的一个js里面去进行模拟的,也就是页面最后的
API文件里面的,大家如果不会用这个的话,可以直接将模拟的接口数据放到data里面,效果是一样的,我这样做只是为了方便购物车提交订单后生成订单的时候,可以继续模拟
网友评论