• 微信小程序 ---- 慕尚花坊 购物车


    购物车

    01. 购物车-封装购物车接口 API

    思路分析:

    为了方便后续进行购物车模块的开发,我们在这一节将购物车所有的接口封装成接口 API 函数

    落地代码:

    import http from '../utils/http'
    
    /**
     * @description 获取购物车列表数据
     * @returns Promise
     */
    export const reqCartList = () => {
      return http.get('/mall-api/cart/getCartList')
    }
    
    /**
     * @description 加入购物车
     * @param {*} data
     * @returns Promise
     */
    export const reqAddCart = (data) => {
      return http.get(`/cart/addToCart/${data.goodsId}/${data.count}`, data)
    }
    
    /**
     * @description 更新商品的选中状态
     * @param {*} goodsId 商品 id
     * @param {*} isChecked 商品的选中状态
     * @returns Promise
     */
    export const reqUpdateChecked = (goodsId, isChecked) => {
      return http.get(`/cart/checkCart/${goodsId}/${isChecked}`)
    }
    
    /**
     * @description 全选和全不选
     * @param {*} isChecked 商品的选中状态
     * @returns Promise
     */
    export const reqCheckAllCart = (isChecked) => {
      return http.get(`/cart/checkAllCart/${isChecked}`)
    }
    
    /**
     * @description 删除购物车商品
     * @param {*} goodsId 商品 id
     * @returns Promise
     */
    export const reqDelCart = (goodsId) => {
      return http.get(`/cart/delete/${goodsId}`)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    02. 加入购物车-模板分析和渲染

    业务介绍

    点击加入购物车和立即购买的时候,展示购物弹框,在弹框中需要用户选择购买数量和祝福语

    点击加入购物车和立即购买,触发的是同一个弹框。

    因此点击弹框中的确定按钮时,我们需要区分当前是加入购物车操作还是立即购买操作。

    这时候定义一个状态 buyNow 做区分,buyNow 等于 1 代表是立即购买,否则是加入购物车

    产品需求

    1. 如果点击的是加入购物车,需要将当前商品加入到购物车

    2. 如果点击的是立即购买,需要跳转到结算支付页面,立即购买该商品

    3. 如果是立即购买,不支持购买多个商品

    结构分析

    点击立即购买和加入购物车的时候,通过 show 属性,控制弹框的隐藏和展示

    
    <van-goods-action>
      
    +   <van-goods-action-button text="加入购物车" type="warning" bindtap="handleAddcart" />
    +   <van-goods-action-button text="立即购买" bindtap="handeGotoBuy" />
    van-goods-action>
    
    
    
    
    <van-action-sheet show="{{ show }}" bind:close="onClose">
      <view class="sheet-wrapper">
          
        
          
        
    +     <view class="buy-btn" wx:if="{{ buyNow === 0 }}">
          
          <van-stepper value="{{ count }}" bind:change="onChangeGoodsCount" />
        view>
          
        
      view>
    van-action-sheet>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    点击立即购买和加入购物车的时候,通过 buyNow 属性,来区分是进行的某种操作

    Page({
     
      // 页面的初始数据
      data: {
        goodsInfo: {}, // 商品详情
        show: false, // 加入购物车和立即购买时显示的弹框
        count: 1, // 商品购买数量,默认是 1
        blessing: '', // 祝福语
    +     buyNow: '' // 是否立即购买
      },
      
    
      // 加入购物车
      handleAddcart() {
        this.setData({
          show: true,
    +       buyNow: 0
        })
      },
    
      // 立即购买
      handeGotoBuy() {
        this.setData({
          show: true,
    +       buyNow: 1
        })
      },
    
        
      // 代码略...
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    03. 加入购物车-关联 Store 对象

    思路分析:

    当用户点击加入购物车 或者 立即购买时,需要判断用户是否进行了登录。

    我们需要使用 Token 进行判断,因此需要让页面和 Store 对象建立关联。

    这时候可以使用 BehaviorWithStore 让页面 和 Store 对象建立关联。

    落地代码:

    ➡️ /behaviors/userBehavior.js

    // 导入 BehaviorWithStore 让页面和 Store 对象建立关联
    import { BehaviorWithStore } from 'mobx-miniprogram-bindings'
    // 导入用户 Store
    import { userStore } from '@/stores/userstore'
    
    export const userBehavior = BehaviorWithStore({
      storeBindings: {
        store: userStore,
        fields: ['token']
      }
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ➡️ /behaviors/userBehavior.js

    import { reqGoodsInfo } from '@/api/goods'
    import { reqAddCart } from '@/api/cart'
    + import { userBehavior } from '@/behaviors/userBehavior'
    
    Page({
    +   behaviors: [userBehavior],
      
      // 代码略...
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    04. 加入购物车和立即购买区分处理

    思路分析:

    点击加入购物车以及立即购买以后,需要先判断是否进行了登录,如果用户没有登录过,需要先跳转到登录页面进行登录。

    如果点击的是 加入购物车,我们只需要调用 加入购物车 接口即可 (需要获取商品的 ID 、购买数量、祝福语)

    如果点击的是 立即购买,我们需要携带参数跳转到商品结算页面 (获取商品的 ID 以及 祝福语跳转到结算页面)

    购买数量的限制有 4 个限制,这 4 个限制直接使用 Vant 组件提供的属性进行限制即可:

    1. 必须是正整数,最小是1,最大是200
    2. 若输入小于1,则重置为1
    3. 若输入大于200,则重置为200
    4. 若输入的是其他值,则重置为1

    实现步骤:

    1. Stepper 步进器组件,通过value设置输入值,同时绑定change事件,并将值同步到 data
    2. 根据接口文档,导入封装的购物车的接口 API
    3. 点击弹框按钮的时候,判断点击的加入购物车还是立即购买,执行不同的操作

    落地代码:

    ➡️ /modules/goodsModule/pages/detail/detail.html

    <van-stepper
      value="{{ count }}"
    +  integer
    +  min="1"
    +  max="200"
      bind:change="onChangeGoodsCount"
    />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ➡️ /modules/goodsModule/pages/detail/detail.js

    // 监听是否更改了购买数量
    onChangeGoodsCount(event) {
      // 将最新的购买数量同步到 data
      this.setData({
        count: Number(event.detail)
      })
    },
    
    // 弹框的确定按钮
    async handleSubmit() {
     // 解构获取数据
     const { token, count, blessing, buyNow } = this.data
     const goodsId = this.goodsId
      
      // 如果没有 token ,让用户新登录
      if (!this.data.token) {
        wx.navigateTo({
          url: '/pages/login/login'
        })
          
        return
      }
    
      // 将用户输入的值转成 Number 类型
      const count = Number(event.detail)
      // 验证购买数量的正则
      const reg = /^([1-9]|[1-9]\d|1\d{2}|200)$/
      // 使用正则验证
      const res = reg.test(count)
    
      // 如果验证没有通过,直接返回,不执行后续的逻辑
      if (!res) return
     
    
      // 加入购物车
      if (buyNow === 0) {
        // 加入购物车
        const res = await reqAddGood({ goodsId, count, blessing })
    
        if (res.code === 200) {
            
          wx.showToast({
            title: '加入购物车成功'
          })
          
          this.setData({
            show: false
          })
            
        }
      } else {
        // 立即购买
        wx.navigateTo({
          url: `/pages/order/detail/index?goodsId=${goodsId}&blessing=${blessing}`
        })
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    05. 加入购物车-展示购物车购买数量

    思路分析

    判断用户是否进行了登录。

    如果没有登录过,则不展示购物车商品的数量。

    如果用户登录过,则需要展示购物车商品的数量,则获取购物车列表数据,通过累加计算得出商品购买数量

    实现步骤

    1. 进入商品详情,调用方法,在方法中判断token是否存在
    2. 如何存在,则获取购物车列表数据,通过累加计算得出商品购买数量,展示购买的数量
    3. 不存在,不执行任何逻辑,

    落地代码

    ➡️ /modules/goodsModule/pages/detail/detail.js

    Page({
        
      data: {
        // coding...
    +     allCount: '' // 购物车商品总数量
      },
        
          // 弹框的确定按钮
      async handleSubmit() {
        // 如果没有 token ,让用户新登录
        if (!this.data.token) {
          wx.navigateTo({
            url: '/pages/login/login'
          })
    
          return
        }
    
        // 解构获取数据
        const { count, blessing, allCount } = this.data
        const goodsId = this.goodsId
    
        // 加入购物车
        if (this.data.buyNow === 0) {
          // 加入购物车
          const res = await reqAddCart({ goodsId, count, blessing })
    
          if (res.code === 200) {
            wx.toast({
              title: '加入购物车成功',
              icon: 'success',
              mask: false
            })
    
    +         // 购物车购买数量合计
    +         this.getCartCount()
    
            this.setData({
              show: false
            })
          }
        } else {
          // 立即购买
          wx.navigateTo({
            url: `/pages/order/detail/detail?goodsId=${goodsId}&blessing=${blessing}`
          })
        }
      },
      
    +   // 计算购买数量
    +   async getCartCount() {
    +     // 如果没有 token ,说明用户是第一次访问小程序,没有进行登录过
    +     if (!this.data.token) return
    + 
    +     // 获取购物的商品
    +     const res = await reqCartList()
    + 
    +     if (res.data.length !== 0) {
    +       // 购物车商品累加
    +       let allCount = 0
    + 
    +       // 获取购物车商品数量
    +       res.data.forEach((item) => {
    +         allCount += item.count
    +       })
    + 
    +       // 将购物车购买数量赋值
    +       this.setData({
    +         // 展示的数据要求是字符串
    +         allCount: (allCount > 99 ? '99+' : allCount) + ''
    +       })
    +     }
    +   },
    
      onLoad(options) {
        // 接收传递的商品 ID,并且将 商品 ID 挂载到 this 上面
        this.goodsId = options.goodsId
    
        // 调用获取商品详情数据的方法
        this.getGoodsInfo()
    
    +     // 计算购买数量
    +     this.getCartCount()
      }
        
      // coding...
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    06. 购物车-购物车关联 Store 对象

    思路分析:

    当用户进入购物车页面时时,需要判断用户是否进行了登录来控制页面的展示效果

    这时候我们就需要使用 Token 进行判断,因此需要让页面和 Store 对象建立关联。

    因为购物车页面采用的 Component 方法进行构建

    这时候可以使用 ComponentWithStore 让页面 和 Store 对象建立关联。

    落地代码:

    ➡️/pages/cart/components/cart.js

    + import { ComponentWithStore } from 'mobx-miniprogram-bindings'
    + import { userStore } from '@/stores/userstore'
    + import { reqCartList } from '@/api/cart'
    
    + ComponentWithStore({
    +   storeBindings: {
    +     store: userStore,
    +     fields: ['token']
    +   },
    
      // 组件的初始数据
      data: {
        cartList: [],
        emptyDes: '还没有添加商品,快去添加吧~'
      },
    
    +   // 组件的方法列表
    +   methods: {
    +     // 处理页面的展示
    +     async showTipList() {
    +       // 将 token 进行解构
    +       const { token } = this.data
    +
    + 		console.log(token)
    +     },
    
        onShow() {
    +       this.showTipList()
        }
      }
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    07. 购物车-获取并渲染购物车列表

    思路分析:

    1. 如果没有进行登录,购物车页面需要展示文案:您尚未登录,点击登录获取更多权益

    2. 如果用户进行登录,获取购物车列表数据

      • 购物车没有商品,展示文案: 还没有添加商品,快去添加吧~

      • 购物车列表有数据,需要使用数据对页面进行渲染

    实现步骤:

    1. 导入封装好的获取列表数据的 API 函数
    2. onShow 钩子中,根据产品的需求,处理页面的提示
    3. 在获取到数据以后,使用后端返回的数据对页面进行渲染

    落地代码:

    ➡️/pages/cart/cart.js

    import { ComponentWithStore } from 'mobx-miniprogram-bindings'
    import { userStore } from '@/stores/userstore'
    import { reqCartList } from '@/api/cart'
    
    ComponentWithStore({
      storeBindings: {
        store: userStore,
        fields: ['token']
      },
    
      // 组件的初始数据
      data: {
        cartList: [],
        emptyDes: '还没有添加商品,快去添加吧~'
      },
    
      // 组件的方法列表
      methods: {
    +     // 获取购物车列表数据 + 处理页面的展示
    +     async showTipGetList() {
    +       // 将 token 进行解构
    +       const { token } = this.data
    + 
    +       // 1. 如果没有登录,购物车列表,展示文案:您尚未登录,点击登录获取更多权益
    +       if (!token) {
    +         this.setData({
    +           emptyDes: '您尚未登录,点击登录获取更多权益',
    +           cartList: []
    +         })
    + 
    +         return
    +       }
    + 
    +       // 获取商品列表数据
    +       const { data: cartList, code } = await reqCartList()
    + 
    +       if (code === 200) {
    +         // 2. 如果用户登录,购物车列表为空,展示文案: 还没有添加商品,快去添加吧~
    +         this.setData({
    +           cartList,
    +           emptyDes: cartList === 0 && '还没有添加商品,快去添加吧~'
    +         })
    +       }
    +     },
    
        // 页面展示时触发
        onShow() {
    +       this.showTipGetList()
        }
      }
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    ➡️/pages/cart/components/cart.wxml

    <view>
      <view
        wx:if="{{ token && cartList.length }}"
        class="container goods-wrap"
        bindtap="onSwipeCellPageTap"
      >
        <view class="cart-wrap">
    +       <view class="goods-item" wx:for="{{ cartList }}" wx:key="id">
            <van-swipe-cell class="goods-swipe" right-width="{{ 65 }}">
              <view class="goods-info">
                <view class="left">
                  <van-checkbox
                    checked-color="#FA4126"
    +                 value="{{ item.checked }}"
                  >van-checkbox>
                view>
                <view class="mid">
    +               <image class="img" src="{{ item.imageUrl }}" />
                view>
                <view class="right">
    +               <view class="title"> {{ item.name }} view>
                  <view class="buy">
                    <view class="price">
                      <view class="symbol">¥view>
    +                   <view class="num">{{ item.price }}view>
                    view>
                    <view class="buy-btn">
    +                   <van-stepper value="{{ item.count }}" />
                    view>
                  view>
                view>
              view>
              <view slot="right" class="van-swipe-cell__right">删除view>
            van-swipe-cell>
          view>
        view>
    
        
        <van-submit-bar price="{{ 3050 }}" button-text="去结算" tip="{{ true }}">
          <van-checkbox value="{{ true }}" checked-color="#FA4126"> 全选 van-checkbox>
        van-submit-bar>
      view>
    
      <van-empty wx:else description="{{ emptyDes }}">
    +     <navigator url="/pages/index/index" wx:if="{{ token }}">
    +       <van-button round type="danger" class="bottom-button">去购物van-button>
    +     navigator>
    + 
    +     <navigator url="/pages/login/login" wx:else>
    +       <van-button round type="danger" class="bottom-button">去登录van-button>
    +     navigator>
      van-empty>
    view>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    08. 购物车-更新商品的购买状态

    思路分析:

    点击商品的复选框时,更新商品的购买状态。

    1. 获取商品最新的购买状态,将最新的状态同步到服务器(需要调用封装的接口 API 函数,0 不购买,1 购买)
    2. 在服务器更新状态更新成功以后,将本地的数据一并改变。

    实现步骤:

    1. 导入封装好的获取列表数据的 API 函数
    2. 当点击切换切换商品状态的时候,调用 reqUpdateGoodStatus,并传参
    3. 在更新成功,将本地的数据一并改变。

    落地代码:

    ➡️ /pages/cart/cart.wxml

    <van-checkbox
      checked-color="#FA4126"
    +   value="{{ item.isChecked }}"
    +   bind:change="updateChecked"
    +   data-id="{{ item.goodsId }}"
    +   data-index="{{ index }}"
    >van-checkbox>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ➡️ /pages/cart/cart.js

    import { ComponentWithStore } from 'mobx-miniprogram-bindings'
    import { userStore } from '@/stores/userstore'
    
    + import { reqCartList, reqUpdateChecked } from '@/api/cart'
    
    Component({
    
      // coding...
    
      // 组件的方法列表
      methods: {
        // 切换商品的选中状态
        async updateChecked(event) {
          // 获取最新的选中状态
          const { detail } = event
          // 获取商品的索引和 id
          const { id, index } = event.target.dataset
          // 将最新的状态格式化成后端所需要的数据格式
          const isChecked = detail ? 1 : 0
    
          // 调用接口,传入参数,更新商品的状态
          const res = await reqUpdateChecked(id, isChecked)
    
          // 如果数据更新成功,需要将本地的数据一同改变
          if (res.code === 200) {
            this.setData({
              [`cartList[${index}].isChecked`]: isChecked
            })
          }
        },
    
        // 获取购物车列表数据
        async getCartList() {
          // coding...
        }
      }
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    09. 购物车-控制全选按钮的选中状态

    思路分析:

    购物车列表中每个商品的状态 isCheckd 都是 1,说明每个商品都需要进行购买。

    这时候就需要控制底部工具栏全选按钮的选中效果。

    基于购物车列表中已有的数据,产生一个新的数据,来控制全选按钮的选中效果,可以使用 计算属性 来实现。

    安装 框架拓展 computed

    # 安装并构建 框架拓展 computed
    npm i miniprogram-computed
    
    • 1
    • 2

    实现步骤:

    1. cart 组件中引入 miniprogram-computed ,然后再 behaviors 中进行注册
    2. 新建 computed 配置项,新增 allStatus 函数用来判断是否是全选

    落地代码:

    ➡️ /pages/cart/cart.js

    import { ComponentWithStore } from 'mobx-miniprogram-bindings'
    import { userStore } from '@/stores/userstore'
    import { reqCartList, reqUpdateChecked } from '@/api/cart'
    
    + const computedBehavior = require('miniprogram-computed').behavior
    
    ComponentWithStore({
        
    +   // 注册计算属性
    +   behaviors: [computedBehavior],
        
    +   computed: {
    +     // 判断是否全选
    +     // computed 函数中不能访问 this ,只有 data 对象可供访问
    +     // 这个函数的返回值会被设置到 this.data.selectAllStatus 字段中
    +     selectAllStatus(data) {
    +       return (
    +         data.cartList.length !== 0 && data.cartList.every((item) => item.isChecked === 1)
    +       )
    +     }
    +   }
    
     // 其他代码略...
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    ➡️ /pages/cart/cart.wxml

    
    <van-submit-bar price="{{ 3050 }}" button-text="去结算" tip="{{ true }}">
    +   <van-checkbox value="{{ selectAllStatus }}" checked-color="#FA4126">
        全选
      van-checkbox>
    van-submit-bar>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    10. 购物车-实现全选和全不选功能

    思路分析:

    点击全选,控制所有商品的选中与全不选效果

    1. 点击全选按钮,获取全选按钮的选中状态(true, false),同时控制所有商品的选中与全不选效果
    2. 在获取到全选按钮状态以后,同时需要将状态同步给服务器 (1 是全选,0 是全不选)
    3. 在服务器更新成功以后,需要将本地的购物车商品选中状态也进行改变

    实现步骤:

    1. 导入封装好的全选的 API 函数
    2. 当点击全选和全不选按钮的时候,调用 reqCheckAll,并传参
    3. 在更新成功,将本地的数据一并改变。

    落地代码:

    ➡️ /pages/cart/cart.wxml

    
    <van-submit-bar price="{{ 3050 }}" button-text="去结算" tip="{{ true }}">
      <van-checkbox
        value="{{ selectAllStatus }}"
        checked-color="#FA4126"
        bind:change="changeAllStatus"
      >
        全选
      van-checkbox>
    van-submit-bar>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ➡️ /pages/cart/cart.js

    ComponentWithStore({
        
      // coding...
        
      methods: {
        // coding...
          
        // 全选和全不选功能
        async updateAllStatus(event) {
          // 获取全选和全不选的状态
          const isChecked = event.detail ? 1 : 0
          // 调用接口,更新服务器中商品的状态
          const res = await reqCheckAllStatus(isChecked)
    
          // 如果更新成功,需要将本地的数据一同改变
          if (res.code === 200) {
            // 将数据进行拷贝
            const newCart = JSON.parse(JSON.stringify(this.data.cartList))
            // 将数据进行更改
            newCart.forEach((item) => (item.isChecked = isChecked))
    
            // 进行赋值
            this.setData({
              cartList: newCart
            })
          }
        },
          
        // coding...
      }
    
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    11. 购物车-更新商品购买数量思路分析

    思路分析:

    在输入框中输入购买的数量,并**不是直接将输入的数量同步给服务器,而是需要计算差值**,服务器端进行处理

    差值的计算公式:

    差值 = 新值 - 旧值
    
    例如:
    
    1. 原来是 1,用户输入 11, 差值是:11 - 1 = 10,传递给服务器的是:10,服务器接收到 10 + 1 = 11 
    2. 原来是 11,用户输入 5, 差值是:5 - 11 = -6,传递给服务器的是:-6,服务器接收到 -6 + 11 = 5
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    📌 注意事项:

    更新购买数量 和 加入购物车,使用的是同一个接口,为什么加入购物车没有计算差值,

    这是因为在加入购物车以后,服务器对商品购买数量直接进行了累加。

    例如:之前购物车添加了某个商品,购买数量是 1 个,商品详情又加入 1 个, 直接累加,在购物车显示购买 2 个

    12. 购物车-更新商品的购买数量

    思路分析:

    1. 必须是正整数,最小是1,最大是200
    2. 如果输入的值大于200,输入框购买数量需要重置为200
    3. 输入的值不合法或者小于1,还原为之前的购买数量
    const reg = /^([1-9]|[1-9]\d|1\d{2}|200)$/
    
    • 1

    实现步骤:

    1. 给输入框绑定监听值是否改变的事件,同时传递商品的 ID id 和 商品的购买之前的购买数量 num
    2. 在事件处理程序中获取到最新的数据,然后进行差值的运算
    3. 发送请求即可

    落地代码:

    ➡️ /pages/cart/cart.wxml

    <van-stepper
    +   integer
    +   min="1"
    +   max="200"
      value="{{ item.count }}"
    +   data-id="{{ item.goodsId }}"
    +   data-oldbuynum="{{ item.count }}"
    +   data-index="{{ index }}"
    +   bindchange="changeBuyNum"
    />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ➡️ /pages/cart/cart.js

    // 更新购买的数量
    async changeBuyNum(event) {
      // 获取最新的购买数量,
      // 如果用户输入的值大于 200,购买数量需要重置为 200
      // 如果不大于 200,直接返回用户输入的值
      let buynum = event.detail > 200 ? 200 : event.detail
      // 获取商品的 ID 和 索引
      const { id: goodsId, index, oldbuynum } = event.target.dataset
    
      // 验证用户输入的值,是否是 1 ~ 200 直接的正整数
      const reg = /^([1-9]|[1-9]\d|1\d{2}|200)$/
    
      // 对用户输入的值进行验证
      const regRes = reg.test(buynum)
    
      // 如果验证没有通过,需要重置为之前的购买数量
      if (!regRes) {
        this.setData({
          [`cartList[${index}].count`]: oldbuynum
        })
    
        return
      }
    
      // 如果通过,需要计算差值,然后将差值发送给服务器,让服务器进行逻辑处理
      const disCount = buynum - oldbuynum
    
      // 如果购买数量没有发生改变,不发送请求
      if (disCount === 0) return
    
      // 发送请求:购买的数量 和 差值
      const res = await reqAddCart({ goodsId, count: disCount })
    
      // 服务器更新购买数量成功以后,更新本地的数据
      if (res.code === 200) {
        this.setData({
          [`cartList[${index}].count`]: buynum
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    13. 购物车-更新商品购买数量防抖

    思路分析:

    每次改变购物车购买数量的时候,都会触发 changeBuyNum 事件处理程序,这会频繁的向后端发送请求,给服务器造成压力

    我们希望用户在输入最终的购买数量,或者停止频繁点击加、减的以后在发送请求,在将购买数量同步到服务器。

    这时候就需要使用 防抖 来进行代码优化。

    Licia 是实用 JavaScript 工具库,该库目前拥有超过 400 个模块,同时支持浏览器、node 及小程序运行环境。可以极大地提高开发效率。

    licia 官网

    licia 中文使用文档

    落地代码:

    ➡️ /pages/cart/cart.js

    // 从 miniprogram-licia 导入防抖函数
    import { debounce } from 'miniprogram-licia'
    
    // 更新购买的数量
    + changeBuyNum: debounce(async function (event) {
    +   // 代码略...
    + }, 500)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    14. 购物车-购物车商品合计

    思路分析:

    在订单提交栏位置,展示要购买商品的总金额。

    需要判断购物车中哪些商品被勾选,然后将勾选商品的价格进行累加。

    当用户更新了商品的状态,或者更新了商品的购买数量,我们都需要重新计算订单总金额。

    我们需要基于购物车列表的数据,产生订单总金额,在这里我们使用依然使用 computed 来实现商品合计的功能

    实现步骤:

    1. computed 配置项,新增 totalPrice 函数用来计算商品价格总和

    落地代码:

    ➡️ /pages/cart/cart.wxml

    
    <van-submit-bar
      wx:if="{{ cartList.length }}"
      price="{{ totalPrice }}"
      button-text="去结算"
      tip="{{ true }}"
    >
      <van-checkbox
        value="{{ selectAllStatus }}"
        checked-color="#FA4126"
        bindchange="selectAllStatus"
      >
        全选
      van-checkbox>
    van-submit-bar>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    ➡️ /pages/cart/cart.js

    ComponentWithStore({
        
      // coding...
        
      // 定义计算属性
      computed: {
    
        // coding...
    
        // 计算商品价格总和
        totalPrice(data) {
          let totalPrice = 0
    
          data.cartList.forEach((item) => {
            // 如果商品的 isChecked 属性等于,说明该商品被选中的
            if (item.isChecked === 1) {
              totalPrice += item.count * item.price
            }
          })
    
          return totalPrice
        }
      },
        
        
      // coding...
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    15. 购物车-删除购物车中的商品

    思路分析:

    点击删除按钮的时候,需要将对应的购物车商品进行删除

    实现步骤:

    1. 导入封装的接口 API 函数,同时导入处理删除自动关闭效果的 behaviors 并进行注册
    2. 在点击删除以后,调用 API 函数,在删除购物车商品成功以后,给用户提示

    落地代码:

    ➡️ /pages/cart/components/cart.wxml

    +  <view bindtap="onSwipeCellPage">
        
      
        
      <van-swipe-cell
        class="goods-swipe"
        right-width="{{ 65 }}"
    +     id="swipe-cell-{{ item.goodsId }}"
    +     bind:open="swipeCellOpen"
    +     bind:click="onSwipeCellClick"
      >
        <van-cell-group border="{{ false }}">
          <view class="goods-info">
            <view class="left">
              <van-checkbox
                checked-color="#FA4126"
                value="{{ item.isChecked }}"
                bindchange="updateChecked"
                data-id="{{ item.goodsId }}"
                data-index="{{ index }}"
              >van-checkbox>
            view>
            <view class="mid">
              <image class="img" src="{{ item.imageUrl }}" />
            view>
            <view class="right">
              <view class="title"> {{ item.name }} view>
              <view class="buy">
                <view class="price">
                  <view class="symbol">¥view>
                  <view class="num">{{ item.price }}view>
                view>
                <view class="buy-btn">
                  <van-stepper
                    min="1"
                    max="200"
                    integer
                    value="{{ item.count }}"
                    data-id="{{ item.goodsId }}"
                    data-index="{{ index }}"
                    data-oldbuynum="{{ item.count }}"
                    bindchange="changeBuyNum"
                  />
                view>
              view>
            view>
          view>
        van-cell-group>
        <view
          slot="right"
          class="van-swipe-cell__right"
    +       bindtap="delCartGoods"
    +       data-id="{{ item.goodsId }}"
        >
          删除
        view>
      van-swipe-cell>
        
        
      
    view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    ➡️ /pages/cart/components/cart.wxml

    // 导入接口 API 函数
    import {
      reqCartList,
      reqUpdateChecked,
      reqCheckAllStatus,
      reqAddCart,
    +   reqDelCartGoods
    } from '@/api/cart'
    
    + // 导入让删除滑块自动弹回的 behavior
    + import { swipeCellBehavior } from '@/behaviors/swipeCell'
    
    
    ComponentWithStore({
      // 注册 behavior
    +   behaviors: [swipeCellBehavior, computedBehavior],
        
      // 组件的方法列表
      methods: {
    
        // coding...
    
    +   // 删除购物车中的商品
    +   async delCartGoods(event) {
    +     // 获取需要删除商品的 id
    +     const { id } = event.currentTarget.dataset
    + 
    +     // 询问用户是否删除该商品
    +     const modalRes = await wx.modal({
    +       content: '您确认删除该商品吗 ?'
    +     })
    + 
    +     if (modalRes) {
    +       await reqDelCartGoods(id)
    + 
    +       this.showTipGetList()
    +     }
    +   },
    
    +     onHide() {
    +       // 在页面隐藏的时候,需要让删除滑块自动弹回
    +       this.onSwipeCellCommonClick()
    +     }
      }
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
  • 相关阅读:
    压缩算法:基于FPGA的Varint编码实现(附代码)
    SH-SSS丨《ISSD: 基于迭代式语音分离的说话人日志系统》论文线上分享
    前端体验优化(4)——数据
    LeetCode刷题系列 -- 20. 有效的括号
    Android字体大小dp,sp,px系统设置字体大小变化表现
    Flink内部数据交换源码分析(二)之上游输出以及下游读取过程
    mysql体系结构及引擎
    【30】c++设计模式——>状态模式
    四、安装vmtools
    使用libevent实现回显服务器
  • 原文地址:https://blog.csdn.net/qq_63358859/article/details/136394944