首先在全局app.json配置一下项目需要的页面和底部tabBar
"pages": [
"pages/index/index",
"pages/category/index",
"pages/goods_list/index",
"pages/goods_detail/index",
"pages/cart/index",
"pages/collect/index",
"pages/order/index",
"pages/search/index",
"pages/user/index",
"pages/feedback/index",
"pages/login/index",
"pages/auth/index",
"pages/pay/index"
],
底部tabBar
"tabBar": {
"color": "#999",
"selectedColor": "#ff2d4a",
"backgroundColor": "#fafafa",
"position": "bottom",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "icons/home.png",
"selectedIconPath": "icons/home-o.png"
},
{
"pagePath": "pages/category/index",
"text": "分类",
"iconPath": "icons/category.png",
"selectedIconPath": "icons/category-o.png"
}
,
{
"pagePath": "pages/cart/index",
"text": "购物车",
"iconPath": "icons/cart.png",
"selectedIconPath": "icons/cart-o.png"
}
,
{
"pagePath": "pages/user/index",
"text": "我的",
"iconPath": "icons/my.png",
"selectedIconPath": "icons/my-o.png"
}
]
},
接下来是首页的基本布局,顶部搜索框,轮播图、九宫格布局、楼层布局
对搜索框进行拆分,方便复用
首先创建一个components文件夹用来存储拆分的子组件
在子组件的json文件中引入微信小程序自带的轮播图组件
{
"usingComponents": {
"mp-searchbar": "weui-miniprogram/searchbar/searchbar"
},
"component":true
}
在页面上直接使用
<mp-searchbar bindselectresult="selectResult" search="{{search}}" bindtap="focus" cancel='{{false}}'></mp-searchbar>
在父组件的json文件中注册一下子组件
{
"usingComponents": {
"search":"/components/search/index"
}
}
然后在父组件中直接当作普通标签使用
<search></search>
请求轮播图的数据,将原生的请求方式修改为promise的方式
在需要用到的页面的js文件中的最上方引入封装好的方法
import { request } from "../../request/index.js";
再对数据进行获取
// 获取轮播图数据
getSwiperList(){
request({ url: "/home/swiperdata" })
.then(result => {
this.setData({
swiperList: result
})
})
},
最后使用小程序自带的轮播图组件将图片渲染上去就好了
然后是分类页面
我们先将数据和页面的关系梳理清楚
在获取数据的时候,我们将左侧和右侧的数据分别取出来
// 1 使用es7的async await来发送请求
const res = await request({ url: "/categories" });
this.Cates = res;
// 把接口的数据存入到本地存储中
wx.setStorageSync("cates", { time: Date.now(), data: this.Cates });
// 构造左侧的大菜单数据
let leftMenuList = this.Cates.map(v => v.cat_name);
console.log(leftMenuList)
// 构造右侧的商品数据
let rightContent = this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
},
然后给左侧的菜单添加点击事件,通过索引找到右侧相应的数据进行渲染就可以了
handleItemTap(e) {
/*
1 获取被点击的标题身上的索引
2 给data中的currentIndex赋值就可以了
3 根据不同的索引来渲染右侧的商品内容
*/
const { index } = e.currentTarget.dataset;
let rightContent = this.Cates[index].children;
this.setData({
currentIndex: index,
rightContent,
})
}
点击右侧商品跳转到匹配的商品列表
点击的时候将该商品的id传过去
<view class="goods_list">
<navigator
wx:for="{{item1.children}}"
wx:for-index="index2"
wx:for-item="item2"
wx:key="cat_id"
url="/pages/goods_list/index?cid={{item2.cat_id}}"
>
<image mode="widthFix" src="{{item2.cat_icon}}"></image>
<view class="goods_name">{{item2.cat_name}}</view>
</navigator>
</view>
获取商品列表数据的时候将需要用到的id,pagesize等参数带进去
// 获取商品列表数据
async getGoodsList(){
const res=await request({url:"/goods/search",data:this.QueryParams});
// 获取 总条数
const total=res.total;
// 计算总页数
this.totalPages=Math.ceil(total/this.QueryParams.pagesize);
// console.log(this.totalPages);
this.setData({
// 拼接了数组
goodsList:[...this.data.goodsList,...res.goods]
})
},
1.用户上滑页面 滚动条触底 开始加载下一页数据, 找到滚动条触底事件 微信小程序官方开发文档寻找
2.判断还有没有下一页数据, 获取到总页数 只有总条数
总页数 = Math.ceil(总条数 / 页容量 pagesize)
总页数 = Math.ceil( 23 / 10 ) = 3,
获取到当前的页码 pagenum,
判断一下 当前的页码是否大于等于 总页数
表示 没有下一页数据
3 假如没有下一页数据 弹出一个提示
4 假如还有下一页数据 来加载下一页数据
当前的页码 ++
重新发送请求
数据请求回来 要对data中的数组 进行 拼接 而不是全部替换!!!
onReachBottom(){
// 1 判断还有没有下一页数据
if(this.QueryParams.pagenum>=this.totalPages){
wx.showToast({ title: '没有下一页数据' });
}else{
this.QueryParams.pagenum++;
this.getGoodsList();
}
},
下拉刷新页面
1 触发下拉刷新事件 需要在页面的json文件中开启一个配置项
找到 触发下拉刷新的事件
2 重置 数据 数组
3 重置页码 设置为1
4 重新发送请求
5 数据请求回来 需要手动的关闭 等待效果
// 下拉刷新事件
onPullDownRefresh(){
// 1 重置数组
this.setData({
goodsList:[]
})
// 2 重置页码
this.QueryParams.pagenum=1;
// 3 发送请求
this.getGoodsList();
}
})
点击商品列表中某一条数据的时候,将id传过去,通过id查找到对应的商品详情数据
url="/pages/goods_detail/index?goods_id={{item.goods_id}}"
获取详情页面的数据信息
// 获取商品详情数据
async getGoodsDetail(goods_id) {
const goodsObj = await request({ url: "/goods/detail", data: { goods_id } });
this.GoodsInfo = goodsObj;
// 1 获取缓存中的商品收藏的数组
let collect = wx.getStorageSync("collect") || [];
// 2 判断当前商品是否被收藏
let isCollect = collect.some(v => v.goods_id === this.GoodsInfo.goods_id);
this.setData({
goodsObj: {
goods_name: goodsObj.goods_name,
goods_price: goodsObj.goods_price,
// iphone部分手机 不识别 webp图片格式
// 最好找到后台 让他进行修改
// 临时自己改 确保后台存在 1.webp => 1.jpg
goods_introduce: goodsObj.goods_introduce.replace(/\.webp/g, '.jpg'),
pics: goodsObj.pics
},
isCollect
})
},
然后对详情页面的数据进行渲染和布局
当用户点击加入购物车的时候,先绑定点击事件,获取缓存中的购物车数据,然后判断当前商品是否已经存在购物车,已经存在的话,修改商品数据,执行商品数量++,重新把购物车数组填充回缓存中,不存在的话,直接给购物车数组添加一个新元素,带上数量num属性,重新把购物车数组填充回缓存中,最后弹出添加成功的提示框
// 点击 加入购物车
handleCartAdd() {
// 1 获取缓存中的购物车 数组
let cart = wx.getStorageSync("cart") || [];
// 2 判断 商品对象是否存在于购物车数组中
let index = cart.findIndex(v => v.goods_id === this.GoodsInfo.goods_id);
if (index === -1) {
//3 不存在 第一次添加
this.GoodsInfo.num = 1;
this.GoodsInfo.checked = true;
cart.push(this.GoodsInfo);
} else {
// 4 已经存在购物车数据 执行 num++
cart[index].num++;
}
// 5 把购物车重新添加回缓存中
wx.setStorageSync("cart", cart);
// 6 弹窗提示
wx.showToast({
title: '加入成功',
icon: 'success',
// true 防止用户 手抖 疯狂点击按钮
mask: true
});
当用户点击收藏的时候,先加载缓存中商品收藏的数据,判断当前商品是否被缓存,如果有,把该商品从缓存中删除,并将页面收藏的图标改为false,反之一样。
// 点击 商品收藏图标
handleCollect(){
let isCollect=false;
// 1 获取缓存中的商品收藏数组
let collect=wx.getStorageSync("collect")||[];
// 2 判断该商品是否被收藏过
let index=collect.findIndex(v=>v.goods_id===this.GoodsInfo.goods_id);
// 3 当index!=-1表示 已经收藏过
if(index!==-1){
// 能找到 已经收藏过了 在数组中删除该商品
collect.splice(index,1);
isCollect=false;
wx.showToast({
title: '取消成功',
icon: 'success',
mask: true
});
}else{
// 没有收藏过
collect.push(this.GoodsInfo);
isCollect=true;
wx.showToast({
title: '收藏成功',
icon: 'success',
mask: true
});
}
// 4 把数组存入到缓存中
wx.setStorageSync("collect", collect);
// 5 修改data中的属性 isCollect
this.setData({
isCollect
})
}
给购物车页面的获取收货地址绑定点击事件,用户点击按钮的时候调用小程序内置 api 获取用户的收货地址 wx.chooseAddress,最后将获取到的地址缓存到本地存储中
// 选择收货地址
async chooseAddress() {
const res = await wepy.chooseAddress().catch(err => err)
if (res.errMsg !== 'chooseAddress:ok') {
return
}
this.addressInfo = res
wepy.setStorageSync('address', res)
this.$apply()
}
购物车页面渲染,先获取到本地存储中的数据,手动添加选中的状态,和初始数量
全选和反选
1 全选复选框绑定事件 change
2 获取 data中的全选变量 allChecked
3 直接取反 allChecked=!allChecked
4 遍历购物车数组 让里面 商品 选中状态跟随 allChecked 改变而改变
5 把购物车数组 和 allChecked 重新设置回data 把购物车重新设置回 缓存中
handeItemChange(e) {
// 1 获取被修改的商品的id
const goods_id = e.currentTarget.dataset.id;
// 2 获取购物车数组
let { cart } = this.data;
// 3 找到被修改的商品对象
let index = cart.findIndex(v => v.goods_id === goods_id);
// 4 选中状态取反
cart[index].checked = !cart[index].checked;
this.setCart(cart);
},
全选的实现 数据的展示, 获取缓存中的购物车数组,根据购物车中的商品数据 所有的商品都被选中 checked=true 全选就被选中
handleItemAllCheck() {
// 1 获取data中的数据
let { cart, allChecked } = this.data;
// 2 修改值
allChecked = !allChecked;
// 3 循环修改cart数组 中的商品选中状态
cart.forEach(v => v.checked = allChecked);
// 4 把修改后的值 填充回data或者缓存中
this.setCart(cart);
},
商品数量的编辑
“+” “-” 按钮 绑定同一个点击事件 区分的关键 自定义属性
1 “+” “+1”
2 “-” “-1”
1 传递被点击的商品id goods_id
2 获取data中的购物车数组 来获取需要被修改的商品对象
3 当 购物车的数量 =1 同时 用户 点击 “-”
弹窗提示(showModal) 询问用户 是否要删除
1 确定 直接执行删除
2 取消 什么都不做
4 直接修改商品对象的数量 num
5 把cart数组 重新设置回 缓存中 和data中 this.setCart
async handleItemNumEdit(e) {
// 1 获取传递过来的参数
const { operation, id } = e.currentTarget.dataset;
// 2 获取购物车数组
let { cart } = this.data;
// 3 找到需要修改的商品的索引
const index = cart.findIndex(v => v.goods_id === id);
// 4 判断是否要执行删除
if (cart[index].num === 1 && operation === -1) {
// 4.1 弹窗提示
const res = await showModal({ content: "您是否要删除?" });
if (res.confirm) {
cart.splice(index, 1);
this.setCart(cart);
}
} else {
// 4 进行修改数量
cart[index].num += operation;
// 5 设置回缓存和data中
this.setCart(cart);
}
},
点击结算
1 判断有没有收货地址信息
2 判断用户有没有选购商品
3 经过以上的验证 跳转到 支付页面!
// 点击 结算
async handlePay(){
// 1 判断收货地址
const {address,totalNum}=this.data;
if(!address.userName){
await showToast({title:"您还没有选择收货地址"});
return;
}
// 2 判断用户有没有选购商品
if(totalNum===0){
await showToast({title:"您还没有选购商品"});
return ;
}
// 3 跳转到 支付页面
wx.navigateTo({
url: '/pages/pay/index'
});
}
})
支付按钮
1 先判断缓存中有没有token
2 没有 跳转到授权页面 进行获取token
3 有token 。。。
4 创建订单 获取订单编号
5 已经完成了微信支付
6 手动删除缓存中 已经被选中了的商品
7 删除后的购物车数据 填充回缓存
8 再跳转页面
async handleOrderPay() {
try {
// 1 判断缓存中有没有token
const token = wx.getStorageSync("token");
// 2 判断
if (!token) {
wx.navigateTo({
url: '/pages/auth/index'
});
return;
}
// 3 创建订单
// 3.1 准备 请求头参数
// const header = { Authorization: token };
// 3.2 准备 请求体参数
const order_price = this.data.totalPrice;
const consignee_addr = this.data.address.all;
const cart = this.data.cart;
let goods = [];
cart.forEach(v => goods.push({
goods_id: v.goods_id,
goods_number: v.num,
goods_price: v.goods_price
}))
const orderParams = { order_price, consignee_addr, goods };
// 4 准备发送请求 创建订单 获取订单编号
const { order_number } = await request({ url: "/my/orders/create", method: "POST", data: orderParams });
// 5 发起 预支付接口
const { pay } = await request({ url: "/my/orders/req_unifiedorder", method: "POST", data: { order_number } });
console.log(pay);
// 6 发起微信支付
await requestPayment(pay);
// 7 查询后台 订单状态
const res = await request({ url: "/my/orders/chkOrder", method: "POST", data: { order_number } });
await showToast({ title: "支付成功" });
// 8 手动删除缓存中 已经支付了的商品
let newCart=wx.getStorageSync("cart");
newCart=newCart.filter(v=>!v.checked);
wx.setStorageSync("cart", newCart);
// 8 支付成功了 跳转到订单页面
wx.navigateTo({
url: '/pages/order/index'
});
} catch (error) {
await showToast({ title: "支付失败" })
console.log(error);
}
}
通过给点击按钮设置open-type属性,值为getUserInfo,配合bindgetuserinfo来获取用户信息
通过点击获取到用户信息,然后存到本地存储中,最后再通过wx.navigateBack跳转到上一页
用户信息打印
最后将用户的头像渲染到页面上,进行简单的布局