Files
soul/components/modules/referral/withdrawal-modal.tsx
卡若 b60edb3d47 feat: 完整重构小程序匹配功能 + 修复UI对齐 + 文章数据API
主要更新:
1. 按H5网页端完全重构匹配功能(match页面)
   - 4种匹配类型: 创业合伙/资源对接/导师顾问/团队招募
   - 资源对接等类型弹出手机号/微信号输入框
   - 去掉重新匹配按钮,改为返回按钮

2. 修复所有卡片对齐和宽度问题
   - 目录页附录卡片居中
   - 首页阅读进度卡片满宽度
   - 我的页面菜单卡片对齐
   - 推广中心分享卡片统一宽度

3. 修复目录页图标和文字对齐
   - section-icon固定40rpx宽高
   - section-title与图标垂直居中

4. 更新真实完整文章标题(62篇)
   - 从book目录读取真实markdown文件名
   - 替换之前的简化标题

5. 新增文章数据API
   - /api/db/chapters - 获取完整书籍结构
   - 支持按ID获取单篇文章内容
2026-01-21 15:49:12 +08:00

248 lines
9.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState, useEffect } from "react"
import { X, Wallet, CheckCircle, AlertCircle, Phone, MessageCircle, CreditCard } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { useStore } from "@/lib/store"
interface WithdrawalModalProps {
isOpen: boolean
onClose: () => void
availableAmount: number
}
export function WithdrawalModal({ isOpen, onClose, availableAmount }: WithdrawalModalProps) {
const { requestWithdrawal, user } = useStore()
const [amount, setAmount] = useState<string>("")
const [method, setMethod] = useState<"wechat" | "alipay">("wechat")
const [account, setAccount] = useState("")
const [name, setName] = useState("")
const [isSubmitting, setIsSubmitting] = useState(false)
const [isSuccess, setIsSuccess] = useState(false)
// 检查是否已绑定支付方式
const hasBindWechat = !!user?.wechat
const hasBindAlipay = !!user?.alipay
const hasAnyPaymentMethod = hasBindWechat || hasBindAlipay
// 自动填充已绑定的账号
useEffect(() => {
if (method === "wechat" && user?.wechat) {
setAccount(user.wechat)
} else if (method === "alipay" && user?.alipay) {
setAccount(user.alipay)
}
}, [method, user])
if (!isOpen) return null
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
const amountNum = parseFloat(amount)
if (isNaN(amountNum) || amountNum <= 0 || amountNum > availableAmount) {
alert("请输入有效的提现金额")
return
}
if (!account || !name) {
alert("请填写完整的提现信息")
return
}
setIsSubmitting(true)
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 1000))
requestWithdrawal(amountNum, method, account, name)
setIsSubmitting(false)
setIsSuccess(true)
}
const handleClose = () => {
setIsSuccess(false)
setAmount("")
setAccount("")
setName("")
onClose()
}
// 未绑定支付方式的提示
if (!hasAnyPaymentMethod) {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={handleClose} />
<div className="relative w-full max-w-sm bg-[#1c1c1e] rounded-2xl overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200">
<button
onClick={handleClose}
className="absolute top-3 right-3 p-1.5 bg-white/10 rounded-full text-white/60 hover:bg-white/20 z-10"
>
<X className="w-5 h-5" />
</button>
<div className="p-6 text-center">
<div className="w-16 h-16 bg-orange-500/20 rounded-full flex items-center justify-center mx-auto mb-4">
<AlertCircle className="w-8 h-8 text-orange-400" />
</div>
<h3 className="text-xl font-bold text-white mb-2"></h3>
<p className="text-white/60 text-sm mb-6">
"我的"
</p>
<div className="grid grid-cols-2 gap-3 mb-6">
<div className="p-4 rounded-xl bg-white/5 border border-white/10">
<MessageCircle className="w-6 h-6 text-[#07C160] mx-auto mb-2" />
<p className="text-white/60 text-xs"></p>
</div>
<div className="p-4 rounded-xl bg-white/5 border border-white/10">
<CreditCard className="w-6 h-6 text-[#1677FF] mx-auto mb-2" />
<p className="text-white/60 text-xs"></p>
</div>
</div>
<Button
onClick={handleClose}
className="w-full bg-[#00CED1] hover:bg-[#00CED1]/90 text-black font-medium"
>
</Button>
</div>
</div>
</div>
)
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={handleClose} />
<div className="relative w-full max-w-sm bg-[#1c1c1e] rounded-2xl overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200">
<button
onClick={handleClose}
className="absolute top-3 right-3 p-1.5 bg-white/10 rounded-full text-white/60 hover:bg-white/20 z-10"
>
<X className="w-5 h-5" />
</button>
{isSuccess ? (
<div className="p-8 flex flex-col items-center text-center">
<div className="w-16 h-16 bg-green-500/20 rounded-full flex items-center justify-center mb-4">
<CheckCircle className="w-8 h-8 text-green-400" />
</div>
<h3 className="text-xl font-bold text-white mb-2"></h3>
<p className="text-sm text-white/60 mb-6">
1-3
</p>
<Button onClick={handleClose} className="w-full bg-green-500 hover:bg-green-600 text-white">
</Button>
</div>
) : (
<form onSubmit={handleSubmit} className="p-6">
<div className="flex items-center gap-2 mb-6">
<Wallet className="w-5 h-5 text-[#FFD700]" />
<h3 className="text-lg font-bold text-white"></h3>
</div>
<div className="space-y-4 mb-6">
<div className="space-y-2">
<Label htmlFor="amount" className="text-white/80">
<span className="text-[#00CED1]">(: ¥{availableAmount.toFixed(2)})</span>
</Label>
<div className="relative">
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-white/50">¥</span>
<Input
id="amount"
type="number"
min="10"
max={availableAmount}
step="0.01"
value={amount}
onChange={(e) => setAmount(e.target.value)}
className="pl-7 bg-white/5 border-white/10 text-white placeholder:text-white/30"
placeholder="最低10元"
/>
</div>
</div>
<div className="space-y-2">
<Label className="text-white/80"></Label>
<div className="flex gap-3">
{hasBindWechat && (
<button
type="button"
onClick={() => setMethod("wechat")}
className={`flex-1 py-3 px-4 rounded-xl border text-sm font-medium transition-colors flex items-center justify-center gap-2 ${
method === "wechat"
? "border-[#07C160] bg-[#07C160]/10 text-[#07C160]"
: "border-white/10 bg-white/5 text-white/60"
}`}
>
<MessageCircle className="w-4 h-4" />
</button>
)}
{hasBindAlipay && (
<button
type="button"
onClick={() => setMethod("alipay")}
className={`flex-1 py-3 px-4 rounded-xl border text-sm font-medium transition-colors flex items-center justify-center gap-2 ${
method === "alipay"
? "border-[#1677FF] bg-[#1677FF]/10 text-[#1677FF]"
: "border-white/10 bg-white/5 text-white/60"
}`}
>
<CreditCard className="w-4 h-4" />
</button>
)}
</div>
</div>
<div className="space-y-2">
<Label htmlFor="account" className="text-white/80">
{method === "wechat" ? "微信号" : "支付宝账号"}
</Label>
<Input
id="account"
value={account}
onChange={(e) => setAccount(e.target.value)}
placeholder={method === "wechat" ? "请输入微信号" : "请输入支付宝账号"}
className="bg-white/5 border-white/10 text-white placeholder:text-white/30"
/>
{((method === "wechat" && user?.wechat) || (method === "alipay" && user?.alipay)) && (
<p className="text-xs text-[#00CED1]"></p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="name" className="text-white/80"></Label>
<Input
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="请输入收款人真实姓名"
className="bg-white/5 border-white/10 text-white placeholder:text-white/30"
/>
</div>
</div>
<Button
type="submit"
className="w-full bg-[#FFD700] hover:bg-[#FFD700]/90 text-black font-bold"
disabled={isSubmitting || !amount || !account || !name}
>
{isSubmitting ? "提交中..." : "确认提现"}
</Button>
</form>
)}
</div>
</div>
)
}