368 lines
9.7 KiB
Vue
368 lines
9.7 KiB
Vue
<template>
|
||
<view class="payment-example">
|
||
<view class="example-header">
|
||
<text class="example-title">支付功能示例</text>
|
||
</view>
|
||
|
||
<!-- 示例1: 购买报告 -->
|
||
<view class="example-section">
|
||
<text class="example-section-title">示例1: 购买命名报告</text>
|
||
<view class="example-card">
|
||
<view class="example-card-content">
|
||
<text class="example-card-name">张三命名报告</text>
|
||
<text class="example-card-desc">包含六维分析、周易卦象等</text>
|
||
<text class="example-card-price">¥99</text>
|
||
</view>
|
||
<view class="example-card-btn" @click="buyReport">
|
||
<text class="example-card-btn-text">立即购买</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 示例2: 推广合伙人 -->
|
||
<view class="example-section">
|
||
<text class="example-section-title">示例2: 开通推广合伙人</text>
|
||
<view class="example-card">
|
||
<view class="example-card-content">
|
||
<text class="example-card-name">推广合伙人权益</text>
|
||
<text class="example-card-desc">开启睡后收入之旅</text>
|
||
<text class="example-card-price">¥99</text>
|
||
</view>
|
||
<view class="example-card-btn" @click="buyPartner">
|
||
<text class="example-card-btn-text">立即开通</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 示例3: 测试支付 -->
|
||
<view class="example-section">
|
||
<text class="example-section-title">示例3: 测试支付(0.01元)</text>
|
||
<view class="example-card">
|
||
<view class="example-card-content">
|
||
<text class="example-card-name">测试商品</text>
|
||
<text class="example-card-desc">用于测试支付流程</text>
|
||
<text class="example-card-price">¥0.01</text>
|
||
</view>
|
||
<view class="example-card-btn" @click="testPay">
|
||
<text class="example-card-btn-text">测试支付</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单列表 -->
|
||
<view class="example-section">
|
||
<text class="example-section-title">最近订单</text>
|
||
<view v-if="orders.length === 0" class="example-empty">
|
||
<text class="example-empty-text">暂无订单</text>
|
||
</view>
|
||
<view v-else class="example-orders">
|
||
<view v-for="order in orders" :key="order.out_trade_no" class="example-order">
|
||
<view class="example-order-info">
|
||
<text class="example-order-name">{{ order.business_type }}</text>
|
||
<text class="example-order-time">{{ order.paid_at || '待支付' }}</text>
|
||
</view>
|
||
<view class="example-order-right">
|
||
<text class="example-order-amount">¥{{ order.total_amount }}</text>
|
||
<text class="example-order-status" :class="`status-${order.status}`">
|
||
{{ getStatusText(order.status) }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付弹窗 -->
|
||
<PaymentModal :visible="showPayment" :product-name="paymentInfo.productName"
|
||
:product-desc="paymentInfo.productDesc" :product-icon="paymentInfo.productIcon" :amount="paymentInfo.amount"
|
||
:business-type="paymentInfo.businessType" :business-id="paymentInfo.businessId" @close="showPayment = false"
|
||
@success="handlePaymentSuccess" @fail="handlePaymentFail" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive, onMounted } from 'vue';
|
||
import PaymentModal from '../PaymentModal.vue';
|
||
import type { QueryOrderResponse } from '@/api/types';
|
||
|
||
declare const uni: any;
|
||
|
||
const showPayment = ref(false);
|
||
const orders = ref<QueryOrderResponse[]>([]);
|
||
|
||
const paymentInfo = reactive({
|
||
productName: '',
|
||
productDesc: '',
|
||
productIcon: '📦',
|
||
amount: 0,
|
||
businessType: '',
|
||
businessId: 0
|
||
});
|
||
|
||
// 购买报告
|
||
const buyReport = () => {
|
||
paymentInfo.productName = '张三命名报告';
|
||
paymentInfo.productDesc = '包含六维分析、周易卦象、开运建议等';
|
||
paymentInfo.productIcon = '📊';
|
||
paymentInfo.amount = 99;
|
||
paymentInfo.businessType = 'naming_report';
|
||
paymentInfo.businessId = 123;
|
||
showPayment.value = true;
|
||
};
|
||
|
||
// 购买推广合伙人
|
||
const buyPartner = () => {
|
||
paymentInfo.productName = '推广合伙人权益';
|
||
paymentInfo.productDesc = '开启睡后收入之旅';
|
||
paymentInfo.productIcon = '💼';
|
||
paymentInfo.amount = 99;
|
||
paymentInfo.businessType = 'partner_apply';
|
||
paymentInfo.businessId = 456;
|
||
showPayment.value = true;
|
||
};
|
||
|
||
// 测试支付
|
||
const testPay = () => {
|
||
paymentInfo.productName = '测试商品';
|
||
paymentInfo.productDesc = '用于测试支付流程';
|
||
paymentInfo.productIcon = '🧪';
|
||
paymentInfo.amount = 0.01;
|
||
paymentInfo.businessType = 'test';
|
||
paymentInfo.businessId = 999;
|
||
showPayment.value = true;
|
||
};
|
||
|
||
// 支付成功回调
|
||
const handlePaymentSuccess = (outTradeNo: string) => {
|
||
|
||
// Web环境使用alert,uni-app环境使用showModal
|
||
if (typeof uni?.showModal === 'function') {
|
||
uni.showModal({
|
||
title: '支付成功',
|
||
content: `订单号:${outTradeNo}\n感谢您的购买!`,
|
||
showCancel: false,
|
||
success: () => {
|
||
// 刷新订单列表
|
||
loadOrders();
|
||
}
|
||
});
|
||
} else {
|
||
// Web环境使用原生alert
|
||
alert(`支付成功\n\n订单号:${outTradeNo}\n感谢您的购买!`);
|
||
loadOrders();
|
||
}
|
||
};
|
||
|
||
// 支付失败回调
|
||
const handlePaymentFail = (message: string) => {
|
||
console.log('支付失败:', message);
|
||
|
||
// Web环境使用alert,uni-app环境使用showModal
|
||
if (typeof uni?.showModal === 'function') {
|
||
uni.showModal({
|
||
title: '支付失败',
|
||
content: message || '支付过程中出现问题,请重试',
|
||
showCancel: false
|
||
});
|
||
} else {
|
||
// Web环境使用原生alert
|
||
alert(`支付失败\n\n${message || '支付过程中出现问题,请重试'}`);
|
||
}
|
||
};
|
||
|
||
// 加载订单列表(示例数据)
|
||
const loadOrders = () => {
|
||
// 实际应该调用API获取订单列表
|
||
orders.value = [
|
||
{
|
||
out_trade_no: 'ORDER_001',
|
||
status: 'paid',
|
||
total_amount: 99,
|
||
paid_amount: 99,
|
||
paid_at: '2026-01-15 10:30:00',
|
||
business_type: 'naming_report',
|
||
business_id: 123
|
||
},
|
||
{
|
||
out_trade_no: 'ORDER_002',
|
||
status: 'pending',
|
||
total_amount: 99,
|
||
business_type: 'partner_apply',
|
||
business_id: 456
|
||
}
|
||
];
|
||
};
|
||
|
||
// 获取状态文本
|
||
const getStatusText = (status: string) => {
|
||
const statusMap: Record<string, string> = {
|
||
pending: '待支付',
|
||
paid: '已支付',
|
||
cancelled: '已取消',
|
||
refunded: '已退款'
|
||
};
|
||
return statusMap[status] || status;
|
||
};
|
||
|
||
onMounted(() => {
|
||
loadOrders();
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.payment-example {
|
||
padding: 32rpx;
|
||
background-color: #f5f5f5;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.example-header {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.example-title {
|
||
font-size: 40rpx;
|
||
font-weight: 700;
|
||
color: #2c2c2c;
|
||
}
|
||
|
||
.example-section {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.example-section-title {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #2c2c2c;
|
||
margin-bottom: 16rpx;
|
||
display: block;
|
||
}
|
||
|
||
.example-card {
|
||
background-color: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.example-card-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.example-card-name {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #2c2c2c;
|
||
}
|
||
|
||
.example-card-desc {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.example-card-price {
|
||
font-size: 40rpx;
|
||
font-weight: 700;
|
||
color: #8b2323;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.example-card-btn {
|
||
width: 100%;
|
||
padding: 20rpx 0;
|
||
background-color: #8b2323;
|
||
border-radius: 12rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.example-card-btn-text {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
}
|
||
|
||
.example-empty {
|
||
text-align: center;
|
||
padding: 64rpx 0;
|
||
}
|
||
|
||
.example-empty-text {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.example-orders {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.example-order {
|
||
background-color: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.example-order-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.example-order-name {
|
||
font-size: 28rpx;
|
||
color: #2c2c2c;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.example-order-time {
|
||
font-size: 20rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.example-order-right {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.example-order-amount {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #2c2c2c;
|
||
}
|
||
|
||
.example-order-status {
|
||
font-size: 20rpx;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.status-paid {
|
||
background-color: #e8f5e9;
|
||
color: #4caf50;
|
||
}
|
||
|
||
.status-pending {
|
||
background-color: #fff3e0;
|
||
color: #ff9800;
|
||
}
|
||
|
||
.status-cancelled {
|
||
background-color: #f5f5f5;
|
||
color: #999;
|
||
}
|
||
|
||
.status-refunded {
|
||
background-color: #ffebee;
|
||
color: #f44336;
|
||
}
|
||
</style>
|