小程序购物车

2019-05-06  本文已影响0人  Domino_2018

目前一个商城小程序项目正在进行中,然后有个购物车的功能值得注意下,因为暂时是在构建前端页面上,所以暂时是没有后台部分的,需求如下:


购物车功能示意

首先给大家讲一下思路:
进入本页面的时候,从接口获取购物车数据,然后渲染到页面的商品列表里面,然后根据返回的单价和数量计算总价,然后页面功能主要是根据所选的商品计算总价。差不多就是这些了。
下面我们一步一步的来实现:
一.循环接收接口返回的商品信息(我这里是模拟的接口数据)

/*这里是模拟的接口返回数据*/
  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里面,效果是一样的,我这样做只是为了方便购物车提交订单后生成订单的时候,可以继续模拟

上一篇下一篇

猜你喜欢

热点阅读