upload project source code
This commit is contained in:
313
前端源码/uni-app/components/PaymentModal.vue
Normal file
313
前端源码/uni-app/components/PaymentModal.vue
Normal file
@@ -0,0 +1,313 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user