336 lines
9.3 KiB
Vue
336 lines
9.3 KiB
Vue
<template>
|
||
<view class="pay-container">
|
||
<view class="pay_title">请扫码付款</view>
|
||
<!-- 支付方式切换 -->
|
||
<view class="tab-wrap">
|
||
<view
|
||
:class="['tab-item', payType === 'alipay' ? 'active' : '']"
|
||
@tap="handlePayTypeChange('alipay')"
|
||
>
|
||
<image src="../../static/img/member/zfbx.png" class="pay-icon" v-if="payType==='alipay'"/>
|
||
<image src="../../static/img/member/zfb.png" class="pay-icon" v-else/>
|
||
<text>支付宝</text>
|
||
</view>
|
||
<view
|
||
:class="['tab-item', payType === 'wechat' ? 'active' : '']"
|
||
@tap="handlePayTypeChange('wechat')"
|
||
>
|
||
<image src="../../static/img/member/wxx.png" class="pay-icon" v-if="payType==='wechat'" />
|
||
<image src="../../static/img/member/wx.png" class="pay-icon" v-else/>
|
||
<text>微信</text>
|
||
</view>
|
||
</view>
|
||
<!-- 二维码区 -->
|
||
<view class="qr-section">
|
||
<image
|
||
:src="alipayQrcode"
|
||
class="qr-img"
|
||
mode="aspectFit"
|
||
alt="扫码二维码"
|
||
/>
|
||
<view class="tip">
|
||
<span v-if="leftSeconds > 0">
|
||
剩余支付时间:
|
||
<span class="countdown">
|
||
{{ Math.floor(leftSeconds / 60) }}:{{ (leftSeconds % 60).toString().padStart(2, '0') }}
|
||
</span>
|
||
</span>
|
||
<span v-else>
|
||
二维码已过期,请刷新页面获取新的二维码。
|
||
</span>
|
||
<br/>
|
||
</view>
|
||
</view>
|
||
<!-- 售后说明区 -->
|
||
<view class="after-sale">
|
||
<view class="as-title">售后说明</view>
|
||
<view class="as-item">付款后1个工作日内出具评估报告,报告可下载。</view>
|
||
<view class="as-item">可申请退款售后,每笔订单只可申请一次退款。</view>
|
||
<view class="as-item">我们将跟踪贷款业务结果,贷款未成功将自动退款。</view>
|
||
<view class="as-item">客服电话:400-xxxx-xxxx</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
props: {
|
||
orderId: {
|
||
type: [String, Number],
|
||
required: true
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
payType: 'alipay', // 当前选择的支付方式
|
||
alipayQrcode: require('../../static/img/member/code.png'),
|
||
qrcodeLoaded: false,
|
||
endtime: 0, // 服务器返回的结束时刻(时间戳,秒)
|
||
leftSeconds: 0,
|
||
order_no: '',
|
||
payTimer: null, // 定时查询支付状态的timer
|
||
countdownTimer: null // 倒计时timer
|
||
};
|
||
},
|
||
watch: {
|
||
orderId: {
|
||
immediate: true,
|
||
handler(newVal) {
|
||
// 当 orderId 变化时重新拉取二维码
|
||
if (newVal) {
|
||
this.resetTimers();
|
||
this.qrcodeLoaded = false;
|
||
this.alipayQrcode = require('../../static/img/member/code.png');
|
||
this.leftSeconds = 0;
|
||
this.endtime = 0;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
created() {
|
||
// 组件加载时自动拉取 orderId 的二维码
|
||
if (this.orderId) {
|
||
this.getQrcode('alipay');
|
||
}
|
||
},
|
||
beforeDestroy() {
|
||
// 组件销毁时,清理所有定时器
|
||
this.resetTimers();
|
||
},
|
||
methods: {
|
||
resetTimers() {
|
||
if (this.payTimer) {
|
||
clearInterval(this.payTimer);
|
||
this.payTimer = null;
|
||
}
|
||
if (this.countdownTimer) {
|
||
clearInterval(this.countdownTimer);
|
||
this.countdownTimer = null;
|
||
}
|
||
},
|
||
async handlePayTypeChange(type) {
|
||
if (this.payType === type) return; // 避免重复拉取
|
||
this.payType = type;
|
||
this.resetTimers();
|
||
this.leftSeconds = 0;
|
||
this.endtime = 0;
|
||
this.alipayQrcode = require('../../static/img/member/code.png');
|
||
this.getQrcode(type);
|
||
},
|
||
// 切换支付方式时,调用后端接口获取二维码
|
||
async getQrcode(type) {
|
||
if (!this.orderId) return;
|
||
|
||
this.resetTimers(); // 切换支付方式/刷新二维码前先清理所有定时器
|
||
|
||
this._post(
|
||
'/asset.Asset/pay',
|
||
{
|
||
pay_type: type === 'alipay' ? 30 : 20,
|
||
id: this.orderId
|
||
},
|
||
res => {
|
||
// 正常会有服务端传回支付二维码、截止时间、订单号
|
||
this.alipayQrcode = res.data.qr_code_url;
|
||
this.order_no = res.data.order_no;
|
||
// 假设服务端返回剩余秒数,推荐直接返回leftSeconds,否则自己算
|
||
// 兼容两种情况:res.data.leftSeconds 或 res.data.pay_end_time
|
||
if (typeof res.data.leftSeconds !== 'undefined') {
|
||
this.leftSeconds = res.data.leftSeconds;
|
||
this.endtime = Math.floor(Date.now() / 1000) + this.leftSeconds;
|
||
} else if (typeof res.data.pay_end_time !== 'undefined') {
|
||
this.endtime = res.data.pay_end_time;
|
||
this.leftSeconds = this.endtime - Math.floor(Date.now() / 1000);
|
||
} else {
|
||
// fallback,3分钟过期
|
||
this.leftSeconds = 180;
|
||
this.endtime = Math.floor(Date.now() / 1000) + 180;
|
||
}
|
||
this.startCountdownAndPayCheck();
|
||
},
|
||
err => {
|
||
this.resetTimers();
|
||
// 生成失败时隐藏支付弹窗并提示
|
||
this.$emit('close'); // 通知父组件关闭弹窗
|
||
const msg = (err && err.data && err.data.msg) ? err.data.msg : '获取二维码失败';
|
||
this.$message.error(msg);
|
||
}
|
||
);
|
||
},
|
||
startCountdownAndPayCheck() {
|
||
// 安全起见再次清理
|
||
this.resetTimers();
|
||
|
||
// 设置倒计时定时器,精确倒计时
|
||
this.countdownTimer = setInterval(() => {
|
||
if (this.leftSeconds > 0) {
|
||
this.leftSeconds -= 1;
|
||
}
|
||
if (this.leftSeconds <= 0) {
|
||
this.leftSeconds = 0;
|
||
this.resetTimers();
|
||
// 自动关闭弹窗
|
||
this.$emit('close');
|
||
this.$message.error('二维码已过期,请刷新页面获取新的二维码。');
|
||
}
|
||
}, 1000);
|
||
|
||
// 设置支付状态查询定时器
|
||
this.payTimer = setInterval(() => {
|
||
if (this.order_no && this.leftSeconds > 0) {
|
||
this._post(
|
||
'/asset.Asset/payStatus',
|
||
{
|
||
order_no: this.order_no
|
||
},
|
||
res => {
|
||
// 如果支付成功,根据业务逻辑刷新页面/跳转/弹窗...
|
||
if (res.data && res.data.pay_status === 20) {
|
||
this.resetTimers();
|
||
this.$emit('pay-success', this.order_no);
|
||
this.$message.success('支付成功');
|
||
//支付成功后关闭弹窗,强制刷新页面
|
||
this.$emit('close');
|
||
this.$nextTick(() => {
|
||
window.location.reload();
|
||
});
|
||
}
|
||
// 若未支付则轮询,不需要额外处理
|
||
},
|
||
err => {
|
||
// 支付状态查询失败,也需要轮询,不紧急关闭
|
||
// 但你可以根据实际业务在失败次数过多时终止(此处只做错误提示)
|
||
// this.$message.error('支付状态查询失败');
|
||
}
|
||
);
|
||
}
|
||
}, 3000);
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.pay-container {
|
||
width: 65vh;
|
||
margin: 0 auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-top: -30px;
|
||
}
|
||
.pay_title{
|
||
font-family: Source Han Sans, Source Han Sans;
|
||
font-weight: 700;
|
||
font-size: 18px;
|
||
color: #3D3D3D;
|
||
line-height: 22px;
|
||
text-align: center;
|
||
font-style: normal;
|
||
text-transform: none;
|
||
margin-bottom: 24px;
|
||
}
|
||
.tab-wrap {
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: 28px;
|
||
.tab-item {
|
||
width: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 19px;
|
||
font-family: Source Han Sans, Source Han Sans;
|
||
font-weight: 400;
|
||
font-size: 16px;
|
||
color: #767676;
|
||
border-radius: 6px 6px 0 0;
|
||
padding: 12px 38px;
|
||
cursor: pointer;
|
||
border-bottom: 2px solid #e4e4e4;
|
||
.pay-icon {
|
||
width: 18px;
|
||
height: 18px;
|
||
margin-right: 8px;
|
||
}
|
||
}
|
||
.tab-item:last-child {
|
||
margin-right: 0;
|
||
}
|
||
.tab-item.active {
|
||
border-bottom: 2px solid #f48612;
|
||
box-shadow: 0 6px 18px 0 rgba(244, 134, 18, 0.04);
|
||
font-family: Source Han Sans, Source Han Sans;
|
||
font-weight: 500;
|
||
font-size: 16px;
|
||
color: #F78A30;
|
||
line-height: 22px;
|
||
}
|
||
}
|
||
|
||
.qr-section {
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-bottom: 24px;
|
||
.qr-img {
|
||
width: 160px;
|
||
height: 160px;
|
||
border: 1px solid #f2f2f2;
|
||
border-radius: 8px;
|
||
background: #fcfcfc;
|
||
margin-bottom: 14px;
|
||
}
|
||
.remark {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 16px;
|
||
color: #666;
|
||
margin-bottom: 15px;
|
||
.remark-icon {
|
||
width: 18px;
|
||
height: 18px;
|
||
margin-right: 7px;
|
||
}
|
||
text {
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
.tip {
|
||
width: 100%;
|
||
text-align: center;
|
||
font-family: Source Han Sans, Source Han Sans;
|
||
font-weight: 400;
|
||
font-size: 12px;
|
||
color: #767676;
|
||
line-height: 22px;
|
||
margin-top: 2px;
|
||
}
|
||
}
|
||
|
||
.after-sale {
|
||
width: 100%;
|
||
padding: 20px 0 0 0;
|
||
.as-title {
|
||
margin-bottom: 7px;
|
||
font-family: Source Han Sans, Source Han Sans;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
color: #3D3D3D;
|
||
}
|
||
.as-item {
|
||
margin-bottom: 4px;
|
||
font-family: Source Han Sans, Source Han Sans;
|
||
font-weight: 400;
|
||
font-size: 12px;
|
||
color: #767676;
|
||
line-height: 22px;
|
||
text-align: left;
|
||
}
|
||
}
|
||
|
||
/* 可以根据实际图片和页面风格微调样式 */
|
||
</style>
|