314 lines
7.3 KiB
Vue
314 lines
7.3 KiB
Vue
<template>
|
||
<view v-if="visible" class="payment-modal" @click="handleClose">
|
||
<view class="payment-modal-content" @click.stop>
|
||
<!-- 关闭按钮 -->
|
||
<view class="payment-close-btn" @click="handleClose">
|
||
<text class="payment-close-icon">×</text>
|
||
</view>
|
||
|
||
<!-- 商品信息 -->
|
||
<view class="payment-product">
|
||
<view class="payment-product-icon">{{ productIcon }}</view>
|
||
<text class="payment-product-name">{{ productName }}</text>
|
||
<text class="payment-product-desc">{{ productDesc }}</text>
|
||
</view>
|
||
|
||
<!-- 价格 -->
|
||
<view class="payment-price">
|
||
<text class="payment-price-symbol">¥</text>
|
||
<text class="payment-price-amount">{{ amount }}</text>
|
||
</view>
|
||
|
||
<!-- 支付方式 -->
|
||
<view class="payment-method">
|
||
<view class="payment-method-item payment-method-active">
|
||
<view class="payment-method-left">
|
||
<text class="payment-method-icon">💳</text>
|
||
<text class="payment-method-label">微信支付</text>
|
||
</view>
|
||
<view class="payment-method-check">✓</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付按钮 -->
|
||
<view class="payment-submit-btn" :class="{ 'payment-submit-disabled': paying }" @click="handlePay">
|
||
<text class="payment-submit-text">{{ paying ? '支付中...' : '立即支付' }}</text>
|
||
</view>
|
||
|
||
<!-- 提示 -->
|
||
<view class="payment-tips">
|
||
<text class="payment-tips-text">支付即代表同意《服务协议》和《隐私政策》</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref } from "vue";
|
||
import { wxPay } from "@/utils/payment";
|
||
import { isWechatBrowser, payWithWechatJsapiH5 } from "@/utils/wechat-h5-jsapi-pay";
|
||
|
||
declare const uni: any;
|
||
|
||
interface Props {
|
||
visible: boolean;
|
||
productName: string; // 商品名称
|
||
productDesc?: string; // 商品描述
|
||
productIcon?: string; // 商品图标
|
||
amount: number; // 支付金额
|
||
businessType: string; // 业务类型
|
||
businessId: number; // 业务ID
|
||
}
|
||
|
||
const props = withDefaults(defineProps<Props>(), {
|
||
productDesc: '',
|
||
productIcon: '📦'
|
||
});
|
||
|
||
const emit = defineEmits<{
|
||
close: [];
|
||
success: [outTradeNo: string];
|
||
fail: [msg: string];
|
||
}>();
|
||
|
||
const paying = ref(false);
|
||
|
||
// 处理支付:微信 H5 内与财运月度详批一致走 JSAPI;其它环境走 uni 支付封装
|
||
const handlePay = async () => {
|
||
if (paying.value) return;
|
||
|
||
paying.value = true;
|
||
|
||
try {
|
||
if (typeof window !== 'undefined' && isWechatBrowser()) {
|
||
const r = await payWithWechatJsapiH5({
|
||
description: props.productDesc || props.productName,
|
||
totalAmountYuan: props.amount,
|
||
businessType: props.businessType,
|
||
businessId: props.businessId,
|
||
});
|
||
if (r.redirected) {
|
||
return;
|
||
}
|
||
if (r.ok) {
|
||
emit('success', r.outTradeNo || '');
|
||
handleClose();
|
||
} else if (r.msg && r.msg !== 'not_wechat') {
|
||
emit('fail', r.msg || '支付失败');
|
||
}
|
||
return;
|
||
}
|
||
|
||
const result = await wxPay({
|
||
description: props.productName,
|
||
total_amount: props.amount,
|
||
business_type: props.businessType,
|
||
business_id: props.businessId
|
||
});
|
||
|
||
if (result.success) {
|
||
uni.showToast({ title: '支付成功', icon: 'success' });
|
||
emit('success', result.outTradeNo || '');
|
||
handleClose();
|
||
} else {
|
||
emit('fail', (result as any).msg || '支付失败');
|
||
}
|
||
} catch (error: any) {
|
||
console.error('支付失败:', error);
|
||
uni.showToast({ title: error.msg || '支付失败', icon: 'none' });
|
||
emit('fail', error.msg || '支付失败');
|
||
} finally {
|
||
paying.value = false;
|
||
}
|
||
};
|
||
|
||
// 关闭弹窗
|
||
const handleClose = () => {
|
||
if (paying.value) {
|
||
uni.showToast({ title: '支付进行中,请稍候', icon: 'none' });
|
||
return;
|
||
}
|
||
emit('close');
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.payment-modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.6);
|
||
display: flex;
|
||
align-items: flex-end;
|
||
justify-content: center;
|
||
z-index: 9999;
|
||
}
|
||
|
||
.payment-modal-content {
|
||
width: 100%;
|
||
background-color: #fff;
|
||
border-radius: 32rpx 32rpx 0 0;
|
||
padding: 48rpx 32rpx;
|
||
padding-bottom: calc(48rpx + env(safe-area-inset-bottom, 0px));
|
||
position: relative;
|
||
animation: slideUp 0.3s ease-out;
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from {
|
||
transform: translateY(100%);
|
||
}
|
||
|
||
to {
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.payment-close-btn {
|
||
position: absolute;
|
||
top: 24rpx;
|
||
right: 24rpx;
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.payment-close-icon {
|
||
font-size: 48rpx;
|
||
color: #999;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* 商品信息 */
|
||
.payment-product {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
margin-bottom: 48rpx;
|
||
}
|
||
|
||
.payment-product-icon {
|
||
font-size: 80rpx;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.payment-product-name {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #2c2c2c;
|
||
}
|
||
|
||
.payment-product-desc {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
/* 价格 */
|
||
.payment-price {
|
||
text-align: center;
|
||
margin-bottom: 48rpx;
|
||
}
|
||
|
||
.payment-price-symbol {
|
||
font-size: 40rpx;
|
||
color: #8b2323;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.payment-price-amount {
|
||
font-size: 72rpx;
|
||
color: #8b2323;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* 支付方式 */
|
||
.payment-method {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.payment-method-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 24rpx;
|
||
background-color: #f5f5f5;
|
||
border-radius: 16rpx;
|
||
border: 2rpx solid transparent;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.payment-method-active {
|
||
background-color: rgba(139, 35, 35, 0.05);
|
||
border-color: #8b2323;
|
||
}
|
||
|
||
.payment-method-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.payment-method-icon {
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.payment-method-label {
|
||
font-size: 28rpx;
|
||
color: #2c2c2c;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.payment-method-check {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border-radius: 50%;
|
||
background-color: #8b2323;
|
||
color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
/* 支付按钮 */
|
||
.payment-submit-btn {
|
||
width: 100%;
|
||
padding: 28rpx 0;
|
||
background-color: #8b2323;
|
||
border-radius: 16rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: 24rpx;
|
||
transition: opacity 0.3s;
|
||
}
|
||
|
||
.payment-submit-btn:active {
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.payment-submit-disabled {
|
||
opacity: 0.6;
|
||
}
|
||
|
||
.payment-submit-text {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #f2e6d8;
|
||
}
|
||
|
||
/* 提示 */
|
||
.payment-tips {
|
||
text-align: center;
|
||
}
|
||
|
||
.payment-tips-text {
|
||
font-size: 20rpx;
|
||
color: #999;
|
||
}
|
||
</style>
|