import { defineComponent, ref, reactive, computed, onUnmounted } from 'vue'; import { NButton, NSpin } from 'naive-ui'; import styles from './index.module.less'; import TheQrCode from '@/components/TheQrCode'; import qs from 'query-string'; import { getPaymentConfig, createVipOrder, getOrderDetail } from '@/api/payment'; import { useUserStore } from '@/store/modules/users'; import { getHttpOrigin } from '/src/helpers/utils'; // API返回的套餐类型 interface VipPackageApiItem { id: number; title: string; number: number; unit: 'DAY' | 'MONTH' | 'YEAR'; originalPrice: number; currentPrice: number; free?: { number: number; unit: 'DAY' | 'MONTH' | 'YEAR'; }; } // UI使用的套餐类型 interface VipPackage { id: number; name: string; price: number; days: number; originalPrice: number; freeText?: string; // 赠送信息 } export default defineComponent({ name: 'VipPurchaseModal', props: { /** 是否有取消按钮 */ hasCancel: { type: Boolean, default: false } }, emits: ['close', 'success'], setup(_, { emit }) { const userStore = useUserStore(); // VIP套餐列表(从API获取) const vipPackages = ref([]); const selectedPackageId = ref(0); const loading = ref(false); const hasClicked = ref(false); // 防重复点击标记 const pageLoading = ref(true); // 页面初始加载状态 const showQrCode = ref(false); const qrCodeUrl = ref(''); const orderNo = ref(''); const pollingTimer = ref(null); // 支付配置 const paymentConfig = reactive({ paymentType: '', paymentChannel: '', wxAppId: '' }); // 计算总天数 const calculateDays = (item: VipPackageApiItem): number => { let days = 0; // 主时长 switch (item.unit) { case 'DAY': days += item.number; break; case 'MONTH': days += item.number * 30; break; case 'YEAR': days += item.number * 365; break; } // 赠送时长 if (item.free) { switch (item.free.unit) { case 'DAY': days += item.free.number; break; case 'MONTH': days += item.free.number * 30; break; case 'YEAR': days += item.free.number * 365; break; } } return days; }; // 生成赠送信息 const generateFreeText = (item: VipPackageApiItem): string | undefined => { if (!item.free || item.free.number <= 0) return undefined; const freeUnit = item.free.unit === 'DAY' ? '天' : item.free.unit === 'MONTH' ? '个月' : '年'; return `赠送${item.free.number}${freeUnit}`; }; // 选中的套餐信息 const currentPackage = computed( () => vipPackages.value.find(p => p.id === selectedPackageId.value) || vipPackages.value[0] ); // 获取支付配置和VIP套餐 const fetchPaymentConfig = async () => { try { const { data } = await getPaymentConfig(); if (data && Array.isArray(data)) { data.forEach((item: any) => { if (item.paramName === 'payment_service_provider') { const provider = JSON.parse(item.paramValue); paymentConfig.paymentType = provider.vendor; paymentConfig.paymentChannel = provider.channel; paymentConfig.wxAppId = provider.wxAppId || ''; } if ( item.paramName === 'teacher_vip_purchase_list' && item.paramValue ) { const packageList: VipPackageApiItem[] = JSON.parse( item.paramValue ); vipPackages.value = packageList.map(pkg => ({ id: pkg.id, name: pkg.title, price: pkg.currentPrice, days: calculateDays(pkg), originalPrice: pkg.originalPrice, freeText: generateFreeText(pkg) })); // 默认选中中间的那个套餐 const defaultIndex = Math.floor(vipPackages.value.length / 2); selectedPackageId.value = vipPackages.value[defaultIndex]?.id || 1; } }); } } catch (e) { console.error('获取支付配置失败', e); } finally { pageLoading.value = false; } }; // 开始轮询订单状态 const startPolling = () => { if (pollingTimer.value) { clearInterval(pollingTimer.value); } pollingTimer.value = window.setInterval(async () => { try { const { data } = await getOrderDetail(orderNo.value); if (data && data.status === 'PAID') { stopPolling(); window.$message.success('支付成功'); handleCancel(); await userStore.getInfo(); emit('success'); emit('close'); } } catch (e) { console.error('轮询订单状态失败', e); } }, 3000); }; // 停止轮询 const stopPolling = () => { if (pollingTimer.value) { clearInterval(pollingTimer.value); pollingTimer.value = null; } }; // 取消支付 const handleCancel = () => { stopPolling(); showQrCode.value = false; qrCodeUrl.value = ''; orderNo.value = ''; hasClicked.value = false; }; // 创建订单并生成二维码链接 const handlePurchase = async () => { if (hasClicked.value || loading.value) return; hasClicked.value = true; loading.value = true; try { const pkg = currentPackage.value; const goodsInfos = [ { goodsId: pkg.id, goodsName: pkg.name, goodsPrice: pkg.price, goodsNum: 1, goodsType: 'TEACHER_VIP' } ]; // 创建订单 const orderRes = await createVipOrder({ orderType: 'TEACHER_VIP', paymentType: paymentConfig.paymentType, paymentChannel: paymentConfig.paymentChannel, paymentCashAmount: pkg.price, paymentCouponAmount: 0, goodsInfos, orderName: '乐器AI学练工具', orderDesc: '乐器AI学练工具' }); if (orderRes.data) { orderNo.value = orderRes.data.orderNo; const payConfig = orderRes.data.paymentConfig?.paymentConfig || orderRes.data.paymentConfig; // 生成二维码链接,跳转到 classroom-app 的 payDefine 页面 const params = qs.stringify({ pay_channel: paymentConfig.paymentChannel, wxAppId: payConfig.wxAppId, alipayAppId: payConfig.alipayAppId || '', paymentType: paymentConfig.paymentType, body: '乐器AI学练工具', price: pkg.price, orderNo: payConfig.merOrderNo || orderNo.value, userId: payConfig.userId }); // getHttpOrigin() + qrCodeUrl.value = getHttpOrigin() + '/classroom-app/#/payDefine?' + params; console.log(qrCodeUrl.value); showQrCode.value = true; console.log(qrCodeUrl.value, 'value'); startPolling(); } } catch (e: any) { hasClicked.value = false; window.$message.error(e.msg || '创建订单失败'); } finally { loading.value = false; } }; // 初始化获取支付配置 fetchPaymentConfig(); // 组件卸载时清理 onUnmounted(() => { stopPolling(); }); // 计算会员是否过期 const membershipEndTime = userStore.getUserInfo?.membershipEndTime; const isVipExpired = computed(() => { return !membershipEndTime || new Date(membershipEndTime) < new Date(); }); return () => (
{!showQrCode.value ? (
{isVipExpired.value && (
您的会员已过期,请续费后继续使用
)}
{vipPackages.value.map((pkg: VipPackage) => (
(selectedPackageId.value = pkg.id)} >
{pkg.name}
{pkg.freeText || ''}
{pkg.price} ¥{pkg.originalPrice}
))}
emit('close')}> 取消 续费 ¥{currentPackage.value?.price}
) : (
扫码支付
{qrCodeUrl.value && ( )}
请使用微信扫描二维码完成支付
订单号:{orderNo.value}
取消支付
)}
); } });