jcloud/dashboard/src2/components/OrderCheckout.vue

434 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="show" class="fixed inset-0 bg-black bg-opacity-60 z-[9999] flex items-center justify-center overflow-auto p-4">
<div class="bg-white rounded-xl shadow-2xl w-full max-w-xl mx-auto relative animate-fadeIn">
<button @click="onClose" class="absolute top-4 right-4 p-2 rounded-full hover:bg-gray-100 transition-colors z-10">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
<div class="px-8 py-10">
<div v-if="$resources.processBalancePayment.loading || $resources.processAlipayPayment.loading || $resources.processWechatPayment.loading" class="text-center py-8">
<div class="h-12 w-12 mx-auto animate-spin text-blue-600 mb-4 flex items-center justify-center">
<i class="fe fe-loader text-3xl"></i>
</div>
<p class="text-gray-700 text-lg">正在处理支付请稍候...</p>
</div>
<div v-else-if="paymentMethod === 'wechatpay' && paymentQrCode" class="text-center py-6">
<div class="relative mb-6">
<div class="flex items-center justify-center">
<WeChatPayLogo class="h-10" />
</div>
</div>
<div class="text-center mb-6">
<div class="text-sm text-gray-600 mb-2">扫一扫付款</div>
<div class="text-3xl font-bold text-[#FF0036]">{{ order?.total_amount }} </div>
</div>
<div class="flex justify-center">
<div class="qrcode-container bg-white p-4 border border-gray-100 shadow-sm rounded-lg">
<img
:src="paymentQrCodeImage"
alt="微信支付二维码"
class="qrcode-image"
/>
</div>
</div>
<div class="mt-6 text-center">
<p class="text-gray-600">
请使用微信扫描二维码完成支付
</p>
<p class="text-gray-500 text-sm mt-2">二维码有效期 15 分钟</p>
</div>
</div>
<div v-else-if="paymentMethod === 'alipay' && paymentUrl" class="text-center py-6">
<div class="mb-6">
<AlipayLogo class="h-10 mx-auto" />
</div>
<h3 class="text-lg font-medium text-gray-900 mb-4">请在新页面完成支付宝支付</h3>
<p class="text-gray-600 mb-6">
如果没有自动跳转请点击下方按钮打开支付页面
</p>
<div class="space-y-4">
<button
class="w-full px-6 py-3 bg-[#1677FF] text-white rounded-lg hover:bg-[#0E5FD8] transition-colors shadow-sm"
@click="window.open(paymentUrl, '_blank')"
>
打开支付页面
</button>
<p class="text-gray-600 text-sm mt-4">
支付完成后请稍等片刻系统会自动继续创建站点
</p>
<button
class="w-full mt-4 px-6 py-3 bg-gray-100 rounded-lg hover:bg-gray-200 text-gray-700 transition-colors"
@click="paymentMethod = null"
>
返回选择其他支付方式
</button>
</div>
</div>
<div v-else class="space-y-6">
<div class="bg-gray-50 p-4 rounded-lg mb-6">
<p class="text-gray-700 mb-2 font-medium">
站点URL{{ order?.title }}
</p>
<p class="text-xl font-bold text-gray-900">
订单金额: {{ $format.userCurrency(order?.total_amount) }}
</p>
</div>
<ErrorMessage
class="mt-3"
:message="$resources.processBalancePayment.error || $resources.processAlipayPayment.error || $resources.processWechatPayment.error"
/>
<p class="text-gray-700 font-medium">
选择支付方式
</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
<button
class="p-6 border border-gray-200 rounded-lg flex flex-col items-center justify-center hover:bg-gray-50 hover:border-gray-300 transition-all transform hover:-translate-y-1"
@click="processPayment('balance')"
:disabled="$resources.processBalancePayment.loading || $resources.processAlipayPayment.loading || $resources.processWechatPayment.loading"
>
<span class="text-gray-800 font-medium">余额支付</span>
</button>
<button
@click="processPayment('alipay')"
:disabled="$resources.processBalancePayment.loading || $resources.processAlipayPayment.loading || $resources.processWechatPayment.loading"
class="p-6 border border-gray-200 rounded-lg hover:bg-gray-50 hover:border-gray-300 transition-all transform hover:-translate-y-1"
>
<div class="flex flex-col items-center">
<div class="mb-3">
<AlipayLogo class="h-10" />
</div>
</div>
</button>
<button
@click="processPayment('wechatpay')"
:disabled="$resources.processBalancePayment.loading || $resources.processAlipayPayment.loading || $resources.processWechatPayment.loading"
class="p-6 border border-gray-200 rounded-lg hover:bg-gray-50 hover:border-gray-300 transition-all transform hover:-translate-y-1"
>
<div class="flex flex-col items-center">
<div class="mb-3">
<WeChatPayLogo class="h-10" />
</div>
</div>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { toast } from 'vue-sonner';
import { Teleport } from 'vue';
import { DashboardError } from '../utils/error';
import AlipayLogo from '../logo/AlipayLogo.vue';
import WeChatPayLogo from '../logo/WeChatPayLogo.vue';
export default {
name: 'OrderCheckout',
props: {
value: {
type: Boolean,
default: false
},
order: {
type: Object,
required: true
},
orderType: {
type: String,
default: '订单'
}
},
components: {
AlipayLogo,
WeChatPayLogo
},
data() {
return {
paymentMethod: null,
paymentUrl: null,
paymentQrCode: null,
paymentQrCodeImage: null,
orderId: null,
checkInterval: null,
localShow: false
};
},
computed: {
show: {
get() {
return this.value || this.localShow;
},
set(val) {
this.$emit('input', val);
this.localShow = val;
}
}
},
resources: {
processBalancePayment() {
return {
url: 'jcloud.api.billing.process_balance_payment_for_order',
params: {
order_id: this.order.order_id || this.order.name
},
validate() {
if (!this.order) {
throw new DashboardError('缺少订单信息');
}
},
onSuccess(response) {
// 检查响应中的success字段
if (response.status === "Error" || response.success === false) {
// 显示错误消息
toast.error(response.message || '支付失败,请确保余额充足');
this.$emit('payment-error', { message: response.message || '余额不足' });
return;
}
// 支付成功,通知父组件
toast.success('支付成功!');
// 确保传递完整的订单信息,并更新订单状态为已支付
const orderData = {
...this.order,
...(response.order || {}),
status: '已支付' // 明确设置订单状态为已支付
};
this.$emit('payment-success', orderData);
this.onClose();
}
};
},
processAlipayPayment() {
return {
url: 'jcloud.api.billing.process_alipay_order',
params: {
order_id: this.order.order_id || this.order.name,
},
validate() {
if (!this.order) {
throw new DashboardError('缺少订单信息');
}
},
onSuccess(response) {
// 保存订单ID用于后续状态检查
this.orderId = this.order.order_id || this.order.name;
this.paymentUrl = response.payment_url;
// 支付宝则打开新窗口
window.open(response.payment_url, '_blank');
toast.success('支付页面已在新窗口打开');
// 开始轮询订单状态
this.startPaymentCheck();
}
};
},
processWechatPayment() {
return {
url: 'jcloud.api.billing.process_wechatpay_order',
params: {
order_id: this.order.order_id || this.order.name,
},
validate() {
if (!this.order) {
throw new DashboardError('缺少订单信息');
}
},
onSuccess(response) {
// 保存订单ID用于后续状态检查
this.orderId = this.order.order_id || this.order.name;
// 确保设置正确的数据
this.paymentQrCode = response.payment_url;
this.paymentQrCodeImage = response.qr_code_image || null;
// 开始轮询订单状态
this.startPaymentCheck();
}
};
},
checkPaymentStatus() {
return {
url: 'jcloud.api.billing.check_order_payment_status',
params: {
order_id: this.order.order_id || this.order.name
},
onSuccess(data) {
if (data && data.status === '已支付') {
// 支付成功,停止轮询并通知父组件
this.stopPaymentCheck();
toast.success('支付成功!');
// 确保传递完整的订单信息,包括原始订单和服务器返回的数据
const orderData = { ...this.order, ...(data.order || {}) };
this.$emit('payment-success', orderData);
this.onClose();
}
}
};
}
},
watch: {
value: {
immediate: true,
handler(newVal) {
this.localShow = newVal;
}
}
},
methods: {
onClose() {
this.stopPaymentCheck();
this.paymentMethod = null;
this.paymentUrl = null;
this.paymentQrCode = null;
this.paymentQrCodeImage = null;
this.show = false;
// 向父组件发出关闭信号,确保父组件也知道弹窗已关闭
this.$emit('close');
},
processPayment(method) {
this.paymentMethod = method;
if (method === 'balance') {
// 处理余额支付
this.$resources.processBalancePayment.submit();
} else if (method === 'alipay') {
// 处理支付宝支付
this.$resources.processAlipayPayment.submit();
} else if (method === 'wechatpay') {
// 处理微信支付
this.$resources.processWechatPayment.submit();
}
},
startPaymentCheck() {
this.checkInterval = setInterval(() => {
this.$resources.checkPaymentStatus.submit();
}, 3000);
// 15分钟后停止检查
setTimeout(() => {
this.stopPaymentCheck();
}, 900000);
},
stopPaymentCheck() {
if (this.checkInterval) {
clearInterval(this.checkInterval);
this.checkInterval = null;
}
}
},
mounted() {
this.localShow = true;
},
beforeUnmount() {
this.stopPaymentCheck();
}
};
</script>
<style scoped>
.payment-icon {
display: flex;
align-items: center;
justify-content: center;
height: auto;
width: 100px;
max-width: 100px;
min-width: 70px;
flex-shrink: 0;
}
.alipay-svg, .wechat-svg {
width: 100%;
height: auto;
max-height: 36px;
}
@media (max-width: 640px) {
.payment-icon {
width: 80px;
min-width: 60px;
}
.alipay-svg, .wechat-svg {
max-height: 30px;
}
}
/* 添加微信支付二维码相关样式 */
.qrcode-container {
height: 250px;
width: 250px;
margin: 0 auto;
padding: 5px;
border: 1px solid #e5e7eb;
border-radius: 12px;
transition: all 0.3s ease;
}
.qrcode-image {
height: 100%;
width: 100%;
object-fit: contain;
}
.close-button {
position: absolute;
top: -12px;
right: -12px;
background: #f3f4f6;
border-radius: 50%;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
color: #6b7280;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
transition: all 0.2s;
}
.close-button:hover {
background: #e5e7eb;
color: #374151;
transform: scale(1.05);
}
/* 添加渐入动画 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fadeIn {
animation: fadeIn 0.3s ease-out;
}
</style>