写项目之前必须要有接口文档 准备工作接口文档是必不可少的
参考文档我们还可以参考 小程序的开发文档、阿里巴巴字体 iconfont 、mdn…
项目的搭建
搭建项目目录的结构 相当于分模块的去放我们需要的文件 使项目的结构可以清晰明了
然后就是分析页面 搭建项目基本项目的页面
{
"pages": [ //项目页面的路由信息
"pages/index/index",
"pages/category/index",
]}
引入项目中需要的字体图标库 并且可以保证全局引入 这样才可以在所有的页面使用
打开阿⾥巴巴字体图标 ⽹站
. 选择的图标
. 添加⾄项⽬
. 下载到本地
. 将样式⽂件 由 css 修改为 wxss
. 在app.wxss⼩程序中引⼊
页面使用图标 对应的图标名称
项目的搭建 搭建项目的tabber 结构 参考 小程序 API 文档
请求数据的接口进行封装 这样就可以 不必要的重复的代码 http文件去封装
三个部分:
0ne:api.js ------------放接口地址集合 统一的整体的来管理
module.exports={}导出 有多个地址 对象的形式导出
two:fach.js ----------------封装微信 网络请求模块 导出一个promis 实例对象 会接收三个形参 (url,data,method)
分别是 请求的路径、请求数据的参数、请求数据的方法
promis对象中的(resolve, reject) 分别代表成功和失败之后执行的回调函数
导出一个 Promise 对象实例 里面封装接口请求的方法用来重复使用
http.js --------- 封装请求的方法 首先需要 引入 fach.js 和 api.js 这样才可以进行完美的封装
为了减少重复 无用的代码 可设置基本路径的地址 baseUrl
接口的理解 api ---- 接口 h8------ 版本号
let baseUrl = “https://api-hmugo-web.itheima.net/api/public/v1”
然后就是定义函数 分别请求对应的接口
需要值得注意的是 必须要在全局的app.js 中 全局的引入http 并且 全局的导出 这样在需要的页面 引入 才可以使用它获取
到数据 否则就会报错
到这里 差不多项目的基本准备工作就差不多真被完毕了 接下来就是 分析 各个页面之间的联系 并且 对组件进行拆分和封装
单个页面的逻辑 组件的拆分
单个页面的逻辑 组件的拆分
首页页面
是我们看到的home 首页 搜索、轮播图、导航、和楼层都可以进行封装
子组件我们注册在 components 中
另外可以根据页面 json 中的
{
"component": true, //来判断当前组件是否是子组件
"usingComponents": {}
}
我们在home 父组件页面 请求到轮播图的数据 首先 在api.js 中定义好 轮播图数据的路径
然后在http.js 中 请求数据 接口不需要参数的话就 不写参数 为空 请求的方法为get 统一导出后 在 父组件
home.js 中 我们要全局的引入我们封装的请求方法在
* 生命周期函数--监听页面加载 的时候请求数据
onLoad: function (options) {
// 请求轮播图数据
// /home/swiperdata
app.http.banner().then(res => {
// console.log(res)
数据保存到data中
this.setData({
banner: res.data.message
})
})
并且需要我们传递给子组件
<!-- 轮播图的组件 -->
<w-swiper list="{{banner}}"></w-swiper>
<!-- 轮播图的组件 -->
使用自定义属性的方法
在子组件js来接收数据
properties:{
list:{
type:Array
}
}
接收数据 并且可以 定义数据类型 和默认数据
然后在页面中渲染我们接受到的数据
<swiper autoplay="true" indicator-dots="true" indicator-color="pink" class="tops-s">
<block wx:for="{{list}}" wx:key="index">
<swiper-item>
<image src="{{item.image_src}}"></image>
</swiper-item>
</block>
</swiper>
可以参考 小程序API 文档来对
来实现轮播图的效果 导航部分 和 楼层部分也是一样的流程 父传子 只需要
对页面进行css的样式进行修改
分类页面
首先一眼页面是分为左右两部分 右边是侧边导航 左边是数据的渲染 也是一个二级导航的效果 首先根据接口 请求到数据
数据拿到之后进分析 只有对数据最够 明白才能让我们更快更好的的完成页面
这边 拿到的数据是 先渲染左边的侧边导航栏 然后我们给侧边导航地每一项一个点击事件 change
<view class="lefts" wx:for="{{srot}}" wx:key="index">
<view bindtap="change" data-index="{{index}}">
{{item.cat_name}}
</view>
</view>
并且使用自定义属性 data-index="{{index}}" 把他的下标传递过去
在js 页面
// 方法
change(e) {
// 保存点击的下标 ---
this.setData({
i: e.currentTarget.dataset.index
});
// 根据 保持的·下标来获取右边的数据
this.setData({
tabs: this.data.srot[this.data.i].children
})
// tabs:this.data.srot[this.data.i].children[0].children
// console.log(this.data.tabs);
},
通过e 事件源 来保存下来 我们点击的每一项数据的下标
然后 把符合当前下标的数据 的children 中的数据保存下来 这样就可以实现 点击左边的哪一项 切换到哪一项的数据 了,因为
拿到的数据结构就是 每一下项数据里面都有自己的children 右边展示的也是根据左边的数据而展示他的children的
列表页面
<data-cid="{{item.cat_id}}" 自定义属性 保存 一下需要的cid
给每一项的数据一个点击事件 然后 同样保存 代表每一项的cat_id 并给每一项一个点击事件 gotolist
// 点击跳转
gotolist(e) {
// console.log(e)
// 保存 自己定义的属性 从事件源里获取
let cid = e.currentTarget.dataset.cid;
// 页面跳转 把保存 的 cid 参数到另一个页面 使用
wx.navigateTo({
url:'/pages/goods_list/goods_list?cid=' + cid,
})
},
在页面跳转的时候 吧 保存来的 cat_cid 保存下来 并且 使用 字符串拼接的形式 传递参数 到 列表页面
通过(options) 吧参数保存下来 然后在请求 页面数据的时候 携带参数 这样才可以 获取到 数据 最后进行数据的渲染即可
/**
* 生命周期函数--监听页面加载
*/
// options 获取 需要接受的数据
onLoad: function (options) {
console.log(options)
// 保存一下动态的cid
this.Paramsztt.cid = options.cid;
// console.log(options);
// 请求到的数据 参数信息------必须按照接口来写 {参数的位置 不需要参数默认是空 需要的话是对象形式的传递参数 }
// pagenum ---请求到的页码 pagesize---请求到的每页多少条数据
// 保存下餐数信息
// 请求接口里的数据
app.http.shoplist(this.Paramsztt).then(res => {
console.log(options)
// 获取 总条数
const total = res.data.message.total;
console.log(total)
// 计算总页数
this.data.totalPages = Math.ceil(total / this.Paramsztt.pagesize);
console.log(total);
console.log(this.Paramsztt.pagesize);
console.log(this.data.totalPages)
// 拼接了数组
// this.setData({
// // shoplist: [...this.data.shoplist, ...res.goods]
// })
this.setData({
// 保存获取到的数据 哈哈哈哈
shoplist: res.data.message.goods
})
})
},
下拉刷新
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
// 下拉的事件
onPullDownRefresh() {
// 1 重置数组
this.setData({
shoplist: []
}),
// 2 重置页码
this.Paramsztt.pagenum = 1;
// 3 发送请求
// 请求接口里的数据
app.http.shoplist(this.Paramsztt).then(res => {
// console.log(options)
// 获取 总条数
const total = res.data.message.total;
console.log(total)
// 计算总页数
this.data.totalPages = Math.ceil(total / this.Paramsztt.pagesize);
console.log(total);
console.log(res)
console.log(this.Paramsztt.pagesize);
console.log(this.data.totalPages)
// 拼接了数组
// this.setData({
// shoplist: [...this.data.shoplist,]
// }),
this.setData({
// 保存获取到的数据 哈哈哈哈
shoplist: res.data.message.goods
// 需要来拼接的数据
})
//
})
wx.stopPullDownRefresh()
// this.getGoodsList();
},
///下拉 数据 重新的返回第一页数据
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
// 先判断还有几页数据
// 如果当前的页面大于总条数
if (this.Paramsztt.pagenum >= this.totalPages) {
// 就显示没有下一页的数据
wx.showToast({
title: '没有下一页了',
})
} else {
// 还有下一页数据
this.Paramsztt.pagenum++;
// 请求接口里的数据
app.http.shoplist(this.Paramsztt).then(res => {
// console.log(options)
// 获取 总条数
const total = res.data.message.total;
console.log(total)
// 计算总页数
this.data.totalPages = Math.ceil(total / this.Paramsztt.pagesize);
// console.log(total);
console.log(res)
// console.log(this.Paramsztt.pagesize);
// console.log(this.data.totalPages)
// 拼接了数组
// this.setData({
// shoplist: [...this.data.shoplist,]
// }),
// this.setData({
// // 保存获取到的数据 哈哈哈哈
// shoplist:res.data.message.goods
// })
})
// this.getGoodsList();
}
},
详情页面
循环shoplist 数据
<view wx:for="{{shoplist}}" wx:key="index" class="box"
bindtap="gotodetail" data-goods_id="{{item.goods_id}}">
点击的时候吧每一项的goods_id 保存下来 然后传给详情页
// 跳转到 详情页
gotodetail(e) {
// console.log(ad)
// console.log(e.currentTarget.dataset.goods_id)
let i = e.currentTarget.dataset.goods_id
wx.redirectTo({
url: '/pages/goods_detail/goods_detail?goods_id=' + i,
})
this.setData({
i: e.currentTarget.dataset.goods_id
})
},
在详情页面
* 生命周期函数--监听页面加载
*/
onShow: function () {
// 获取本地数据里的 数据
this.obj.cart = wx.getStorageSync('cart') || [];
// // getGoodsDetail
// let pages = getCurrentPages();
// let currentPage = pages[pages.length - 1];
// let options = currentPage.options;
// const { goods_id } = options;
// // this.getGoodsDetail(goods_id);
},
// 请求的数据保存携带的参数 使用的是封装的方法
// onLoad 的时间段
onLoad: function (options) {
console.log(options);
// 保存点击传过来的id
this.setData({
id: options.goods_id
})
// console.log(this.data.id);
// 请求数据 携带保存的参数
app.http.detail({ goods_id: this.data.id }).then(res => {
console.log(res)
// this.goodsObj=res.data.message
this.setData({
//保存获取到的详情的信息
GoodsInfo: res.data.message,
// 保存一个对象 用来添加字段
// goodsObj:res.data.message
});
console.log(this.data.GoodsInfo)
// 获取缓存中的商品收藏的数组
let collect = wx.getStorageSync("collect") || []
// 判断是否被收藏数据
let isCollect = collect.some(v => v.goods_id == this.GoodsInfo.goods_id);
})
},
购物车页面
// 添加购物车
handleCartAdd() {
//console.log(this.data.GoodsInfo)
let k = false;
// 保存
let price = this.data.GoodsInfo.goods_price;
let image = this.data.GoodsInfo.goods_small_logo;
console.log(image);
let flag = false;
// 选中的状态
let num = 1;
let name = this.data.GoodsInfo.goods_name;
let id = this.data.GoodsInfo.goods_id;
// 循环
this.obj.cart.forEach(item => {
if (item.id == id) {
k = true;
item.num++
}
})
if (!k) {
// 直接push 一个对象 存到 购物车的数组中去数据
this.obj.cart.push({
price,
image,
flag,
num,
name,
id
})
}
console.log(this.obj.cart);
// 存到本地存储
wx.setStorageSync('cart', this.obj.cart);
// 提示弹窗
wx.showToast({
title: '加入成功',
icon: 'success',
// // true 防止用户 手抖 疯狂点击按钮
mask: true
// // 无语可可可可可
})
},
收藏页面
// 点击 商品收藏图标 有问题
// handleCollect
handleCollect() {
// 定义图标的变量 一开始是 false 状态
let isCollect = false;
// 1 获取缓存中的商品收藏数组 没有就是空数组
let collect = wx.getStorageSync("collect") || [];
// 2 判断该商品是否被收藏过 findIndex 查找 是否存在这一项 如果存在就返回下边
// 如果不存在就返回 -1
// index 保存下来 循环遍历拿到本地数组的每一项
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 把数组存入到缓存中 本地的缓存中 并且名字叫 setStorageSync
wx.setStorageSync("collect", collect);
// 5 修改data中的属性 isCollect
this.setData({
isCollect
})
}
})
客服、分享页面
<view class="iconfont icon-kefu"></view>
<view>客服</view>
<button open-type="contact"></button>
</view>
<view class="tool_item">
<view class="iconfont icon-yixianshi-"></view>
<view>分享</view>
<button open-type="share"></button>
</view>
结算的页面
全选 判断
// 全选 上面控制下面的数据
checkAll(e) {
// console.log(e)
// 判断
if (this.data.active == true) {
this.data.cart.forEach(item => {
item.flag = false;
})
this.data.active = false
}
// 选中的要放在新的本地数据的数组里面
else {
this.data.cart.forEach(item => {
item.flag = true;
})
this.data.active = true
}
// 保存确定过状态后的数据状态
this.setData({
cart: this.data.cart,
active: this.data.active
});
// 判断购物车里的数据是否是选中的状态 支付页面需要使用
// 只展示的是选中过的数据
console.log(this.data.cart);
this.maxprice()
},
复选框的全选
// 全选
changebox(e) {
console.log(e)
let that = this
let ids = e.detail.value;
that.data.cart.forEach(item => {
item.flag = false;
});
ids.forEach(i => {
that.data.cart.forEach((item, index) => {
if (item.id == i) {
that.data.cart[index].flag = true;
}
})
})
// 单选
let arrLength = ids.length
//
let shopLength = that.data.cart.length
// 判断
if (arrLength == shopLength) {
that.setData({
active: true,
})
} else {
that.setData({
active: false
})
}
this.maxprice()
},
根据选中的状态来计算总价
//计算总价
maxprice() {
let arr = this.data.cart.filter(item => {
return item.flag == true
})
let num = arr.length
// 初始数据
let price = 0
arr.forEach(item => {
price = price + item.num * item.price
})
this.setData({
sum: price,
num: num
})
},
数据的数量加和减
// 加加加
jia(e) {
let id = e.currentTarget.dataset.id
// console.log(this.data.id)
// 判断数据 是否是和当前点击的数据一致,一致就让他减减
this.data.cart.forEach((item, index) => {
if (item.id == id) {
item.num++;
// 判断减不能为零
}
})
this.setData({
cart: this.data.cart
})
//
this.maxprice()
},
// 减减减
jian(e) {
// console.log(this.data.cart)
let id = e.currentTarget.dataset.id
this.data.cart.forEach((item, index) => {
// 判断数据
if (item.id == id) {
item.num--;
}
})
this.setData({
cart: this.data.cart
})
// console.log(e)
this.maxprice()
},
数据每发生一次变化都需要 对总价进行调用
获取地址页面
<!-- 当收货地址 不存在 按钮显示 对象 空对象 bool类型也是true -->
<view class="address_btn" wx:if="{{!address.userName}}" class="shou">
<view class="add" bindtap="add"> + 获取收货地址</view>
</view>
// 收货地址 的方法 使用chooseAddress 微信小程序的内置的方法
add() {
wx.chooseAddress({
success: (result) => {
console.log(result)
this.setData({
address: result
})
// 存到本地存储
wx.setStorageSync('address', this.data.address)
console.log(this.data.address)
// let address = result
// address.all = address
},
})
},
支付页面
点击 支付
async handleOrderPay() {
try {
// 判断缓存中有没有token
const token = wx.getStorageSync("token");
没有用户就跳转
if (!token) {
wx.navigateTo({
url: '/pages/auth/index'
});
return;
}
创建订单
准备 请求头参数
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 };
准备发送请求 创建订单 获取订单编号
const { order_number } = await request({ url: "/my/orders/create", method: "POST", data: orderParams });
发起 预支付接口
const { pay } = await request({ url: "/my/orders/req_unifiedorder", method: "POST", data: { order_number } });
发起微信支付
await requestPayment(pay);
查询后台 订单状态
const res = await request({ url: "/my/orders/chkOrder", method: "POST", data: { order_number } });
await showToast({ title: "支付成功" });
手动删除缓存中 已经支付了的商品**
let newCart=wx.getStorageSync("cart");
newCart=newCart.filter(v=>!v.checked); wx.setStorageSync("cart",
newCart);
支付成功了 跳转到订单页面*
wx.navigateTo({
url: '/pages/order/index'
});
} catch (error) {
await showToast({ title: "支付失败" })
console.log(error);
} }
11. 微信小程序的授权登录
// 小程序授权登录 流程 (auth)
例如
1、首先 获取用户信息
const { encryptedData, rawData, iv, singature } = e.detail;
1
2、获取小程序 登录成功后的code
const { code } = await login();
1
// 保存到一个对象中获取到的用户的参数信息
const loginParams = { encryptedData, rawData,
iv, signature, code };
1
2
3、发送请求 获取用户信息
const { token } = await request({ url: "/users/wxlogin", data: loginParams, method: "post" });
1
4、把token 存入缓存中 同时跳回到上一个页面
wx.setStorageSync("token", token);
1
// 回到 上一页 wx的内置方法
wx.navigateBack({
delta: 1
});
try方法 是跳过报错 直接进入下一步 不会影响代码的执行
try {}catch(error){
await showToast({ title: "支付失败" })
console.log(error)
}