文章目录
- 黑马优购
-
- 1. 项目介绍
- 2. 初始化项目
- 3. 首页
- 4. 优化
- 5. 分类页面
- 6. interceptor 拦截器
- 7. 搜索
- 8. 商品列表
- 9. 商品详情
- 10. 加入购物车
- 11. 购物车
-
- 11.1 实现空白购物车和非空购物车的按需展示
- 11.2 渲染购物车列表标题
- 11.3 循环渲染基本的商品列表结构
- 11.4 通过插槽自定义渲染商品的价格和数量
- 11.5 美化商品价格和数量区域
- 11.6 数量改变时获取最新的数量值和当前商品的Id
- 11.7 完成商品数量的更新操作
- 11.8 在商品之间渲染分割线
- 11.9 通过thumb插槽渲染复选框和缩略图
- 11.10. 美化复选框和缩略图的样式
- 11.11 监听复选框状态变化的事件
- 11.12 修改商品的选中状态
- 11.13 初步实现滑动删除的UI效果
- 11.14 美化滑动单元格右侧的删除按钮
- 11.15 根据Id从购物车列表中删除对应的商品
- 11.16 渲染提交订单区域的UI结构
- 11.17 通过计算属性动态计算勾选商品的总价格
- 11.18 通过计算属性判断全选的状态
- 11.19 点击全选更新所有商品的选中状态
- 11.20 为TabBar中的购物车添加数字徽章
- 11.21 为商品详情页的购物车图标添加数字徽章
- 11.22 提交订单
- 12. 确认订单
- 13. 订单支付
- 14. 订单列表
- 15. 发布小程序
黑马优购
1. 项目介绍
首页、分类、搜索、商品列表、商品详情、购物车、支付
2. 初始化项目
2.1 初始化项目
- 运行
wepy init standard heima_ugo命令,初始化小程序项目 - 运行
cd heima_ugo进入项目根目录 - 运行
npm install安装所有依赖项 - 运行
wepy build --watch命令,开启 wepy 项目的实时编译功能 - 打开微信开发者工具,加载 wepy 项目并查看效果
- 解决 ESLint 语法报错问题
2.2 梳理项目结构
- 清理并重置
src -> pages -> index.wpy首页 - 在根目录的
.prettierrc配置文件内,新增"semi": false配置,防止每次格式化代码,添加分号的问题 - 清理并重置
src -> app.wpy中的代码,将style和script标签中,不必要的代码删除掉 - 清空
src -> components和src -> mixins目录 - 将梳理完毕后的项目,上传至码云
2.3 绘制 tabBar
-
新建
src -> pages -> tabs文件夹,用来存放所有 tabBar 相关的页面 -
删除
src -> pages -> index.wpy页面,并在tabs目录中,新建home.wpy,cates.wpy,search.wpy,cart.wpy,me.wpy五个 tabBar 相关的页面 -
将页面路径,记录到
src -> app.wpy文件的config -> pages节点中,示例代码如下:pages: [ 'pages/tabs/home', 'pages/tabs/cates', 'pages/tabs/search', 'pages/tabs/cart', 'pages/tabs/me' ] -
新建
src -> assets目录,并将素材中的icons文件夹,拷贝到项目src -> assets目录中 -
在
src -> app.wpy文件中,新增tabBar节点,并做如下配置:tabBar: { // 选中的文本颜色 selectedColor: '#D81E06', // tabBar 的列表 list: [ { // 页面路径 pagePath: 'pages/tabs/home', // 显示的文本 text: '首页', // 默认图标 iconPath: '/assets/icons/home.png', // 选中图标 selectedIconPath: '/assets/icons/home-active.png' }, { pagePath: 'pages/tabs/cates', text: '分类', iconPath: '/assets/icons/cates.png', selectedIconPath: '/assets/icons/cates-active.png' }, { pagePath: 'pages/tabs/search', text: '搜索', iconPath: '/assets/icons/search.png', selectedIconPath: '/assets/icons/search-active.png' }, { pagePath: 'pages/tabs/cart', text: '购物车', iconPath: '/assets/icons/cart.png', selectedIconPath: '/assets/icons/cart-active.png' }, { pagePath: 'pages/tabs/me', text: '我的', iconPath: '/assets/icons/my.png', selectedIconPath: '/assets/icons/my-active.png' } ] }
2.4 修改导航栏样式
打开 src -> app.wpy 文件,找到 window 节点,并配置如下:
window: {
// 页面背景色
backgroundTextStyle: 'dark',
// 导航条背景色
navigationBarBackgroundColor: '#D81E06',
// 导航条标题文本
navigationBarTitleText: '黑马优购',
// 导航条标题文字颜色
navigationBarTextStyle: 'white'
}
3. 首页
3.1 为异步 API 启用 Promise 功能
-
打开
src -> app.wpy文件 -
找到 constructor() 构造函数
-
在构造函数的最后,新增如下代码:
constructor() { super() this.use('requestfix') // 通过下面这一行代码,可以为异步的API, // 开启Promise功能,这样,异步API调用的结果,返回值是Promise对象 this.use('promisify') }
3.2 轮播图数据渲染
-
获取轮播图数据
// 获取轮播图数据的函数 async getSwiperData() { const { data: res } = await wepy.get('/home/swiperdata') if (res.meta.status !== 200) { return wepy.baseToast() } this.swiperList = res.message this.$apply() } -
使用
wepy.showToast()弹框提示 -
使用
swiper组件和swiper-item组件渲染轮播图效果 -
使用
navigator组件将images图片包裹起来,从而点击图片实现跳转<!-- 轮播图区域 --> <swiper circular indicator-dots> <swiper-item wx:for="{ {swiperList}}" wx:key="index"> <navigator url="{ {item.navigator_url}}" open-type="{ {item.open_type}}"> <image src="{ {item.image_src}}" /> </navigator> </swiper-item> </swiper> -
设置
swiper组件的高度为350rpx从而实现轮播图在不同屏幕的自适应swiper { height: 350rpx; navigator, image { height: 100%; width: 750rpx; } }
3.3 获取首页分类选项数据
// 获取首页分类相关的数据项
async getCateItems() {
const {
data: res } = await wepy.get('/home/catitems')
if (res.meta.status !== 200) {
return wepy.baseToast()
}
this.cateItems = res.message
this.$apply()
}
3.4 渲染分类数据项对应的UI结构
<!-- 分类区域 -->
<view class="cates">
<block wx:for="{
{cateItems}}" wx:key="index">
<navigator url="/pages/tabs/cates" open-type="{
{item.open_type}}" wx:if="{
{item.navigator_url !== undefined}}" hover-class="none">
<image src="{
{item.image_src}}" />
</navigator>
<image src="{
{item.image_src}}" wx:else/>
</block>
</view>
3.5 美化分类数据项的UI显示效果
.cates {
display: flex;
justify-content: space-around;
margin: 40rpx 0;
image {
width: 128rpx;
height: 140rpx;
}
}
3.6 获取楼层相关的数据
onLoad() {
this.getSwiperData()
this.getCateItems()
// 在页面加载完成后,自动获取楼层数据
this.getFloorData()
}
// 获取楼层相关的数据
async getFloorData() {
const {
data: res } = await wepy.get('/home/floordata')
if (res.meta.status !== 200) {
return wepy.baseToast()
}
this.floorData = res.message
// 通知页面,data中数据发生了变化,需要强制页面重新渲染一次
this.$apply()
}
3.7 渲染楼层UI结构
<!-- 楼层区域 -->
<view class="floor-container">
<view class="floor-item" wx:for="{
{floorData}}" wx:key="index">
<!-- 楼层的标题 -->
<image class="floor-item-title" src="{
{item.floor_title.image_src}}"/>
<!-- 楼层的图片 -->
<view class="floor-img-box">
<image class="floor-item-pic" wx:for="{
{item.product_list}}" wx:key="index" src="{
{item.image_src}}" style="width: {
{
item.image_width}}rpx;" @tap="goGoodsList({
{item.navigator_url}})"/>
</view>
</view>
</view>
3.8 美化楼层UI结构
.floor-container {
.floor-item {
.floor-item-title {
height: 50rpx;
width: 640rpx;
display: block;
}
.floor-img-box {
.floor-item-pic {
float: left;
height: 190rpx;
margin: 8rpx;
margin-top: 0;
&:nth-child(1) {
height: 390rpx;
}
}
}
}
}
3.9 点击楼层图片跳转到商品列表页面
methods = {
// 点击楼层中的每一张图片,都要跳转到商品列表页面
goGoodsList(url) {
wepy.navigateTo({
url
})
}
}
4. 优化
4.1 把页面的业务逻辑抽离到单独的 mixin 文件中
为了精简每个小程序页面的代码,可以将 script 中的业务逻辑,抽离到对应的 mixin 文件中,具体步骤:
-
在
src -> mixins文件夹中,新建与页面路径对应的.js文件,并初始化基本的代码结构如下:import wepy from 'wepy' // 注意,必须继承自 wepy.mixin export default class extends wepy.mixin { } -
在对应的页面中,可以导入并使用对应的 mixin,具体代码如下:
<script> import wepy from 'wepy' // 1. 导入外界的 mixin 文件,并接受 // @ 就代表 src 这一层路径 import mix from '@/mixins/tabs/home.js' export default class extends wepy.page { // 2. 把导入的 mix 对象,挂载到 mixins 这个数据中就行 mixins = [mix] } </script>
4.2 封装 baseToast 函数提示错误消息
-
为了提高项目的维护性、可用性、扩展性,可以将常用的 js 逻辑,封装到
src -> baseAPI.js文件中:import wepy from 'wepy' /** * 弹框提示一个无图标的 Toast 消息 * @str 要提示的消息内容 */ wepy.baseToast = function(str = '获取数据失败!') { wepy.showToast({ title: str, // 弹框期间不会携带任何图标 icon: 'none', duration: 1500 }) } -
在
app.wpy中导入执行baseAPI.js文件中的代码:<script> import wepy from 'wepy' import 'wepy-async-function' // 导入并执行 baseAPI.js 中的所有代码 import '@/baseAPI.js' </script>
4.3 封装 wepy.get 函数发起get请求
在小程序项目中,需要经常发起数据请求,因此,可以将 wepy.request() 函数封装,在全局挂在 wepy.get() 函数,从而发起 Get 请求,代码如下:
// src/baseAPI.js
import wepy from 'wepy'
// 请求根路径
const baseURL = 'https://www.zhengzhicheng.cn/api/public/v1'
/**
* 发起 get 请求的 API
* @url 请求的地址,为相对路径,必须以 / 开头
* @data 请求的参数对象
*/
wepy.get = function(url, data = {
}) {
return wepy.request({
url: baseURL + url,
method: 'GET',
data
})
}
4.4 封装 wepy.post 函数发起get请求
在小程序项目中,需要经常发起数据请求,因此,可以将 wepy.request() 函数封装,在全局挂在 wepy.post() 函数,从而发起 Post 请求,代码如下:
// src/baseAPI.js
import wepy from 'wepy'
// 请求根路径
const baseURL = 'https://www.zhengzhicheng.cn/api/public/v1'
/**
* 发起 post 请求的 API
* @url 请求的地址,为相对路径,必须以 / 开头
* @data 请求的参数对象
*/
wepy.post = function (url, data = {
}) {
return wepy.request({
url: baseURL + url,
method: 'POST',
data
})
}
5. 分类页面
5.1 自定义分类页面的编译模式
- 点击工具栏中,编译模式的下拉菜单,选择新建编译模式
- 填写编译模式的名称
- 选择启动页面的路径
- 确认添加
5.2 获取分类数据列表
async getCateList() {
const {
data: res } = await wepy.get('/categories')
if (res.meta.status !== 200) {
return wepy.baseToast()
}
this.cateList = res.message
this.secondCate = res.message[0].children
this.$apply()
}
5.3 下载并安装 vant 小程序UI组件库
- 访问
vant-weapp的 Github 主页 https://github.com/youzan/vant-weapp - 点击
Clone or Download按钮 - 选择
Download ZIP - 解压下载的
vant-weapp-dev.zip - 进入解压后的目录,将
lib目录重命名为vant - 把重命名为
vant的目录,复制到src -> assets目录中
5.4 将 vant 中的徽章组件注册为全局组件
-
打开
app.wpy文件 -
在
config节点内,新增usingComponents节点,具体代码如下:config = { // 引用并注册全局组件 usingComponents: { // 徽章组件 'van-badge': './assets/vant/badge/index', 'van-badge-group': './assets/vant/badge-group/index' } }
5.5 渲染左侧的一级分类列表结构
<van-badge-group active="{
{ active }}" bind:change="onChange">
<van-badge title="{
{item.cat_name}}" wx:for="{
{cateList}}" wx:key="index" />
</van-badge-group>
5.6 使用 scroll-view 优化左侧分类的滚动效果
<!-- 左侧的滚动视图区域 -->
<scroll-view class="left" scroll-y style="height: 200px;">
<van-badge-group active="{
{ active }}" bind:change="onChange">
<van-badge title="{
{item.cat_name}}" wx:for="{
{cateList}}" wx:key="index" />
</van-badge-group>
</scroll-view>
5.7 动态获取窗口的可用高度
onLoad() {
// 动态获取屏幕可用的高度
this.getWindowHeight()
this.getCateList()
}
// 动态获取屏幕可用的高度
async getWindowHeight() {
const res = await wepy.getSystemInfo()
if (res.errMsg === 'getSystemInfo:ok') {
this.wh = res.windowHeight
this.$apply()
}
}
5.8 根据一级分类的变化动态切换二级分类数据
methods = {
onChange(e) {
// e.detail 是点击项的索引
// console.log(e.detail)
this.secondCate = this.cateList[e.detail].children
}
}
5.9 渲染二级和三级分类的UI结构
<!-- 右侧滚动视图区域 -->
<scroll-view class="right" scroll-y style="height: {
{
wh}}px;">
<!-- 循环创建二级分类 -->
<block wx:for="{
{secondCate}}" wx:key="index">
<van-row>
<van-col span="24" style="text-align:center;">
<text class="cate_title" space="ensp">/ {
{item.cat_name}} /</text>
</van-col>
</van-row>
<!-- 三级分类 -->
<van-row>
<block wx:for="{
{item.children}}" wx:key="index">
<van-col span="8" class="cell" @tap="goGoodsList({
{item.cat_id}})">
<image src="{
{item.cat_icon}}" class="thumbImg" />
<view class="thumbTitle">{
{item.cat_name}}</view>
</van-col>
</block>
</van-row>
</block>
</scroll-view>
5.10 点击三级分类跳转到商品列表页面
methods = {
// 点击跳转到商品列表页面,同时把商品分类的 cid 传递过去
goGoodsList(cid) {
wepy.navigateTo({
url: '/pages/goods_list?cid=' + cid
})
}
}
6. interceptor 拦截器
6.1 介绍 wepy 中的拦截器
可以使用WePY提供的全局拦截器对原生API的请求进行拦截。
具体方法是配置API的config、fail、success、complete回调函数。参考示例:
import wepy from 'wepy';
export default class extends wepy.app {
constructor () {
// this is not allowed before super()
super();
// 拦截request请求
this.intercept('request', {
// 发出请求时的回调函数
config (p) {
// 对所有request请求中的OBJECT参数对象统一附加时间戳属性
p.timestamp = +new Date();
console.log('config request: ', p);
// 必须返回OBJECT参数对象,否则无法发送请求到服务端
return p;
},
// 请求成功后的回调函数
success (p) {
// 可以在这里对收到的响应数据对象进行加工处理
console.log('request success: ', p);
// 必须返回响应数据对象,否则后续无法对响应数据进行处理
return p;
},
//请求失败后的回调函数
fail (p) {
console.log('request fail: ', p);
// 必须返回响应数据对象,否则后续无法对响应数据进行处理
return p;
},
// 请求完成时的回调函数(请求成功或失败都会被执行)
complete (p) {
console.log('request complete: ', p);
}
});
}
}
6.2 实现数据加载期间的loading效果
打开 app.wpy,在 constructor() 构造函数中,通过拦截器实现loading效果,具体代码如下:
constructor() {
super()
this.use('requestfix')
// 通过这一行代码,可以为异步的API,开启Promise功能,这样,异步API调用的结果,返回值是Promise对象
this.use('promisify')
// 拦截器
this.intercept('request', {
// 发出请求时的回调函数
config(p) {
// 显示loading效果
wepy.showLoading({
title: '数据加载中...'
})
// 必须返回OBJECT参数对象,否则无法发送请求到服务端
return p
},
// 请求成功后的回调函数
success(p) {
// 必须返回响应数据对象,否则后续无法对响应数据进行处理
return p
},
// 请求失败后的回调函数
fail(p) {
// 必须返回响应数据对象,否则后续无法对响应数据进行处理
return p
},
// 请求完成时的回调函数(请求成功或失败都会被执行)
complete(p) {
// 隐藏loading效果
wepy.hideLoading()

本文档详细介绍了如何从零开始构建一个名为“黑马优购”的小程序项目,涵盖项目初始化、页面设计、前后端交互等核心内容。通过实战演练,读者可以深入理解小程序的开发流程和技术要点。

3882

被折叠的 条评论
为什么被折叠?



