微信小程序开发week8-慕尚花坊项目

1.结算支付

1.1配置分包并跳转到结算页面

随着项目功能的增加,项目体积也随着增大,从而影响小程序的加载速度,影响用户的体验。

因此我们需要将 `结算支付` 功能配置成一个分包,

当用户在访问设置页面时,还预先加载 `结算支付` 所在的分包

复制代码
`➡️ app.json`

```json
"subPackages": [
  {
    "root": "modules/settingModule",
    "name": "settingModule",
    "pages": [
      "pages/address/add/index",
      "pages/address/list/index",
      "pages/profile/profile"
    ]
  },
  {
    "root": "modules/goodModule",
    "name": "goodModule",
    "pages": ["pages/goods/list/list", "pages/goods/detail/detail"]
  },
+   {
+     "root": "modules/orderPayModule",
+     "name": "orderPayModule",
+     "pages": [
+       "pages/order/detail/detail",
+       "pages/order/list/list"
+     ]
+   }
],
"preloadRule": {
  "pages/settings/settings": {
    "network": "all",
    "packages": ["settingModule"]
  },
  "pages/category/category": {
    "network": "all",
    "packages": ["goodModule"]
  },
+   "pages/cart/cart": {
+     "network": "all",
+     "packages": ["orderPayModule"]
+   }
}

```



`➡️ pages/cart/cart.js`

```js
// 跳转到订单结算页面
toOrder() {
  if (this.data.totalPrice === 0) {
    wx.toast({
      title: '请选择需要购买的商品'
    })

    return
  }

  // 跳转到订单的结算页面
  wx.navigateTo({
    url: '/modules/orderPayModule/pages/order/detail/detail'
  })
}
```



`➡️ pages/cart/cart.wxml`

```html
<van-submit-bar
  wx:if="{{ cartList.length }}"
  price="{{ totalPrice * 100 }}"
  button-text="去结算"
  tip="{{ true }}"
+  bindsubmit="toOrder"
>
  <van-checkbox
    value="{{ selectAllStatus }}"
    checked-color="#FA4126"
    bindchange="selectAllStatus"
  >
    全选
  </van-checkbox>
</van-submit-bar>
```

1.2封装结算支付的接口 API

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

复制代码
`➡️ /api/orderpay.js`

```js
import http from '@/utils/http'

/**
 * @description 获取订单详情
 * @returns Promise
 */
export const reqOrderInfo = () => {
  return http.get('/order/trade')
}

/**
 * @description 获取订单列表
 * @param {*} page 页码
 * @param {*} limit 每页展示的条数
 * @returns Promise
 */
export const reqOrderList = (page, limit) => {
  return http.get(`/order/order/${page}/${limit}`)
}

/**
 * @description 获取订单收货地址
 * @returns Promise
 */
export const reqOrderAddress = () => {
  return http.get('/userAddress/getOrderAddress')
}

/**
 * @description 获取立即购买商品的详情信息
 * @param { Object } params { goodsId: 商品 Id,  blessing:祝福语 }
 * @returns Promise
 */
export const reqBuyNowGoods = ({ goodsId, ...data }) => {
  return http.get(`/order/buy/${goodsId}`, data)
}

/**
 * @description 提交订单
 * @returns Promise
 */
export const reqSubmitOrder = () => {
  return http.post('/order/submitOrder')
}

/**
 * @description 获取微信预支付信息
 * @param {*} orderNo 订单 ID
 * @returns Promise
 */
export const reqPreBuyInfo = (orderNo) => {
  return http.get(`/webChat/createJsapi/${orderNo}`)
}

/**
 * @description 微信支付状态查询
 * @param {*} orderNo
 * @returns Promise
 */
export const reqPayStatus = (orderNo) => {
  return http.get(`/webChat/queryPayStatus/${orderNo}`)
}

```

1.3商品结算-获取收货地址

复制代码
`➡️ /pages/order/detail/index.js`

```js
import { getTradeAddress } from '../../../api/order'

Page({
    
  data: {
    // coding...
+     orderAddress: {} // 收货地址
  },

+   // 获取收货地址
+   async getAddress() {
+     const { data: orderAddress } = await reqOrderAddress()
+ 
+     this.setData({
+       orderAddress
+     })
+   },

+   // 页面展示时触发的钩子函数
+   onShow() {
+     this.getAddress()
+   }
})
```



`➡️ /pages/order/detail/index.wxml`

```html
<!--pages/order/index.wxml-->
<view class="container order">
  <view class="address-card">
    <!-- 添加收货地址 -->
    <view wx:if="{{ !tradeAddress.id }}" class="add-address"  bindtap="toAddress">
      <van-icon size="22px" name="add" />
      <view>添加收货地址</view>
    </view>

    <view wx:else class="order-address flex">
      <view class="address-content">
        <view class="title">{{ tradeAddress.fullAddress }}</view>
        <view class="info flex">
          <text>{{ tradeAddress.name }}</text>
          <text>{{ tradeAddress.phone }}</text>
        </view>
      </view>

      <view class="select-address">
        <navigator class="navigator" url="/modules/settingModule/pages/address/list/index">
          <van-icon color="#bbb" name="arrow" size="22px" />
        </navigator>
      </view>
    </view>

    <view class="top-line"></view>
  </view>

  <view class="order-info">
    <!-- coding... -->
  </view>

</view>
```

1.4商品结算-更新收货地址功能

复制代码
`➡️ app.js`

```js
App({
    
+   // 定义全局共享的数据
+   globalData: {
+    address: {}
+  }
    
  // coding...
})
```





`➡️ /pages/address/list/index.html`

```html
<!-- 每一个收货地址 -->

<view
  class="info"
+   bindtap="changeAddress"
+   data-id="{{ item.id }}"
>
  <view class="user-info">
    <text>{{ item.name }}</text>
    <text>{{ item.phone }}</text>
    <text wx:if="{{ item.isDefault === 1 }}" class="default-tag">默认</text>
  </view>

  <view class="address-info"> {{ item.fullAddress }} </view>
</view>
```



`➡️ /pages/address/list/index.js`

```js
// 导入接口 API 函数
import { reqAddressList, reqDelAddress } from '@/api/address'
import { swipeCellBehavior } from '@/behaviors/swipeCell'

+ // 获取全局的应用实例
+ const app = getApp()

Page({

  // coding...

+   // 切换收货地址
+   changeAddress(event) {
+     // 判断是否是从订单结算页面进入
+     if (this.flag !== '1') return
+ 
+     // 获取到点击的收货地址 id
+     const addressId = event.currentTarget.dataset.id
+     // 从收货地址列表中获取到获取到点击的收货地址详细信息
+     const address = this.data.addressList.find((item) => item.id === addressId)
+ 
+     // 如果获取成功,将数据存储到 globalData 中
+     if (address) {
+       app.globalData.address = address
+       wx.navigateBack()
+     }
+   },

+   onLoad(options) {
+     this.flag = options.flag
+   }
})

```



`➡️ /pages/order/detail/index.wxml`

```html
<view class="select-address">
  <navigator
    class="navigator"
+     url="/modules/settingModule/pages/address/list/index?flag=1"
  >
    <van-icon color="#bbb" name="arrow" size="22px" />
  </navigator>
</view>
```



`➡️ /pages/order/detail/index.js`

```js
  // 获取订单页面的收货地址
  async getAddress() {
      
+     // 如果 globalData 存在收货地址,取出收货地址
+     if (app.globalData.address.id) {
+       this.setData({
+         orderAddress: app.globalData.address
+       })
+ 
+       // 在赋值以后需要将收货地址清空
+       app.globalData.address = {}
+ 
+       return
+     }

    // 如果 globalData 中不存在收货地址,获取收货地址渲染即可
    const { data: orderAddress } = await reqOrderAddress()

    this.setData({
      orderAddress
    })
  },
```

1.5商品结算-获取订单详情数据

复制代码
`➡️ /pages/order/detail/index.js`

```js
+ import { reqOrderAddress, reqOrderInfo } from '@/api/orderpay'

Page({
    
  data: {
    // coding...
    orderAddress: {}, // 收货地址
+     orderInfo: {}, // 订单商品详情
  },
     
+   // 获取订单详情
+   async getOrderInfo() {
+     const { data: orderInfo } = await reqOrderInfo()
+ 
+     // 判断是否存在祝福语
+     // 如果需要购买多个商品,挑选第一个填写了祝福语的商品进行赋值
+     const orderGoods = orderInfo.cartVoList.find((item) => item.blessing !== '')
+ 
+     this.setData({
+       orderInfo,
+       blessing: orderGoods && orderGoods.blessing
+     })
+   },

  // 在页面展示的时候进行触发
  onShow() {
    // 获取收货地址
    this.getAddress()

+     // 获取订单结算页面的商品信息
+     this.getOrderInfo()
  },
})
```



`➡️ /pages/order/detail/index.wxml`

```html
<!--pages/order/index.wxml-->
<view class="container order">
  <view class="address-card">
    <!-- 添加收货地址 -->
    <!-- coding... -->
  </view>

  <view class="goods-wraper">
    <!-- 商品清单 -->
    <view class="goods-list">

+       <view class="goods-item flex" wx:for="{{ tradeInfo.cartVoList }}" wx:key="goodsId">
        <view class="img">
+           <image src="{{ item.imageUrl }}" />
        </view>
        <view class="content">
+           <view class="goods-title">{{ item.name }}</view>
          <view class="goods-price">
+             <view class="price"> ¥ {{ item.price }}</view>
+             <view>x {{ item.count }}</view>
          </view>
        </view>
      </view>
    </view>
  </view>

  <view class="payment">
    <!-- 支付方式 -->
    <view class="time-wraper flex">
      <image src="/static/images/payment_wxzf.png" />
      <view class="title">支付方式</view>
      <van-checkbox value="{{true}}"></van-checkbox>
    </view>
  </view>

  <!-- 支付区域 -->
  <view class="footer flex">
+     <view class="left"> ¥ {{ tradeInfo.totalAmount }} </view>
    <viwe class="right">结算</viwe>
  </view>

  <!-- 日期选择弹框 -->
  <van-popup show="{{ show }}" round position="bottom" custom-style="height: 50%" bind:close="onClose">
    <van-datetime-picker type="date" min-date="{{ minDate }}" model:value="{{ currentDate }}" bind:confirm="onConfirmTimerPicker" bind:cancel="onCancelTimePicker" />
  </van-popup>

</view>
```

1.6商品结算-获取立即购买数据

复制代码
`➡️ /pages/order/detail/index.js`

```js
import {
  reqOrderAddress,
  reqOrderInfo,
+   reqBuyNowGoods
} from '@/api/orderpay'

Page({

    
  // 获取订单详情
  async getOrderInfo() {
+ 	  // 从 data 中结构数据      
+     const { goodsId, blessing } = this.data

+	 // 判断是否存在商品 id,
     // 如果存在调用立即购买商品详情的接口
     // 不存在调用获取订单详情数据接口
+    const { data: orderInfo } = goodsId
      ? await reqBuyNowGoods({ goodsId, blessing })
      : await reqOrderInfo()

    // 判断是否存在祝福语
    // 如果需要购买多个商品,挑选第一个填写了祝福语的商品进行赋值
    const orderGoods = orderInfo.cartVoList.find((item) => item.blessing !== '')
      
    this.setData({
      orderInfo,
      orderGoods && orderGoods.blessing
    })
  }
    
 +  // 接收立即购买传递的参数
 +  onLoad (options) {
 +    this.setData({
 +       ...options
 +    })
 +  },

  // 在页面展示的时候进行触发
  onShow() {
    // 获取收货地址
    this.getAddress()

    // 获取订单结算页面的商品信息
    this.getOrderInfo()
  }
})

```

1.7商品结算-收集送达时间

复制代码
`➡️ /pages/order/detail/index.js`

```js
import { formatTime } from '../../../utils/formatTime.js'

Page({
  
  // coding...

  // 期望送达日期确定按钮
  onConfirmTimerPicker(event) {
    // 使用 new Date 将时间戳转换成 JS 中的日期对象
    const time = formatTime(new Date(event.detail))
    
    // 将转换以后的时间赋值给送到时间
    this.setData({
      show: false,
      deliveryDate: time
    })
  }
 
  // coding...
}
```

1.8商品结算-表单数据验证

使用 `async-validator` 对代码进行验证

  1. 收货地址不能为空

  2. 订购人姓名不能为空,且不能输入特殊字符

  3. 订购人手机号不能为空,且输入的手机号必须合法

  4. 送达日期不能为空

    js 复制代码
    import { reqOrderAddress, reqOrderInfo, reqBuyNowGoods } from '@/api/orderpay'
    // 导入 async-validator 对参数进行验证
    import Schema from 'async-validator'
    // 导入格式化时间的方法
    import { formatTime } from '@/utils/formatTime'
    
    // 获取应用实例
    const app = getApp()
    
    Page({
      data: {
        buyName: '', // 订购人姓名
        buyPhone: '', // 订购人手机号
        deliveryDate: '', // 期望送达日期
        blessing: '', // 祝福语
        show: false, // 期望送达日期弹框
        orderAddress: {}, // 收货地址
        orderInfo: {}, // 订单商品详情
        minDate: new Date().getTime(),
        currentDate: new Date().getTime()
      },
    
      async submitOrder() {
        // 从 data 中结构数据
        const {
          buyName,
          buyPhone,
          deliveryDate,
          blessing,
          orderInfo,
          orderAddress
        } = this.data
    
        // 组织请求参数
        const params = {
          buyName,
          buyPhone,
          deliveryDate,
          remarks: blessing,
          cartList: orderInfo.cartVoList,
          userAddressId: orderAddress.id
        }
    
        // 对请求参数进项验证
        const { valid } = await this.validatorPerson(params)
    
        // 打印验证结果
        console.log(valid)
      },
    
      // 对新增收货地址请求参数进行验证
      validatorPerson(params) {
        // 验证收货人,是否只包含大小写字母、数字和中文字符
        const nameRegExp = '^[a-zA-Z\\d\\u4e00-\\u9fa5]+$'
    
        // 验证手机号,是否符合中国大陆手机号码的格式
        const phoneReg = '^1(?:3\\d|4[4-9]|5[0-35-9]|6[67]|7[0-8]|8\\d|9\\d)\\d{8}$'
    
        // 创建验证规则
        const rules = {
          userAddressId: [{ required: true, message: '请选择收货地址' }],
          buyName: [
            { required: true, message: '请输入收货人姓名' },
            { pattern: nameRegExp, message: '收货人姓名不合法' }
          ],
          buyPhone: [
            { required: true, message: '请输入收货人手机号' },
            { pattern: phoneReg, message: '收货人手机号不合法' }
          ],
          deliveryDate: { required: true, message: '请选择送达时间' }
        }
    
        // 传入验证规则进行实例化
        const validator = new Schema(rules)
    
        // 调用实例方法对请求参数进行验证
        // 注意:我们希望将验证结果通过 Promise 的形式返回给函数的调用者
        return new Promise((resolve) => {
          validator.validate(params, (errors) => {
            if (errors) {
              // 如果验证失败,需要给用户进行提示
              wx.toast({ title: errors[0].message })
              // 如果属性值是 false,说明验证失败
              resolve({ valid: false })
            } else {
              // 如果属性值是 true,说明验证成功
              resolve({ valid: true })
            }
          })
        })
      },
        
      
      // coding....
    })

1.9小程序支付-小程序支付流程

**小程序支付图示:**

<img src="http://8.131.91.46:6677/mina/floor/小程序支付流程.png" style="zoom:80%; border:1px solid #ccc" />

**前端需要做的事情:**

  1. `生成平台订单`:前端调用接口,向后端传递需要购买的商品信息、收货人信息,[后端生成平台订单,返回订单编号]

  2. `获取预付单信息`:将订单编号发送给后端后, [后端向微信服务器获取预付单信息,后端会将微信服务器返回的预付单信息进行加密,然后将加密以后的预付单信息返回给前端]

  3. `发起微信支付`:前端调用 wx.requestPayment() 发起微信支付

  4. `查询支付状态`:调用接口查询支付状态

1.10小程序支付-创建平台订单

**思路分析:**

用户在完成选购流程,确认商品信息、订购人、收货人等信息无误后,

用户需要点击提交订单按钮,开始进行下单支付,这时候需要先创建平台订单。

**实现步骤:**

  1. 在提交订单的事件处理函数中调用封装的接口 API 函数

  2. 在接口调用成功以后,将服务器响应的订单编码挂载到页面实例上。

    ➡️ /pages/order/detail/index.js

    js 复制代码
    import {
      reqOrderAddress,
      reqOrderInfo,
      reqBuyNowGoods,
    +   reqSubmitOrder
    } from '@/api/orderpay'
    
    Page({
       
      // coding...
        
      // 提交订单
        // 处理提交订单
      async submitOrder() {
        // 需要从 data 中解构数据
        const {
          buyName,
          buyPhone,
          deliveryDate,
          blessing,
          orderAddress,
          orderInfo
        } = this.data
    
        // 需要根据接口要求组织请求参数
        const params = {
          buyName,
          buyPhone,
          cartList: orderInfo.cartVoList,
          deliveryDate,
          remarks: blessing,
          userAddressId: orderAddress.id
        }
    
        // 对请求参数进行验证
        const { valid } = await this.validatorPerson(params)
    
    +     // 如果验证失败,直接 return,不执行后续的逻辑处理
    +     if (!valid) return
    + 
    +     // 调用接口,创建平台订单
    +     const res = await reqSubmitOrder(params)
    + 
    +     // 在平台订单创建成功以后,将订单编号挂载到页面实例上
    +     if (res.code === 200) {
    +       // 将订单编号挂载到页面实例上
    +       this.orderNo = res.data
    +     }
      }
        
        
      // coding...
    })

1.11小程序支付-获取预付单信息

**思路分析:**

将订单编号发送给公司的后端,公司的后端会从数据库找到对应订单的信息。

然后调用微信服务器的 下单接口 进行创建订单,订单创建成功以后,微信服务器会给公司后端返回预付单信息。

公司后端对返回的预付单信息进行加密,返回给小程序客户端。

这一步,咱们需要做的就是:订单编号发送给公司的后端,其他逻辑时后端来完成的。

> 📌:注意事项:

> ​ 小程序支付后面的代码,大伙在实现的时候,会出现异常。

> ​ 这是因为没有小程序的开发权限,以后在实际开发中,只需要参考当前流程进行开发即可

复制代码
`➡️ /pages/order/detail/index.js`

```js
Page({
    // 处理提交订单
  async submitOrder() {
    // 需要从 data 中解构数据
    const {
      buyName,
      buyPhone,
      deliveryDate,
      blessing,
      orderAddress,
      orderInfo
    } = this.data

    // 需要根据接口要求组织请求参数
    const params = {
      buyName,
      buyPhone,
      cartList: orderInfo.cartVoList,
      deliveryDate,
      remarks: blessing,
      userAddressId: orderAddress.id
    }

    // 对请求参数进行验证
    const { valid } = await this.validatorPerson(params)

    // 如果请求参数验证失败,直接 return ,不执行后续的逻辑
    if (!valid) return

    // 调用接口,创建平台订单
    const res = await reqSubmitOrder(params)

    if (res.code === 200) {
      // 在平台订单创建成功以后,需要将服务器、后端返回的订单编号挂载到页面实例上
      this.orderNo = res.data
      
+       // 获取预付单信息、支付参数
+       this.advancePay()
    }
  },

+   // 获取预付单信息、支付参数
+   async advancePay() {
+     // 调用接口,获取预付单信息、支付参数
+     const payParams = await reqPrePayInfo(this.orderNo)
+ 
+     if (payParams.code === 200) {
+       console.log(res.data)
+     }
+   },

})
```

1.12小程序支付-发起微信支付

**知识点:**

小程序客户端在接收支付参数后,调用 `wx.requestPayment()` 发起微信支付,

唤醒支付弹窗,用户开输入支付密码或者进行指纹等操作,微信服务器会进行验证,如果验证成功,就会发起支付。

然后会将支付结果返回给公司后端,也会返回给 `wx.requestPayment()`

并且会微信通知用户支付结果

复制代码
`➡️ /pages/order/detail/index.js`

```js
// 获取预付单信息、支付参数
async advancePay() {
 try {
    const payParams = await reqPrePayInfo(this.orderNo)

    if (payParams.code === 200) {
      // 进行微信支付
      const payInfo = await wx.requestPayment(payParams.data)
      
      console.log(payInfo)
    }
  } 
  catch {
    wx.toast({ title: '支付遇到问题,请联系客服', icon: 'error' })
  }
}

```

1.13小程序支付-支付状态查询

**思路分析:**

通过调用后端接口获取支付状态,如果支付成功,需要给用户提示,同时跳转到订单列表页面。

公司后端开始向微信服务器发送请求,查询支付结果

公司服务器会将微信服务器返回的支付结果,返回到客户端

客户端根据查询结果跳转到订单列表页面

复制代码
`➡️ /pages/order/detail/index.js`

```js
// 获取预付单信息、支付参数
async advancePay() {
  try {
    const payParams = await reqPrePayInfo(this.orderNo)

    if (payParams.code === 200) {
      // payParams.data 就是获取的支付参数

      // 调用  wx.requestPayment 发起微信支付
      const payInfo = await wx.requestPayment(payParams.data)
      
      // 获取支付结果
      if (payInfo.errMsg === 'requestPayment:ok') {
        // 查询订单的支付状态
        const payStatus = await reqPayStatus(this.orderNo)

        if (payStatus.code === 200) {
          wx.redirectTo({
            url: '/pages/order/list/index',
            success: () => {
              wx.toast({
                title: '支付成功',
                icon: 'success
              })
            }
          })
        }
      }
      
    }
  } catch (error) {
    wx.toast({
      title: '支付失败,请联系客服',
      icon: 'error'
    })
  }
},
```

2.订单列表

2.1封装订单列表接口 `API`

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

复制代码
`➡️ api/orderpay.js`

```js
/**
 * @description 获取订单列表
 * @returns Promise
 */
export const reqOrderList = (page, limit) => {
  return http.get(`/order/order/${page}/${limit}`)
}

```

2.2获取订单列表数据并渲染

**思路分析:**

当用户从个人中心页面点击进入订单中心的时候,就需要获取到订单中心的数据。

在页面调用 `API` 函数获取订单列表的数据,

在获取到数据以后,使用后端返回的数据对页面进行渲染

复制代码
`➡️ modules/orderPayModule/pages/order/list/list.js`

```js
+ // 导入封装的接口 API 函数
+ import { reqOrderList } from '@/api/orderpay'

Page({
  // 页面的初始数据
  data: {
    orderList: [1, 2, 3], // 订单列表
+     page: 1, // 页码
+     limit: 10, // 每页展示的条数
+     total: 0 // 订单列表总条数
  },

+   // 获取订单列表
+   async getOrderList() {
+     // 解构获取数据
+     const { page, limit } = this.data
+     // 调用接口获取订单列表数据
+     const res = await reqOrderList(page, limit)
+ 
+     if (res.code === 200) {
+       this.setData({
+         orderList: res.data.records,
+         total: res.data.total
+       })
+     }
+   },

+   // 生命周期函数--监听页面加载
+   onLoad() {
+     this.getOrderList()
+   }

})

```



`➡️ modules/orderPayModule/pages/order/list/list.wxml`

```html
<!--pages/order/list/index.wxml-->
<view class="order-container container">
+   <view class="order-list" wx:if="{{ orderList.length > 0 }}">
+     <view class="order-item" wx:for="{{ orderList }}" wx:key="index">
+       <view class="order-item-header list-flex">
        <view class="orderno">订单号<text class="no">{{ orderList.orderNo }}</text></view>
+         <view class="order-status {{ item.orderStatus === 1 ? 'order-active' : '' }}">
+           {{ item.orderStatus === 1 ? '已支付' : '未支付'}}
+         </view>
      </view>
      <view
        class="goods-item list-flex"
+         wx:for="{{ item.orderDetailList }}"
+         wx:key="id"
+         wx:for-item="goods"
+         wx:for-index="goodsIndex"
      >
        <view class="left">
+           <image src="{{ goods.imageUrl }}" mode="widthFix" class="img" />
        </view>
        <view class="mid">
+           <view class="goods-name">{{ goods.name }}</view>
+           <view class="goods-blessing">{{ goods.blessing }}</view>
        </view>
        <view class="right">
+           <view class="goods-price">¥{{ goods.price }}</view>
+           <view class="goods-count">x{{ goods.count }}</view>
        </view>
      </view>
      <view class="order-item-footer">
        <view class="total-amount list-flex">
          <text class="text">实付</text>
+           <text class="price"><text>¥</text>{{ item.totalAmount }}</text>
        </view>
      </view>
    </view>
  </view>
  <van-empty wx:else description="还没有购买商品,快去购买吧~" />
</view>

```

2.3订单列表上拉加载更多

**思路分析:**

当用户进行了上拉操作时,需要在 `.js` 文件中声明 `onReachBottom` 方法,用来监听页面的上拉触底行为

当用户上拉时,需要对 `page` 参数进行加 1 即可,

当参数发生改变后,需要重新发送请求,拿最新的 `page` 向服务器要数据

在下一页的商品数据返回以后,需要将下一页的数据和之前的数据进行合并

复制代码
`➡️ modules/orderPayModule/pages/order/list/list.js`

```js
// 导入封装的接口 API 函数
import { reqOrderList } from '@/api/orderpay'

Page({
  // 页面的初始数据
  data: {
    orderList: [1, 2, 3], // 订单列表
    page: 1, // 页码
    limit: 10, // 每页展示的条数
    total: 0 // 订单列表总条数
  },

  // 获取订单列表
  async getOrderList() {
    // 解构获取数据
    const { page, limit } = this.data
    // 调用接口获取订单列表数据
    const res = await reqOrderList(page, limit)

    if (res.code === 200) {
      this.setData({
+         orderList: [...this.data.orderList, ...res.data.records],
        total: res.data.total
      })
    }
  },

+   // 页面上拉触底事件的处理函数
+   onReachBottom() {
+     // 解构数据
+     const { page } = this.data
+ 
+     // 更新 page
+     this.setData({
+       page: page + 1
+     })
+ 
+     // 重新发送请求
+     this.getOrderList()
+   },

  // 生命周期函数--监听页面加载
  onLoad() {
    this.getOrderList()
  }
})

```

2.4判断数据是否加载完毕

**思路分析:**

如何判断数据是否加载完成 ❓

可以使用后端返回的 `total` 和 `goodsList` 进行对比,如果 total 大于 `goodsList` ,说明订单中心数据没有加载完,可以继续上拉加载更多。

目前还没有接收 `total`,需要先将后台返回的 total 进行赋值到 data 中,然后使用 `onReachBottom` 中进行判断

复制代码
```js
// 页面上拉触底事件的处理函数
onReachBottom() {
+   // 解构数据
+   const { page, total, orderList } = this.data
+ 
+   // 数据总条数 和 订单列表长度进行对比
+   if (total === orderList.length) {
+     return wx.toast({ title: '数据加载完毕' })
+   }

  // 更新 page
  this.setData({
    page: page + 1
  })

  // 重新发送请求
  this.getOrderList()
}

```

2.5节流阀进行列表节流

在用户网速很慢的情况下,如果用户在距离底部来回的进行多次滑动,可能会发送一些无意义的请求、造成请求浪费的情况,因此需要给上拉加载添加节流功能。

我们使用节流阀来给订单列表添加节流功能。

在 `data` 中定义节流阀状态 `isLoading`,默认值是 `false`。

在请求发送之前,将 `isLoading` 设置为 `true`,表示请求正在发送。

在请求结束以后,将 `isLoading` 设置为 `false`,表示请求已经完成。

在 `onReachBottom` 事件监听函数中,对 `isLoading` 进行判断,如果数据正在请求中,不请求下一页的数据。

复制代码
`➡️ modules/orderPayModule/pages/order/list/list.js`

```js
// 导入封装的接口 API 函数
import { reqOrderList } from '@/api/orderpay'

Page({
  // 页面的初始数据
  data: {
    orderList: [1, 2, 3], // 订单列表
    page: 1, // 页码
    limit: 10, // 每页展示的条数
    total: 0, // 订单列表总条数
+     isLoading: false // 判断数据是否记载完毕
  },

  // 获取订单列表
  async getOrderList() {
    // 解构获取数据
    const { page, limit } = this.data

+     // 数据正在请求中
+     this.data.isLoading = true

    // 调用接口获取订单列表数据
    const res = await reqOrderList(page, limit)

+     // 数据加载完毕
+     this.data.isLoading = false

    if (res.code === 200) {
      this.setData({
        orderList: [...this.data.orderList, ...res.data.records],
        total: res.data.total
      })
    }
  },

  // 页面上拉触底事件的处理函数
  onReachBottom() {
+     // 解构数据
+     const { page, total, orderList, isLoading } = this.data

+     // 判断是否加载完毕,如果 isLoading 等于 true
+     // 说明数据还没有加载完毕,不加载下一页数据
+     if (isLoading) return

    // 数据总条数 和 订单列表长度进行对比
    if (total === orderList.length) {
      return wx.toast({ title: '数据加载完毕' })
    }

    // 更新 page
    this.setData({
      page: page + 1
    })

    // 重新发送请求
    this.getOrderList()
  },

  // 生命周期函数--监听页面加载
  onLoad() {
    this.getOrderList()
  }
})

```

3. 代码优化

3.1分享功能

**思路分析:**

目前小程序页面都没有配置分享功能,需要给小程序页面设置分享功能。

但是并不是所有页面都需要设置分享功能,

具体哪些页面需要设置分享功能,可以和产品经理进行协商。

  1. 首页

  2. 商品列表

  3. 商品详情

    js 复制代码
    // 转发功能
    onShareAppMessage() {
      return {
        title: '所有的怦然心动,都是你',
        path: '/pages/index/index',
        imageUrl: '../../assets/images/love.jpg'
      }
    },
    
    // 转发到朋友圈功能
    onShareTimeline() {}

3.2优化-分包调整

**思路分析:**

  1. 将 [设置页面] 配置到 [设置模块分包],在访问个人中心页面时,提前预下载 [设置模块分包]

  2. 进入订单结算页面时,提前预下载 [设置模块分包]

    ➡️ app.json

    json 复制代码
    {
        "subPackages": [
        {
          "root": "modules/settingModule",
          "name": "settingModule",
          "pages": [
    +         "pages/settings/settings",
            "pages/address/add/index",
            "pages/address/list/index",
            "pages/profile/profile"
          ]
        },
        {
          "root": "modules/goodModule",
          "name": "goodModule",
          "pages": ["pages/goods/list/list", "pages/goods/detail/detail"]
        },
        {
          "root": "modules/orderPayModule",
          "name": "orderPayModule",
          "pages": ["pages/order/detail/detail", "pages/order/list/list"]
        }
      ],
      "preloadRule": {
    +     "pages/my/my": {
          "network": "all",
          "packages": ["settingModule"]
        },
    +     "modules/orderPayModule/pages/order/detail/detail": {
    +       "network": "all",
    +       "packages": ["settingModule"]
    +     },
        "pages/category/category": {
          "network": "all",
          "packages": ["goodModule"]
        },
        "pages/cart/cart": {
          "network": "all",
          "packages": ["orderPayModule"]
        }
      },
    }

3.3优化-关键按钮添加防抖函数

**思路分析:**

为了防止用户频繁点击按钮而导致的重复提交或者多次请求的问题,

我们需要给关键按钮添加防抖函数,这里可以使用 `licia` 提供的防抖函数

```js

import { debounce } from 'miniprogram-licia'

```

  1. 登录按钮

  2. 提交订单

    js 复制代码
    import { debounce } from 'miniprogram-licia'
    
    debounce(function () {
        
      // coding...
        
    }, 500)

3.4优化-代码质量检测

如何使用微信开发者工具进行代码质量检测

<img src="http://8.131.91.46:6677/mina/floor/代码质量检测.png" style="zoom: 56%;" />

代码质量检测标准:

<img src="http://8.131.91.46:6677/mina/floor/代码质量检测标准.png" style="zoom:80%;" />

相关推荐
admin and root9 小时前
Claude+Trae大模型 配置Chrome MCP联动Yakit自动化渗透测试
微信小程序·渗透测试·自动化·攻防演练·ai安全·claude code·ai自动化渗透测试
code_li1 天前
小程序上线需要的资质证书汇总
小程序·上线·发布·资质
hnxaoli1 天前
统信小程序(十三)循环键鼠操作程序
python·小程序
i查拉图斯特拉如是1 天前
使用workbuddy 30分钟搭建微信小程序
微信小程序·小程序
IceSugarJJ1 天前
Open-AutoGLM项目学习
语言模型·微信小程序·github
2501_916008891 天前
Mac 上生成 AppStoreInfo.plist 文件,App Store 上架
android·macos·ios·小程序·uni-app·iphone·webview
咖啡の猫1 天前
小程序协同工作和发布
小程序
维双云1 天前
小程序怎么制作工具?与其盲目找开发,不如先分清自己要哪一种
小程序
qq_9109462921 天前
校园共享电动车租赁小程序
小程序