feat: Implement iOS-style UI, payment, referral, and reading experience improvements

This commit is contained in:
卡若
2026-01-09 12:24:15 +08:00
parent d781dc07ed
commit 326c9e6905
12 changed files with 1173 additions and 478 deletions

View File

@@ -2,8 +2,7 @@
import { useState, useEffect } from "react"
import Link from "next/link"
import { ChevronLeft, Copy, Share2, Users, Wallet, MessageCircle, ImageIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { ChevronLeft, Copy, Share2, Users, Wallet, MessageCircle, ImageIcon, TrendingUp, Gift, Check, ArrowRight } 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"
@@ -30,10 +29,10 @@ export default function ReferralPage() {
if (!isLoggedIn || !user) {
return (
<div className="min-h-screen bg-app-bg text-app-text flex items-center justify-center pb-20">
<div className="text-center">
<p className="text-app-text-muted mb-4"></p>
<Link href="/" className="text-app-brand hover:underline">
<div className="min-h-screen bg-black text-white flex items-center justify-center pb-20">
<div className="text-center glass-card p-8">
<p className="text-[var(--app-text-secondary)] mb-4"></p>
<Link href="/" className="btn-ios inline-block">
</Link>
</div>
@@ -83,143 +82,172 @@ export default function ReferralPage() {
alert("朋友圈文案已复制!\n\n打开微信 → 发朋友圈 → 粘贴即可")
}
const handleShareToSoul = async () => {
const shareText = `在Soul派对房听卡若讲了好多真实的创业故事他把这些故事整理成了一本书《一场SOUL的创业实验场》推荐给你们
每天早上6-9点直播这本书就是直播内容的精华版。
链接: ${referralLink}`
await navigator.clipboard.writeText(shareText)
alert("Soul分享文案已复制\n\n打开Soul → 发动态 → 粘贴即可")
}
return (
<div className="min-h-screen bg-app-bg text-app-text pb-24">
{/* Header */}
<header className="sticky top-0 z-50 bg-app-bg/90 backdrop-blur-md border-b border-app-border">
<div className="max-w-xs mx-auto px-4 py-3 flex items-center">
<Link href="/my" className="flex items-center gap-1 text-app-text-muted hover:text-app-text">
<ChevronLeft className="w-5 h-5" />
<div className="min-h-screen bg-black text-white pb-24 page-transition">
{/* 背景光效 */}
<div className="fixed inset-0 -z-10">
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[500px] h-[500px] bg-[var(--app-brand)] opacity-[0.05] blur-[150px] rounded-full" />
</div>
{/* Header - iOS风格 */}
<header className="sticky top-0 z-50 glass-nav safe-top">
<div className="max-w-md mx-auto px-4 py-3 flex items-center">
<Link href="/my" className="w-8 h-8 rounded-full bg-[var(--app-bg-secondary)] flex items-center justify-center touch-feedback">
<ChevronLeft className="w-5 h-5 text-[var(--app-text-secondary)]" />
</Link>
<h1 className="flex-1 text-center text-sm font-semibold"></h1>
<div className="w-5" />
<h1 className="flex-1 text-center font-semibold"></h1>
<div className="w-8" />
</div>
</header>
<main className="max-w-xs mx-auto px-4 py-4">
{/* Earnings Card */}
<div className="bg-gradient-to-br from-app-brand/20 to-app-card rounded-xl p-4 border border-app-brand/30 mb-3">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
<Wallet className="w-4 h-4 text-app-brand" />
<span className="text-app-text-muted text-xs"></span>
<main className="max-w-md mx-auto px-4 py-6">
{/* 收益卡片 - 毛玻璃渐变 */}
<div className="relative glass-card-heavy p-6 mb-6 overflow-hidden">
{/* 背景装饰 */}
<div className="absolute top-0 right-0 w-32 h-32 bg-[var(--app-brand)] opacity-[0.15] blur-[50px] rounded-full" />
<div className="relative">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<div className="w-10 h-10 rounded-xl bg-[var(--app-brand-light)] flex items-center justify-center">
<Wallet className="w-5 h-5 text-[var(--app-brand)]" />
</div>
<div>
<p className="text-[var(--app-text-tertiary)] text-xs"></p>
<p className="text-[var(--app-brand)] text-xs font-medium">{distributorShare}% </p>
</div>
</div>
<div className="text-right">
<p className="text-3xl font-bold text-white glow-text">¥{totalEarnings.toFixed(2)}</p>
<p className="text-[var(--app-text-tertiary)] text-xs">: ¥{pendingEarnings.toFixed(2)}</p>
</div>
</div>
<span className="text-app-brand text-xs">{distributorShare}%</span>
</div>
<p className="text-2xl font-bold text-app-text mb-0.5">¥{totalEarnings.toFixed(2)}</p>
<p className="text-app-text-muted text-xs mb-3">: ¥{pendingEarnings.toFixed(2)}</p>
<Button
disabled={totalEarnings < 10}
onClick={() => setShowWithdrawal(true)}
className="w-full bg-app-brand hover:bg-app-brand-hover text-white h-8 text-xs"
>
{totalEarnings < 10 ? `满10元可提现` : "申请提现"}
</Button>
</div>
{/* Stats */}
<div className="grid grid-cols-2 gap-2 mb-3">
<div className="bg-app-card/60 rounded-lg p-2.5 text-center">
<Users className="w-4 h-4 text-app-brand mx-auto mb-1" />
<p className="text-base font-bold text-app-text">{referralUsers}</p>
<p className="text-app-text-muted text-xs"></p>
</div>
<div className="bg-app-card/60 rounded-lg p-2.5 text-center">
<Share2 className="w-4 h-4 text-app-brand mx-auto mb-1" />
<p className="text-base font-bold text-app-text">{referralPurchases.length}</p>
<p className="text-app-text-muted text-xs"></p>
<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>
</div>
{/* Referral link */}
<div className="bg-app-card/60 rounded-xl p-3 border border-app-border mb-3">
<p className="text-app-text text-xs font-medium mb-2"></p>
<div className="flex gap-2 mb-2">
<div className="flex-1 bg-app-bg rounded-lg px-2.5 py-1.5 text-app-text-muted text-xs truncate font-mono">
{/* 数据统计 */}
<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>
<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>
</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">
{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
<button
onClick={handleCopy}
size="sm"
variant="outline"
className="border-app-border text-app-text hover:bg-app-card bg-transparent text-xs h-7 px-2"
className="w-12 h-12 rounded-xl bg-[var(--app-brand-light)] flex items-center justify-center text-[var(--app-brand)] touch-feedback"
>
<Copy className="w-3 h-3 mr-1" />
{copied ? "已复制" : "复制"}
</Button>
{copied ? <Check className="w-5 h-5" /> : <Copy className="w-5 h-5" />}
</button>
</div>
<p className="text-app-text-muted text-xs">
: <span className="text-app-brand font-mono">{user.referralCode}</span>
<p className="text-[var(--app-text-tertiary)] text-xs">
{distributorShare}%
</p>
</div>
{/* Share buttons - improved for WeChat/Soul */}
<div className="space-y-2 mb-3">
<Button
{/* 分享按钮 */}
<div className="space-y-3 mb-6">
<button
onClick={() => setShowPoster(true)}
className="w-full bg-indigo-600 hover:bg-indigo-700 text-white py-4 text-xs"
className="w-full glass-card p-4 flex items-center gap-4 touch-feedback"
>
<ImageIcon className="w-4 h-4 mr-2" />
广
</Button>
<Button
<div className="w-12 h-12 rounded-xl bg-[var(--ios-indigo)]/20 flex items-center justify-center">
<ImageIcon className="w-6 h-6 text-[var(--ios-indigo)]" />
</div>
<div className="flex-1 text-left">
<p className="text-white font-medium">广</p>
<p className="text-[var(--app-text-tertiary)] text-xs"></p>
</div>
<ArrowRight className="w-5 h-5 text-[var(--app-text-tertiary)]" />
</button>
<button
onClick={handleShareToWechat}
className="w-full bg-green-600 hover:bg-green-700 text-white py-4 text-xs"
className="w-full glass-card p-4 flex items-center gap-4 touch-feedback"
>
<MessageCircle className="w-4 h-4 mr-2" />
</Button>
<Button
<div className="w-12 h-12 rounded-xl bg-[#07C160]/20 flex items-center justify-center">
<MessageCircle className="w-6 h-6 text-[#07C160]" />
</div>
<div className="flex-1 text-left">
<p className="text-white font-medium"></p>
<p className="text-[var(--app-text-tertiary)] text-xs"></p>
</div>
<ArrowRight className="w-5 h-5 text-[var(--app-text-tertiary)]" />
</button>
<button
onClick={handleShare}
variant="outline"
className="w-full border-app-border text-app-text hover:bg-app-card bg-transparent py-4 text-xs"
className="w-full glass-card p-4 flex items-center gap-4 touch-feedback"
>
</Button>
<div className="w-12 h-12 rounded-xl bg-[var(--app-bg-secondary)] flex items-center justify-center">
<Share2 className="w-6 h-6 text-[var(--app-text-secondary)]" />
</div>
<div className="flex-1 text-left">
<p className="text-white font-medium"></p>
<p className="text-[var(--app-text-tertiary)] text-xs">使</p>
</div>
<ArrowRight className="w-5 h-5 text-[var(--app-text-tertiary)]" />
</button>
</div>
<PosterModal
isOpen={showPoster}
onClose={() => setShowPoster(false)}
referralLink={referralLink}
referralCode={user.referralCode}
nickname={user.nickname}
/>
<WithdrawalModal
isOpen={showWithdrawal}
onClose={() => setShowWithdrawal(false)}
availableAmount={totalEarnings}
/>
{/* Recent earnings */}
{/* 收益明细 */}
{referralPurchases.length > 0 && (
<div className="bg-app-card/60 rounded-xl border border-app-border">
<div className="p-2.5 border-b border-app-border">
<p className="text-app-text text-xs font-medium"></p>
<div className="glass-card overflow-hidden">
<div className="px-5 py-4 border-b border-[var(--app-separator)]">
<h3 className="text-white font-semibold"></h3>
</div>
<div className="divide-y divide-app-border max-h-40 overflow-auto">
{referralPurchases.slice(0, 5).map((purchase) => (
<div key={purchase.id} className="p-2.5 flex items-center justify-between">
<div>
<p className="text-app-text text-xs">{purchase.type === "fullbook" ? "整本书" : "单节"}</p>
<p className="text-app-text-muted text-xs">
{new Date(purchase.createdAt).toLocaleDateString("zh-CN")}
</p>
<div className="max-h-60 overflow-auto scrollbar-hide">
{referralPurchases.slice(0, 10).map((purchase, idx) => (
<div
key={purchase.id}
className={`px-5 py-4 flex items-center justify-between ${idx !== referralPurchases.length - 1 ? 'border-b border-[var(--app-separator)]' : ''}`}
>
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-[var(--app-brand-light)] flex items-center justify-center">
<Gift className="w-5 h-5 text-[var(--app-brand)]" />
</div>
<div>
<p className="text-white text-sm font-medium">
{purchase.type === "fullbook" ? "整本书购买" : "单节购买"}
</p>
<p className="text-[var(--app-text-tertiary)] text-xs">
{new Date(purchase.createdAt).toLocaleDateString("zh-CN")}
</p>
</div>
</div>
<p className="text-app-brand text-sm font-semibold">
<p className="text-[var(--app-brand)] font-semibold">
+¥{(purchase.referrerEarnings || 0).toFixed(2)}
</p>
</div>
@@ -227,7 +255,34 @@ export default function ReferralPage() {
</div>
</div>
)}
{/* 空状态 */}
{referralPurchases.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)]" />
</div>
<p className="text-white font-medium mb-2"></p>
<p className="text-[var(--app-text-tertiary)] text-sm">
{distributorShare}%
</p>
</div>
)}
</main>
<PosterModal
isOpen={showPoster}
onClose={() => setShowPoster(false)}
referralLink={referralLink}
referralCode={user.referralCode}
nickname={user.nickname}
/>
<WithdrawalModal
isOpen={showWithdrawal}
onClose={() => setShowWithdrawal(false)}
availableAmount={totalEarnings}
/>
</div>
)
}