微信小程序 ---- 慕尚花坊 商品管理

商品管理

01. 配置商品管理分包

思路分析:

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

因此我们需要将 商品列表商品详情 功能配置成一个分包,

当用户在访问设置页面时,还预先加载 商品列表商品详情 所在的分包

在分包后,通过查看代码依赖查看是否分包完成

📌 注意事项

  1. 在配置好 商品列表商品详情 的分包以后,需要更改页面中的跳转路径
  2. PS:可以利用项目全局搜索的功能,进行批量更改

实现步骤:

  1. modules 目录下创建 goodModule 文件夹,用来存放商品管理分包
  2. app.jsonsubpackages 进行商品管理分包配置
  3. app.jsonpreloadRule 进行商品管理分包配置

落地代码:

➡️ 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"]
    }
  ],
  "preloadRule": {
    "pages/settings/settings": {
      "network": "all",
      "packages": ["settingModule"]
    },
+     "pages/category/category": {
+       "network": "all",
+       "packages": ["goodModule"]
+     }
  }
}

02. 封装商品模块接口 API

思路分析:

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

落地代码:

➡️ api/goods.js

js 复制代码
import http from '../utils/http'

/**
 * @description 获取商品列表
 * @return Promise
 */
export const reqGoodsList = ({ limit, page, ...reset }) => {
  return http.get(`/mall-api/goods/list/${page}/${limit}`, reset)
}

/**
 * @description 获取商品详情
 * @param {*} goodsId 商品Id
 * @returns Promise
 */
export const reqGoodsInfo = (goodsId) => {
  return http.get(`/mall-api/goods/${goodsId}`)
}

03. 商品列表-准备列表请求参数

思路分析:

当用户点击了商品分类以后,需要获取对应分类的商品列表信息,因此我们需要先获取到该分类的 id,只要获取到 id 以后,才能向服务器获取对应分类的商品列表信息。同时我们需要查看接口文档,查看是否需要使用其他参数,我们提前将参数准备好。

首先熟悉接口文档:获取商品分页列表

参数名称 参数说明 是否必须
limit 每页记录数 true
page 当前页码 true
category1Id 一级分类的 Id (从首页导航分类区域点击进入) false
category2Id 二级分类的 Id (从分类页面点击进入二级分类进入) false

通过接口文档得知,我们需要以上的参数,我们先将参数提前声明,然后在发起请求获取商品列表的数据

实现步骤:

  1. 在商品列表的 data 字段中,根据接口文档,定义商品列表接口需要使用的字段
  2. 在商品列表的 onLoad 钩子函数中接收请求的参数,并将请求参数进行合并

落地代码:

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

js 复制代码
Page({
  // 页面的初始数据
  data: {
    goodsList: [], // 商品列表数据
    isFinish: false, // 判断数据是否加载完毕

+     // 接口请求参数
+     requestData: {
+       page: 1, // 页码
+       limit: 10, // 每页请求多少条数据
+       category1Id: '', // 一级分类 id
+       category2Id: '' // 二级分类 id
+     }
  },

+     // 生命周期函数--监听页面加载
+     onLoad(options) {
+       // 接收传递的参数
+       Object.assign(this.data.requestData, options)
+     }
})

04. 商品列表-获取商品列表数据并渲染

思路分析:

在准备商品列表的请求参数以后,

在页面调用 API 函数获取商品列表的数据,在获取到数据以后,使用后端返回的数据对页面进行渲染。

实现步骤:

  1. /pages/goods/list/list.js 中导入封装好的获取商品列表的 API 函数
  2. 页面数据在页面加载的时候进行调用,在 onLoad 钩子函数中调用 reqGoodsList 方法
  3. 在获取到数据以后,使用后端返回的数据对页面进行渲染

落地代码:

➡️ /modules/goodsModules/pages/list/list.js

js 复制代码
+ import { reqGoodsList } from '../../../../../api/good'

Page({
  // 页面的初始数据
  data: {
    goodsList: [], // 商品列表数据
+     total: 0, // 数据总条数
    isFinish: false, // 判断数据是否加载完毕
    // 接口请求参数
    requestData: {
      page: 1, // 页码
      limit: 10, // 每页请求多少条数据
      category1Id: '', // 一级分类 id
      category2Id: '' // 二级分类 id
    }
  },

+   // 获取商品列表的数据
+   async getGoodsList() {
+     // 调用 API 获取数据
+     const { data } = await reqGoodsList(this.data.requestData)
+ 
+     // 将返回的数据赋值给 data 中的变量
+     this.setData({
+       goodsList: data.records,
+       total: data.total
+     })
+   },

  // 生命周期函数--监听页面加载
  onLoad(options) {
    // 接收传递的参数
    Object.assign(this.data.requestData, options)

+    // 获取商品列表的数据
+    this.getGoodsList()
  }
})

➡️ /modules/goodsModule/pages/list/list.wxml

html 复制代码
<view class="container">
  <!-- 商品列表功能 -->
+   <view class="goods-list" wx:if="{{ goodsList.length }}">
+     <block wx:for="{{ goodsList }}" wx:key="id">
+       <goods-card goodItem="{{ item }}"></goods-card>
+     </block>

    <!-- 数据是否加载完毕 -->
    <view class="finish" hidden="{{ !isFinish }}">数据加载完毕~~~</view>
  </view>

+   <!-- 商品为空的时候展示的结构 -->
+   <van-empty wx:else description="该分类下暂无商品,去看看其他商品吧~">
+     <van-button round type="danger" class="bottom-button" bindtap="gotoBack">
+       查看其他商品
+     </van-button>
+   </van-empty>
</view>

05. 商品列表-实现上拉加载更多功能

思路分析:

当用户从下向上滑动屏幕时,需要加载更多的商品数据。

首先需要在 .js 文件中声明 onReachBottom 方法监听用户是否进行了上拉

当用户上拉时,需要对 page 页码进行加 1,代表要请求下一页的数据

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

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

实现步骤:

  1. list.js 文件中声明 onReachBottom 事件处理函数,监听用户的上拉行为
  2. onReachBottom 函数中加 page 进行加 1 的操作,同时发送请求获取下一页数据
  3. getGoodsList 函数中,实现参数的合并

落地代码:

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

js 复制代码
import { reqGoodsList } from '../../../api/goods'

Page({

  // coding...

  // 获取商品列表的数据
  async getGoodsList() {
    // 调用 API 获取数据
    const { data } = await reqGoodsList(this.data.params)

    // 将返回的数据赋值给 data 中的变量
    this.setData({
+       goodsList: [...this.data.goodsList, ...data.records],
      total: data.total
    })
  },

  // coding...

+   // 监听页面的上拉操作
+   onReachBottom() {
+     let { page } = this.data.requestData
+ 
+     // 页码 + 1
+     this.setData({
+       requestData: { ...this.data.requestData, page: page + 1 }
+     })
+ 
+     // 重新发送请求
+     this.getGoodsList()
+   }

})

06. 商品列表-判断数据是否加载完毕

思路分析:

上一节我们实现了上拉加载功能。

在这一节,我们需要相关的优化:判断数据是否已经加载完,如果加载已经加载完毕,需要给用户进行提示。

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

可以使用后端返回的 totalgoodsList 进行对比,如果 total 大于等于 goodsList ,说明商品列表数据没有加载完,可以继续上拉加载更多。

在模板中,我们通过 totalgoodsList 进行对比,决定是否展示对应的文案

实现步骤:

  1. 在数据返回以后,将数据中的 total 赋值给 data 中的变量 total
  2. onReachBottom 中进行 totalgoodsList 进行对比
  3. 模板中使用 totalgoodsList 进行对比

落地代码:

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

js 复制代码
import { reqGoodsList } from '../../../api/goods'

Page({
    
  // coding...
    
  // 监听页面的上拉操作
  onReachBottom() {
+     // 从 data 中解构数据
+     const { total, goodsList, requestData } = this.data
+     let { page } = requestData
+ 
+     // 判断数据是否加载完毕
+     if (total === goodsList.length) {
+       // 如果相等,数据数据加载完毕
+       // 如果数据加载完毕,需要给用户提示,同时不继续加载下一个数据
+       this.setData({
+         isFinish: true
+       })
+ 
+       return
+     }

    // 页码 + 1
    this.setData({
      requestData: { ...this.data.requestData, page: (page += 1) }
    })

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

07. 商品列表-节流阀进行列表节流

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

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

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

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

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

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

落地代码:

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

js 复制代码
import { reqGoodsList } from '../../../../../api/good'

Page({
  // 页面的初始数据
  data: {
    goodsList: [], // 商品列表数据
    isFinish: false, // 判断数据是否加载完毕
+     isLoading: false, // 判断数据是否记载完毕
    total: 0, // 列表总数据量
    // 接口请求参数
    requestData: {
      page: 1, // 页码
      limit: 10, // 每页请求多少条数据
      category1Id: '', // 一级分类 id
      category2Id: '' // 二级分类 id
    }
  },

  // 获取商品列表的数据
  async getGoodsList() {
+     // 数据真正请求中
+     this.data.isLoading = true

    // 调用 API 获取数据
    const { data } = await reqGoodsList(this.data.requestData)

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

    // 将返回的数据赋值给 data 中的变量
    this.setData({
      goodsList: [...this.data.goodsList, ...data.records],
      total: data.total
    })
  },

  // 监听页面的上拉操作
  onReachBottom() {
    // 从 data 中解构数据
    const { total, goodsList, requestData, isLoading } = this.data
    let { page } = requestData

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

    // 判断数据是否加载完毕
    // coding...
  }
})

08. 商品列表-实现下拉刷新功能

下拉刷新是小程序中常见的一种刷新方式,当用户下拉页面时,页面会自动刷新,以便用户获取最新的内容。

小程序中实现上拉加载更多的方式:

  1. 页面.json 中开启允许下拉,同时可以配置 窗口、loading 样式等

  2. 页面.js 中定义 onPullDownRefresh 事件监听用户下拉刷新

落地代码:

➡️ /modules/goodsModule/pages/list/list.json

json 复制代码
{
  "usingComponents": {
    "goods-card": "/components/goods-card/goods-card"
  },

  "navigationBarTitleText": "商品列表",
  "enablePullDownRefresh": true,
  "backgroundColor": "#f7f4f8",
  "backgroundTextStyle": "dark"
}

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

js 复制代码
// 监听页面的下拉刷新
onPullDownRefresh() {
  // 将数据进行重置
  this.setData({
    goodsList: [],
    total: 0,
    isFinish: false,
    requestData: { ...this.data.requestData, page: 1 }
  })

  // 重新获取列表数据
  this.getGoodsList()
}

09. 商品详情-获取并渲染商品详情

思路分析:

点击首页轮播图以及点击商品列表商品的时候,需要跳转到商品详情页面

在跳转时将商品的id 传递到了商品详情页面,只需要使用 id 向后端服务器请求数据,获取对应商品的详情数据

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

实现步骤:

  1. /pages/goods/detail/detail.js 中导入封装好的获取商品列表的 API 函数
  2. 页面数据在页面加载的时候进行调用,在 onLoad 钩子函数中调用 reqGoodsInfo 方法
  3. 在获取到数据以后,使用后端返回的数据对页面进行渲染

落地代码:

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

js 复制代码
+ import { reqGoodsInfo } from '../../../api/goods'

Page({

  // 页面的初始数据
  data: {
    goodsInfo: {}, // 商品详情
    show: false, // 控制加入购物车和立即购买弹框的显示
    count: 1, // 商品购买数量,默认是 1
    blessing: '' // 祝福语
  },

+   // 获取商品的详情
+   async getGoodsInfo() {
+     // 调用接口、传入参数、获取商品详情
+     const { data: goodsInfo } = await reqGoodsInfo(this.goodsId)
+ 
+     // 将商品详情数据赋值给 data 中的变量
+     this.setData({
+       goodsInfo
+     })
+   },

+   // 生命周期函数--监听页面加载
+   onLoad(options) {
+     // 将商品 id 挂载到页面实例上
+     this.goodsId = options.goodsId ? options.goodsId : ''
+ 
+     // 获取商品详情的数据
+     this.getGoodsInfo()
+   }

  // coding...
})

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

html 复制代码
<view class="container goods-detail">
  <!-- 商品大图 -->
  <view class="banner-img">
+     <image class="img" src="{{ goodsInfo.imageUrl }}" />
  </view>

  <!-- 商品的基本信息 -->
  <view class="content">
    <view class="price">
+       <view class="price-num">¥{{ goodsInfo.price }}</view>
+       <view class="price-origin-num">¥{{ goodsInfo.marketPrice }}</view>
    </view>
+     <view class="title">{{ goodsInfo.name }}</view>
+     <view class="desc">{{ goodsInfo.material }}</view>
  </view>

  <!-- 商品的详细信息 -->
  <view class="detail">
    <image
+       wx:for="{{ goodsInfo.detailList}}"
+       wx:key="index"
+       src="{{ item }}"
      class="img"
      mode="widthFix"
    />
  </view>

  <!-- 商品的底部商品导航 -->
  <van-goods-action>
    <!-- 代码略... -->
  </van-goods-action>

  <!-- 加入购物车、立即购买弹框 -->
  <!-- show 控制弹框的隐藏和展示 -->
  <!-- bind:close 点击关闭弹框时触发的回调 -->
  <van-action-sheet show="{{ show }}" bind:close="onClose">
    <view class="sheet-wrapper">
      <view class="goods-item">
        <!-- 需要购买的商品图片 -->
        <view class="mid">
+           <image class="img" src="{{ goodsInfo.imageUrl }}" />
        </view>

        <!-- 商品基本信息 -->
        <view class="right">
          <!-- 商品名字 -->
+           <view class="title"> {{ goodsInfo.name }} </view>
          <!-- 商品价格 -->
          <view class="buy">
            <view class="price">
              <view class="symbol">¥</view>
+               <view class="num">{{ goodsInfo.price }}</view>
            </view>

            <!-- 购买数量弹框 -->
            <view class="buy-btn">
              <!-- Stepper 步进器,由增加按钮、减少按钮和输入框组成,控制购买数量 -->
              <van-stepper value="{{ count }}" bind:change="onChangeGoodsCount" />
            </view>
          </view>
        </view>
      </view>

      <!-- 祝福语输入框 -->
      <view class="time-wraper">
        <!-- 代码略... -->
      </view>

      <!-- 取消、确定弹框 -->
      <view class="sheet-footer-btn">
        <van-button block type="primary" round> 确定 </van-button>
      </view>
    </view>
  </van-action-sheet>
</view>

10. 商品详情-详情图片预览功能

思路分析:

当点击商品的图片时,需要将图片进行全屏预览

如果想实现该功能,需要使用小程序提供的 APIwx.previewImage(),用来在新页面中全屏预览图片。预览的过程中用户可以进行保存图片、发送给朋友等操作。语法如下:

js 复制代码
wx.previewImage({
  current: '', // 当前显示图片的 http 链接
  urls: [] // 需要预览的图片 http 链接列表
})

实现步骤:

  1. 给展示大图的 image 组件绑定点击事件,同时通过自定义属性的方式,传递当前需要显示的图片http 链接
  2. 同时商品详情的数组数据传递给 urls 数组即可

落地代码:

➡️ /pages/goods/detail/detail.html

html 复制代码
<!-- 商品大图 -->
<view class="banner-img">
  <image
     class="img"
     src="{{ goodsInfo.imageUrl }}"
     bindtap="previewImg"
  />
</view>

➡️ /pages/goods/detail/detail.js

js 复制代码
// 预览商品图片
previewImg() {
  // 调用预览图片的 API
  wx.previewImage({
    urls: this.data.goodsInfo.detailList
  })
}

优化:配置 @ 路径别名优化访问路径

在对小程序进行分包时,如果访问小程序根目录下的文件,那么访问的路径就会很长。

在 Vue 中,可以使用 @ 符号指向源码目录,简化路径,小程序也给提供了配置的方式。

在小程序中可以在 app.json 中使用 resolveAlias 配置项用来自定义模块路径的映射规则。

官方文档: resolveAlias

json 复制代码
{
  "resolveAlias": {
    "@/*": "/*"
  }
}

📌:注意事项:

  1. resolveAlias 进行的是路径匹配,其中的 key 和 value 须以 /* 结尾

  2. 如果在 project.config.json 中指定了 miniprogramRoot,则 /* 指代的根目录是 miniprogramRoot 对应的路径,而不是开发者工具项目的根目录

相关推荐
丁总学Java6 小时前
微信小程序,点击bindtap事件后,没有跳转到详情页,有可能是app.json中没有正确配置页面路径
微信小程序·小程序·json
说私域7 小时前
基于开源 AI 智能名片、S2B2C 商城小程序的用户获取成本优化分析
人工智能·小程序
mosen8687 小时前
Uniapp去除顶部导航栏-小程序、H5、APP适用
vue.js·微信小程序·小程序·uni-app·uniapp
qq22951165028 小时前
微信小程序的汽车维修预约管理系统
微信小程序·小程序·汽车
尚梦15 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
小飞哥liac18 小时前
微信小程序的组件
微信小程序
stormjun19 小时前
Java基于微信小程序的私家车位共享系统(附源码,文档)
java·微信小程序·共享停车位·私家车共享停车位小程序·停车位共享
paopaokaka_luck20 小时前
基于Spring Boot+Vue的助农销售平台(协同过滤算法、限流算法、支付宝沙盒支付、实时聊天、图形化分析)
java·spring boot·小程序·毕业设计·mybatis·1024程序员节
Bessie2341 天前
微信小程序eval无法使用的替代方案
微信小程序·小程序·uni-app
shenweihong1 天前
javascript实现md5算法(支持微信小程序),可分多次计算
javascript·算法·微信小程序