前言
在电商的核心交易流程中,购物车是其中非常重要的一环,它承担商品加购、价格计算、促销活动展示等功能,与会员系统、商品系统、库存系统、订单系统等紧密结合。
vant-weapp的GoodsAction商品导航api
GoodsAction Props
| 参数 | 说明 | 类型 | 默认值 | 
|---|---|---|---|
| safe-area-inset-bottom | 是否为 iPhoneX 留出底部安全距离 | boolean | true | 
GoodsActionIcon Props
| 参数 | 说明 | 类型 | 默认值 | 
|---|---|---|---|
| text | 按钮文字 | string | - | 
| icon | 图标类型,可选值见icon组件 | string | - | 
| info | 图标右上角提示信息 | string/number | - | 
| url | 点击后跳转的链接地址 | string | - | 
| link-type | 链接跳转类型,可选值为 redirectTo switchTab reLaunch | string | navigateTo | 
| id | 标识符 | string | - | 
| disabled | 是否禁用按钮 | boolean | false | 
| loading | 是否显示为加载状态 | boolean | false | 
| open-type | 微信开放能力,具体支持可参考 微信官方文档 | string | - | 
| app-parameter | 打开 APP 时,向 APP 传递的参数 | string | - | 
| lang | 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文 | string | en | 
| session-from | 会话来源 | string | - | 
| send-message-title | 会话内消息卡片标题 | string | 当前标题 | 
| send-message-path | 会话内消息卡片点击跳转小程序路径 | string | 当前分享路径 | 
| send-message-img | sendMessageImg | string | 截图 | 
| show-message-card | 显示会话内消息卡片 | string | false | 
| class-prefix v1.10.1 | 类名前缀 | string | van-icon | 
GoodsActionButton Props
| 参数 | 说明 | 类型 | 默认值 | 
|---|---|---|---|
| text | 按钮文字 | string | - | 
| color | 按钮颜色,支持传入 linear-gradient 渐变色 | string | - | 
| url | 点击后跳转的链接地址 | string | - | 
| link-type | 链接跳转类型,可选值为 redirectTo switchTab reLaunch | string | navigateTo | 
| id | 标识符 | string | - | 
| type | 按钮类型,可选值为 primary warning danger | string | danger | 
| plain | 是否为朴素按钮 | boolean | false | 
| size | 按钮尺寸,可选值为 normal large small mini | string | normal | 
| disabled | 是否禁用按钮 | boolean | false | 
| loading | 是否显示为加载状态 | boolean | false | 
| open-type | 微信开放能力,具体支持可参考 微信官方文档 | string | - | 
| app-parameter | 打开 APP 时,向 APP 传递的参数 | string | - | 
| lang | 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文 | string | en | 
| session-from | 会话来源 | string | - | 
| send-message-title | 会话内消息卡片标题 | string | 当前标题 | 
| send-message-path | 会话内消息卡片点击跳转小程序路径 | string | 当前分享路径 | 
| send-message-img | sendMessageImg | string | 截图 | 
| show-message-card | 显示会话内消息卡片 | string | false | 
Events
| 事件名 | 说明 | 参数 | 
|---|---|---|
| bind:click | 按钮点击事件回调 | - | 
GoodsActionIcon Slot
| 名称 | 说明 | 
|---|---|
| icon | 自定义图标 | 
GoodsActionButton Slot
| 名称 | 说明 | 
|---|---|
| / | 按钮显示内容 | 
GoodsActionIcon 外部样式类
| 类名 | 说明 | 
|---|---|
| icon-class | 图标样式类 | 
| text-class | 文字样式类 | 
GoodsActionButton 外部样式类
| 类名 | 说明 | 
|---|---|
| custom-class | 根节点样式类 | 
一、商品购物车功能实现
// miniprogram/pages/cart/index.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    showLoginPanel:false,
    cartIdSelectedResult:[],
    allIsSelected:false,
    editMode:false,
    carts:[],
    totalPrice:0
  },
  // 重新计算总价
  calcTotalPrice(){
    let totalPrice = 0
    let ids = this.data.cartIdSelectedResult
    let carts = this.data.carts
    ids.forEach(id=>{
      carts.some(item=>{
        if (item.id == id){
          totalPrice += item.price * item.num 
          return true 
        }
        return false
      })
    })
    this.setData({
      totalPrice
    })
  },
  //改变编辑模式
  changeEditMode(){
    let editMode = !this.data.editMode
    this.setData({
      editMode
    })
  },
  onSelectGoodsItem(e){
    let cartIdSelectedResult = e.detail
    this.setData({
      cartIdSelectedResult,
    });
    this.calcTotalPrice()
  },
  onSelectAll(event) {
    let allIsSelected = event.detail
    let cartIdSelectedResult = this.data.cartIdSelectedResult
    cartIdSelectedResult.length = 0
    if (allIsSelected){
      let carts = this.data.carts
      for(let j=0;j<carts.length;j++){
        cartIdSelectedResult.push(`${carts[j].id}`)
      }
    }
    this.setData({
      allIsSelected,
      cartIdSelectedResult
    });
    this.calcTotalPrice()
  },
  /**
   * 页面显示的时候去加载数据
   */
  async onShow(){
    let res = await getApp().wxp.request4({
      url:'http://localhost:3000/user/my/carts',
      method:'get'
    })
    if (res.data.msg == "ok"){
      let carts = res.data.data 
      this.setData({
        carts
      })
    }
  },
  onCartConfirm(e){
    // 拿到列表数据
    let carts = this.data.carts 
    let cartData = []
    let ids = this.data.cartIdSelectedResult
    if (ids.length == 0){
      wx.showModal({
        title: '未选择商品',
        showCancel: false
      })
      return
    }
    ids.forEach(id=>{
      carts.some(item=>{
        if (item.id == id){
          cartData.push(Object.assign({}, item))
          return true 
        }
        return false
      })
    })
    wx.navigateTo({
      url: `/pages/confirm-order/index`,
      success: function(res) {
        res.eventChannel.emit('cartData', { data: cartData })
      }
    })
  },
  async onCartGoodsNumChanged(e){
    let cartGoodsId = e.currentTarget.dataset.id 
    let oldNum = parseInt( e.currentTarget.dataset.num )
    // console.log('e.detail', typeof e.detail, cartGoodsId, oldNum)
    let num = e.detail
    let data = {num}
    let res = await getApp().wxp.request4({
      url:`http://localhost:3000/user/my/carts/${cartGoodsId}`,
      method:'put',
      data 
    })
    if (res.data.msg == 'ok'){
      wx.showToast({
        title: num > oldNum ? '增加成功' : '减少成功',
      })
      // 修复数据
      let carts = this.data.carts
      carts.some(item=>{
        if (item.id == cartGoodsId){
          item.num = num 
          return true 
        }
        return false
      })
      this.calcTotalPrice()
    }
  },
  async removeCartGoods(e){
    let ids = this.data.cartIdSelectedResult
    if (ids.length == 0){
      wx.showModal({
        title: '没有选择商品',
        showCancel: false
      })
      return 
    }
    let data = {ids}
    let res = await getApp().wxp.request4({
      url:'http://localhost:3000/user/my/carts',
      method:'delete',
      data
    })
    if (res.data.msg == 'ok'){
      let carts = this.data.carts
      for(let j=0;j<ids.length;j++){
        let id = ids[j]
        carts.some((item,index)=>{
          if (item.id == id){
            carts.splice(index,1)
            return true 
          }
          return false 
        })
      }
      this.setData({
        carts
      })
    }
  },
})
{
  "usingComponents": {
    "van-cell": "@vant/weapp/cell/index",
    "van-cell-group": "@vant/weapp/cell-group/index",
    "van-button": "@vant/weapp/button/index",
    "van-card": "@vant/weapp/card/index",
    "van-stepper": "@vant/weapp/stepper/index",
    "van-submit-bar": "@vant/weapp/submit-bar/index",
    "van-checkbox": "@vant/weapp/checkbox/index",
    "van-checkbox-group": "@vant/weapp/checkbox-group/index",
    "LoginPanel": "../../components/login"
  }
}
<!--miniprogram/pages/cart/index.wxml-->
<!-- <text>miniprogram/pages/cart/index.wxml</text> -->
<van-cell-group>
	<van-cell title="购物车" value="" label="" border="{{ false }}">
		<van-button bindtap="changeEditMode" slot="right-icon" plain type="info" size="mini">{{editMode?'完成':'编辑'}}</van-button>
	</van-cell>
</van-cell-group>
<block wx:for="{{carts}}" wx:key="id">
<van-checkbox-group value="{{ cartIdSelectedResult }}" bind:change="onSelectGoodsItem">
	<view class="goods-card-container">
		<view style="width:30px;display:flex;align-items:center;justify-content:center;">
			<van-checkbox icon-size="17px" shape="square" name="{{item.id}}"></van-checkbox>
		</view>
		<view style="flex:1;background:white;">
			<van-card custom-class="goods-card" price="{{item.price*item.num/100}}元" desc="{{item.sku_desc}}" title="{{item.goods_name}}" thumb="{{item.goods_image}}">
				<view slot="footer">
					<van-stepper data-num="{{item.num}}" data-id="{{item.id}}" bind:change="onCartGoodsNumChanged" value="{{item.num}}" step="1" />
				</view>
			</van-card>
		</view>
	</view>
</van-checkbox-group>
</block>
<van-submit-bar bind:submit="onCartConfirm" wx:if="{{!editMode}}" price="{{ totalPrice }}" button-text="提交订单">
	<van-checkbox value="{{ allIsSelected }}" bind:change="onSelectAll" shape="square">全选</van-checkbox>
	<view wx:if="{{false}}" slot="tip">您的收货地址</view>
</van-submit-bar>
<van-cell-group wx:else title="" style="position: fixed;bottom: 0;left: 0;width: 100%;">
	<van-cell>
		<view slot="title">
			<van-checkbox value="{{ allIsSelected }}" bind:change="onSelectAll" shape="square" name="all">
				全选
			</van-checkbox>
		</view>
		<van-button bindtap="removeCartGoods" size="mini" slot="right-icon" plain type="info">删除</van-button>
	</van-cell>
</van-cell-group>
<LoginPanel show="{{showLoginPanel}}"></LoginPanel>
/* miniprogram/pages/cart/index.wxss */
.goods-card{
  background-color: #fefefe !important;
}
.goods-card-container {
  display:flex;margin:10px;background:#fefefe;
}
.goods-card-container + .goods-card-container{
  padding-top: 10px;
}
二、效果










