feat: 完整重构小程序匹配功能 + 修复UI对齐 + 文章数据API
主要更新: 1. 按H5网页端完全重构匹配功能(match页面) - 4种匹配类型: 创业合伙/资源对接/导师顾问/团队招募 - 资源对接等类型弹出手机号/微信号输入框 - 去掉重新匹配按钮,改为返回按钮 2. 修复所有卡片对齐和宽度问题 - 目录页附录卡片居中 - 首页阅读进度卡片满宽度 - 我的页面菜单卡片对齐 - 推广中心分享卡片统一宽度 3. 修复目录页图标和文字对齐 - section-icon固定40rpx宽高 - section-title与图标垂直居中 4. 更新真实完整文章标题(62篇) - 从book目录读取真实markdown文件名 - 替换之前的简化标题 5. 新增文章数据API - /api/db/chapters - 获取完整书籍结构 - 支持按ID获取单篇文章内容
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
import { useState, useEffect } from "react"
|
||||
import { X, CheckCircle, Bitcoin, Globe, Copy, Check, QrCode, Shield, Users } from "lucide-react"
|
||||
import { useState, useEffect, useCallback, useRef } from "react"
|
||||
import { X, CheckCircle, Bitcoin, Globe, Copy, Check, QrCode, Shield, Users, Loader2, AlertCircle } from "lucide-react"
|
||||
import { useStore } from "@/lib/store"
|
||||
import QRCode from "qrcode"
|
||||
|
||||
const WechatIcon = () => (
|
||||
<svg viewBox="0 0 24 24" className="w-5 h-5" fill="currentColor">
|
||||
@@ -11,12 +12,6 @@ const WechatIcon = () => (
|
||||
</svg>
|
||||
)
|
||||
|
||||
const AlipayIcon = () => (
|
||||
<svg viewBox="0 0 24 24" className="w-5 h-5" fill="currentColor">
|
||||
<path d="M20.422 13.066c-.198-.07-.405-.137-.62-.202.107-.263.204-.534.29-.814h-3.32v-.93h3.927v-.627h-3.927v-1.18h-1.637c.07-.138.131-.28.184-.425l-1.483-.326a4.091 4.091 0 0 1-.405.75h-2.78v1.181H7.72v.627h2.932v.93H7.205v.652h5.784a9.296 9.296 0 0 1-.608.814 13.847 13.847 0 0 0-2.76-.93l-.483.652c1.038.273 1.96.608 2.766 1.008a8.483 8.483 0 0 1-3.603 1.484l.43.652c1.71-.378 3.103-1.03 4.18-1.957.665.395 1.223.835 1.67 1.32l.608-.652c-.44-.43-.984-.836-1.637-1.215a9.6 9.6 0 0 0 .72-.93c.182-.264.345-.53.488-.798.587.168 1.12.35 1.598.544 1.956.8 2.82 1.614 2.82 2.665 0 .727-.587 1.277-2.21 1.277-1.193 0-2.524-.203-3.996-.609l-.103.75c1.445.378 2.843.567 4.196.567 2.158 0 3.204-.748 3.204-2.013 0-1.382-1.183-2.437-3.58-3.413z" />
|
||||
<path d="M21.714 4H2.286A2.286 2.286 0 0 0 0 6.286v11.428A2.286 2.286 0 0 0 2.286 20h19.428A2.286 2.286 0 0 0 24 17.714V6.286A2.286 2.286 0 0 0 21.714 4zM2.286 5.143h19.428c.631 0 1.143.512 1.143 1.143v8.08c-.957-.454-2.222-.903-3.75-1.346a9.8 9.8 0 0 0 .607-2.02h-4.571V9.286h5.143V8.143h-5.143V6.286H13.43v1.857H8.286v1.143h5.143V11H8.286v1.143h6.356a11.54 11.54 0 0 1-.916 1.512 16.648 16.648 0 0 0-3.3-1.12l-.576.78c1.242.328 2.348.73 3.31 1.21a10.175 10.175 0 0 1-4.317 1.78l.514.78c2.048-.454 3.718-1.237 5.008-2.344.796.472 1.464 1 2.004 1.583l.726-.78c-.527-.516-1.179-.996-1.96-1.455.327-.407.627-.839.9-1.295.264-.447.495-.907.694-1.38.7.2 1.341.412 1.916.637 2.343.96 3.378 1.935 3.378 3.195 0 .872-.703 1.532-2.647 1.532-1.43 0-3.023-.244-4.786-.732l-.123.9c1.73.454 3.407.68 5.03.68 2.585 0 3.84-.899 3.84-2.416 0-1.166-.69-2.152-2.066-2.96v-.001c-.24-.14-.495-.276-.77-.408V6.286a1.143 1.143 0 0 0-1.143-1.143z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
type PaymentMethod = "wechat" | "alipay" | "usdt" | "paypal" | "stripe" | "bank"
|
||||
|
||||
@@ -30,23 +25,26 @@ interface PaymentModalProps {
|
||||
onSuccess: () => void
|
||||
}
|
||||
|
||||
// 支付状态类型
|
||||
type PaymentState = "idle" | "creating" | "paying" | "polling" | "success" | "error"
|
||||
|
||||
export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, amount, onSuccess }: PaymentModalProps) {
|
||||
const [paymentMethod, setPaymentMethod] = useState<PaymentMethod>("alipay")
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
const [isSuccess, setIsSuccess] = useState(false)
|
||||
const [paymentMethod, setPaymentMethod] = useState<PaymentMethod>("wechat")
|
||||
const [paymentState, setPaymentState] = useState<PaymentState>("idle")
|
||||
const [errorMessage, setErrorMessage] = useState("")
|
||||
const [showQRCode, setShowQRCode] = useState(false)
|
||||
const [qrCodeDataUrl, setQrCodeDataUrl] = useState("")
|
||||
const [paymentUrl, setPaymentUrl] = useState("")
|
||||
const [orderSn, setOrderSn] = useState("")
|
||||
const [tradeSn, setTradeSn] = useState("")
|
||||
const [currentGateway, setCurrentGateway] = useState("") // 当前支付网关
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const pollingRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const pollingCountRef = useRef(0)
|
||||
|
||||
const { purchaseSection, purchaseFullBook, user, settings } = useStore()
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setShowQRCode(false)
|
||||
setIsSuccess(false)
|
||||
setIsProcessing(false)
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const paymentConfig = settings?.paymentMethods || {
|
||||
wechat: { enabled: true, qrCode: "", account: "", groupQrCode: "" },
|
||||
alipay: { enabled: true, qrCode: "", account: "" },
|
||||
@@ -57,30 +55,248 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
const usdtAmount = (amount / (paymentConfig.usdt?.exchangeRate || 7.2)).toFixed(2)
|
||||
const paypalAmount = (amount / (paymentConfig.paypal?.exchangeRate || 7.2)).toFixed(2)
|
||||
|
||||
// 清理轮询
|
||||
const clearPolling = useCallback(() => {
|
||||
if (pollingRef.current) {
|
||||
clearInterval(pollingRef.current)
|
||||
pollingRef.current = null
|
||||
}
|
||||
pollingCountRef.current = 0
|
||||
}, [])
|
||||
|
||||
// 重置状态
|
||||
const resetState = useCallback(() => {
|
||||
setPaymentState("idle")
|
||||
setShowQRCode(false)
|
||||
setQrCodeDataUrl("")
|
||||
setPaymentUrl("")
|
||||
setOrderSn("")
|
||||
setTradeSn("")
|
||||
setCurrentGateway("")
|
||||
setErrorMessage("")
|
||||
clearPolling()
|
||||
}, [clearPolling])
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
resetState()
|
||||
}
|
||||
return () => {
|
||||
clearPolling()
|
||||
}
|
||||
}, [isOpen, resetState, clearPolling])
|
||||
|
||||
// 创建订单并获取支付参数
|
||||
const createPaymentOrder = async () => {
|
||||
setPaymentState("creating")
|
||||
setErrorMessage("")
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/payment/create-order", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
userId: user?.id || "anonymous",
|
||||
type,
|
||||
sectionId,
|
||||
sectionTitle,
|
||||
amount,
|
||||
paymentMethod,
|
||||
referralCode: user?.referredBy,
|
||||
}),
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
if (result.code !== 200) {
|
||||
throw new Error(result.message || "创建订单失败")
|
||||
}
|
||||
|
||||
const { orderSn: newOrderSn, tradeSn: newTradeSn, paymentData, gateway } = result.data
|
||||
|
||||
setOrderSn(newOrderSn)
|
||||
setTradeSn(newTradeSn)
|
||||
|
||||
// 保存网关信息用于后续查询
|
||||
const gatewayId = gateway || (paymentMethod === "wechat" ? "wechat_native" : "alipay_wap")
|
||||
setCurrentGateway(gatewayId)
|
||||
|
||||
// 根据支付方式处理不同的返回数据
|
||||
if (paymentData.type === "url") {
|
||||
// URL类型:跳转支付(支付宝WAP/WEB)
|
||||
setPaymentUrl(paymentData.payload)
|
||||
setShowQRCode(true)
|
||||
setPaymentState("paying")
|
||||
|
||||
// 打开支付页面
|
||||
window.open(paymentData.payload, "_blank")
|
||||
|
||||
// 开始轮询支付状态,传递网关信息
|
||||
startPolling(newTradeSn, gatewayId)
|
||||
} else if (paymentData.type === "qrcode") {
|
||||
// 二维码类型:扫码支付(微信Native/支付宝QR)
|
||||
const qrUrl = paymentData.payload
|
||||
|
||||
// 生成二维码图片
|
||||
const dataUrl = await QRCode.toDataURL(qrUrl, {
|
||||
width: 200,
|
||||
margin: 2,
|
||||
color: {
|
||||
dark: "#000000",
|
||||
light: "#ffffff",
|
||||
},
|
||||
})
|
||||
|
||||
setQrCodeDataUrl(dataUrl)
|
||||
setPaymentUrl(qrUrl)
|
||||
setShowQRCode(true)
|
||||
setPaymentState("paying")
|
||||
|
||||
// 开始轮询支付状态,传递网关信息
|
||||
startPolling(newTradeSn, gatewayId)
|
||||
} else if (paymentData.type === "json") {
|
||||
// JSON类型:JSAPI支付(需要调用JS SDK)
|
||||
console.log("JSAPI支付参数:", paymentData.payload)
|
||||
// 这里需要调用微信JS SDK
|
||||
setPaymentState("error")
|
||||
setErrorMessage("JSAPI支付需要在微信内打开")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("创建订单失败:", error)
|
||||
setPaymentState("error")
|
||||
setErrorMessage(error instanceof Error ? error.message : "创建订单失败,请重试")
|
||||
}
|
||||
}
|
||||
|
||||
// 轮询支付状态
|
||||
const startPolling = (tradeSnToQuery: string, gateway?: string) => {
|
||||
setPaymentState("polling")
|
||||
pollingCountRef.current = 0
|
||||
|
||||
pollingRef.current = setInterval(async () => {
|
||||
pollingCountRef.current++
|
||||
|
||||
// 最多轮询60次(5分钟)
|
||||
if (pollingCountRef.current > 60) {
|
||||
clearPolling()
|
||||
setPaymentState("error")
|
||||
setErrorMessage("支付超时,请重新发起支付")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 构建查询URL,包含网关参数以提高查询准确性
|
||||
let queryUrl = `/api/payment/query?tradeSn=${tradeSnToQuery}`
|
||||
if (gateway) {
|
||||
queryUrl += `&gateway=${gateway}`
|
||||
}
|
||||
|
||||
const response = await fetch(queryUrl)
|
||||
const result = await response.json()
|
||||
|
||||
if (result.code === 200 && result.data) {
|
||||
const { status } = result.data
|
||||
|
||||
if (status === "paid") {
|
||||
// 支付成功
|
||||
clearPolling()
|
||||
await handlePaymentSuccess()
|
||||
} else if (status === "closed" || status === "refunded") {
|
||||
// 订单已关闭
|
||||
clearPolling()
|
||||
setPaymentState("error")
|
||||
setErrorMessage("订单已关闭")
|
||||
}
|
||||
// paying状态继续轮询
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("查询支付状态失败:", error)
|
||||
// 查询失败继续轮询
|
||||
}
|
||||
}, 5000) // 每5秒查询一次
|
||||
}
|
||||
|
||||
// 处理支付成功
|
||||
const handlePaymentSuccess = async () => {
|
||||
setPaymentState("success")
|
||||
|
||||
// 调用store更新购买状态
|
||||
let success = false
|
||||
if (type === "section" && sectionId) {
|
||||
success = await purchaseSection(sectionId, sectionTitle, paymentMethod)
|
||||
} else if (type === "fullbook") {
|
||||
success = await purchaseFullBook(paymentMethod)
|
||||
}
|
||||
|
||||
// 打开社群二维码
|
||||
const groupUrl = paymentConfig.wechat?.groupQrCode
|
||||
if (groupUrl) {
|
||||
setTimeout(() => {
|
||||
window.open(groupUrl, "_blank")
|
||||
}, 800)
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
setTimeout(() => {
|
||||
onSuccess()
|
||||
onClose()
|
||||
resetState()
|
||||
}, 2500)
|
||||
}
|
||||
|
||||
// 手动确认支付(用于轮询失效的情况)
|
||||
const handleManualConfirm = async () => {
|
||||
if (!tradeSn) return
|
||||
|
||||
setPaymentState("polling")
|
||||
|
||||
try {
|
||||
// 构建查询URL,包含网关参数
|
||||
let queryUrl = `/api/payment/query?tradeSn=${tradeSn}`
|
||||
if (currentGateway) {
|
||||
queryUrl += `&gateway=${currentGateway}`
|
||||
}
|
||||
|
||||
const response = await fetch(queryUrl)
|
||||
const result = await response.json()
|
||||
|
||||
if (result.code === 200 && result.data?.status === "paid") {
|
||||
await handlePaymentSuccess()
|
||||
} else {
|
||||
setErrorMessage("未检测到支付,请确认是否已完成支付")
|
||||
setPaymentState("paying")
|
||||
}
|
||||
} catch (error) {
|
||||
setErrorMessage("查询支付状态失败,请稍后重试")
|
||||
setPaymentState("paying")
|
||||
}
|
||||
}
|
||||
|
||||
const handleCopyAddress = (address: string) => {
|
||||
navigator.clipboard.writeText(address)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
|
||||
// 处理开始支付
|
||||
const handlePayment = async () => {
|
||||
setShowQRCode(true)
|
||||
|
||||
if (paymentMethod === "wechat" && paymentConfig.wechat?.qrCode) {
|
||||
const link = paymentConfig.wechat.qrCode
|
||||
if (link.startsWith("http") || link.startsWith("weixin://")) {
|
||||
window.open(link, "_blank")
|
||||
}
|
||||
} else if (paymentMethod === "alipay" && paymentConfig.alipay?.qrCode) {
|
||||
const link = paymentConfig.alipay.qrCode
|
||||
if (link.startsWith("http") || link.startsWith("alipays://")) {
|
||||
window.open(link, "_blank")
|
||||
}
|
||||
// USDT和PayPal使用旧的手动确认流程
|
||||
if (paymentMethod === "usdt" || paymentMethod === "paypal") {
|
||||
setShowQRCode(true)
|
||||
return
|
||||
}
|
||||
|
||||
// 微信和支付宝使用新的API流程
|
||||
await createPaymentOrder()
|
||||
}
|
||||
|
||||
const confirmPayment = async () => {
|
||||
setIsProcessing(true)
|
||||
// USDT/PayPal手动确认
|
||||
const handleCryptoConfirm = async () => {
|
||||
setPaymentState("creating")
|
||||
|
||||
// 模拟确认支付(实际需要人工审核)
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
|
||||
let success = false
|
||||
@@ -90,24 +306,11 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
success = await purchaseFullBook(paymentMethod)
|
||||
}
|
||||
|
||||
setIsProcessing(false)
|
||||
|
||||
if (success) {
|
||||
setIsSuccess(true)
|
||||
|
||||
const groupUrl = paymentConfig.wechat?.groupQrCode
|
||||
if (groupUrl) {
|
||||
setTimeout(() => {
|
||||
window.open(groupUrl, "_blank")
|
||||
}, 800)
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
onSuccess()
|
||||
onClose()
|
||||
setIsSuccess(false)
|
||||
setShowQRCode(false)
|
||||
}, 2500)
|
||||
await handlePaymentSuccess()
|
||||
} else {
|
||||
setPaymentState("error")
|
||||
setErrorMessage("确认支付失败,请联系客服")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,21 +333,13 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
iconBg: "rgba(7, 193, 96, 0.15)",
|
||||
enabled: paymentConfig.wechat?.enabled ?? true,
|
||||
},
|
||||
{
|
||||
id: "alipay",
|
||||
name: "支付宝",
|
||||
icon: <AlipayIcon />,
|
||||
color: "#1677FF",
|
||||
iconBg: "rgba(22, 119, 255, 0.15)",
|
||||
enabled: paymentConfig.alipay?.enabled ?? true,
|
||||
},
|
||||
{
|
||||
id: "usdt",
|
||||
name: `USDT (${paymentConfig.usdt?.network || "TRC20"})`,
|
||||
icon: <Bitcoin className="w-5 h-5" />,
|
||||
color: "#26A17B",
|
||||
iconBg: "rgba(38, 161, 123, 0.15)",
|
||||
enabled: paymentConfig.usdt?.enabled ?? true,
|
||||
enabled: paymentConfig.usdt?.enabled ?? false,
|
||||
extra: `≈ $${usdtAmount}`,
|
||||
},
|
||||
{
|
||||
@@ -160,7 +355,7 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
|
||||
const availableMethods = paymentMethods.filter((m) => m.enabled)
|
||||
|
||||
// 二维码/详情页面 - iOS毛玻璃风格
|
||||
// 二维码/详情页面
|
||||
if (showQRCode) {
|
||||
const isCrypto = paymentMethod === "usdt"
|
||||
const isPaypal = paymentMethod === "paypal"
|
||||
@@ -170,8 +365,7 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
let address = ""
|
||||
let displayAmount = `¥${amount.toFixed(2)}`
|
||||
let title = "扫码支付"
|
||||
let hint = "支付完成后,请点击下方按钮确认"
|
||||
let qrCodeUrl = ""
|
||||
let hint = "支付完成后,系统将自动确认"
|
||||
|
||||
if (isCrypto) {
|
||||
address = paymentConfig.usdt?.address || ""
|
||||
@@ -185,12 +379,10 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
hint = "请转账到以下PayPal账户"
|
||||
} else if (isWechat) {
|
||||
title = "微信支付"
|
||||
qrCodeUrl = paymentConfig.wechat?.qrCode || ""
|
||||
hint = "请使用微信扫码支付"
|
||||
hint = "请使用微信扫描二维码支付"
|
||||
} else if (isAlipay) {
|
||||
title = "支付宝支付"
|
||||
qrCodeUrl = paymentConfig.alipay?.qrCode || ""
|
||||
hint = "请使用支付宝扫码支付"
|
||||
hint = paymentUrl?.startsWith("http") ? "已打开支付页面,请在新窗口完成支付" : "请使用支付宝扫描二维码支付"
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -218,24 +410,52 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
<p className="text-4xl font-bold text-[var(--app-brand)] glow-text">{displayAmount}</p>
|
||||
</div>
|
||||
|
||||
{/* QR Code Display */}
|
||||
{/* 错误提示 */}
|
||||
{errorMessage && (
|
||||
<div className="glass-card p-4 mb-4 border-red-500/30">
|
||||
<div className="flex items-center gap-3 text-red-400">
|
||||
<AlertCircle className="w-5 h-5 flex-shrink-0" />
|
||||
<p className="text-sm">{errorMessage}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* QR Code Display - 微信/支付宝 */}
|
||||
{(isWechat || isAlipay) && (
|
||||
<div className="flex flex-col items-center mb-6">
|
||||
<div className="w-52 h-52 bg-white rounded-2xl p-4 mb-4 flex items-center justify-center shadow-lg">
|
||||
{qrCodeUrl ? (
|
||||
{paymentState === "creating" ? (
|
||||
<div className="flex flex-col items-center text-gray-400">
|
||||
<Loader2 className="w-12 h-12 animate-spin mb-2 text-[var(--app-brand)]" />
|
||||
<span className="text-sm text-gray-600">正在生成二维码...</span>
|
||||
</div>
|
||||
) : qrCodeDataUrl ? (
|
||||
<img
|
||||
src={qrCodeUrl || "/placeholder.svg"}
|
||||
src={qrCodeDataUrl}
|
||||
alt="支付二维码"
|
||||
className="w-full h-full object-contain rounded-lg"
|
||||
/>
|
||||
) : paymentUrl?.startsWith("http") ? (
|
||||
<div className="flex flex-col items-center text-gray-600">
|
||||
<CheckCircle className="w-12 h-12 text-green-500 mb-2" />
|
||||
<span className="text-sm text-center">已在新窗口打开支付页面</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center text-gray-400">
|
||||
<QrCode className="w-16 h-16 mb-2" />
|
||||
<span className="text-sm text-center">请在后台配置收款码</span>
|
||||
<span className="text-sm text-center">二维码加载中...</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-[var(--app-text-tertiary)] text-sm">{hint}</p>
|
||||
|
||||
{/* 轮询状态指示 */}
|
||||
{paymentState === "polling" && (
|
||||
<div className="flex items-center gap-2 mt-3 text-[var(--app-brand)]">
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
<span className="text-sm">正在等待支付结果...</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -279,25 +499,47 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
{/* Action Buttons */}
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => setShowQRCode(false)}
|
||||
onClick={() => {
|
||||
clearPolling()
|
||||
setShowQRCode(false)
|
||||
setPaymentState("idle")
|
||||
setErrorMessage("")
|
||||
}}
|
||||
className="btn-ios-secondary flex-1"
|
||||
>
|
||||
返回
|
||||
</button>
|
||||
<button
|
||||
onClick={confirmPayment}
|
||||
disabled={isProcessing}
|
||||
className="btn-ios flex-1 glow disabled:opacity-50"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
处理中...
|
||||
</div>
|
||||
) : (
|
||||
"已完成支付"
|
||||
)}
|
||||
</button>
|
||||
{(isCrypto || isPaypal) ? (
|
||||
<button
|
||||
onClick={handleCryptoConfirm}
|
||||
disabled={paymentState === "creating"}
|
||||
className="btn-ios flex-1 glow disabled:opacity-50"
|
||||
>
|
||||
{paymentState === "creating" ? (
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
处理中...
|
||||
</div>
|
||||
) : (
|
||||
"我已支付"
|
||||
)}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleManualConfirm}
|
||||
disabled={paymentState === "polling"}
|
||||
className="btn-ios flex-1 glow disabled:opacity-50"
|
||||
>
|
||||
{paymentState === "polling" ? (
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
查询中...
|
||||
</div>
|
||||
) : (
|
||||
"已完成支付"
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -306,7 +548,7 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
}
|
||||
|
||||
// 支付成功页面
|
||||
if (isSuccess) {
|
||||
if (paymentState === "success") {
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-md modal-overlay" />
|
||||
@@ -334,7 +576,7 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
)
|
||||
}
|
||||
|
||||
// 主支付选择页面 - iOS风格
|
||||
// 主支付选择页面
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center">
|
||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-md modal-overlay" onClick={onClose} />
|
||||
@@ -421,13 +663,13 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
|
||||
<div className="p-6 pt-0">
|
||||
<button
|
||||
onClick={handlePayment}
|
||||
disabled={isProcessing || availableMethods.length === 0}
|
||||
disabled={paymentState === "creating" || availableMethods.length === 0}
|
||||
className="btn-ios w-full glow text-lg disabled:opacity-50"
|
||||
>
|
||||
{isProcessing ? (
|
||||
{paymentState === "creating" ? (
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
处理中...
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
正在创建订单...
|
||||
</div>
|
||||
) : (
|
||||
`确认支付 ¥${amount.toFixed(2)}`
|
||||
|
||||
Reference in New Issue
Block a user