all
This commit is contained in:
@@ -0,0 +1,335 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user