目录
支付模块的一个大致过程主要分为
支付页面的一个基础布局、数据的渲染、支付流程、等待支付还有一个支付结果的展示
支付页面 - 基础布局
分析
基础布局的话 就是配置路由和支付页面基础布局
创建页面
新建文件 src/views/Member/Pay.vue
<script name="XtxPayPage" setup lang="ts"></script>
<template>
<div class="xtx-pay-page">
<div class="container">
<XtxBread>
<XtxBreadItem to="/">首页</XtxBreadItem>
<XtxBreadItem to="/cart">购物车</XtxBreadItem>
<XtxBreadItem>支付订单</XtxBreadItem>
</XtxBread>
<!-- 付款信息 -->
<div class="pay-info">
<span class="icon iconfont icon-queren2"></span>
<div class="tip">
<p>订单提交成功!请尽快完成支付。</p>
<p>支付还剩 <span>24分59秒</span>, 超时后将取消订单</p>
</div>
<div class="amount">
<span>应付总额:</span>
<span>¥5673.00</span>
</div>
</div>
<!-- 付款方式 -->
<div class="pay-type">
<p class="head">选择以下支付方式付款</p>
<div class="item">
<p>支付平台</p>
<a class="btn wx" href="javascript:;"></a>
<a class="btn alipay" href="javascript:;"></a>
</div>
<div class="item">
<p>支付方式</p>
<a class="btn" href="javascript:;">招商银行</a>
<a class="btn" href="javascript:;">工商银行</a>
<a class="btn" href="javascript:;">建设银行</a>
<a class="btn" href="javascript:;">农业银行</a>
<a class="btn" href="javascript:;">交通银行</a>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="less">
.pay-info {
background: #fff;
display: flex;
align-items: center;
height: 240px;
padding: 0 80px;
.icon {
font-size: 80px;
color: #1dc779;
}
.tip {
padding-left: 10px;
flex: 1;
p {
&:first-child {
font-size: 20px;
margin-bottom: 5px;
}
&:last-child {
color: #999;
font-size: 16px;
}
}
}
.amount {
span {
&:first-child {
font-size: 16px;
color: #999;
}
&:last-child {
color: @priceColor;
font-size: 20px;
}
}
}
}
.pay-type {
margin-top: 20px;
background-color: #fff;
padding-bottom: 70px;
p {
line-height: 70px;
height: 70px;
padding-left: 30px;
font-size: 16px;
&.head {
border-bottom: 1px solid #f5f5f5;
}
}
.btn {
width: 150px;
height: 50px;
border: 1px solid #e4e4e4;
text-align: center;
line-height: 48px;
margin-left: 30px;
color: #666666;
display: inline-block;
&.active,
&:hover {
border-color: @xtxColor;
}
&.alipay {
background: url(https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/7b6b02396368c9314528c0bbd85a2e06.png)
no-repeat center / contain;
}
&.wx {
background: url(https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/c66f98cff8649bd5ba722c2e8067c6ca.jpg)
no-repeat center / contain;
}
}
}
</style>
配置路由
src/router/index.ts 配置二级路由,不需要加 /
{
path: 'member/pay',
component: () => import('@/views/Member/Pay.vue')
}
订单跳转页
订单跳转时要传递订单id参数
页面跳转需要思考的问题
router.push()
和router.replace()
的区别是什么?- push 追加,后退能回到前页面
- replace 替换,后退不能回到前页面
- 提交订单后跳转到 支付详情页,是通过
push
合适,还是用replace
更合适呢,为什么?replace
更合适,下单后商品信息已经没有了,后退回去无意义。 - 跳转时传递订单
id
const submitCheckout = async () => {
// 如果地址为空,不能提交订单
if (!checkout.showAddress) {
return Message.warning('请选择收货地址')
}
const res = await request.post('/member/order', {
goods: checkout.checkoutInfo.goods.map((item) => {
return {
skuId: item.skuId,
count: item.count,
}
}),
addressId: checkout.showAddress.id,
})
// 成功提醒用户
Message({ type: 'success', text: '下单成功~' })
// 🔔重新获取购物车列表
+ cart.getCartList()
// 跳转到支付页面
+ router.replace('/member/pay?id=' + res.data.result.id)
}
支付页面 - 数据渲染
分析
就是渲染真实支付数据
(1)先提供数据类型
export interface OrderPayInfo {
id: string
createTime: string
payType: number
orderState: number
payLatestTime: string
countdown: number
postFee: number
payMoney: number
payChannel: number
totalMoney: number
totalNum: number
deliveryTimeType: number
receiverContact: string
receiverMobile: string
provinceCode: string
cityCode: string
countyCode: string
receiverAddress: string
payTime?: any
consignTime?: any
endTime?: any
closeTime?: any
evaluationTime?: any
skus: {
id: string
spuId: string
name: string
quantity: number
image: string
realPay: number
curPrice: number
totalMoney?: any
properties: {
propertyMainName: string
propertyValueName: string
}[]
attrsText: string
}[]
arrivalEstimatedTime?: any
}
(2)发送请求获取支付数据
<script name="XtxPayPage" setup lang="ts">
import { ApiRes } from '@/types/data'
import request from '@/utils/request'
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { OrderPayInfo } from '@/types/order'
const route = useRoute()
const id = route.query.id
const order = ref<OrderPayInfo>({} as OrderPayInfo)
onMounted(async () => {
const res = await request.get<ApiRes<OrderPayInfo>>(`/member/order/${id}`)
order.value = res.data.result
})
</script>
(3)渲染支付信息
<!-- 付款信息 -->
<div class="pay-info">
<span class="icon iconfont icon-queren2"></span>
<div class="tip">
<p>订单提交成功!请尽快完成支付。</p>
<p>
支付还剩 <span>{{ order?.countdown }}</span
>, 超时后将取消订单
</p>
</div>
<div class="amount">
<span>应付总额:</span>
<span>¥{{ order?.totalMoney }}</span>
</div>
</div>
支付页面 -倒计时渲染
(1)开始倒计时
const showTime = ref(0)
onMounted(async () => {
const res = await request.get<ApiRes<OrderPayInfo>>(`/member/order/${id}`)
console.log('GET', `/member/order/${id}`, res.data.result)
order.value = res.data.result
const { time, start } = useCountDown(order.value.countdown)
start()
watch(time, (value) => {
showTime.value = value
})
})
(2)安装dayjs
yarn add dayjs
(3)提供方法(注意:vue3没有过滤器)
const formatTime = (time: number) => {
return dayjs.unix(time).format('mm分ss秒')
}
(4)渲染
<p>
支付还剩 <span>{{ formatTime(showTime) }} </span>, 超时后将取消订单
</p>
(5)倒计时为0,重新跳到购物车页面
// 将初始值, 同步给showTime
showTime.value = order.value.countdown
watch(time, (value) => {
showTime.value = value
if (value <= 0) {
// 跳转页面, 并提醒用户, 订单已超时
router.replace('/cart')
Message.warning('订单已超时, 请重新下单')
}
})
支付 - 支付流程
分析
步骤
●PC前台点击支付按钮,新开标签页打开后台提供的支付链接带上订单ID还有回跳地址
●后台服务发起支付,等待支付结果,修改订单状态,回跳PC前台结果页
●PC前台在结果页获取回跳URL参数订单ID查询支付状态,展示支付结果
# 支付地址回调地址(可变)
http://www.corho.com:8080/#/pay/callback
买家账号jfjbwb4477@sandbox.com
登录密码111111
支付密码111111
支付 - 等待支付
目的:支付打开新页,当前页打开提示框
- 在
utils/request.ts
中导出基础路径
export const baseURL = 'https://apipc-xiaotuxian-front.itheima.net/'
const request = axios.create({
baseURL,
// baseURL: 'http://pcapi-xiaotuxian-front.itheima.net/',
timeout: 5000
})
2.拼接支付链接
const redirectUrl = encodeURIComponent('http://www.corho.com:8080/#/pay/callback')
const payUrl = `${baseURL}pay/aliPay?orderId=${route.query.id}&redirect=${redirectUrl}`
3.增加跳转链接
<a class="btn alipay" :href="payUrl"></a>
支付结果展示
实现步骤
- 准备一个基础页面
- 根据地址订单ID查询订单状态进行展示,或者是地址栏支付结果
具体代码
(1)准备结果页面 src/views/Member/Pay/Callback.vue
<script setup lang="ts">
//
</script>
<template>
<div class="xtx-pay-page">
<div class="container">
<XtxBread>
<XtxBreadItem to="/">首页</XtxBreadItem>
<XtxBreadItem to="/cart">购物车</XtxBreadItem>
<XtxBreadItem>支付结果</XtxBreadItem>
</XtxBread>
<!-- 支付结果 -->
<div class="pay-result">
<span class="iconfont icon-queren2 green"></span>
<!-- <span class="iconfont icon-shanchu red" ></span> -->
<p class="tit">订单支付成功</p>
<p class="tip">我们将尽快为您发货,收货期间请保持手机畅通</p>
<p>支付方式:<span>微信支付</span></p>
<p>支付金额:<span>¥1899.00</span></p>
<div class="btn">
<XtxButton type="primary" style="margin-right:20px">查看订单</XtxButton>
<XtxButton type="gray">进入首页</XtxButton>
</div>
<p class="alert">
<span class="iconfont icon-tip"></span>
温馨提示:小兔鲜儿不会以订单异常、系统升级为由要求您点击任何网址链接进行退款操作,保护资产、谨慎操作。
</p>
</div>
</div>
</div>
</template>
<style scoped lang="less">
.pay-result {
padding: 100px 0;
background: #fff;
text-align: center;
> .iconfont {
font-size: 100px;
}
.green {
color: #1dc779;
}
.red {
color: @priceColor;
}
.tit {
font-size: 24px;
}
.tip {
color: #999;
}
p {
line-height: 40px;
font-size: 16px;
}
.btn {
margin-top: 50px;
}
.alert {
font-size: 12px;
color: #999;
margin-top: 50px;
}
}
</style>
(2)配置路由
{
path: '/',
component: Layout,
children: [
...,
{
path: '/pay/callback',
component: () => import('@/views/member/pay/callback.vue')
}
]
},
(3)根据地址订单id查询订单状态进行展示
<script setup lang="ts">
import { ApiRes } from '@/types/data'
import { OrderResponse } from '@/types/order'
import request from '@/utils/request'
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const payResult = route.query.payResult === 'true'
const order = ref<OrderResponse>({} as OrderResponse)
onMounted(async () => {
// 根据订单id获取订单详情
const res = await request.get<ApiRes<OrderResponse>>(
`/member/order/${route.query.orderId}`
)
order.value = res.data.result
})
</script>
<template>
<div class="xtx-pay-page">
<div class="container">
<XtxBread>
<XtxBreadItem to="/">首页</XtxBreadItem>
<XtxBreadItem to="/cart">购物车</XtxBreadItem>
<XtxBreadItem>支付结果</XtxBreadItem>
</XtxBread>
<!-- 支付结果 -->
<div class="pay-result">
<span v-if="payResult" class="iconfont icon-queren2 green"></span>
<span v-else class="iconfont icon-shanchu red"></span>
<p class="tit">{{ payResult ? '订单支付成功' : '订单支付失败' }}</p>
<p class="tip">我们将尽快为您发货,收货期间请保持手机畅通</p>
<p>支付方式:<span>支付宝支付</span></p>
<p>
支付金额:<span>{{ order.payMoney }}</span>
</p>
<div class="btn">
<XtxButton type="primary" style="margin-right: 20px">
查看订单
</XtxButton>
<XtxButton type="gray">进入首页</XtxButton>
</div>
<p class="alert">
<span class="iconfont icon-tip"></span>
温馨提示:小兔鲜儿不会以订单异常、系统升级为由要求您点击任何网址链接进行退款操作,保护资产、谨慎操作。
</p>
</div>
</div>
</div>
</template>