目录
gitee仓库地址:https://gitee.com/CMD-UROOT/sph-project/commits/master
大家根据上传历史进行查找你需要的代码
上一篇文章我们利用ElementUI已经实现了点击立即支付按钮,弹出弹出框的操作

我们需要在弹出框中生成一个二维码,市场上有许多插件,是可以生成二维码的
我们打开这个网址 npm
搜索qrcode 或者直接打开这个网址: qrcode - npm


npm i qrcode --save

在pages/Pay/index.vue中:引入qrcode
也就是我们需要使用qrcode的组件中去引用

我们知道在我们的项目中
支付页面的数据中有一个payInfo里面有一个codeUrl数据是字符串,我们要利用这个字符串来生成二维码

在pages/Pay/index.vue中:

打印:点击立即支付控制台弹出二维码地址

点进地址:就是一个二维码了

二维码需要在弹出框的中间进行展示
所以:
在pages/Pay/index.vue中:


扫码支付可能成功,也可能失败,如果成功需要跳转路由,如果失败需要提示支付失败,支付成功与失败,是后台返回的,所以我们需要发请求
那么需要发多少次请求呢,因为支付是用户行为,我们将来得向服务器问,用户支付成功还是失败,我们需要一直问
也就是说当弹出框弹出来的时候,就需要一直向后台询问,用户支付的情况
请求地址:/api/payment/weixin/queryPayStatus/{orderId}
请求方式:get
参数:orderId


在api/index.js中:

知识点
长轮询 和短轮询是什么?
长轮询:
http 长轮询是服务器收到请求后如果有数据, 立刻响应请求; 如果没有数据就会 hold 一段时间,这段时间内如果有数据立刻响应请求; 如果时间到了还没有数据, 则响应 http 请求;浏览器受到 http 响应后立在发送一个同样http 请求查询是否有数据;
短轮询
http端轮询是服务器收到请求不管是否有数据都直接响应 http 请求; 浏览器受到 http 响应隔一段时间在发送同样的http 请求查询是否有数据;
两者相同点:
可以看出 http 长轮询和 http 短轮询的都会 hold 一段时间;两者不同点
间隔发生在服务端还是浏览器端: http 长轮询在服务端会 hold 一段时间, http 短轮询在浏览器端 “hold”一段时间;
也就是说当弹出框弹出来的时候,就需要一直向后台询问,用户支付的情况
(有的人肯定会说,为什么不用户支付后点击确认这个点来判断用户是否支付成功,非要用什么长轮询,一直询问后台,那么麻烦,那你得思考一个问题,万一用户支付完了断网了,用户还需要重新支付一次再点击确定吗?)
在pages/Pay/index.vue中:定义一个timer


测试:
当我们点击立即支付按钮的时候,只要一弹出弹出框,就会一直向服务器发请求,我们这里利用的是定时器来控制发请求

如果我们扫描支付了,就会看到控制台出现:

如果支付成功,我们需要隐藏支付的弹出框,清除定时器 ,存储code为200,跳转路由
存储code是为了,验证用户是否支付成功,支付成功code为200,这个时候用户再点击已支付成功按钮,才能跳转到支付成功的页面

在pages/Pay/index.vue中:


完整代码:
- // 弹出框
- async open(){
- //生成二维码(地址)
- let url = await QRCode.toDataURL(this.payInfo.codeUrl)
- // console.log(url)
- this.$alert(`<img src=${url} />`, '请微信支付', {
- dangerouslyUseHTMLString: true, //是否将 message 属性作为 HTML 片段处理
- center:true, //是否居中布局
- showCancelButton:true, //是否显示取消按钮
- cancelButtonText:"支付遇见问题",//取消按钮的文本内容
- confirmButtonText:"已支付成功",//支付按钮的文本内容
- showClose:true,//右上角的叉子没了
- });
- //你需要知道支付成功|失败
- //支付成功,路由跳转,如果支付失败,提示信息
- //定时器没有,开启一个新的定时器
- if(!this.timer){
- this.timer = setInterval(async()=>{
- //发请求获取用户支付状态
- let result = await this.$API.reqPayStatus(this.orderId)
- // console.log(result)
- //如果code==200
- if(result.code==200){
- //第一步:清除定时器
- clearInterval(this.timer)
- this.timer = null
- //保存支付成功返回的code
- this.code = result.code
- //关闭弹出框
- this.$msgbox.close()
- //跳转到下一路由
- this.$router.push('/paysuccess')
- }
- },1000)
- }
- }
但是跳转下一路由我们这里还没写,我们这里写一下:
在pages文件下新建PaySuccess文件,里面在创建一个index.vue

pages/PaySucess/index.vue里面的完整代码:
- <template>
- <div class="paysuccess">
-
- <div class="success">
- <h3>
- <img src="./images/right.png" width="48" height="48">
- 恭喜您,支付成功啦!
- </h3>
- <div class="paydetail">
- <p class="button">
- <router-link class="btn-look" to="/center">查看订单</router-link>
- <router-link class="btn-goshop" to="/">继续购物</router-link>
- </p>
- </div>
- </div>
-
- </div>
- </template>
-
- <script>
- export default {
- name: 'PaySuccess',
- }
- </script>
-
- <style lang="less" scoped>
- .paysuccess {
- margin: 20px auto;
- padding: 25px;
- border: 1px solid rgb(211, 211, 211);
- width: 1200px;
- width: 1148px;
-
- .success {
- width: 500px;
- margin: 0 auto;
-
- h3 {
- margin: 20px 0;
- font-weight: 700;
- font-size: 20px;
- line-height: 30px;
-
- img {
- max-width: 100%;
- vertical-align: middle;
- border: 0;
- margin-right: 14px;
- }
- }
-
- .paydetail {
- margin-left: 66px;
- font-size: 15px;
-
- .button {
- margin: 30px 0;
- line-height: 26px;
-
- a {
- display: inline-block;
- box-sizing: border-box;
- text-align: center;
- vertical-align: middle;
- cursor: pointer;
- border-radius: 2px;
- user-select: none;
- font-size: 18px;
- padding: 4px 20px;
- line-height: 22px;
- text-decoration: none;
-
- &.btn-look {
- margin-right: 13px;
- color: #fff;
- background-color: #e1251b;
- border: 1px solid #e1251b;
- }
-
- &.btn-goshop {
- color: #666;
- background-color: #eee;
- border: 1px solid #e1e1e1;
- }
- }
- }
- }
- }
- }
- </style>

测试一下:没问题,可以使用

出现这个页面的时候,我们并没有支付的情况下,我们点击已支付成功, 弹出框也会消失,正常业务逻辑是要支付以后点击已支付成功,弹出框才会消失的

比如用户真的支付成功了,应该跳转到支付成功的页面
用户假的支付成功了,应该弹出一个让用户继续支付
用户点击支付遇见问题,应该弹一个联系管理员之类的
ElementUI官网:Element - The world's most popular Vue UI framework
这个时候我们利用ElementUI官网的MessageBox里面的beforeClose来解决这个问题,
因为上面提到的三种情况,都是在MessageBox关闭前执行的,所以这个方法,就是在关闭之前在帮你执行一些事情

在pages/Pay/index.vue中:
写上这个回调后,我们再点击支付遇见问题,和已支付成功,就不会关闭了,他要等待我们把里面的函数处理完了,才会进行下一步操作

但是问题来了,我们怎么知道我们点击的按钮是谁,我们得区分按钮
ElementUI那张图已经说的很清楚了
测试:
第一个参数是可以用户区分是哪个按钮


第二个参数:当前组件实例

第三个参数:关闭弹出框的方法
我们可以调用这个done(),可以让我们点击已支付成功和支付遇到问题的时候,关闭弹出框


- // 弹出框
- async open(){
- //生成二维码(地址)
- let url = await QRCode.toDataURL(this.payInfo.codeUrl)
- // console.log(url)
- this.$alert(`<img src=${url} />`, '请微信支付', {
- dangerouslyUseHTMLString: true, //是否将 message 属性作为 HTML 片段处理
- center:true, //是否居中布局
- showCancelButton:true, //是否显示取消按钮
- cancelButtonText:"支付遇见问题",//取消按钮的文本内容
- confirmButtonText:"已支付成功",//支付按钮的文本内容
- showClose:false,//右上角的叉子没了
- //关闭弹出框的配置值
- beforeClose:(type,instance,done)=>{
- // console.log(type,instance,done)
- //type:区分取消|确定按钮
- //instance:当前组件实例
- //done:关闭弹出框的方法
- if(type=='cancel'){
- alert('请联系管理员华哥')
- //清除定时器
- clearInterval(this.timer)
- this.timer = null
- //关闭弹出框
- done()
- }else{
- //判断是否真的支付了
- if(this.code==200){
- clearInterval(this.timer)
- this.timer = null
- done()
- //跳转到下一路由
- this.$router.push('/paysuccess')
- }
- }
- }
- });
- //你需要知道支付成功|失败
- //支付成功,路由跳转,如果支付失败,提示信息
- //定时器没有,开启一个新的定时器
- if(!this.timer){
- this.timer = setInterval(async()=>{
- //发请求获取用户支付状态
- let result = await this.$API.reqPayStatus(this.orderId)
- // console.log(result)
- //如果code==200
- if(result.code==200){
- //第一步:清除定时器
- clearInterval(this.timer)
- this.timer = null
- //保存支付成功返回的code
- this.code = result.code
- //关闭弹出框
- this.$msgbox.close()
- //跳转到下一路由
- this.$router.push('/paysuccess')
- }
- },1000)
- }
- }
注意上面完整代码中新增的代码,我截图放下面了

这个时候我们点击支付遇到问题:

不付款 点击已支付成功,不会有任何反应
付款后 点击已支付成功,会跳转到支付成功的页面:

还有一点就是我们可以不用支付,点击已支付成功跳转到支付成功的页面
开发人员:为了自己方便,这里的判断先不要了

到此微信支付功能就实现了