个人名片:
🐼作者简介:一名大二在校生,讨厌编程🎋
🐻❄️个人主页🥇:小新爱学习.
🐼个人WeChat:见文末
🕊️系列专栏:🖼️
- 零基础学Java——小白入门必备
- 重识C语言——复习回顾
- 计算机网络体系———深度详讲
- 微信小程序开发——实战开发
- 基于黑马优选的小程序开发实战教程
🐓每日一句:🍭努力的意义是给所爱之人一个美好的未来!
文章目录
- 10.4 三秒后自动跳转
- 10.4.1 三秒后自动跳转到登录页面
- 10.4.2 登录成功之后再返回之前的页面
- 10.5 微信支付
- 10.5.1 在请求头中添加 Token 身份认证的字段
- 10.5.2 微信支付的流程
- 10.5.3 创建订单
- 10.5.4 订单预支付
- 10.5.5 发起微信支付
- 10.6 分支的合并与提交
10.4 三秒后自动跳转
10.4.1 三秒后自动跳转到登录页面
需求描述:在购物车页面,当用户点击 “结算” 按钮时,如果用户没有登录,则 3 秒后自动跳转到登录页面
- 在
my-settle
组件的methods
节点中,声明一个叫做showTips
的方法,专门用来展示倒计时的提示消息:
// 展示倒计时的提示消息
showTips(n) {// 调用 uni.showToast() 方法,展示提示消息uni.showToast({// 不展示任何图标icon: 'none',// 提示的消息title: '请登录后再结算!' + n + ' 秒后自动跳转到登录页',// 为页面添加透明遮罩,防止点击穿透mask: true,// 1.5 秒后自动消失duration: 1500})
}
- 在
data
节点中声明倒计时的秒数:
data() {return {// 倒计时的秒数seconds: 3}
}
- 改造
结算
按钮的click
事件处理函数,如果用户没有登录,则预调用一个叫做delayNavigate
的方法,进行倒计时的导航跳转:
// 点击了结算按钮
settlement() {// 1. 先判断是否勾选了要结算的商品if (!this.checkedCount) return uni.$showMsg('请选择要结算的商品!')// 2. 再判断用户是否选择了收货地址if (!this.addstr) return uni.$showMsg('请选择收货地址!')// 3. 最后判断用户是否登录了,如果没有登录,则调用 delayNavigate() 进行倒计时的导航跳转// if (!this.token) return uni.$showMsg('请先登录!')if (!this.token) return this.delayNavigate()
},
- 定义
delayNavigate
方法,初步实现倒计时的提示功能:
// 延迟导航到 my 页面
delayNavigate() {// 1. 展示提示消息,此时 seconds 的值等于 3this.showTips(this.seconds)// 2. 创建定时器,每隔 1 秒执行一次setInterval(() => {// 2.1 先让秒数自减 1this.seconds--// 2.2 再根据最新的秒数,进行消息提示this.showTips(this.seconds)}, 1000)
},
上述代码的问题:定时器不会自动停止,此时秒数会出现等于 0 或小于 0 的情况!
- 在
data
节点中声明定时器的 Id 如下:
data() {return {// 倒计时的秒数seconds: 3,// 定时器的 Idtimer: null}
}
- 改造
delayNavigate
方法如下:
// 延迟导航到 my 页面
delayNavigate() {this.showTips(this.seconds)// 1. 将定时器的 Id 存储到 timer 中this.timer = setInterval(() => {this.seconds--// 2. 判断秒数是否 <= 0if (this.seconds <= 0) {// 2.1 清除定时器clearInterval(this.timer)// 2.2 跳转到 my 页面uni.switchTab({url: '/pages/my/my'})// 2.3 终止后续代码的运行(当秒数为 0 时,不再展示 toast 提示消息)return}this.showTips(this.seconds)}, 1000)
},
上述代码的问题:seconds 秒数不会被重置,导致第 2 次,3 次,n 次 的倒计时跳转功能无法正常工作
- 进一步改造 delayNavigate 方法,在执行此方法时,立即将 seconds 秒数重置为 3 即可:
// 延迟导航到 my 页面
delayNavigate() {// 把 data 中的秒数重置成 3 秒this.seconds = 3this.showTips(this.seconds)this.timer = setInterval(() => {this.seconds--if (this.seconds <= 0) {clearInterval(this.timer)uni.switchTab({url: '/pages/my/my'})return}this.showTips(this.seconds)}, 1000)
}
10.4.2 登录成功之后再返回之前的页面
核心实现思路:在自动跳转到登录页面成功之后,把返回页面的信息存储到 vuex 中,从而方便登录成功之后,根据返回页面的信息重新跳转回去。
返回页面的信息对象,主要包含 { openType, from } 两个属性,其中 openType 表示以哪种方式导航回之前的页面;from 表示之前页面的 url 地址;
- 在
store/user.js
模块的state
节点中,声明一个叫做redirectInfo
的对象如下:
// state 数据
state: () => ({// 收货地址address: JSON.parse(uni.getStorageSync('address') || '{}'),// 登录成功之后的 token 字符串token: uni.getStorageSync('token') || '',// 用户的基本信息userinfo: JSON.parse(uni.getStorageSync('userinfo') || '{}'),// 重定向的 object 对象 { openType, from }redirectInfo: null
}),
- 在
store/user.js
模块的mutations
节点中,声明一个叫做updateRedirectInfo
的方法:
mutations: {// 更新重定向的信息对象updateRedirectInfo(state, info) {state.redirectInfo = info}
}
- 在
my-settle
组件中,通过mapMutations
辅助方法,把m_user
模块中的updateRedirectInfo
方法映射到当前页面中使用:
methods: {// 把 m_user 模块中的 updateRedirectInfo 方法映射到当前页面中使用...mapMutations('m_user', ['updateRedirectInfo']),
}
- 改造
my-settle
组件methods
节点中的delayNavigate
方法,当成功跳转到my
页面 之后,将重定向的信息对象存储到vuex
中:
// 延迟导航到 my 页面
delayNavigate() {// 把 data 中的秒数重置成 3 秒this.seconds = 3this.showTips(this.seconds)this.timer = setInterval(() => {this.seconds--if (this.seconds <= 0) {// 清除定时器clearInterval(this.timer)// 跳转到 my 页面uni.switchTab({url: '/pages/my/my',// 页面跳转成功之后的回调函数success: () => {// 调用 vuex 的 updateRedirectInfo 方法,把跳转信息存储到 Store 中this.updateRedirectInfo({// 跳转的方式openType: 'switchTab',// 从哪个页面跳转过去的from: '/pages/cart/cart'})}})return}this.showTips(this.seconds)}, 1000)
}
- 在
my-login
组件中,通过mapState
和mapMutations
辅助方法,将vuex
中需要的数据和方法,映射到当前页面中使用:
// 按需导入辅助函数
import { mapMutations, mapState } from 'vuex'export default {computed: {// 调用 mapState 辅助方法,把 m_user 模块中的数据映射到当前用组件中使用...mapState('m_user', ['redirectInfo']),},methods: {// 调用 mapMutations 辅助方法,把 m_user 模块中的方法映射到当前组件中使用...mapMutations('m_user', ['updateUserInfo', 'updateToken', 'updateRedirectInfo']),},
}
改造 my-login 组件中的 getToken 方法,当登录成功之后,预调用 this.navigateBack() 方法返回登录之前的页面:// 调用登录接口,换取永久的 token
async getToken(info) {// 省略其它代码...// 判断 vuex 中的 redirectInfo 是否为 null// 如果不为 null,则登录成功之后,需要重新导航到对应的页面this.navigateBack()
}
在 my-login 组件中,声明 navigateBack 方法如下:// 返回登录之前的页面
navigateBack() {// redirectInfo 不为 null,并且导航方式为 switchTabif (this.redirectInfo && this.redirectInfo.openType === 'switchTab') {// 调用小程序提供的 uni.switchTab() API 进行页面的导航uni.switchTab({// 要导航到的页面地址url: this.redirectInfo.from,// 导航成功之后,把 vuex 中的 redirectInfo 对象重置为 nullcomplete: () => {this.updateRedirectInfo(null)}})}
}
10.5 微信支付
10.5.1 在请求头中添加 Token 身份认证的字段
原因说明:只有在登录之后才允许调用支付相关的接口,所以必须为有权限的接口添加身份认证的请求头字段
- 打开项目根目录下的
main.js
,改造 $http.beforeRequest
请求拦截器中的代码如下:
// 请求开始之前做一些事情
$http.beforeRequest = function(options) {uni.showLoading({title: '数据加载中...',})// 判断请求的是否为有权限的 API 接口if (options.url.indexOf('/my/') !== -1) {// 为请求头添加身份认证字段options.header = {// 字段的值可以直接从 vuex 中进行获取Authorization: store.state.m_user.token,}}
}
10.5.2 微信支付的流程
-
创建订单
-
请求创建订单的
API
接口:把(订单金额、收货地址、订单中包含的商品信息)
发送到服务器 -
服务器响应的结果:订单编号
-
-
订单预支付
-
请求订单预支付的
API
接口:把(订单编号)
发送到服务器 -
服务器响应的结果:订单预支付的参数对象,里面包含了订单支付相关的必要参数
-
-
发起微信支付
-
调用
uni.requestPayment()
这个API
,发起微信支付;把步骤 2 得到的 “订单预支付对象” 作为参数传递给 uni.requestPayment() 方法 -
监听
uni.requestPayment()
这个API
的success
,fail
,complete
回调函数
-
10.5.3 创建订单
- 改造
my-settle
组件中的settlement
方法,当前三个判断条件通过之后,调用实现微信支付的方法:
// 点击了结算按钮
settlement() {// 1. 先判断是否勾选了要结算的商品if (!this.checkedCount) return uni.$showMsg('请选择要结算的商品!')// 2. 再判断用户是否选择了收货地址if (!this.addstr) return uni.$showMsg('请选择收货地址!')// 3. 最后判断用户是否登录了// if (!this.token) return uni.$showMsg('请先登录!')if (!this.token) return this.delayNavigate()// 4. 实现微信支付功能this.payOrder()
},
- 在
my-settle
组件中定义payOrder
方法如下,先实现创建订单的功能:
// 微信支付
async payOrder() {// 1. 创建订单// 1.1 组织订单的信息对象const orderInfo = {// 开发期间,注释掉真实的订单价格,// order_price: this.checkedGoodsAmount,// 写死订单总价为 1 分钱order_price: 0.01,consignee_addr: this.addstr,goods: this.cart.filter(x => x.goods_state).map(x => ({ goods_id: x.goods_id, goods_number: x.goods_count, goods_price: x.goods_price }))}// 1.2 发起请求创建订单const { data: res } = await uni.$http.post('/api/public/v1/my/orders/create', orderInfo)if (res.meta.status !== 200) return uni.$showMsg('创建订单失败!')// 1.3 得到服务器响应的“订单编号”const orderNumber = res.message.order_number// 2. 订单预支付// 3. 发起微信支付}
10.5.4 订单预支付
- 改造
my-settle
组件的payOrder
方法,实现订单预支付功能:
// 微信支付
async payOrder() {// 1. 创建订单// 1.1 组织订单的信息对象const orderInfo = {// 开发期间,注释掉真实的订单价格,// order_price: this.checkedGoodsAmount,// 写死订单总价为 1 分钱order_price: 0.01,consignee_addr: this.addstr,goods: this.cart.filter(x => x.goods_state).map(x => ({ goods_id: x.goods_id, goods_number: x.goods_count, goods_price: x.goods_price }))}// 1.2 发起请求创建订单const { data: res } = await uni.$http.post('/api/public/v1/my/orders/create', orderInfo)if (res.meta.status !== 200) return uni.$showMsg('创建订单失败!')// 1.3 得到服务器响应的“订单编号”const orderNumber = res.message.order_number// 2. 订单预支付// 2.1 发起请求获取订单的支付信息const { data: res2 } = await uni.$http.post('/api/public/v1/my/orders/req_unifiedorder', { order_number: orderNumber })// 2.2 预付订单生成失败if (res2.meta.status !== 200) return uni.$showError('预付订单生成失败!')// 2.3 得到订单支付相关的必要参数const payInfo = res2.message.pay// 3. 发起微信支付}
10.5.5 发起微信支付
- 改造
my-settle
组件的payOrder
方法,实现微信支付的功能:
// 微信支付
async payOrder() {// 1. 创建订单// 1.1 组织订单的信息对象const orderInfo = {// 开发期间,注释掉真实的订单价格,// order_price: this.checkedGoodsAmount,// 写死订单总价为 1 分钱order_price: 0.01,consignee_addr: this.addstr,goods: this.cart.filter(x => x.goods_state).map(x => ({ goods_id: x.goods_id, goods_number: x.goods_count, goods_price: x.goods_price }))}// 1.2 发起请求创建订单const { data: res } = await uni.$http.post('/api/public/v1/my/orders/create', orderInfo)if (res.meta.status !== 200) return uni.$showMsg('创建订单失败!')// 1.3 得到服务器响应的“订单编号”const orderNumber = res.message.order_number// 2. 订单预支付// 2.1 发起请求获取订单的支付信息const { data: res2 } = await uni.$http.post('/api/public/v1/my/orders/req_unifiedorder', { order_number: orderNumber })// 2.2 预付订单生成失败if (res2.meta.status !== 200) return uni.$showError('预付订单生成失败!')// 2.3 得到订单支付相关的必要参数const payInfo = res2.message.pay// 3. 发起微信支付// 3.1 调用 uni.requestPayment() 发起微信支付const [err, succ] = await uni.requestPayment(payInfo)// 3.2 未完成支付if (err) return uni.$showMsg('订单未支付!')// 3.3 完成了支付,进一步查询支付的结果const { data: res3 } = await uni.$http.post('/api/public/v1/my/orders/chkOrder', { order_number: orderNumber })// 3.4 检测到订单未支付if (res3.meta.status !== 200) return uni.$showMsg('订单未支付!')// 3.5 检测到订单支付完成uni.showToast({title: '支付完成!',icon: 'success'})}
10.6 分支的合并与提交
- 将
settle
分支进行本地提交:
git add .
git commit -m "完成了登录和支付功能的开发"
- 将本地的
settle
分支推送到码云:
git push -u origin settle
- 将本地
settle 分
支中的代码合并到master
分支:
git checkout master
git merge settle
git push
- 删除本地的 settle 分支:
git branch -d settle