目录
文章目录
1、准备工作。
2、封装一些公共的组件
3、请求接口渲染页面
4、列表页面渲染
5、详细列表下拉刷新,上拉加载
下拉刷新
上拉加载更多
6、详情页
小程序收藏
分享
分享,是我们小程序封装好的,所以直接使用就可以了。
客服功能
7、购物车页面
7-5、支付
8、我的页面
我们先把wx.request封装好。然后我们去配置路由。把尾部的导航配置出来。
就是我们的首页、分类、购物车、还有我的。
在全局的app.json中配置
{
"pages": [ // 这个是我们的页面,有点类似我们的vue路由
"pages/index/index",
"pages/category/category",
"pages/goods_list/goods_list",
"pages/goods_detail/goods_detail",
"pages/cart/cart",
"pages/collect/collect",
"pages/order/order",
"pages/search/search",
"pages/user/user",
"pages/feedback/feedback",
"pages/login/login",
"pages/auth/auth",
"pages/pay/pay"
],
"tabBar": {
"list": [{ // 这个是我们小程序内置的下部导航
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "/icons/home.png", // 没被选中的图片
"selectedIconPath": "/icons/home-o.png" // 被选中时的图片
},{
"pagePath": "pages/category/category",
"text": "分类",
"iconPath": "/icons/category.png",
"selectedIconPath": "/icons/category-o.png"
},{
"pagePath": "pages/cart/cart",
"text": "购物车",
"iconPath": "/icons/cart.png",
"selectedIconPath": "/icons/cart-o.png"
},{
"pagePath": "pages/user/user",
"text": "我的",
"iconPath": "/icons/my.png",
"selectedIconPath": "/icons/my-o.png"
}]
},
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#eb4450",
"navigationBarTextStyle": "white"
},
}
首先看一下我们页面中哪些功能被使用了2次或以上,我们就都封装成公共的组件。比如一些搜素、头部等。在根目录下创建一个components文件夹。然后创建一个公用的文件夹
这个文件夹中存放我们公用的组件
我们准备工作就完成了。
我是先写的首页,使用我们封装好的wx.request请求来数据,拿着数据去渲染页面就可以了。
到了列表页面,我们左边导航和右边数据是外部盒子的一个滚动条,而不是整个页面的,左边和右边的滚动条是不互相影响的,因此我们使用了一个小程序内置的标签。
// 作为一个盒子使用,给定一个高度或宽度,让里面的内容在这个区域出现滚动条
// scroll-y y轴开启滚动条
// scroll-x x轴开启滚动条
然后应为头部上边有一个搜素组件,占据了一定的位置,我们这个盒子的高度不能设置为100%。
这时我们可以使用css中的一个计算方法calc()方法来计算高度。
height:calc(100vh - 80px);
// 假设我们搜素组件的高度为80px
// 原生css 中的计算
这样我们就计算出来页面剩余的高度。接着就是给每一个内容绑定一个点击事件。跳转页面了。当然,跳转的时候我们需要把当前点击的id通过路由传参发送出去。
路由传参:一般常用的有2种,
wx.navigateTo({
// 不可以跳转到tabBar页面,就是我们在全局的app.json设置的tabBar属性里的那几个路由
url:'/pages/goods_list/goods_list?id=' + id // 我们要跳转到哪个页面
// 如果要传参,我们可以直接拼接就可
})
wx.redirectTo({ // 只能跳转tabBar页面
url:'/pages/goods_list/goods_list?id=' + id
})
// 注意:我们传过去的数据是在this.options 这个对象中
5-1、下拉刷新
首先我们肯定是要把页面先渲染出来,然后在实现我们的功能。
下拉刷新:首先我们要开启页面的下拉刷新,在当前页面的json中写配置。
"enablePullDownRefresh":true, // 开启用户下拉
"backgroundTextStyle":"dark" // 下拉时loading颜色
// 需要加上这2个属性
然后我们页面有一个监听用于下拉动作的生命周期函数,我们可以重新请求第一页数据。
思路:我们小程序有封装好的下拉动作,开启后,在使用小程序内置的监听用户下拉动作的函数,在里面重新请求第一页的数据
首先在我们要使用下拉刷新页面的json配置中写入
"enablePullDownRefresh":true,
// 开启下拉刷新 "backgroundTextStyle":"dark"
// 改变刷新时出现的样式,默认是白色。
然后在我们的开启下拉刷新的页面就可以下拉了。
onPullDownRefresh:function(){ // onPullDownRefresh 专门用来监听用户的下拉动作
wx.request({ // 用户下拉后我们重新请求一次
url:'https://wwwaksdha',
data:{pagenum:1,pagesize:10},// 发送的参数 请求第一页的数据 每页10条数据
method:'get', // 请求方式
success(res){
this.setData({
list:res.data.list // 我们重新赋值,让他渲染页面
})
}
})
}
思路:当我们滚动条到底部的时候,我们就去请求下一页的数据,然后把请求过来的数据和原数据进行一个合并。
首先我们要知道一个小程序的内置函数,页面上拉触底事件的处理函数 onReachBottom
page({
data:{
pagenum:1,
pagesize:10,
list:[]
},
onReachBottom: function () { // 当我们滚动条到底触发的函数
let oneself = this; // 因为下边在一个函数中,他的this指向是改变的。
wx.request({ // 重新发送请求
url:'https://ashdkas', // 请求路径
data:{pagenum:this.data.pagenum,pagesize:this.pagesize}, // 请求参数 pagenum是当前页
method:'get', // 请求方式
success(res){ // 成功回调
oneself.setData({ // 修改数据
list:[...oneself.data.list,...res.data.list] // 我们让当前的数据和请求来的数据合并成一个数组
// 我用到的是展开运算符,大概意思就是 把数组中的每一个元素都展开。
});
}
})
},
})
5-2、上拉加载
首先我们要知道,小程序中有一个生命周期函数是:监听用户的滚动条是否触底。
知道这个生命周期函数就简单了,我们可以在全局定义一个num,表示当前页数,然后我们在页面显示的时候后台肯定会给我们返回一个数据的总条数。
然后了,我们根据这个总条数计算出一共需要多少页。
公式:总页数 / 每页多少条数据 = 一共有多少页
注意:因为我们页数如果是10 / 3 肯定有小数点,所以这里我们需要向上取整的。
let page = Math.ceil(total / 10); // 当然,这个10也可以是个变量的嘛。
我们在监听到用户触底的时候,首先让当前页num+1,然后判断一下是否大于我们计算出来的总页就可以了。下面我们就去请求num页的数据
我们最后给每一条数据绑定一个点击事件,然后传过去一个id即可。
首先我们刚进详情页肯定是去根据详情列表页传过来的id去请求接口,然后使用返回的数据去渲染。
详情页大概分为收藏、加入购物车、分享、联系客服。
6-1、收藏:在我们进入页面时,需要判断我们这条数据有没有收藏过,如果收藏过,就让收藏的星星变亮。
当我们单击收藏时,我们肯定要判断当前这条数据有没有被收藏,如果被收藏是要取消收藏的,如果每有收藏就收藏。最后存入本地存储。
6-2、加入购物车:当我们点击加入购物车时,首先判断我们购物车内有没有这个商品,如果有就让这个商品的数量+1,如果没有就添加。最后存入本地存储。
6-3、分享:我们给分享一个按钮,给这个按钮一个open-type="share"属性。
6-4、联系客服:我们给联系客服一个按钮,给这个按钮一个open-type="contact"属性。
首先是进入页面
实现思路:首先,我们进入详情页的时候需要获取一下本地存储收藏的数据,然后拿这个数据根当前获取到详情页的id进行对比,如果有相同的id,我们可以把一个变量变为true,然后我们的收藏图片的类名根据这个变量的true/false来改变。
wxml页面
// 使用三元运算符,如果这个变量为true,就加第一个,如果为false,就加第二个类名
// 字体图标来自 案例巴巴矢量图标库
js代码
page({
onLoad: function (options) { // 页面加载执行
let arr = wx.getStorageSync('collect') || []; // 获取本地存储种的数据
app.http.category.getGoodsDetail({ goods_id: options.id }).then(res => {
// 封装好的wx.request
// 请求详情页数据
let data = res.data.message;
this.setData({
flag: arr.some(item => { // 判断一个数组中有没有符合条件的,如果有返回true,否则返回flase
return item.id == data.goods_id
}),
obj: data,
})
})
},
})
点击的时候
实现思路:当我们点击的时候,获取一下本地存储的数据,然后我们可以使用findIndex方法来判断,如果每一项的id等于我们当前页的id就会返回当前项的下标,我们可以根据这个下标进行判断,如果下标不等于-1,就是本地存储种有,我们就删除。否则就是没有,我们就添加。在判断下面我们那个变量取一下自身的反。在最后把我们的数据存一下本地存储。
// 点击 商品收藏图标
handleCollect() {
let isCollect = false;
// 1 获取缓存中的商品收藏数组
let collect = wx.getStorageSync("collect") || [];
// 2 判断该商品是否被收藏过
let index = collect.findIndex(v => v.goods_id === this.data.goods_Detail.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.data.goods_Detail);
// console.log(this.data.goods_Detail);
isCollect = true;
wx.showToast({
title: '收藏成功',
icon: 'success',
mask: true
});
}
// 4 把数组存入到缓存中
wx.setStorageSync("collect", collect);
// 5 修改data中的属性 isCollect
this.setData({
isCollect
})
}
})
// 这个属性是我们小程序封装好的
客服功能页是我们小程序封装好的。
// 这个属性是我们小程序封装好的
购物车页面有:全选、增加/减少数量、复选框和全选交互、计算总价、获取收货地址、支付。
7-1、全选:我们添加购物车的时候,给每一条数据都加上一个flag,表述当前复选框的checked存在不存在。点击全选,我们把这个flag属性全部变为true,然后在点一下就变为flag。
购物车的复选框状态同步
小程序的购物车
添加购物车时,我会添加一个属性用于判断我们的商品有没有没选中,如果被选中呢,就会让这个状态呢变为true,没选中的变为false。然后循环渲染数据的时候,我们复选框的checked属性就根据这个状态的true/false来添加/删除。
这样做我们在切换不同页面时保证复选框的状态不会发生改变。且当重新更新页面时也会同步我们勾选的商品。
比如下边这个wxml
我们使用小程序自带的复选框,传入的是一组选中的id 是以数组的形式
购物车
{
{item.goods_name}}
¥{
{item.goods_price}}
-
{
{item.num}}
+
写好wxml之后呢,就改写我们的js了,首先我们需要在生命周期函数—监听页面显示时获取我们的购物车数据。组件通信小程序只能使用本地存储。
data: {
cart: [],
全部数据
},
onShow: function () {
// 获取本地存储的数据
const cart = wx.getStorageSync("cart") || []
this.setData({
// 赋值并重新渲染页面
cart
})
},
首先拿到第一步呢,我们肯定是要先把每一个复选框的状态给同步了,这样我们在写价格啊,选中的数量…。我上面使用的handeItemChange事件名。
实现思路是:我们这个事件中,默认有一个事件对象e,在这个e里面呢,有我们当前选中的复选框value值,且是一个数组形式,比如我们选中2个就有2个id,选中3个就会有3个id。
然后我们可以根据这个数组中相应的id把他的状态变为true/false。
// 商品选中
handeItemChange(e) {
// 1 获取被修改的商品的id
let good = e.detail.value; //我们选中的复选框id 都在这个数组中,传入的是我们选中复选框的value值
this.data.cart.forEach(item => { //首相把我们的数据状态全部变为false
item.checked = false; //全部不选中状态
});
good.forEach(item => {//循环我们选中的数据的id
this.data.cart.forEach(i => {//然后循环我们的所有数据
if (item == i.goods_id) {//使用每一项id跟我们所有的数据每一项的id进行对比
i.checked = true //如果相等就为true
}
})
})
然后就是我们的计算总价了。
执行思路:首先我们可以筛选出商品中所有状态为true的。然后我们在计算这个数组的总价。
aaa() {
let price = 0;
let pic = 0;//用于保存我们的总价
let arr = this.data.cart.filter(item => {
return item.checked == true; //筛选出状态为true的商品
});
arr.forEach(item => { //遍历筛选出的商品
price = price + item.num * item.goods_price;//然后计算总价,当前价格*当前商品的数量
});
arr.forEach(item => {
pic += item.num
})
this.setData({ //接着我们使用this.setData 改变数据,页面会自动更新
cart: this.data.cart,
totalPrice: price,
totalNum: pic
})
//这里是封装了一个函数因为触发事件就需要重新计算
},
// 点击结算
// 点击结算
// 1 判断有没有收货地址信息
// 2 判断用户有没有选购商品
// 3 经过以上的验证 跳转到 支付页面!
handlePay() {
const {
address,
totalNum
} = this.data;
if (!address.userName) {
wx.showToast({
title: "你没有添加收货地址",
})
return
}
if (totalNum === 0) {
wx.showToast({
title: "你还没有选择商品",
})
return
}
wx.navigateTo({
url: '/pages/pay/index',
})
},
7-2、点击复选框:我们渲染的时候使用
// 标签,把我们一组的复选框包裹住
// 他有一个bindchange 事件,当我们点击这一组内的复选框时触发。
7-3、购物车的数量加减。
都注册一个点击事件,当我们点击减号时,我们传入一个下标,让后让当前下标的num数量-1,判断当前数量是否小于1,如果小于1就提示是否删除。
当我们点击加号时,我们还是传入一个下标,让当前下标的num属性+1。
// 减
jian(e) {
let pic = 0
let index = e.currentTarget.dataset.index;
//
if (this.data.cart[index].num <= 1) {
wx.showModal({
title: "提示",
content: "您是否要删除?",
success: (res) => {
if (res.confirm == true) {
this.data.cart.splice(index, 1);
let arr = this.data.cart.filter(item => {
return item.checked == true;
});
if (arr.length == 0) {
this.data.allChecked = false
} else {
this.data.allChecked = true
}
let price = 0;
arr.forEach(item => {
price = price + item.num * item.goods_price;
});
this.setData({
cart: this.data.cart,
totalPrice: price,
totalNum: arr.length,
allChecked: this.data.allChecked
})
}
}
})
} else {
this.data.cart[index].num--;
}
this.setData({
cart: this.data.cart
});
this.aaa()
},
// 加
jia(e) {
// console.log(e);
const index = e.currentTarget.dataset.index;
console.log(index);
this.data.cart[index].num++
this.aaa()
},
7-4、点击获取地址时,给一个点击事件,之后使用
handChooseAddress({});
//来获取收货地址
首先我们给一个元素点击事件
然后我们在事件中使用
// 点击收货地址
handChooseAddress() {
// 用来获取收货地址了
wx.chooseAddress({
// 接口调用的成功函数
success: (result) => {
let address = result;
console.log(result);
// 获取信息 收货地址的api
address.all = address.provinceName + address.cityName + address.countyName + address.detailInfo;
// 存入到缓存中
wx.setStorageSync(
"address",
address
)
},
})
},
点击支付时,我们需要判断用户有没有登录呢,如果没有登录就让他去登录。查看详情
如果已经登录,我们就需要准备数据,把价格、用户购买的物品、用户都购买了哪些物品都发送到后台。然后我们先发送请求创建一个订单号,接着发起预支付接口,然后发起微信支付,支付完成后我们就手动删除掉用户购买的商品,然后跳转到订单页面。
首先我们肯定是判断用户有没登录,一般都是判断本地有没有token。
如果没有,让用户取登录。
如果有,
// 1.获取用户的信息 返回encryptedData,rawData,iv,signature
// 2.小程序登陆 返回code
// 3.提交数据到自己的后台执行post 请求提交数据 encryptedData,rawData,iv,signature,code
// 4.将token和用戶数据rawData存入本地存储
// 点击支付
bindget(e) {
console.log(e);
new Promise((resolve, reject) => {
wx.login({
// 接口调用成功的回调函数
success: (res) => {
// 调用接口获取登录凭证(code)
let code = res.code; //返回一个code
// 四个参数
const loadingParams = {
encryptedData: e.detail.encryptedData,
rawData: e.detail.rawData,
iv: e.detail.iv,
signature: e.detail.signature,
code
} // 创建一个对象 执行resolve
resolve(loadingParams); // 把创建的对象当作参数
},
fail(err) {
reject(err);
}
})
}).then(res => { // res 就是resolve 中的参数
console.log(res);
wx.request({
url: 'https://api-hmugo-web.itheima.net/api/public/v1/users/wxlogin', // 相当于登录的接口
data: res,
method: 'post',
success(res) {
// res中一般会包含一个token
console.log(res);
wx.navigateBack({ // 返回上一个页面
delta: 1
});
},
fail(err) {
console.log(err);
}
})
})
},
我们需要先判断本地存储中有没有登录过,如果没有登录过,就提示让用户去登录。
登录:我们写好登录页面后,给一个登录按钮,然后写一个点击事件,在这个事件中使用
// 点击登录
handleGetUserInfo(e) {
// console.log(e.detail.userInfo);
const userInfo = e.detail.userInfo;
wx.setStorageSync("userinfo", [userInfo]); //本地存储
wx.reLaunch({
url: '/pages/user/index', //返回上一页
})
},