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:
596
app/my/page.tsx
596
app/my/page.tsx
@@ -2,27 +2,24 @@
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { User, ChevronRight, Copy, Check, Home, List, TrendingUp, Gift, Star, Info } from "lucide-react"
|
||||
import { User, ChevronRight, Home, List, Gift, Star, Info, Users, Wallet, Footprints, Eye, BookOpen, Clock, ArrowUpRight, Phone, MessageCircle, CreditCard, X, Check, Loader2, Settings } from "lucide-react"
|
||||
import { useStore } from "@/lib/store"
|
||||
import { AuthModal } from "@/components/modules/auth/auth-modal"
|
||||
import { getFullBookPrice, getTotalSectionCount } from "@/lib/book-data"
|
||||
|
||||
function PlanetIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="12" cy="12" r="8" fill="currentColor" opacity="0.9" />
|
||||
<ellipse cx="12" cy="12" rx="11" ry="4" fill="none" stroke="currentColor" strokeWidth="1.5" opacity="0.6" />
|
||||
<circle cx="9" cy="10" r="1.5" fill="white" opacity="0.4" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default function MyPage() {
|
||||
const router = useRouter()
|
||||
const { user, isLoggedIn, logout, getAllPurchases, settings } = useStore()
|
||||
const { user, isLoggedIn, logout, getAllPurchases, settings, updateUser } = useStore()
|
||||
const [showAuthModal, setShowAuthModal] = useState(false)
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [activeTab, setActiveTab] = useState<"overview" | "footprint">("overview")
|
||||
|
||||
// 绑定弹窗状态
|
||||
const [showBindModal, setShowBindModal] = useState(false)
|
||||
const [bindType, setBindType] = useState<"phone" | "wechat" | "alipay">("phone")
|
||||
const [bindValue, setBindValue] = useState("")
|
||||
const [isBinding, setIsBinding] = useState(false)
|
||||
const [bindError, setBindError] = useState("")
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
@@ -36,25 +33,63 @@ export default function MyPage() {
|
||||
)
|
||||
}
|
||||
|
||||
const fullBookPrice = getFullBookPrice()
|
||||
const totalSections = getTotalSectionCount()
|
||||
const purchasedCount = user?.hasFullBook ? totalSections : user?.purchasedSections?.length || 0
|
||||
const readingMinutes = Math.floor(Math.random() * 100)
|
||||
const bookmarks = user?.purchasedSections?.length || 0
|
||||
|
||||
const authorInfo = settings?.authorInfo || {
|
||||
name: "卡若",
|
||||
description: "连续创业者,私域运营专家",
|
||||
liveTime: "06:00-09:00",
|
||||
platform: "Soul派对房",
|
||||
}
|
||||
|
||||
const handleCopyCode = () => {
|
||||
if (user?.referralCode) {
|
||||
navigator.clipboard.writeText(user.referralCode)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
// 绑定账号
|
||||
const handleBind = async () => {
|
||||
if (!bindValue.trim()) {
|
||||
setBindError("请输入内容")
|
||||
return
|
||||
}
|
||||
|
||||
if (bindType === "phone" && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
setBindError("请输入正确的手机号")
|
||||
return
|
||||
}
|
||||
|
||||
if (bindType === "wechat" && bindValue.length < 6) {
|
||||
setBindError("微信号至少6位")
|
||||
return
|
||||
}
|
||||
|
||||
if (bindType === "alipay" && !bindValue.includes("@") && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
setBindError("请输入正确的支付宝账号")
|
||||
return
|
||||
}
|
||||
|
||||
setIsBinding(true)
|
||||
setBindError("")
|
||||
|
||||
try {
|
||||
// 模拟API调用
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
// 更新用户信息
|
||||
if (updateUser && user) {
|
||||
const updates: any = {}
|
||||
if (bindType === "phone") updates.phone = bindValue
|
||||
if (bindType === "wechat") updates.wechat = bindValue
|
||||
if (bindType === "alipay") updates.alipay = bindValue
|
||||
updateUser(user.id, updates)
|
||||
}
|
||||
|
||||
setShowBindModal(false)
|
||||
setBindValue("")
|
||||
alert("绑定成功!")
|
||||
} catch (error) {
|
||||
setBindError("绑定失败,请重试")
|
||||
} finally {
|
||||
setIsBinding(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 打开绑定弹窗
|
||||
const openBindModal = (type: "phone" | "wechat" | "alipay") => {
|
||||
setBindType(type)
|
||||
setBindValue("")
|
||||
setBindError("")
|
||||
setShowBindModal(true)
|
||||
}
|
||||
|
||||
// 底部导航组件
|
||||
@@ -70,12 +105,12 @@ export default function MyPage() {
|
||||
<List className="w-5 h-5 text-gray-500 mb-1" />
|
||||
<span className="text-gray-500 text-xs">目录</span>
|
||||
</button>
|
||||
{/* 匹配按钮 - 小星球图标 */}
|
||||
{/* 找伙伴按钮 */}
|
||||
<button onClick={() => router.push("/match")} className="flex flex-col items-center py-2 px-6 -mt-4">
|
||||
<div className="w-14 h-14 rounded-full bg-gradient-to-br from-[#00CED1] to-[#20B2AA] flex items-center justify-center shadow-lg shadow-[#00CED1]/30">
|
||||
<PlanetIcon className="w-7 h-7 text-white" />
|
||||
<Users className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
<span className="text-gray-500 text-xs mt-1">匹配</span>
|
||||
<span className="text-gray-500 text-xs mt-1">找伙伴</span>
|
||||
</button>
|
||||
<button className="flex flex-col items-center py-2 px-4">
|
||||
<User className="w-5 h-5 text-[#00CED1] mb-1" />
|
||||
@@ -94,7 +129,7 @@ export default function MyPage() {
|
||||
<h1 className="text-lg font-medium text-[#00CED1]">我的</h1>
|
||||
</div>
|
||||
|
||||
{/* 用户卡片 - 突出个性化 */}
|
||||
{/* 用户卡片 */}
|
||||
<div className="mx-4 mt-4 p-4 rounded-2xl bg-gradient-to-br from-[#1c1c1e] to-[#2c2c2e] border border-[#00CED1]/20">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-16 h-16 rounded-full border-2 border-dashed border-[#00CED1]/50 flex items-center justify-center bg-gradient-to-br from-[#00CED1]/10 to-transparent">
|
||||
@@ -106,99 +141,42 @@ export default function MyPage() {
|
||||
</button>
|
||||
<p className="text-white/30 text-sm">解锁专属权益</p>
|
||||
</div>
|
||||
<div className="px-3 py-1 rounded-full bg-[#00CED1]/20 border border-[#00CED1]/30">
|
||||
<span className="text-[#00CED1] text-xs">VIP</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 个性化数据 */}
|
||||
<div className="grid grid-cols-3 gap-2 pt-4 border-t border-white/10">
|
||||
<div className="text-center p-2 rounded-lg bg-white/5">
|
||||
<p className="text-[#00CED1] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">已读</p>
|
||||
<p className="text-white/40 text-xs">已购章节</p>
|
||||
</div>
|
||||
<div className="text-center p-2 rounded-lg bg-white/5">
|
||||
<p className="text-[#00CED1] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">收藏</p>
|
||||
<p className="text-white/40 text-xs">推荐好友</p>
|
||||
</div>
|
||||
<div className="text-center p-2 rounded-lg bg-white/5">
|
||||
<p className="text-[#00CED1] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">书签</p>
|
||||
<p className="text-[#FFD700] text-xl font-bold">--</p>
|
||||
<p className="text-white/40 text-xs">待领收益</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 收益中心 - 突出收益 */}
|
||||
{/* 分销入口 */}
|
||||
<div className="mx-4 mt-4 p-4 rounded-2xl bg-gradient-to-r from-[#FFD700]/10 via-[#1c1c1e] to-[#1c1c1e] border border-[#FFD700]/20">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-lg bg-[#FFD700]/20 flex items-center justify-center">
|
||||
<TrendingUp className="w-4 h-4 text-[#FFD700]" />
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-[#FFD700] to-[#FFA500] flex items-center justify-center">
|
||||
<Gift className="w-5 h-5 text-black" />
|
||||
</div>
|
||||
<span className="text-white font-medium">收益中心</span>
|
||||
</div>
|
||||
<span className="text-[#FFD700] text-sm bg-[#FFD700]/10 px-3 py-1 rounded-full">90%分成</span>
|
||||
</div>
|
||||
|
||||
<div className="bg-gradient-to-r from-[#FFD700]/5 to-transparent rounded-xl p-4 mb-4">
|
||||
<p className="text-white/50 text-sm text-center mb-1">累计收益</p>
|
||||
<p className="text-[#FFD700] text-3xl font-bold text-center">¥0.00</p>
|
||||
<div className="flex justify-center gap-8 mt-3">
|
||||
<div className="text-center">
|
||||
<p className="text-white/40 text-xs">可提现</p>
|
||||
<p className="text-white font-medium">¥0.00</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-white/40 text-xs">已提现</p>
|
||||
<p className="text-white font-medium">¥0.00</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 mb-4">
|
||||
<button
|
||||
onClick={() => setShowAuthModal(true)}
|
||||
className="py-3 rounded-xl bg-gradient-to-r from-[#FFD700] to-[#FFA500] text-black font-medium flex items-center justify-center gap-2"
|
||||
>
|
||||
<Gift className="w-4 h-4" />
|
||||
生成海报
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowAuthModal(true)}
|
||||
className="py-3 rounded-xl bg-[#2c2c2e] text-white font-medium border border-white/10"
|
||||
>
|
||||
立即提现
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2 pt-4 border-t border-white/10">
|
||||
<div className="text-center">
|
||||
<p className="text-[#FFD700] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">推荐人数</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#FFD700] text-xl font-bold">0</p>
|
||||
<p className="text-white/40 text-xs">成交订单</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#FFD700] text-xl font-bold">90%</p>
|
||||
<p className="text-white/40 text-xs">佣金率</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 pt-4 border-t border-white/10">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-white/50 text-xs">我的邀请码</p>
|
||||
<p className="text-[#FFD700] font-mono text-lg">- - -</p>
|
||||
<p className="text-white font-medium">推广赚收益</p>
|
||||
<p className="text-white/40 text-xs">登录后查看详情</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAuthModal(true)}
|
||||
className="px-4 py-2 rounded-lg bg-[#2c2c2e] text-white text-sm"
|
||||
>
|
||||
复制
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAuthModal(true)}
|
||||
className="px-4 py-2 rounded-lg bg-[#FFD700]/20 text-[#FFD700] text-sm font-medium"
|
||||
>
|
||||
立即登录
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -214,17 +192,6 @@ export default function MyPage() {
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowAuthModal(true)}
|
||||
className="w-full flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">🏷️</span>
|
||||
<span className="text-white">我的书签</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</button>
|
||||
{/* 关于作者 - 小图标入口 */}
|
||||
<button
|
||||
onClick={() => router.push("/about")}
|
||||
className="w-full flex items-center justify-between p-4 active:bg-white/5"
|
||||
@@ -248,6 +215,13 @@ export default function MyPage() {
|
||||
// 已登录状态
|
||||
const userPurchases = getAllPurchases().filter((p) => p.userId === user?.id)
|
||||
const completedOrders = userPurchases.filter((p) => p.status === "completed").length
|
||||
|
||||
// 模拟足迹数据(实际应从数据库获取)
|
||||
const footprintData = {
|
||||
recentChapters: user?.purchasedSections?.slice(-5) || [],
|
||||
matchHistory: [], // 匹配历史
|
||||
totalReadTime: Math.floor(Math.random() * 200) + 50, // 阅读时长(分钟)
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-black text-white pb-24">
|
||||
@@ -255,7 +229,7 @@ export default function MyPage() {
|
||||
<h1 className="text-lg font-medium text-[#00CED1]">我的</h1>
|
||||
</div>
|
||||
|
||||
{/* 用户卡片 - 突出个性化 */}
|
||||
{/* 用户卡片 */}
|
||||
<div className="mx-4 mt-4 p-4 rounded-2xl bg-gradient-to-br from-[#1c1c1e] to-[#2c2c2e] border border-[#00CED1]/20">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-16 h-16 rounded-full border-2 border-[#00CED1] flex items-center justify-center bg-gradient-to-br from-[#00CED1]/20 to-transparent">
|
||||
@@ -268,150 +242,314 @@ export default function MyPage() {
|
||||
<div className="px-3 py-1 rounded-full bg-[#00CED1]/20 border border-[#00CED1]/30">
|
||||
<span className="text-[#00CED1] text-xs flex items-center gap-1">
|
||||
<Star className="w-3 h-3" />
|
||||
VIP
|
||||
创业伙伴
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 个性化数据 */}
|
||||
<div className="grid grid-cols-3 gap-2 pt-4 border-t border-white/10">
|
||||
<div className="text-center p-2 rounded-lg bg-white/5">
|
||||
<p className="text-[#00CED1] text-xl font-bold">{purchasedCount}</p>
|
||||
<p className="text-white/40 text-xs">已读</p>
|
||||
<p className="text-white/40 text-xs">已购章节</p>
|
||||
</div>
|
||||
<div className="text-center p-2 rounded-lg bg-white/5">
|
||||
<p className="text-[#00CED1] text-xl font-bold">{readingMinutes}</p>
|
||||
<p className="text-white/40 text-xs">时长(分)</p>
|
||||
<p className="text-[#00CED1] text-xl font-bold">{user?.referralCount || 0}</p>
|
||||
<p className="text-white/40 text-xs">推荐好友</p>
|
||||
</div>
|
||||
<div className="text-center p-2 rounded-lg bg-white/5">
|
||||
<p className="text-[#00CED1] text-xl font-bold">{bookmarks}</p>
|
||||
<p className="text-white/40 text-xs">书签</p>
|
||||
<p className="text-[#FFD700] text-xl font-bold">
|
||||
{(user?.earnings || 0) > 0 ? `¥${(user?.earnings || 0).toFixed(0)}` : '--'}
|
||||
</p>
|
||||
<p className="text-white/40 text-xs">待领收益</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 收益中心 - 突出收益 */}
|
||||
<div className="mx-4 mt-4 p-4 rounded-2xl bg-gradient-to-r from-[#FFD700]/10 via-[#1c1c1e] to-[#1c1c1e] border border-[#FFD700]/20">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-lg bg-[#FFD700]/20 flex items-center justify-center">
|
||||
<TrendingUp className="w-4 h-4 text-[#FFD700]" />
|
||||
</div>
|
||||
<span className="text-white font-medium">收益中心</span>
|
||||
</div>
|
||||
<span className="text-[#FFD700] text-sm bg-[#FFD700]/10 px-3 py-1 rounded-full">90%分成</span>
|
||||
</div>
|
||||
|
||||
<div className="bg-gradient-to-r from-[#FFD700]/5 to-transparent rounded-xl p-4 mb-4">
|
||||
<p className="text-white/50 text-sm text-center mb-1">累计收益</p>
|
||||
<p className="text-[#FFD700] text-3xl font-bold text-center">¥{(user?.earnings || 0).toFixed(2)}</p>
|
||||
<div className="flex justify-center gap-8 mt-3">
|
||||
<div className="text-center">
|
||||
<p className="text-white/40 text-xs">可提现</p>
|
||||
<p className="text-white font-medium">¥{(user?.pendingEarnings || 0).toFixed(2)}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-white/40 text-xs">已提现</p>
|
||||
<p className="text-white font-medium">¥{(user?.withdrawnEarnings || 0).toFixed(2)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 mb-4">
|
||||
<button
|
||||
onClick={() => router.push("/my/referral")}
|
||||
className="py-3 rounded-xl bg-gradient-to-r from-[#FFD700] to-[#FFA500] text-black font-medium flex items-center justify-center gap-2"
|
||||
>
|
||||
<Gift className="w-4 h-4" />
|
||||
生成海报
|
||||
</button>
|
||||
<button
|
||||
onClick={() => router.push("/my/referral")}
|
||||
className="py-3 rounded-xl bg-[#2c2c2e] text-white font-medium border border-white/10"
|
||||
>
|
||||
立即提现
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2 pt-4 border-t border-white/10">
|
||||
<div className="text-center">
|
||||
<p className="text-[#FFD700] text-xl font-bold">{user?.referralCount || 0}</p>
|
||||
<p className="text-white/40 text-xs">推荐人数</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#FFD700] text-xl font-bold">{completedOrders}</p>
|
||||
<p className="text-white/40 text-xs">成交订单</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-[#FFD700] text-xl font-bold">90%</p>
|
||||
<p className="text-white/40 text-xs">佣金率</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 pt-4 border-t border-white/10">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-white/50 text-xs">我的邀请码</p>
|
||||
<p className="text-[#FFD700] font-mono text-lg">{user?.referralCode || "---"}</p>
|
||||
{/* 收益卡片 - 艺术化设计 */}
|
||||
<div className="mx-4 mt-4 p-4 rounded-2xl bg-gradient-to-br from-[#1a1a2e] via-[#16213e] to-[#0f3460] border border-[#00CED1]/20 relative overflow-hidden">
|
||||
{/* 背景装饰 */}
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-[#FFD700]/10 to-transparent rounded-full -translate-y-1/2 translate-x-1/2" />
|
||||
<div className="absolute bottom-0 left-0 w-24 h-24 bg-gradient-to-tr from-[#00CED1]/10 to-transparent rounded-full translate-y-1/2 -translate-x-1/2" />
|
||||
|
||||
<div className="relative">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Wallet className="w-5 h-5 text-[#FFD700]" />
|
||||
<span className="text-white font-medium">我的收益</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleCopyCode}
|
||||
className="px-4 py-2 rounded-lg bg-[#2c2c2e] text-white text-sm flex items-center gap-2"
|
||||
onClick={() => router.push("/my/referral")}
|
||||
className="text-[#00CED1] text-xs flex items-center gap-1"
|
||||
>
|
||||
{copied ? <Check className="w-4 h-4 text-[#00CED1]" /> : <Copy className="w-4 h-4" />}
|
||||
{copied ? "已复制" : "复制"}
|
||||
推广中心
|
||||
<ArrowUpRight className="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-end gap-6 mb-4">
|
||||
<div>
|
||||
<p className="text-white/50 text-xs mb-1">累计收益</p>
|
||||
<p className="text-3xl font-bold bg-gradient-to-r from-[#FFD700] to-[#FFA500] bg-clip-text text-transparent">
|
||||
¥{(user?.earnings || 0).toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-white/50 text-xs mb-1">可提现</p>
|
||||
<p className="text-xl font-semibold text-white">
|
||||
¥{(user?.pendingEarnings || 0).toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => router.push("/my/referral")}
|
||||
className="w-full py-2.5 rounded-xl bg-gradient-to-r from-[#FFD700]/80 to-[#FFA500]/80 text-black text-sm font-bold flex items-center justify-center gap-2"
|
||||
>
|
||||
<Gift className="w-4 h-4" />
|
||||
推广中心 / 提现
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 菜单列表 */}
|
||||
<div className="mx-4 mt-4 rounded-2xl bg-[#1c1c1e] border border-white/5 overflow-hidden">
|
||||
{/* Tab切换 */}
|
||||
<div className="mx-4 mt-4 flex gap-2">
|
||||
<button
|
||||
onClick={() => router.push("/my/purchases")}
|
||||
className="w-full flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
onClick={() => setActiveTab("overview")}
|
||||
className={`flex-1 py-2.5 rounded-xl text-sm font-medium transition-colors ${
|
||||
activeTab === "overview"
|
||||
? "bg-[#00CED1]/20 text-[#00CED1] border border-[#00CED1]/30"
|
||||
: "bg-[#1c1c1e] text-white/60"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">📦</span>
|
||||
<span className="text-white">我的订单</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
概览
|
||||
</button>
|
||||
<button
|
||||
onClick={() => router.push("/my/bookmarks")}
|
||||
className="w-full flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
onClick={() => setActiveTab("footprint")}
|
||||
className={`flex-1 py-2.5 rounded-xl text-sm font-medium transition-colors flex items-center justify-center gap-1 ${
|
||||
activeTab === "footprint"
|
||||
? "bg-[#00CED1]/20 text-[#00CED1] border border-[#00CED1]/30"
|
||||
: "bg-[#1c1c1e] text-white/60"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">🏷️</span>
|
||||
<span className="text-white">我的书签</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</button>
|
||||
{/* 关于作者 - 小图标入口 */}
|
||||
<button
|
||||
onClick={() => router.push("/about")}
|
||||
className="w-full flex items-center justify-between p-4 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-6 h-6 rounded-full bg-gradient-to-br from-[#00CED1] to-[#20B2AA] flex items-center justify-center">
|
||||
<Info className="w-3 h-3 text-white" />
|
||||
</div>
|
||||
<span className="text-white">关于作者</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
<Footprints className="w-4 h-4" />
|
||||
我的足迹
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mx-4 mt-4">
|
||||
<button
|
||||
onClick={logout}
|
||||
className="w-full py-3 rounded-xl bg-[#1c1c1e] text-[#00CED1] font-medium border border-[#00CED1]/30"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</div>
|
||||
{activeTab === "overview" ? (
|
||||
<>
|
||||
{/* 菜单列表 */}
|
||||
<div className="mx-4 mt-4 rounded-2xl bg-[#1c1c1e] border border-white/5 overflow-hidden">
|
||||
<button
|
||||
onClick={() => router.push("/my/purchases")}
|
||||
className="w-full flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">📦</span>
|
||||
<span className="text-white">我的订单</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-white/40 text-sm">{completedOrders}笔</span>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => router.push("/my/referral")}
|
||||
className="w-full flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-6 h-6 rounded-full bg-gradient-to-br from-[#FFD700] to-[#FFA500] flex items-center justify-center">
|
||||
<Gift className="w-3 h-3 text-black" />
|
||||
</div>
|
||||
<span className="text-white">推广中心</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-[#FFD700] text-sm font-medium">90%佣金</span>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => router.push("/about")}
|
||||
className="w-full flex items-center justify-between p-4 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-6 h-6 rounded-full bg-gradient-to-br from-[#00CED1] to-[#20B2AA] flex items-center justify-center">
|
||||
<Info className="w-3 h-3 text-white" />
|
||||
</div>
|
||||
<span className="text-white">关于作者</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => router.push("/my/settings")}
|
||||
className="w-full flex items-center justify-between p-4 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-6 h-6 rounded-full bg-gray-500/20 flex items-center justify-center">
|
||||
<Settings className="w-3 h-3 text-gray-400" />
|
||||
</div>
|
||||
<span className="text-white">设置</span>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-white/30" />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* 足迹内容 */}
|
||||
<div className="mx-4 mt-4 space-y-4">
|
||||
{/* 阅读统计 */}
|
||||
<div className="p-4 rounded-2xl bg-[#1c1c1e] border border-white/5">
|
||||
<h3 className="text-white font-medium mb-3 flex items-center gap-2">
|
||||
<Eye className="w-4 h-4 text-[#00CED1]" />
|
||||
阅读统计
|
||||
</h3>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="text-center p-3 rounded-xl bg-white/5">
|
||||
<BookOpen className="w-5 h-5 text-[#00CED1] mx-auto mb-1" />
|
||||
<p className="text-white font-bold">{purchasedCount}</p>
|
||||
<p className="text-white/40 text-xs">已读章节</p>
|
||||
</div>
|
||||
<div className="text-center p-3 rounded-xl bg-white/5">
|
||||
<Clock className="w-5 h-5 text-[#FFD700] mx-auto mb-1" />
|
||||
<p className="text-white font-bold">{footprintData.totalReadTime}</p>
|
||||
<p className="text-white/40 text-xs">阅读分钟</p>
|
||||
</div>
|
||||
<div className="text-center p-3 rounded-xl bg-white/5">
|
||||
<Users className="w-5 h-5 text-[#E91E63] mx-auto mb-1" />
|
||||
<p className="text-white font-bold">{footprintData.matchHistory.length || 0}</p>
|
||||
<p className="text-white/40 text-xs">匹配伙伴</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 最近阅读 */}
|
||||
<div className="p-4 rounded-2xl bg-[#1c1c1e] border border-white/5">
|
||||
<h3 className="text-white font-medium mb-3 flex items-center gap-2">
|
||||
<BookOpen className="w-4 h-4 text-[#00CED1]" />
|
||||
最近阅读
|
||||
</h3>
|
||||
{footprintData.recentChapters.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
{footprintData.recentChapters.map((sectionId, index) => (
|
||||
<div
|
||||
key={sectionId}
|
||||
className="flex items-center justify-between p-3 rounded-xl bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-white/30 text-sm">{index + 1}</span>
|
||||
<span className="text-white text-sm">章节 {sectionId}</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => router.push(`/read/${sectionId}`)}
|
||||
className="text-[#00CED1] text-xs"
|
||||
>
|
||||
继续阅读
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-6 text-white/40">
|
||||
<BookOpen className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
||||
<p className="text-sm">暂无阅读记录</p>
|
||||
<button
|
||||
onClick={() => router.push("/chapters")}
|
||||
className="mt-2 text-[#00CED1] text-sm"
|
||||
>
|
||||
去阅读 →
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 匹配记录 */}
|
||||
<div className="p-4 rounded-2xl bg-[#1c1c1e] border border-white/5">
|
||||
<h3 className="text-white font-medium mb-3 flex items-center gap-2">
|
||||
<Users className="w-4 h-4 text-[#00CED1]" />
|
||||
匹配记录
|
||||
</h3>
|
||||
<div className="text-center py-6 text-white/40">
|
||||
<Users className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
||||
<p className="text-sm">暂无匹配记录</p>
|
||||
<button
|
||||
onClick={() => router.push("/match")}
|
||||
className="mt-2 text-[#00CED1] text-sm"
|
||||
>
|
||||
去匹配 →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<BottomNavBar />
|
||||
|
||||
{/* 绑定弹窗 */}
|
||||
{showBindModal && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => !isBinding && setShowBindModal(false)} />
|
||||
<div className="relative w-full max-w-sm bg-[#1c1c1e] rounded-2xl overflow-hidden">
|
||||
<div className="flex items-center justify-between p-4 border-b border-white/10">
|
||||
<h3 className="text-lg font-semibold text-white">
|
||||
绑定{bindType === "phone" ? "手机号" : bindType === "wechat" ? "微信号" : "支付宝"}
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => !isBinding && setShowBindModal(false)}
|
||||
className="w-8 h-8 rounded-full bg-white/10 flex items-center justify-center"
|
||||
>
|
||||
<X className="w-4 h-4 text-white/60" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="p-5">
|
||||
<div className="mb-4">
|
||||
<label className="block text-white/40 text-xs mb-2">
|
||||
{bindType === "phone" ? "手机号" : bindType === "wechat" ? "微信号" : "支付宝账号"}
|
||||
</label>
|
||||
<input
|
||||
type={bindType === "phone" ? "tel" : "text"}
|
||||
value={bindValue}
|
||||
onChange={(e) => setBindValue(e.target.value)}
|
||||
placeholder={
|
||||
bindType === "phone" ? "请输入11位手机号" :
|
||||
bindType === "wechat" ? "请输入微信号" :
|
||||
"请输入支付宝账号"
|
||||
}
|
||||
className="w-full px-4 py-3 rounded-xl bg-black/30 border border-white/10 text-white placeholder-white/30 focus:outline-none focus:border-[#00CED1]/50"
|
||||
disabled={isBinding}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{bindError && (
|
||||
<p className="text-red-400 text-sm mb-4">{bindError}</p>
|
||||
)}
|
||||
|
||||
<p className="text-white/40 text-xs mb-4">
|
||||
{bindType === "phone" && "绑定手机号后可用于找伙伴匹配"}
|
||||
{bindType === "wechat" && "绑定微信号后可用于找伙伴匹配和好友添加"}
|
||||
{bindType === "alipay" && "绑定支付宝后可用于提现收益"}
|
||||
</p>
|
||||
|
||||
<button
|
||||
onClick={handleBind}
|
||||
disabled={isBinding || !bindValue}
|
||||
className="w-full py-3 rounded-xl bg-[#00CED1] text-black font-medium flex items-center justify-center gap-2 disabled:opacity-50"
|
||||
>
|
||||
{isBinding ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
绑定中...
|
||||
</>
|
||||
) : (
|
||||
"确认绑定"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,18 +2,50 @@
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import Link from "next/link"
|
||||
import { ChevronLeft, Copy, Share2, Users, Wallet, MessageCircle, ImageIcon, TrendingUp, Gift, Check, ArrowRight } from "lucide-react"
|
||||
import {
|
||||
ChevronLeft, Copy, Share2, Users, Wallet, MessageCircle, ImageIcon,
|
||||
TrendingUp, Gift, Check, ArrowRight, Bell, Clock, AlertCircle,
|
||||
CheckCircle, UserPlus, Settings, Zap, ChevronDown, ChevronUp
|
||||
} from "lucide-react"
|
||||
import { useStore, type Purchase } from "@/lib/store"
|
||||
import { PosterModal } from "@/components/modules/referral/poster-modal"
|
||||
import { WithdrawalModal } from "@/components/modules/referral/withdrawal-modal"
|
||||
import { AutoWithdrawModal } from "@/components/modules/distribution/auto-withdraw-modal"
|
||||
import { RealtimeNotification } from "@/components/modules/distribution/realtime-notification"
|
||||
|
||||
// 绑定用户类型
|
||||
interface BindingUser {
|
||||
id: string
|
||||
visitorNickname?: string
|
||||
visitorPhone?: string
|
||||
bindingTime: string
|
||||
expireTime: string
|
||||
status: 'active' | 'converted' | 'expired'
|
||||
daysRemaining?: number
|
||||
commission?: number
|
||||
orderAmount?: number
|
||||
}
|
||||
|
||||
export default function ReferralPage() {
|
||||
const { user, isLoggedIn, settings, getAllPurchases, getAllUsers } = useStore()
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [showPoster, setShowPoster] = useState(false)
|
||||
const [showWithdrawal, setShowWithdrawal] = useState(false)
|
||||
const [showAutoWithdraw, setShowAutoWithdraw] = useState(false)
|
||||
const [referralPurchases, setReferralPurchases] = useState<Purchase[]>([])
|
||||
const [referralUsers, setReferralUsers] = useState<number>(0)
|
||||
|
||||
// 绑定用户相关状态
|
||||
const [activeBindings, setActiveBindings] = useState<BindingUser[]>([])
|
||||
const [convertedBindings, setConvertedBindings] = useState<BindingUser[]>([])
|
||||
const [expiredBindings, setExpiredBindings] = useState<BindingUser[]>([])
|
||||
const [expiringCount, setExpiringCount] = useState(0)
|
||||
const [showBindingList, setShowBindingList] = useState(true)
|
||||
const [activeTab, setActiveTab] = useState<'active' | 'converted' | 'expired'>('active')
|
||||
|
||||
// 自动提现状态
|
||||
const [autoWithdrawEnabled, setAutoWithdrawEnabled] = useState(false)
|
||||
const [autoWithdrawThreshold, setAutoWithdrawThreshold] = useState(100)
|
||||
|
||||
useEffect(() => {
|
||||
if (user?.referralCode) {
|
||||
@@ -24,9 +56,77 @@ export default function ReferralPage() {
|
||||
const myReferralPurchases = allPurchases.filter((p) => userIds.includes(p.userId))
|
||||
setReferralPurchases(myReferralPurchases)
|
||||
setReferralUsers(usersWithMyCode.length)
|
||||
|
||||
// 模拟绑定数据(实际从API获取)
|
||||
loadBindingData()
|
||||
}
|
||||
}, [user, getAllPurchases, getAllUsers])
|
||||
|
||||
// 加载绑定数据
|
||||
const loadBindingData = async () => {
|
||||
// 模拟数据 - 实际项目中从 /api/distribution?type=my-bindings&userId=xxx 获取
|
||||
const now = new Date()
|
||||
|
||||
const mockActiveBindings: BindingUser[] = [
|
||||
{
|
||||
id: '1',
|
||||
visitorNickname: '小明',
|
||||
visitorPhone: '138****1234',
|
||||
bindingTime: new Date(Date.now() - 25 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
expireTime: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
status: 'active',
|
||||
daysRemaining: 5,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
visitorNickname: '小红',
|
||||
visitorPhone: '139****5678',
|
||||
bindingTime: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
expireTime: new Date(Date.now() + 20 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
status: 'active',
|
||||
daysRemaining: 20,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
visitorNickname: '阿强',
|
||||
visitorPhone: '137****9012',
|
||||
bindingTime: new Date(Date.now() - 28 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
expireTime: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
status: 'active',
|
||||
daysRemaining: 2,
|
||||
},
|
||||
]
|
||||
|
||||
const mockConvertedBindings: BindingUser[] = [
|
||||
{
|
||||
id: '4',
|
||||
visitorNickname: '小李',
|
||||
visitorPhone: '136****3456',
|
||||
bindingTime: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
expireTime: new Date(Date.now() + 15 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
status: 'converted',
|
||||
commission: 8.91,
|
||||
orderAmount: 9.9,
|
||||
},
|
||||
]
|
||||
|
||||
const mockExpiredBindings: BindingUser[] = [
|
||||
{
|
||||
id: '5',
|
||||
visitorNickname: '小王',
|
||||
visitorPhone: '135****7890',
|
||||
bindingTime: new Date(Date.now() - 35 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
expireTime: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
status: 'expired',
|
||||
},
|
||||
]
|
||||
|
||||
setActiveBindings(mockActiveBindings)
|
||||
setConvertedBindings(mockConvertedBindings)
|
||||
setExpiredBindings(mockExpiredBindings)
|
||||
setExpiringCount(mockActiveBindings.filter(b => (b.daysRemaining || 0) <= 7).length)
|
||||
}
|
||||
|
||||
if (!isLoggedIn || !user) {
|
||||
return (
|
||||
<div className="min-h-screen bg-black text-white flex items-center justify-center pb-20">
|
||||
@@ -82,6 +182,25 @@ export default function ReferralPage() {
|
||||
alert("朋友圈文案已复制!\n\n打开微信 → 发朋友圈 → 粘贴即可")
|
||||
}
|
||||
|
||||
// 获取绑定状态样式
|
||||
const getBindingStatusStyle = (daysRemaining?: number) => {
|
||||
if (!daysRemaining) return 'bg-gray-500/20 text-gray-400'
|
||||
if (daysRemaining <= 3) return 'bg-red-500/20 text-red-400'
|
||||
if (daysRemaining <= 7) return 'bg-orange-500/20 text-orange-400'
|
||||
return 'bg-green-500/20 text-green-400'
|
||||
}
|
||||
|
||||
// 获取绑定状态文本
|
||||
const getBindingStatusText = (binding: BindingUser) => {
|
||||
if (binding.status === 'converted') return '已付款'
|
||||
if (binding.status === 'expired') return '已过期'
|
||||
if (binding.daysRemaining && binding.daysRemaining <= 3) return `${binding.daysRemaining}天后过期`
|
||||
if (binding.daysRemaining && binding.daysRemaining <= 7) return `${binding.daysRemaining}天`
|
||||
return `${binding.daysRemaining || 0}天`
|
||||
}
|
||||
|
||||
const currentBindings = activeTab === 'active' ? activeBindings : activeTab === 'converted' ? convertedBindings : expiredBindings
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-black text-white pb-24 page-transition">
|
||||
{/* 背景光效 */}
|
||||
@@ -96,11 +215,38 @@ export default function ReferralPage() {
|
||||
<ChevronLeft className="w-5 h-5 text-[var(--app-text-secondary)]" />
|
||||
</Link>
|
||||
<h1 className="flex-1 text-center font-semibold">分销中心</h1>
|
||||
<div className="w-8" />
|
||||
<div className="flex items-center gap-2">
|
||||
<RealtimeNotification />
|
||||
<button
|
||||
onClick={() => setShowAutoWithdraw(true)}
|
||||
className="w-8 h-8 rounded-full bg-[var(--app-bg-secondary)] flex items-center justify-center touch-feedback"
|
||||
>
|
||||
<Settings className="w-4 h-4 text-[var(--app-text-secondary)]" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="max-w-md mx-auto px-4 py-6">
|
||||
{/* 过期提醒横幅 */}
|
||||
{expiringCount > 0 && (
|
||||
<div className="mb-4 glass-card p-4 border border-orange-500/30 bg-orange-500/10">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-full bg-orange-500/20 flex items-center justify-center flex-shrink-0">
|
||||
<Bell className="w-5 h-5 text-orange-400" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-white font-medium text-sm">
|
||||
{expiringCount} 位用户绑定即将过期
|
||||
</p>
|
||||
<p className="text-orange-300/80 text-xs mt-0.5">
|
||||
30天内未付款将解除绑定关系
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 收益卡片 - 毛玻璃渐变 */}
|
||||
<div className="relative glass-card-heavy p-6 mb-6 overflow-hidden">
|
||||
{/* 背景装饰 */}
|
||||
@@ -123,57 +269,180 @@ export default function ReferralPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
disabled={totalEarnings < 10}
|
||||
onClick={() => setShowWithdrawal(true)}
|
||||
className="btn-ios w-full glow disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{totalEarnings < 10 ? `满10元可提现` : "申请提现"}
|
||||
</button>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
disabled={totalEarnings < 10}
|
||||
onClick={() => setShowWithdrawal(true)}
|
||||
className="flex-1 btn-ios glow disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{totalEarnings < 10 ? `满10元可提现` : "申请提现"}
|
||||
</button>
|
||||
{autoWithdrawEnabled && (
|
||||
<div className="flex items-center gap-1 px-3 py-2 bg-[var(--app-brand-light)] rounded-xl">
|
||||
<Zap className="w-4 h-4 text-[var(--app-brand)]" />
|
||||
<span className="text-[var(--app-brand)] text-xs font-medium">自动提现</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 数据统计 */}
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
<div className="glass-card p-4 text-center">
|
||||
<div className="w-10 h-10 rounded-xl bg-[var(--ios-blue)]/20 flex items-center justify-center mx-auto mb-2">
|
||||
<Users className="w-5 h-5 text-[var(--ios-blue)]" />
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-white mb-0.5">{referralUsers}</p>
|
||||
<p className="text-[var(--app-text-tertiary)] text-xs">邀请人数</p>
|
||||
<div className="grid grid-cols-4 gap-2 mb-6">
|
||||
<div className="glass-card p-3 text-center">
|
||||
<p className="text-xl font-bold text-white">{activeBindings.length}</p>
|
||||
<p className="text-[var(--app-text-tertiary)] text-[10px]">绑定中</p>
|
||||
</div>
|
||||
<div className="glass-card p-4 text-center">
|
||||
<div className="w-10 h-10 rounded-xl bg-[var(--ios-purple)]/20 flex items-center justify-center mx-auto mb-2">
|
||||
<TrendingUp className="w-5 h-5 text-[var(--ios-purple)]" />
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-white mb-0.5">{referralPurchases.length}</p>
|
||||
<p className="text-[var(--app-text-tertiary)] text-xs">成交订单</p>
|
||||
<div className="glass-card p-3 text-center">
|
||||
<p className="text-xl font-bold text-white">{convertedBindings.length}</p>
|
||||
<p className="text-[var(--app-text-tertiary)] text-[10px]">已付款</p>
|
||||
</div>
|
||||
<div className="glass-card p-3 text-center">
|
||||
<p className="text-xl font-bold text-orange-400">{expiringCount}</p>
|
||||
<p className="text-[var(--app-text-tertiary)] text-[10px]">即将过期</p>
|
||||
</div>
|
||||
<div className="glass-card p-3 text-center">
|
||||
<p className="text-xl font-bold text-white">{referralUsers}</p>
|
||||
<p className="text-[var(--app-text-tertiary)] text-[10px]">总邀请</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 专属链接 */}
|
||||
{/* 分销规则说明 */}
|
||||
<div className="glass-card p-4 mb-6 border border-[var(--app-brand)]/20 bg-[var(--app-brand)]/5">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-[var(--app-brand-light)] flex items-center justify-center flex-shrink-0">
|
||||
<AlertCircle className="w-4 h-4 text-[var(--app-brand)]" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-white font-medium text-sm mb-1">推广规则</p>
|
||||
<ul className="text-[var(--app-text-tertiary)] text-xs space-y-1">
|
||||
<li>• 好友通过你的链接购买,<span className="text-[#FFD700]">立享5%优惠</span></li>
|
||||
<li>• 好友成功付款后,你获得 <span className="text-[var(--app-brand)]">{distributorShare}%</span> 收益</li>
|
||||
<li>• 绑定期<span className="text-[var(--app-brand)]">30天</span>,期满未付款自动解除</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 绑定用户列表 */}
|
||||
<div className="glass-card overflow-hidden mb-6">
|
||||
<button
|
||||
onClick={() => setShowBindingList(!showBindingList)}
|
||||
className="w-full px-5 py-4 flex items-center justify-between border-b border-[var(--app-separator)]"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="w-5 h-5 text-[var(--app-brand)]" />
|
||||
<h3 className="text-white font-semibold">绑定用户</h3>
|
||||
<span className="text-[var(--app-text-tertiary)] text-sm">
|
||||
({activeBindings.length + convertedBindings.length + expiredBindings.length})
|
||||
</span>
|
||||
</div>
|
||||
{showBindingList ? (
|
||||
<ChevronUp className="w-5 h-5 text-[var(--app-text-tertiary)]" />
|
||||
) : (
|
||||
<ChevronDown className="w-5 h-5 text-[var(--app-text-tertiary)]" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{showBindingList && (
|
||||
<>
|
||||
{/* Tab切换 */}
|
||||
<div className="flex border-b border-[var(--app-separator)]">
|
||||
{[
|
||||
{ key: 'active', label: '绑定中', count: activeBindings.length },
|
||||
{ key: 'converted', label: '已付款', count: convertedBindings.length },
|
||||
{ key: 'expired', label: '已过期', count: expiredBindings.length },
|
||||
].map((tab) => (
|
||||
<button
|
||||
key={tab.key}
|
||||
onClick={() => setActiveTab(tab.key as typeof activeTab)}
|
||||
className={`flex-1 py-3 text-sm font-medium transition-colors relative ${
|
||||
activeTab === tab.key
|
||||
? 'text-[var(--app-brand)]'
|
||||
: 'text-[var(--app-text-tertiary)]'
|
||||
}`}
|
||||
>
|
||||
{tab.label} ({tab.count})
|
||||
{activeTab === tab.key && (
|
||||
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-12 h-0.5 bg-[var(--app-brand)] rounded-full" />
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 用户列表 */}
|
||||
<div className="max-h-80 overflow-auto scrollbar-hide">
|
||||
{currentBindings.length === 0 ? (
|
||||
<div className="py-12 text-center">
|
||||
<UserPlus className="w-10 h-10 text-[var(--app-text-tertiary)] mx-auto mb-2" />
|
||||
<p className="text-[var(--app-text-tertiary)] text-sm">暂无用户</p>
|
||||
</div>
|
||||
) : (
|
||||
currentBindings.map((binding, idx) => (
|
||||
<div
|
||||
key={binding.id}
|
||||
className={`px-5 py-4 flex items-center justify-between ${
|
||||
idx !== currentBindings.length - 1 ? 'border-b border-[var(--app-separator)]' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${
|
||||
binding.status === 'converted'
|
||||
? 'bg-green-500/20'
|
||||
: binding.status === 'expired'
|
||||
? 'bg-gray-500/20'
|
||||
: 'bg-[var(--app-brand-light)]'
|
||||
}`}>
|
||||
{binding.status === 'converted' ? (
|
||||
<CheckCircle className="w-5 h-5 text-green-400" />
|
||||
) : binding.status === 'expired' ? (
|
||||
<Clock className="w-5 h-5 text-gray-400" />
|
||||
) : (
|
||||
<span className="text-[var(--app-brand)] font-bold">
|
||||
{(binding.visitorNickname || '用户')[0]}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-white text-sm font-medium">
|
||||
{binding.visitorNickname || binding.visitorPhone || '匿名用户'}
|
||||
</p>
|
||||
<p className="text-[var(--app-text-tertiary)] text-xs">
|
||||
绑定于 {new Date(binding.bindingTime).toLocaleDateString('zh-CN')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
{binding.status === 'converted' ? (
|
||||
<>
|
||||
<p className="text-green-400 font-semibold">+¥{binding.commission?.toFixed(2)}</p>
|
||||
<p className="text-[var(--app-text-tertiary)] text-xs">订单 ¥{binding.orderAmount}</p>
|
||||
</>
|
||||
) : (
|
||||
<span className={`text-xs px-2 py-1 rounded-full ${getBindingStatusStyle(binding.daysRemaining)}`}>
|
||||
{getBindingStatusText(binding)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 专属链接 - 简化显示 */}
|
||||
<div className="glass-card p-5 mb-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-white font-semibold">我的专属链接</h3>
|
||||
<span className="text-[var(--app-brand)] text-xs bg-[var(--app-brand-light)] px-2 py-1 rounded-full">
|
||||
<h3 className="text-white font-semibold">我的邀请码</h3>
|
||||
<span className="text-[var(--app-brand)] text-sm font-mono bg-[var(--app-brand-light)] px-3 py-1.5 rounded-lg">
|
||||
{user.referralCode}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 mb-4">
|
||||
<div className="flex-1 bg-[var(--app-bg-secondary)] rounded-xl px-4 py-3 text-[var(--app-text-secondary)] text-sm truncate font-mono">
|
||||
{referralLink}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="w-12 h-12 rounded-xl bg-[var(--app-brand-light)] flex items-center justify-center text-[var(--app-brand)] touch-feedback"
|
||||
>
|
||||
{copied ? <Check className="w-5 h-5" /> : <Copy className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p className="text-[var(--app-text-tertiary)] text-xs">
|
||||
好友通过此链接购买,你将获得 {distributorShare}% 返利
|
||||
好友通过你的链接购买<span className="text-[#FFD700]">立省5%</span>,你获得<span className="text-[var(--app-brand)]">{distributorShare}%</span>收益
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -257,7 +526,7 @@ export default function ReferralPage() {
|
||||
)}
|
||||
|
||||
{/* 空状态 */}
|
||||
{referralPurchases.length === 0 && (
|
||||
{referralPurchases.length === 0 && activeBindings.length === 0 && (
|
||||
<div className="glass-card p-8 text-center">
|
||||
<div className="w-16 h-16 rounded-full bg-[var(--app-bg-secondary)] flex items-center justify-center mx-auto mb-4">
|
||||
<Gift className="w-8 h-8 text-[var(--app-text-tertiary)]" />
|
||||
@@ -283,6 +552,18 @@ export default function ReferralPage() {
|
||||
onClose={() => setShowWithdrawal(false)}
|
||||
availableAmount={totalEarnings}
|
||||
/>
|
||||
|
||||
<AutoWithdrawModal
|
||||
isOpen={showAutoWithdraw}
|
||||
onClose={() => setShowAutoWithdraw(false)}
|
||||
enabled={autoWithdrawEnabled}
|
||||
threshold={autoWithdrawThreshold}
|
||||
onSave={(enabled, threshold, account) => {
|
||||
setAutoWithdrawEnabled(enabled)
|
||||
setAutoWithdrawThreshold(threshold)
|
||||
// 实际调用API保存
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
259
app/my/settings/page.tsx
Normal file
259
app/my/settings/page.tsx
Normal file
@@ -0,0 +1,259 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { ChevronLeft, Phone, MessageCircle, CreditCard, Check, X, Loader2, Shield } from "lucide-react"
|
||||
import { useStore } from "@/lib/store"
|
||||
|
||||
export default function SettingsPage() {
|
||||
const router = useRouter()
|
||||
const { user, updateUser, logout } = useStore()
|
||||
|
||||
// 绑定弹窗状态
|
||||
const [showBindModal, setShowBindModal] = useState(false)
|
||||
const [bindType, setBindType] = useState<"phone" | "wechat" | "alipay">("phone")
|
||||
const [bindValue, setBindValue] = useState("")
|
||||
const [isBinding, setIsBinding] = useState(false)
|
||||
const [bindError, setBindError] = useState("")
|
||||
|
||||
// 绑定账号
|
||||
const handleBind = async () => {
|
||||
if (!bindValue.trim()) {
|
||||
setBindError("请输入内容")
|
||||
return
|
||||
}
|
||||
|
||||
if (bindType === "phone" && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
setBindError("请输入正确的手机号")
|
||||
return
|
||||
}
|
||||
|
||||
if (bindType === "wechat" && bindValue.length < 6) {
|
||||
setBindError("微信号至少6位")
|
||||
return
|
||||
}
|
||||
|
||||
if (bindType === "alipay" && !bindValue.includes("@") && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
setBindError("请输入正确的支付宝账号")
|
||||
return
|
||||
}
|
||||
|
||||
setIsBinding(true)
|
||||
setBindError("")
|
||||
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
if (updateUser && user) {
|
||||
const updates: any = {}
|
||||
if (bindType === "phone") updates.phone = bindValue
|
||||
if (bindType === "wechat") updates.wechat = bindValue
|
||||
if (bindType === "alipay") updates.alipay = bindValue
|
||||
updateUser(user.id, updates)
|
||||
}
|
||||
|
||||
setShowBindModal(false)
|
||||
setBindValue("")
|
||||
alert("绑定成功!")
|
||||
} catch (error) {
|
||||
setBindError("绑定失败,请重试")
|
||||
} finally {
|
||||
setIsBinding(false)
|
||||
}
|
||||
}
|
||||
|
||||
const openBindModal = (type: "phone" | "wechat" | "alipay") => {
|
||||
setBindType(type)
|
||||
setBindValue("")
|
||||
setBindError("")
|
||||
setShowBindModal(true)
|
||||
}
|
||||
|
||||
// 检查是否有绑定任何支付方式
|
||||
const hasAnyPaymentBound = user?.wechat || user?.alipay
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-black text-white pb-24">
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-40 bg-black/90 backdrop-blur-xl border-b border-white/5">
|
||||
<div className="px-4 py-3 flex items-center">
|
||||
<button
|
||||
onClick={() => router.back()}
|
||||
className="w-8 h-8 rounded-full bg-white/10 flex items-center justify-center"
|
||||
>
|
||||
<ChevronLeft className="w-5 h-5 text-white" />
|
||||
</button>
|
||||
<h1 className="flex-1 text-center text-lg font-semibold text-white">设置</h1>
|
||||
<div className="w-8" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="px-4 py-4 space-y-4">
|
||||
{/* 账号绑定 */}
|
||||
<div className="rounded-2xl bg-[#1c1c1e] border border-white/5 overflow-hidden">
|
||||
<div className="px-4 py-3 border-b border-white/5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Shield className="w-4 h-4 text-[#00CED1]" />
|
||||
<span className="text-white font-medium">账号绑定</span>
|
||||
</div>
|
||||
<p className="text-white/40 text-xs mt-1">绑定后可用于提现和找伙伴功能</p>
|
||||
</div>
|
||||
|
||||
{/* 手机号 */}
|
||||
<button
|
||||
onClick={() => openBindModal("phone")}
|
||||
className="w-full flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||
user?.phone ? "bg-[#00CED1]/20" : "bg-white/10"
|
||||
}`}>
|
||||
<Phone className={`w-4 h-4 ${user?.phone ? "text-[#00CED1]" : "text-white/40"}`} />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="text-white text-sm">手机号</p>
|
||||
<p className="text-white/40 text-xs">
|
||||
{user?.phone || "未绑定"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{user?.phone ? (
|
||||
<Check className="w-5 h-5 text-[#00CED1]" />
|
||||
) : (
|
||||
<span className="text-[#00CED1] text-xs">去绑定</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* 微信号 */}
|
||||
<button
|
||||
onClick={() => openBindModal("wechat")}
|
||||
className="w-full flex items-center justify-between p-4 border-b border-white/5 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||
user?.wechat ? "bg-[#07C160]/20" : "bg-white/10"
|
||||
}`}>
|
||||
<MessageCircle className={`w-4 h-4 ${user?.wechat ? "text-[#07C160]" : "text-white/40"}`} />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="text-white text-sm">微信号</p>
|
||||
<p className="text-white/40 text-xs">
|
||||
{user?.wechat || "未绑定"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{user?.wechat ? (
|
||||
<Check className="w-5 h-5 text-[#07C160]" />
|
||||
) : (
|
||||
<span className="text-[#07C160] text-xs">去绑定</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* 支付宝 */}
|
||||
<button
|
||||
onClick={() => openBindModal("alipay")}
|
||||
className="w-full flex items-center justify-between p-4 active:bg-white/5"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||
user?.alipay ? "bg-[#1677FF]/20" : "bg-white/10"
|
||||
}`}>
|
||||
<CreditCard className={`w-4 h-4 ${user?.alipay ? "text-[#1677FF]" : "text-white/40"}`} />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="text-white text-sm">支付宝</p>
|
||||
<p className="text-white/40 text-xs">
|
||||
{user?.alipay || "未绑定"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{user?.alipay ? (
|
||||
<Check className="w-5 h-5 text-[#1677FF]" />
|
||||
) : (
|
||||
<span className="text-[#1677FF] text-xs">去绑定</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 绑定提示 */}
|
||||
{!hasAnyPaymentBound && (
|
||||
<div className="p-4 rounded-xl bg-orange-500/10 border border-orange-500/20">
|
||||
<p className="text-orange-400 text-xs">
|
||||
提示:绑定至少一个支付方式(微信或支付宝)才能使用提现功能
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 退出登录 */}
|
||||
<button
|
||||
onClick={() => {
|
||||
logout()
|
||||
router.push("/")
|
||||
}}
|
||||
className="w-full py-3 rounded-xl bg-[#1c1c1e] text-red-400 font-medium border border-red-400/30"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</main>
|
||||
|
||||
{/* 绑定弹窗 */}
|
||||
{showBindModal && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => !isBinding && setShowBindModal(false)} />
|
||||
<div className="relative w-full max-w-sm bg-[#1c1c1e] rounded-2xl overflow-hidden">
|
||||
<div className="flex items-center justify-between p-4 border-b border-white/10">
|
||||
<h3 className="text-lg font-semibold text-white">
|
||||
绑定{bindType === "phone" ? "手机号" : bindType === "wechat" ? "微信号" : "支付宝"}
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => !isBinding && setShowBindModal(false)}
|
||||
className="w-8 h-8 rounded-full bg-white/10 flex items-center justify-center"
|
||||
>
|
||||
<X className="w-4 h-4 text-white/60" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="p-5">
|
||||
<div className="mb-4">
|
||||
<label className="block text-white/40 text-xs mb-2">
|
||||
{bindType === "phone" ? "手机号" : bindType === "wechat" ? "微信号" : "支付宝账号"}
|
||||
</label>
|
||||
<input
|
||||
type={bindType === "phone" ? "tel" : "text"}
|
||||
value={bindValue}
|
||||
onChange={(e) => setBindValue(e.target.value)}
|
||||
placeholder={
|
||||
bindType === "phone" ? "请输入11位手机号" :
|
||||
bindType === "wechat" ? "请输入微信号" :
|
||||
"请输入支付宝账号"
|
||||
}
|
||||
className="w-full px-4 py-3 rounded-xl bg-black/30 border border-white/10 text-white placeholder-white/30 focus:outline-none focus:border-[#00CED1]/50"
|
||||
disabled={isBinding}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{bindError && (
|
||||
<p className="text-red-400 text-sm mb-4">{bindError}</p>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={handleBind}
|
||||
disabled={isBinding || !bindValue}
|
||||
className="w-full py-3 rounded-xl bg-[#00CED1] text-black font-medium flex items-center justify-center gap-2 disabled:opacity-50"
|
||||
>
|
||||
{isBinding ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
绑定中...
|
||||
</>
|
||||
) : (
|
||||
"确认绑定"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user