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:
@@ -95,7 +95,7 @@ export function AuthModal({ isOpen, onClose, defaultTab = "login" }: AuthModalPr
|
||||
setError("")
|
||||
}}
|
||||
className={`flex-1 py-4 text-center transition-colors ${
|
||||
tab === "login" ? "text-white border-b-2 border-[#ff3b5c]" : "text-white/40 hover:text-white"
|
||||
tab === "login" ? "text-white border-b-2 border-[#00CED1]" : "text-white/40 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
登录
|
||||
@@ -106,7 +106,7 @@ export function AuthModal({ isOpen, onClose, defaultTab = "login" }: AuthModalPr
|
||||
setError("")
|
||||
}}
|
||||
className={`flex-1 py-4 text-center transition-colors ${
|
||||
tab === "register" ? "text-white border-b-2 border-[#ff3b5c]" : "text-white/40 hover:text-white"
|
||||
tab === "register" ? "text-white border-b-2 border-[#00CED1]" : "text-white/40 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
注册
|
||||
@@ -146,17 +146,15 @@ export function AuthModal({ isOpen, onClose, defaultTab = "login" }: AuthModalPr
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <p className="text-[#ff3b5c] text-sm">{error}</p>}
|
||||
{error && <p className="text-[#00CED1] text-sm">{error}</p>}
|
||||
|
||||
<Button
|
||||
onClick={handleLogin}
|
||||
disabled={isLoading}
|
||||
className="w-full bg-[#ff3b5c] hover:bg-[#ff5c7a] text-white h-12 rounded-xl font-medium"
|
||||
className="w-full bg-[#00CED1] hover:bg-[#00B4B7] text-white h-12 rounded-xl font-medium"
|
||||
>
|
||||
{isLoading ? "登录中..." : "登录"}
|
||||
</Button>
|
||||
|
||||
<p className="text-center text-white/40 text-xs">测试账号:任意11位手机号,密码:123456</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
@@ -217,12 +215,12 @@ export function AuthModal({ isOpen, onClose, defaultTab = "login" }: AuthModalPr
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <p className="text-[#ff3b5c] text-sm">{error}</p>}
|
||||
{error && <p className="text-[#00CED1] text-sm">{error}</p>}
|
||||
|
||||
<Button
|
||||
onClick={handleRegister}
|
||||
disabled={isLoading}
|
||||
className="w-full bg-[#ff3b5c] hover:bg-[#ff5c7a] text-white h-12 rounded-xl font-medium"
|
||||
className="w-full bg-[#00CED1] hover:bg-[#00B4B7] text-white h-12 rounded-xl font-medium"
|
||||
>
|
||||
{isLoading ? "注册中..." : "立即注册"}
|
||||
</Button>
|
||||
|
||||
291
components/modules/distribution/auto-withdraw-modal.tsx
Normal file
291
components/modules/distribution/auto-withdraw-modal.tsx
Normal file
@@ -0,0 +1,291 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { X, Settings, CheckCircle, AlertCircle, Zap } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { useStore } from "@/lib/store"
|
||||
|
||||
interface AutoWithdrawModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
interface AutoWithdrawConfig {
|
||||
enabled: boolean
|
||||
minAmount: number
|
||||
method: 'wechat' | 'alipay'
|
||||
account: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export function AutoWithdrawModal({ isOpen, onClose }: AutoWithdrawModalProps) {
|
||||
const { user } = useStore()
|
||||
const [config, setConfig] = useState<AutoWithdrawConfig>({
|
||||
enabled: false,
|
||||
minAmount: 100,
|
||||
method: 'wechat',
|
||||
account: '',
|
||||
name: '',
|
||||
})
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [isSuccess, setIsSuccess] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
// 加载已保存的配置
|
||||
useEffect(() => {
|
||||
if (isOpen && user?.id) {
|
||||
const savedConfig = localStorage.getItem(`auto_withdraw_config_${user.id}`)
|
||||
if (savedConfig) {
|
||||
try {
|
||||
setConfig(JSON.parse(savedConfig))
|
||||
} catch {
|
||||
// 忽略解析错误
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isOpen, user?.id])
|
||||
|
||||
if (!isOpen) return null
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!user?.id) return
|
||||
|
||||
// 验证
|
||||
if (config.enabled) {
|
||||
if (config.minAmount < 10) {
|
||||
setError('最低提现金额不能少于10元')
|
||||
return
|
||||
}
|
||||
if (!config.account) {
|
||||
setError('请填写提现账号')
|
||||
return
|
||||
}
|
||||
if (!config.name) {
|
||||
setError('请填写真实姓名')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
// 保存配置到本地存储
|
||||
localStorage.setItem(`auto_withdraw_config_${user.id}`, JSON.stringify(config))
|
||||
|
||||
// 如果启用了自动提现,也发送到服务器
|
||||
if (config.enabled) {
|
||||
await fetch('/api/distribution/auto-withdraw-config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
userId: user.id,
|
||||
...config,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
setIsSuccess(true)
|
||||
} catch (err) {
|
||||
setError('保存失败,请重试')
|
||||
console.error('保存自动提现配置失败:', err)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setIsSuccess(false)
|
||||
setError(null)
|
||||
onClose()
|
||||
}
|
||||
|
||||
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-md bg-gradient-to-b from-[#1a1a2e] to-[#0f0f1a] rounded-2xl overflow-hidden shadow-2xl border border-white/10 animate-in fade-in zoom-in-95 duration-200">
|
||||
{/* 关闭按钮 */}
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="absolute top-4 right-4 p-2 rounded-full bg-white/5 hover:bg-white/10 transition-colors z-10"
|
||||
>
|
||||
<X className="w-5 h-5 text-gray-400" />
|
||||
</button>
|
||||
|
||||
{isSuccess ? (
|
||||
/* 成功状态 */
|
||||
<div className="p-8 flex flex-col items-center text-center">
|
||||
<div className="w-20 h-20 bg-green-500/20 rounded-full flex items-center justify-center mb-4">
|
||||
<CheckCircle className="w-10 h-10 text-green-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-white mb-2">设置保存成功</h3>
|
||||
<p className="text-gray-400 text-sm mb-6">
|
||||
{config.enabled
|
||||
? `当可提现金额达到 ¥${config.minAmount} 时,将自动打款到您的${config.method === 'wechat' ? '微信' : '支付宝'}账户`
|
||||
: '自动提现已关闭'
|
||||
}
|
||||
</p>
|
||||
<Button
|
||||
onClick={handleClose}
|
||||
className="w-full bg-[#38bdac] hover:bg-[#2da396] text-white"
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
/* 设置表单 */
|
||||
<div className="p-6">
|
||||
{/* 标题 */}
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[#38bdac]/20 rounded-xl flex items-center justify-center">
|
||||
<Zap className="w-6 h-6 text-[#38bdac]" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-white">自动提现设置</h3>
|
||||
<p className="text-sm text-gray-400">达到金额自动打款到账户</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 错误提示 */}
|
||||
{error && (
|
||||
<div className="mb-4 p-3 bg-red-500/10 border border-red-500/20 rounded-lg flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-red-400" />
|
||||
<span className="text-sm text-red-400">{error}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-5">
|
||||
{/* 启用开关 */}
|
||||
<div className="flex items-center justify-between p-4 bg-white/5 rounded-xl">
|
||||
<div className="flex items-center gap-3">
|
||||
<Settings className="w-5 h-5 text-gray-400" />
|
||||
<span className="text-white font-medium">启用自动提现</span>
|
||||
</div>
|
||||
<Switch
|
||||
checked={config.enabled}
|
||||
onCheckedChange={(checked) => setConfig({ ...config, enabled: checked })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{config.enabled && (
|
||||
<>
|
||||
{/* 最低金额 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-400">达到金额自动提现</Label>
|
||||
<div className="relative">
|
||||
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 font-medium">¥</span>
|
||||
<Input
|
||||
type="number"
|
||||
min="10"
|
||||
step="10"
|
||||
value={config.minAmount}
|
||||
onChange={(e) => setConfig({ ...config, minAmount: Number(e.target.value) })}
|
||||
className="pl-8 bg-white/5 border-white/10 text-white h-12"
|
||||
placeholder="100"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">最低提现金额为10元</p>
|
||||
</div>
|
||||
|
||||
{/* 提现方式 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-400">提现方式</Label>
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfig({ ...config, method: 'wechat' })}
|
||||
className={`flex-1 py-3 px-4 rounded-xl border text-sm font-medium transition-all ${
|
||||
config.method === 'wechat'
|
||||
? 'border-green-500 bg-green-500/10 text-green-400'
|
||||
: 'border-white/10 bg-white/5 text-gray-400 hover:bg-white/10'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 01.213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 00.167-.054l1.903-1.114a.864.864 0 01.717-.098c1.044.303 2.166.468 3.339.468h.319c-.081-.3-.126-.613-.126-.94 0-3.497 3.32-6.336 7.42-6.336.168 0 .335.005.5.015-.591-3.61-4.195-6.286-8.615-6.286z"/>
|
||||
<path d="M18.695 9.37c-3.442 0-6.236 2.302-6.236 5.145 0 2.843 2.794 5.145 6.236 5.145.852 0 1.666-.135 2.412-.384a.632.632 0 01.523.072l1.394.816a.235.235 0 00.122.04.214.214 0 00.213-.215c0-.052-.021-.104-.035-.155l-.285-1.082a.434.434 0 01.156-.484c1.34-.987 2.195-2.443 2.195-4.073 0-2.843-2.794-5.145-6.236-5.145z"/>
|
||||
</svg>
|
||||
微信
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfig({ ...config, method: 'alipay' })}
|
||||
className={`flex-1 py-3 px-4 rounded-xl border text-sm font-medium transition-all ${
|
||||
config.method === 'alipay'
|
||||
? 'border-blue-500 bg-blue-500/10 text-blue-400'
|
||||
: 'border-white/10 bg-white/5 text-gray-400 hover:bg-white/10'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19.5 3h-15A1.5 1.5 0 003 4.5v15A1.5 1.5 0 004.5 21h15a1.5 1.5 0 001.5-1.5v-15A1.5 1.5 0 0019.5 3zm-8.35 13.42c-2.16 0-3.92-1.75-3.92-3.92s1.76-3.92 3.92-3.92 3.92 1.75 3.92 3.92-1.76 3.92-3.92 3.92zm6.52.98c-.87-.4-1.75-.8-2.64-1.18.67-.91 1.16-1.96 1.44-3.08h-2.16v-.85h2.58v-.5h-2.58v-.99h1.85c-.1-.35-.25-.68-.44-.99h-1.41V8.96h3.23v.85h1.16v.85h-1.7c.19.31.34.64.44.99h1.26v.85h-2.33c-.28 1.12-.77 2.17-1.44 3.08.89.38 1.77.78 2.64 1.18-.31.55-.63 1.1-.9 1.64z"/>
|
||||
</svg>
|
||||
支付宝
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 收款账号 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-400">
|
||||
{config.method === 'wechat' ? '微信openid' : '支付宝账号'}
|
||||
</Label>
|
||||
<Input
|
||||
value={config.account}
|
||||
onChange={(e) => setConfig({ ...config, account: e.target.value })}
|
||||
className="bg-white/5 border-white/10 text-white h-12"
|
||||
placeholder={config.method === 'wechat' ? '请输入微信openid' : '请输入支付宝账号'}
|
||||
/>
|
||||
{config.method === 'wechat' && (
|
||||
<p className="text-xs text-gray-500">需要用户授权获取的openid,而非微信号</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 真实姓名 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-400">真实姓名</Label>
|
||||
<Input
|
||||
value={config.name}
|
||||
onChange={(e) => setConfig({ ...config, name: e.target.value })}
|
||||
className="bg-white/5 border-white/10 text-white h-12"
|
||||
placeholder="请输入收款人真实姓名"
|
||||
/>
|
||||
<p className="text-xs text-gray-500">必须与收款账户实名一致</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 保存按钮 */}
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={isLoading}
|
||||
className="w-full mt-6 h-12 bg-[#38bdac] hover:bg-[#2da396] text-white font-medium"
|
||||
>
|
||||
{isLoading ? '保存中...' : '保存设置'}
|
||||
</Button>
|
||||
|
||||
{/* 提示 */}
|
||||
<div className="mt-4 p-3 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
|
||||
<p className="text-xs text-yellow-400/80 leading-relaxed">
|
||||
💡 提示:启用自动提现后,当您的可提现金额达到设定值时,系统将自动发起打款。
|
||||
微信打款需要用户openid(通过公众号授权获取),支付宝打款需要实名认证的账号。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
307
components/modules/distribution/realtime-notification.tsx
Normal file
307
components/modules/distribution/realtime-notification.tsx
Normal file
@@ -0,0 +1,307 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react'
|
||||
import { Bell, X, CheckCircle, AlertCircle, Clock, Wallet, Gift, Info } from 'lucide-react'
|
||||
import { useStore } from '@/lib/store'
|
||||
|
||||
// 消息类型
|
||||
interface NotificationMessage {
|
||||
messageId: string
|
||||
type: string
|
||||
data: {
|
||||
message?: string
|
||||
title?: string
|
||||
content?: string
|
||||
amount?: number
|
||||
commission?: number
|
||||
daysRemaining?: number
|
||||
[key: string]: unknown
|
||||
}
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
interface RealtimeNotificationProps {
|
||||
onNewMessage?: (message: NotificationMessage) => void
|
||||
}
|
||||
|
||||
export function RealtimeNotification({ onNewMessage }: RealtimeNotificationProps) {
|
||||
const { user, isLoggedIn } = useStore()
|
||||
const [notifications, setNotifications] = useState<NotificationMessage[]>([])
|
||||
const [unreadCount, setUnreadCount] = useState(0)
|
||||
const [showPanel, setShowPanel] = useState(false)
|
||||
const [lastTimestamp, setLastTimestamp] = useState(new Date().toISOString())
|
||||
|
||||
// 获取消息
|
||||
const fetchMessages = useCallback(async () => {
|
||||
if (!isLoggedIn || !user?.id) return
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/distribution/messages?userId=${user.id}&since=${encodeURIComponent(lastTimestamp)}`
|
||||
)
|
||||
|
||||
if (!response.ok) return
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (data.success && data.messages?.length > 0) {
|
||||
setNotifications(prev => {
|
||||
const newMessages = data.messages.filter(
|
||||
(m: NotificationMessage) => !prev.some(p => p.messageId === m.messageId)
|
||||
)
|
||||
|
||||
if (newMessages.length > 0) {
|
||||
// 更新未读数
|
||||
setUnreadCount(c => c + newMessages.length)
|
||||
|
||||
// 显示Toast通知
|
||||
newMessages.forEach((msg: NotificationMessage) => {
|
||||
showToast(msg)
|
||||
onNewMessage?.(msg)
|
||||
})
|
||||
|
||||
// 更新最后时间戳
|
||||
const latestTime = newMessages.reduce(
|
||||
(max: string, m: NotificationMessage) => m.timestamp > max ? m.timestamp : max,
|
||||
lastTimestamp
|
||||
)
|
||||
setLastTimestamp(latestTime)
|
||||
|
||||
return [...newMessages, ...prev].slice(0, 50) // 保留最近50条
|
||||
}
|
||||
|
||||
return prev
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[RealtimeNotification] 获取消息失败:', error)
|
||||
}
|
||||
}, [isLoggedIn, user?.id, lastTimestamp, onNewMessage])
|
||||
|
||||
// 轮询获取消息
|
||||
useEffect(() => {
|
||||
if (!isLoggedIn || !user?.id) return
|
||||
|
||||
// 立即获取一次
|
||||
fetchMessages()
|
||||
|
||||
// 每5秒轮询一次
|
||||
const intervalId = setInterval(fetchMessages, 5000)
|
||||
|
||||
return () => clearInterval(intervalId)
|
||||
}, [isLoggedIn, user?.id, fetchMessages])
|
||||
|
||||
// 显示Toast通知
|
||||
const showToast = (message: NotificationMessage) => {
|
||||
// 创建Toast元素
|
||||
const toast = document.createElement('div')
|
||||
toast.className = 'fixed top-20 right-4 z-[100] animate-in slide-in-from-right duration-300'
|
||||
toast.innerHTML = `
|
||||
<div class="bg-[#1a1a2e] border border-gray-700 rounded-xl p-4 shadow-xl max-w-sm">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-10 h-10 rounded-full ${getIconBgClass(message.type)} flex items-center justify-center flex-shrink-0">
|
||||
${getIconSvg(message.type)}
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-white font-medium text-sm">${getTitle(message.type)}</p>
|
||||
<p class="text-gray-400 text-xs mt-1 line-clamp-2">${message.data.message || message.data.content || ''}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
document.body.appendChild(toast)
|
||||
|
||||
// 3秒后移除
|
||||
setTimeout(() => {
|
||||
toast.classList.add('animate-out', 'slide-out-to-right')
|
||||
setTimeout(() => toast.remove(), 300)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
// 获取图标背景色
|
||||
const getIconBgClass = (type: string): string => {
|
||||
switch (type) {
|
||||
case 'binding_expiring':
|
||||
return 'bg-orange-500/20'
|
||||
case 'binding_expired':
|
||||
return 'bg-red-500/20'
|
||||
case 'binding_converted':
|
||||
case 'earnings_added':
|
||||
return 'bg-green-500/20'
|
||||
case 'withdrawal_completed':
|
||||
return 'bg-[#38bdac]/20'
|
||||
case 'withdrawal_rejected':
|
||||
return 'bg-red-500/20'
|
||||
default:
|
||||
return 'bg-blue-500/20'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取图标SVG
|
||||
const getIconSvg = (type: string): string => {
|
||||
switch (type) {
|
||||
case 'binding_expiring':
|
||||
return '<svg class="w-5 h-5 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>'
|
||||
case 'binding_expired':
|
||||
return '<svg class="w-5 h-5 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>'
|
||||
case 'binding_converted':
|
||||
case 'earnings_added':
|
||||
return '<svg class="w-5 h-5 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>'
|
||||
case 'withdrawal_completed':
|
||||
return '<svg class="w-5 h-5 text-[#38bdac]" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>'
|
||||
case 'withdrawal_rejected':
|
||||
return '<svg class="w-5 h-5 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>'
|
||||
default:
|
||||
return '<svg class="w-5 h-5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取标题
|
||||
const getTitle = (type: string): string => {
|
||||
switch (type) {
|
||||
case 'binding_expiring':
|
||||
return '绑定即将过期'
|
||||
case 'binding_expired':
|
||||
return '绑定已过期'
|
||||
case 'binding_converted':
|
||||
return '用户已付款'
|
||||
case 'earnings_added':
|
||||
return '收益增加'
|
||||
case 'withdrawal_approved':
|
||||
return '提现已通过'
|
||||
case 'withdrawal_completed':
|
||||
return '提现已到账'
|
||||
case 'withdrawal_rejected':
|
||||
return '提现被拒绝'
|
||||
case 'system_notice':
|
||||
return '系统通知'
|
||||
default:
|
||||
return '消息通知'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取图标组件
|
||||
const getIcon = (type: string) => {
|
||||
switch (type) {
|
||||
case 'binding_expiring':
|
||||
return <Clock className="w-5 h-5 text-orange-400" />
|
||||
case 'binding_expired':
|
||||
return <AlertCircle className="w-5 h-5 text-red-400" />
|
||||
case 'binding_converted':
|
||||
case 'earnings_added':
|
||||
return <Gift className="w-5 h-5 text-green-400" />
|
||||
case 'withdrawal_completed':
|
||||
return <CheckCircle className="w-5 h-5 text-[#38bdac]" />
|
||||
case 'withdrawal_rejected':
|
||||
return <AlertCircle className="w-5 h-5 text-red-400" />
|
||||
default:
|
||||
return <Info className="w-5 h-5 text-blue-400" />
|
||||
}
|
||||
}
|
||||
|
||||
// 标记消息已读
|
||||
const markAsRead = async () => {
|
||||
if (!user?.id || notifications.length === 0) return
|
||||
|
||||
const messageIds = notifications.slice(0, 10).map(n => n.messageId)
|
||||
|
||||
try {
|
||||
await fetch('/api/distribution/messages', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ userId: user.id, messageIds }),
|
||||
})
|
||||
|
||||
setUnreadCount(0)
|
||||
} catch (error) {
|
||||
console.error('[RealtimeNotification] 标记已读失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 打开面板时标记已读
|
||||
const handleOpenPanel = () => {
|
||||
setShowPanel(true)
|
||||
if (unreadCount > 0) {
|
||||
markAsRead()
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLoggedIn || !user) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 通知铃铛按钮 */}
|
||||
<button
|
||||
onClick={handleOpenPanel}
|
||||
className="relative p-2 rounded-full bg-white/10 hover:bg-white/20 transition-colors"
|
||||
>
|
||||
<Bell className="w-5 h-5 text-white" />
|
||||
{unreadCount > 0 && (
|
||||
<span className="absolute -top-1 -right-1 w-5 h-5 bg-red-500 rounded-full flex items-center justify-center text-xs text-white font-bold">
|
||||
{unreadCount > 9 ? '9+' : unreadCount}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* 通知面板 */}
|
||||
{showPanel && (
|
||||
<div className="fixed inset-0 z-50">
|
||||
<div
|
||||
className="absolute inset-0 bg-black/60 backdrop-blur-sm"
|
||||
onClick={() => setShowPanel(false)}
|
||||
/>
|
||||
|
||||
<div className="absolute top-16 right-4 w-80 max-h-[70vh] bg-[#1a1a2e] border border-gray-700 rounded-xl shadow-2xl overflow-hidden animate-in slide-in-from-top-2 duration-200">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-700">
|
||||
<h3 className="text-white font-semibold">消息通知</h3>
|
||||
<button
|
||||
onClick={() => setShowPanel(false)}
|
||||
className="p-1 rounded-full hover:bg-white/10 transition-colors"
|
||||
>
|
||||
<X className="w-5 h-5 text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 消息列表 */}
|
||||
<div className="max-h-[50vh] overflow-auto">
|
||||
{notifications.length === 0 ? (
|
||||
<div className="py-12 text-center">
|
||||
<Bell className="w-10 h-10 text-gray-600 mx-auto mb-2" />
|
||||
<p className="text-gray-500 text-sm">暂无消息</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="divide-y divide-gray-700/50">
|
||||
{notifications.map((notification) => (
|
||||
<div
|
||||
key={notification.messageId}
|
||||
className="p-4 hover:bg-white/5 transition-colors"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={`w-10 h-10 rounded-full ${getIconBgClass(notification.type)} flex items-center justify-center flex-shrink-0`}>
|
||||
{getIcon(notification.type)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-white font-medium text-sm">
|
||||
{getTitle(notification.type)}
|
||||
</p>
|
||||
<p className="text-gray-400 text-xs mt-1 line-clamp-2">
|
||||
{notification.data.message || notification.data.content || ''}
|
||||
</p>
|
||||
<p className="text-gray-500 text-xs mt-2">
|
||||
{new Date(notification.timestamp).toLocaleString('zh-CN')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { X } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { getTotalSectionCount } from "@/lib/book-data"
|
||||
|
||||
interface PosterModalProps {
|
||||
isOpen: boolean
|
||||
@@ -14,6 +15,9 @@ interface PosterModalProps {
|
||||
export function PosterModal({ isOpen, onClose, referralLink, referralCode, nickname }: PosterModalProps) {
|
||||
if (!isOpen) return null
|
||||
|
||||
// 动态获取案例数量
|
||||
const caseCount = getTotalSectionCount()
|
||||
|
||||
// Use a public QR code API
|
||||
const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(referralLink)}`
|
||||
|
||||
@@ -29,46 +33,92 @@ export function PosterModal({ isOpen, onClose, referralLink, referralCode, nickn
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
{/* Poster Content */}
|
||||
<div className="bg-gradient-to-br from-indigo-900 to-purple-900 text-white p-6 flex flex-col items-center text-center relative overflow-hidden">
|
||||
{/* Decorative circles */}
|
||||
<div className="absolute top-0 left-0 w-32 h-32 bg-white/10 rounded-full -translate-x-1/2 -translate-y-1/2 blur-2xl" />
|
||||
<div className="absolute bottom-0 right-0 w-40 h-40 bg-pink-500/20 rounded-full translate-x-1/3 translate-y-1/3 blur-2xl" />
|
||||
{/* Poster Content - 销售/商业风格 */}
|
||||
<div className="bg-gradient-to-br from-[#0a1628] via-[#0f2137] to-[#1a3a5c] text-white p-6 flex flex-col items-center text-center relative overflow-hidden">
|
||||
{/* 装饰性元素 */}
|
||||
<div className="absolute top-0 left-0 w-40 h-40 bg-[#00CED1]/10 rounded-full -translate-x-1/2 -translate-y-1/2 blur-3xl" />
|
||||
<div className="absolute bottom-0 right-0 w-48 h-48 bg-[#FFD700]/10 rounded-full translate-x-1/3 translate-y-1/3 blur-3xl" />
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-64 h-64 border border-[#00CED1]/5 rounded-full" />
|
||||
|
||||
<div className="relative z-10 w-full flex flex-col items-center">
|
||||
{/* Book Title */}
|
||||
<h2 className="text-xl font-bold mb-1 leading-tight text-white">一场SOUL的<br/>创业实验场</h2>
|
||||
<p className="text-white/80 text-xs mb-6">真实商业故事 · 55个案例 · 每日更新</p>
|
||||
{/* 顶部标签 */}
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="px-3 py-1 text-[10px] font-bold bg-[#FFD700]/20 text-[#FFD700] rounded-full border border-[#FFD700]/30">
|
||||
真实商业案例
|
||||
</span>
|
||||
<span className="px-3 py-1 text-[10px] font-bold bg-[#00CED1]/20 text-[#00CED1] rounded-full border border-[#00CED1]/30">
|
||||
每日更新
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Cover Image Placeholder */}
|
||||
<div className="w-32 h-44 bg-gray-200 rounded shadow-lg mb-6 overflow-hidden relative">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/images/image.png" alt="Book Cover" className="w-full h-full object-cover" />
|
||||
{/* Book Title */}
|
||||
<h2 className="text-2xl font-black mb-1 leading-tight">
|
||||
<span className="bg-gradient-to-r from-white via-[#00CED1] to-white bg-clip-text text-transparent">
|
||||
一场SOUL的
|
||||
</span>
|
||||
<br/>
|
||||
<span className="text-white">创业实验场</span>
|
||||
</h2>
|
||||
<p className="text-white/60 text-xs mb-4">来自Soul派对房的真实商业故事</p>
|
||||
|
||||
{/* 核心数据展示 */}
|
||||
<div className="w-full grid grid-cols-3 gap-2 mb-4 px-2">
|
||||
<div className="bg-white/5 rounded-lg p-2 border border-white/10">
|
||||
<p className="text-2xl font-black text-[#FFD700]">{caseCount}</p>
|
||||
<p className="text-[10px] text-white/50">真实案例</p>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-lg p-2 border border-white/10">
|
||||
<p className="text-2xl font-black text-[#00CED1]">5%</p>
|
||||
<p className="text-[10px] text-white/50">好友优惠</p>
|
||||
</div>
|
||||
<div className="bg-white/5 rounded-lg p-2 border border-white/10">
|
||||
<p className="text-2xl font-black text-[#E91E63]">90%</p>
|
||||
<p className="text-[10px] text-white/50">你的收益</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 特色标签 */}
|
||||
<div className="flex flex-wrap justify-center gap-1 mb-4 px-4">
|
||||
{["人性观察", "行业揭秘", "赚钱逻辑", "创业复盘", "资源对接"].map((tag) => (
|
||||
<span key={tag} className="px-2 py-0.5 text-[10px] bg-white/5 text-white/70 rounded border border-white/10">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Recommender Info */}
|
||||
<div className="flex items-center gap-2 mb-4 bg-white/10 px-3 py-1.5 rounded-full backdrop-blur-sm">
|
||||
<span className="text-xs text-white">推荐人: {nickname}</span>
|
||||
<div className="flex items-center gap-2 mb-3 bg-[#00CED1]/10 px-4 py-2 rounded-full border border-[#00CED1]/20">
|
||||
<div className="w-6 h-6 rounded-full bg-[#00CED1]/30 flex items-center justify-center text-[10px] font-bold text-[#00CED1]">
|
||||
{nickname.charAt(0)}
|
||||
</div>
|
||||
<span className="text-xs text-[#00CED1]">{nickname} 推荐你来读</span>
|
||||
</div>
|
||||
|
||||
{/* 优惠说明 */}
|
||||
<div className="w-full p-3 rounded-xl bg-gradient-to-r from-[#FFD700]/10 to-[#E91E63]/10 border border-[#FFD700]/20 mb-4">
|
||||
<p className="text-center text-xs text-white/80">
|
||||
通过我的链接购买,<span className="text-[#00CED1] font-bold">立省5%</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* QR Code Section */}
|
||||
<div className="bg-white p-2 rounded-lg shadow-lg mb-2">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={qrCodeUrl} alt="QR Code" className="w-32 h-32" />
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={qrCodeUrl} alt="QR Code" className="w-28 h-28" />
|
||||
</div>
|
||||
<p className="text-[10px] text-white/60 mb-1">长按识别二维码试读</p>
|
||||
<p className="text-xs font-mono tracking-wider text-white">邀请码: {referralCode}</p>
|
||||
<p className="text-[10px] text-white/40 mb-1">长按识别 · 立即试读</p>
|
||||
<p className="text-xs font-mono tracking-wider text-[#00CED1]/80">邀请码: {referralCode}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer Actions */}
|
||||
<div className="p-4 bg-gray-50 flex flex-col gap-2">
|
||||
<p className="text-center text-xs text-gray-500 mb-1">
|
||||
长按上方图片保存,或截图分享
|
||||
</p>
|
||||
<Button onClick={onClose} className="w-full" variant="outline">
|
||||
关闭
|
||||
</Button>
|
||||
<p className="text-center text-xs text-gray-500 mb-1">
|
||||
长按上方图片保存,或截图分享
|
||||
</p>
|
||||
<Button onClick={onClose} className="w-full" variant="outline">
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { X, Wallet, CheckCircle } from "lucide-react"
|
||||
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"
|
||||
@@ -14,7 +14,7 @@ interface WithdrawalModalProps {
|
||||
}
|
||||
|
||||
export function WithdrawalModal({ isOpen, onClose, availableAmount }: WithdrawalModalProps) {
|
||||
const { requestWithdrawal } = useStore()
|
||||
const { requestWithdrawal, user } = useStore()
|
||||
const [amount, setAmount] = useState<string>("")
|
||||
const [method, setMethod] = useState<"wechat" | "alipay">("wechat")
|
||||
const [account, setAccount] = useState("")
|
||||
@@ -22,6 +22,20 @@ export function WithdrawalModal({ isOpen, onClose, availableAmount }: Withdrawal
|
||||
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) => {
|
||||
@@ -57,43 +71,91 @@ export function WithdrawalModal({ isOpen, onClose, availableAmount }: Withdrawal
|
||||
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-white rounded-xl overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200">
|
||||
<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-2 right-2 p-1.5 bg-black/10 rounded-full text-gray-500 hover:bg-black/20 z-10"
|
||||
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-100 rounded-full flex items-center justify-center mb-4">
|
||||
<CheckCircle className="w-8 h-8 text-green-600" />
|
||||
<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-gray-900 mb-2">申请提交成功</h3>
|
||||
<p className="text-sm text-gray-500 mb-6">
|
||||
<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-600 hover:bg-green-700 text-white">
|
||||
<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-indigo-600" />
|
||||
<h3 className="text-lg font-bold text-gray-900">申请提现</h3>
|
||||
<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">提现金额 (可提现: ¥{availableAmount.toFixed(2)})</Label>
|
||||
<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-gray-500">¥</span>
|
||||
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-white/50">¥</span>
|
||||
<Input
|
||||
id="amount"
|
||||
type="number"
|
||||
@@ -102,64 +164,77 @@ export function WithdrawalModal({ isOpen, onClose, availableAmount }: Withdrawal
|
||||
step="0.01"
|
||||
value={amount}
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
className="pl-7"
|
||||
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>提现方式</Label>
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMethod("wechat")}
|
||||
className={`flex-1 py-2 px-4 rounded-lg border text-sm font-medium transition-colors ${
|
||||
method === "wechat"
|
||||
? "border-green-600 bg-green-50 text-green-700"
|
||||
: "border-gray-200 hover:bg-gray-50 text-gray-600"
|
||||
}`}
|
||||
>
|
||||
微信支付
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMethod("alipay")}
|
||||
className={`flex-1 py-2 px-4 rounded-lg border text-sm font-medium transition-colors ${
|
||||
method === "alipay"
|
||||
? "border-blue-600 bg-blue-50 text-blue-700"
|
||||
: "border-gray-200 hover:bg-gray-50 text-gray-600"
|
||||
}`}
|
||||
>
|
||||
支付宝
|
||||
</button>
|
||||
<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">{method === "wechat" ? "微信号" : "支付宝账号"}</Label>
|
||||
<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">真实姓名</Label>
|
||||
<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-indigo-600 hover:bg-indigo-700 text-white"
|
||||
className="w-full bg-[#FFD700] hover:bg-[#FFD700]/90 text-black font-bold"
|
||||
disabled={isSubmitting || !amount || !account || !name}
|
||||
>
|
||||
{isSubmitting ? "提交中..." : "确认提现"}
|
||||
|
||||
Reference in New Issue
Block a user