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

@@ -1,7 +1,7 @@
"use client"
import { useState, useEffect } from "react"
import { Button } from "@/components/ui/button"
import { BookOpen } from "lucide-react"
import { BookOpen, Sparkles } from "lucide-react"
import Link from "next/link"
import { getFullBookPrice, getAllSections } from "@/lib/book-data"
import { useStore } from "@/lib/store"
@@ -22,82 +22,95 @@ export function BookCover() {
}, [])
return (
<section className="relative min-h-[85vh] flex flex-col items-center justify-center px-4 py-8 overflow-hidden bg-app-bg">
{/* Background decorative lines - simplified */}
<div className="absolute inset-0 overflow-hidden opacity-50">
<svg className="absolute w-full h-full" viewBox="0 0 800 600" fill="none">
<path
d="M-100 300 Q 200 100, 400 200 T 900 150"
stroke="rgba(56, 189, 172, 0.2)"
strokeWidth="1"
strokeDasharray="8 8"
fill="none"
/>
</svg>
<section className="relative min-h-[90vh] flex flex-col items-center justify-center px-4 py-12 overflow-hidden safe-top">
{/* 动态光效背景 */}
<div className="absolute inset-0 overflow-hidden">
{/* 主光晕 */}
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[500px] h-[500px] bg-[var(--app-brand)] opacity-[0.08] blur-[120px] rounded-full animate-pulse" />
{/* 次光晕 */}
<div className="absolute bottom-1/4 left-1/4 w-[300px] h-[300px] bg-[var(--ios-blue)] opacity-[0.05] blur-[100px] rounded-full" />
<div className="absolute top-1/3 right-1/4 w-[200px] h-[200px] bg-[var(--ios-purple)] opacity-[0.04] blur-[80px] rounded-full" />
</div>
{/* Content - more compact for mobile */}
<div className="relative z-10 w-full max-w-sm mx-auto text-center">
{/* Soul badge */}
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-app-card/80 backdrop-blur-md border border-app-brand/30 mb-6">
<span className="text-app-brand text-sm font-medium">Soul · </span>
{/* 装饰性网格线 */}
<div className="absolute inset-0 opacity-[0.02]" style={{
backgroundImage: `linear-gradient(var(--app-text) 1px, transparent 1px), linear-gradient(90deg, var(--app-text) 1px, transparent 1px)`,
backgroundSize: '60px 60px'
}} />
{/* 内容区域 */}
<div className="relative z-10 w-full max-w-md mx-auto text-center">
{/* Soul 徽章 - 毛玻璃效果 */}
<div className="inline-flex items-center gap-2 px-4 py-2 glass-card mb-8">
<Sparkles className="w-4 h-4 text-[var(--app-brand)]" />
<span className="text-[var(--app-brand)] text-sm font-medium tracking-wide">Soul · </span>
</div>
{/* Main title - smaller on mobile */}
<h1 className="text-3xl font-bold mb-3 leading-tight text-app-text">
{/* 主标题 - iOS风格大字体 */}
<h1 className="text-4xl sm:text-5xl font-bold mb-4 leading-[1.15] text-white tracking-tight">
SOUL的
<br />
<span className="bg-gradient-to-r from-[var(--app-brand)] to-[var(--ios-teal)] bg-clip-text text-transparent">
</span>
</h1>
{/* Subtitle */}
<p className="text-app-text-muted text-sm mb-4">Soul派对房的真实商业故事</p>
{/* 副标题 */}
<p className="text-[var(--app-text-secondary)] text-base mb-4">
Soul派对房的真实商业故事
</p>
{/* Quote - smaller */}
<p className="text-app-text-muted/80 italic text-sm mb-6">"社会不是靠努力,是靠洞察与选择"</p>
{/* 引用语 */}
<p className="text-[var(--app-text-tertiary)] italic text-sm mb-8 px-4">
"社会不是靠努力,是靠洞察与选择"
</p>
{/* Price info - compact card */}
<div className="bg-app-card/60 backdrop-blur-md rounded-xl p-4 mb-6 border border-app-border">
<div className="flex items-center justify-center gap-6 text-sm">
{/* 价格信息卡片 - 毛玻璃效果 */}
<div className="glass-card-light p-5 mb-8">
<div className="flex items-center justify-center gap-8">
<div className="text-center">
<p className="text-xl font-bold text-app-brand">¥{fullBookPrice.toFixed(1)}</p>
<p className="text-app-text-muted text-xs"></p>
<p className="text-3xl font-bold text-[var(--app-brand)] glow-text">
¥{fullBookPrice.toFixed(1)}
</p>
<p className="text-[var(--app-text-tertiary)] text-xs mt-1"></p>
</div>
<div className="w-px h-8 bg-app-border" />
<div className="w-px h-12 bg-[var(--app-separator)]" />
<div className="text-center">
<p className="text-xl font-bold text-app-text">{sectionsCount}</p>
<p className="text-app-text-muted text-xs"></p>
<p className="text-3xl font-bold text-white">{sectionsCount}</p>
<p className="text-[var(--app-text-tertiary)] text-xs mt-1"></p>
</div>
</div>
</div>
{/* Author info - compact */}
<div className="flex justify-between items-center px-2 mb-6 text-sm">
{/* 作者信息 */}
<div className="flex justify-between items-center px-4 mb-8">
<div className="text-left">
<p className="text-app-text-muted text-xs"></p>
<p className="text-app-brand font-medium"></p>
<p className="text-[var(--app-text-tertiary)] text-xs mb-0.5"></p>
<p className="text-[var(--app-brand)] font-semibold"></p>
</div>
<div className="text-right">
<p className="text-app-text-muted text-xs"></p>
<p className="text-app-text font-medium">06:00-09:00</p>
<p className="text-[var(--app-text-tertiary)] text-xs mb-0.5"></p>
<p className="text-white font-semibold">06:00-09:00</p>
</div>
</div>
{/* CTA Button */}
<Link href="/chapters" className="block">
<Button
size="lg"
className="w-full bg-app-brand hover:bg-app-brand-hover text-white py-5 text-base rounded-xl flex items-center justify-center gap-2"
>
{/* CTA按钮 - iOS风格 */}
<Link href="/chapters" className="block touch-feedback">
<button className="btn-ios w-full flex items-center justify-center gap-2 glow">
<BookOpen className="w-5 h-5" />
</Button>
<span></span>
</button>
</Link>
<p className="text-app-text-muted text-xs mt-4"> · 3</p>
<p className="text-[var(--app-text-tertiary)] text-xs mt-5">
· 3
</p>
</div>
{/* Modals */}
{/* 底部渐变遮罩 */}
<div className="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-black to-transparent" />
{/* 弹窗 */}
<AuthModal isOpen={isAuthOpen} onClose={() => setIsAuthOpen(false)} />
<PaymentModal
isOpen={isPaymentOpen}

View File

@@ -1,30 +1,51 @@
export function BookIntro() {
return (
<section className="py-16 px-4">
<section className="py-12 px-4">
<div className="max-w-2xl mx-auto">
{/* Glass card */}
<div className="bg-[#0f2137]/80 backdrop-blur-xl rounded-2xl p-8 border border-[#38bdac]/20">
{/* Quote */}
<blockquote className="text-lg md:text-xl text-gray-200 leading-relaxed mb-6">
"这不是一本教你成功的鸡汤书。这是我每天早上6点到9点在Soul派对房和几百个陌生人分享的真实故事。"
{/* 毛玻璃卡片 */}
<div className="glass-card-heavy p-6 sm:p-8">
{/* 引用图标 */}
<div className="w-10 h-10 rounded-xl bg-[var(--app-brand-light)] flex items-center justify-center mb-6">
<svg className="w-5 h-5 text-[var(--app-brand)]" fill="currentColor" viewBox="0 0 24 24">
<path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"/>
</svg>
</div>
{/* 引用内容 */}
<blockquote className="text-lg sm:text-xl text-white leading-relaxed mb-6 font-light">
<span className="text-[var(--app-text-secondary)]">
69Soul派对房和几百个陌生人分享的真实故事
</span>
</blockquote>
{/* Author */}
<p className="text-[#38bdac] text-lg"> </p>
{/* 作者签名 */}
<div className="flex items-center gap-3 mb-8">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-[var(--app-brand)] to-[var(--ios-teal)] flex items-center justify-center text-white font-semibold text-sm">
</div>
<div>
<p className="text-[var(--app-brand)] font-semibold"></p>
<p className="text-[var(--app-text-tertiary)] text-xs">Soul派对房主理人</p>
</div>
</div>
{/* Stats */}
<div className="mt-8 pt-6 border-t border-gray-700/50 grid grid-cols-3 gap-4 text-center">
<div>
<p className="text-2xl md:text-3xl font-bold text-white">55+</p>
<p className="text-gray-400 text-sm"></p>
{/* 分隔线 */}
<div className="separator-ios mx-0 mb-6" />
{/* 统计数据 */}
<div className="grid grid-cols-3 gap-4 text-center">
<div className="p-3">
<p className="text-2xl sm:text-3xl font-bold text-white mb-1">55+</p>
<p className="text-[var(--app-text-tertiary)] text-xs"></p>
</div>
<div>
<p className="text-2xl md:text-3xl font-bold text-white">11</p>
<p className="text-gray-400 text-sm"></p>
<div className="p-3 border-x border-[var(--app-separator)]">
<p className="text-2xl sm:text-3xl font-bold text-white mb-1">11</p>
<p className="text-[var(--app-text-tertiary)] text-xs"></p>
</div>
<div>
<p className="text-2xl md:text-3xl font-bold text-white">100+</p>
<p className="text-gray-400 text-sm"></p>
<div className="p-3">
<p className="text-2xl sm:text-3xl font-bold text-white mb-1">100+</p>
<p className="text-[var(--app-text-tertiary)] text-xs"></p>
</div>
</div>
</div>

View File

@@ -2,7 +2,7 @@
import Link from "next/link"
import { usePathname } from "next/navigation"
import { Home, MessageCircle, User } from "lucide-react"
import { Home, MessageCircle, User, BookOpen } from "lucide-react"
import { useState } from "react"
import { QRCodeModal } from "./modules/marketing/qr-code-modal"
@@ -10,20 +10,23 @@ export function BottomNav() {
const pathname = usePathname()
const [showQRModal, setShowQRModal] = useState(false)
if (pathname.startsWith("/documentation")) {
// 在文档页面和管理后台不显示底部导航
if (pathname.startsWith("/documentation") || pathname.startsWith("/admin")) {
return null
}
const navItems = [
{ href: "/", icon: Home, label: "首页" },
{ href: "/chapters", icon: BookOpen, label: "目录" },
{ action: () => setShowQRModal(true), icon: MessageCircle, label: "派对群" },
{ href: "/my", icon: User, label: "我的" },
]
return (
<>
<nav className="fixed bottom-0 left-0 right-0 z-40 bg-[#0f2137]/95 backdrop-blur-md border-t border-gray-700/50">
<div className="flex items-center justify-around py-3 max-w-lg mx-auto">
{/* iOS风格底部导航 */}
<nav className="fixed bottom-0 left-0 right-0 z-40 glass-nav safe-bottom">
<div className="flex items-center justify-around py-2 max-w-lg mx-auto">
{navItems.map((item, index) => {
const isActive = item.href ? pathname === item.href : false
const Icon = item.icon
@@ -33,10 +36,18 @@ export function BottomNav() {
<button
key={index}
onClick={item.action}
className="flex flex-col items-center py-2 px-6 text-gray-400 hover:text-[#38bdac] transition-colors"
className="flex flex-col items-center py-2 px-4 sm:px-6 touch-feedback transition-all duration-200"
>
<Icon className="w-6 h-6 mb-1" />
<span className="text-xs">{item.label}</span>
<div className={`w-7 h-7 flex items-center justify-center mb-1 transition-colors ${
isActive ? "text-[var(--app-brand)]" : "text-[var(--app-text-tertiary)]"
}`}>
<Icon className="w-6 h-6" strokeWidth={isActive ? 2.5 : 1.5} />
</div>
<span className={`text-[10px] font-medium transition-colors ${
isActive ? "text-[var(--app-brand)]" : "text-[var(--app-text-tertiary)]"
}`}>
{item.label}
</span>
</button>
)
}
@@ -45,12 +56,22 @@ export function BottomNav() {
<Link
key={index}
href={item.href!}
className={`flex flex-col items-center py-2 px-6 transition-colors ${
isActive ? "text-[#38bdac]" : "text-gray-400 hover:text-white"
}`}
className="flex flex-col items-center py-2 px-4 sm:px-6 touch-feedback transition-all duration-200"
>
<Icon className="w-6 h-6 mb-1" />
<span className="text-xs">{item.label}</span>
<div className={`w-7 h-7 flex items-center justify-center mb-1 transition-colors ${
isActive ? "text-[var(--app-brand)]" : "text-[var(--app-text-tertiary)]"
}`}>
<Icon className="w-6 h-6" strokeWidth={isActive ? 2.5 : 1.5} />
</div>
<span className={`text-[10px] font-medium transition-colors ${
isActive ? "text-[var(--app-brand)]" : "text-[var(--app-text-tertiary)]"
}`}>
{item.label}
</span>
{/* 激活指示器 */}
{isActive && (
<div className="absolute -bottom-0.5 w-1 h-1 rounded-full bg-[var(--app-brand)]" />
)}
</Link>
)
})}

View File

@@ -2,8 +2,7 @@
import { useState, useEffect } from "react"
import Link from "next/link"
import { ChevronLeft, Lock, Share2, BookOpen, Clock, MessageCircle } from "lucide-react"
import { Button } from "@/components/ui/button"
import { ChevronLeft, Lock, Share2, BookOpen, Clock, MessageCircle, ChevronRight, Sparkles } from "lucide-react"
import { type Section, getFullBookPrice, isSectionUnlocked } from "@/lib/book-data"
import { useStore } from "@/lib/store"
import { AuthModal } from "./modules/auth/auth-modal"
@@ -26,6 +25,7 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
const [isQRModalOpen, setIsQRModalOpen] = useState(false)
const [paymentType, setPaymentType] = useState<"section" | "fullbook">("section")
const [fullBookPrice, setFullBookPrice] = useState(9.9)
const [readingProgress, setReadingProgress] = useState(0)
const { user, isLoggedIn, hasPurchased, settings } = useStore()
const distributorShare = settings?.distributorShare || 90
@@ -37,6 +37,19 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
setFullBookPrice(getFullBookPrice())
}, [])
// 阅读进度追踪
useEffect(() => {
const handleScroll = () => {
const scrollTop = window.scrollY
const docHeight = document.documentElement.scrollHeight - window.innerHeight
const progress = Math.min((scrollTop / docHeight) * 100, 100)
setReadingProgress(progress)
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [])
useEffect(() => {
async function loadContent() {
try {
@@ -110,17 +123,29 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
const previewContent = content.slice(0, 500)
return (
<div className="min-h-screen bg-[#0a1628] text-white pb-20">
{/* Header */}
<header className="sticky top-0 z-50 bg-[#0a1628]/90 backdrop-blur-md border-b border-gray-800">
<div className="max-w-3xl mx-auto px-4 py-4 flex items-center justify-between">
<Link href="/chapters" className="flex items-center gap-2 text-gray-400 hover:text-white transition-colors">
<ChevronLeft className="w-5 h-5" />
<span className="hidden sm:inline"></span>
<div className="min-h-screen bg-black text-white pb-20 page-transition">
{/* 阅读进度条 */}
<div className="fixed top-0 left-0 right-0 z-[60] h-0.5 bg-[var(--app-bg-secondary)]">
<div
className="h-full bg-[var(--app-brand)] transition-all duration-150"
style={{ width: `${readingProgress}%` }}
/>
</div>
{/* Header - iOS风格毛玻璃 */}
<header className="sticky top-0 z-50 glass-nav safe-top">
<div className="max-w-3xl mx-auto px-4 py-3 flex items-center justify-between">
<Link
href="/chapters"
className="w-9 h-9 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>
<div className="text-center flex-1 px-4">
<p className="text-gray-500 text-xs">{partTitle}</p>
{chapterTitle && <p className="text-gray-400 text-sm truncate">{chapterTitle}</p>}
<p className="text-[var(--app-text-tertiary)] text-xs">{partTitle}</p>
{chapterTitle && (
<p className="text-[var(--app-text-secondary)] text-sm truncate">{chapterTitle}</p>
)}
</div>
<div className="flex items-center gap-2">
<ReferralShare
@@ -133,147 +158,169 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont
</div>
</header>
{/* Content */}
<main className="max-w-3xl mx-auto px-4 py-8">
{/* Title */}
<div className="mb-8">
<div className="flex items-center gap-2 text-[#38bdac] text-sm mb-2">
<BookOpen className="w-4 h-4" />
<span>{section.id}</span>
{/* 阅读内容 */}
<main className="max-w-2xl mx-auto px-5 sm:px-6 py-8">
{/* 标题区域 */}
<div className="mb-10">
<div className="flex items-center gap-2 mb-4">
<span className="text-[var(--app-brand)] text-sm font-medium bg-[var(--app-brand-light)] px-3 py-1 rounded-full">
{section.id}
</span>
{section.unlockAfterDays && !section.isFree && (
<span className="ml-2 px-2 py-0.5 bg-orange-500/20 text-orange-400 text-xs rounded-full flex items-center gap-1">
<span className="px-3 py-1 bg-[var(--ios-orange)]/20 text-[var(--ios-orange)] text-xs rounded-full flex items-center gap-1">
<Clock className="w-3 h-3" />
{isUnlocked ? "已免费解锁" : `${section.unlockAfterDays}天后免费`}
</span>
)}
</div>
<h1 className="text-2xl md:text-3xl font-bold text-white leading-tight">{section.title}</h1>
<h1 className="text-2xl sm:text-3xl font-bold text-white leading-tight tracking-tight">
{section.title}
</h1>
</div>
{isLoading ? (
<div className="flex items-center justify-center py-20">
<div className="w-8 h-8 border-2 border-[#38bdac] border-t-transparent rounded-full animate-spin" />
// 骨架屏加载
<div className="space-y-4">
{[...Array(8)].map((_, i) => (
<div key={i} className="skeleton h-4 rounded" style={{ width: `${Math.random() * 40 + 60}%` }} />
))}
</div>
) : canAccess ? (
<>
<article className="prose prose-invert prose-lg max-w-none">
<div className="text-gray-300 leading-relaxed whitespace-pre-line">
{/* 正文内容 - 书籍阅读风格 */}
<article className="book-content">
<div className="text-[var(--app-text-secondary)] leading-[1.9] text-[17px]">
{content.split("\n").map((paragraph, index) => (
<p key={index} className="mb-4">
{paragraph}
</p>
paragraph.trim() && (
<p key={index} className="mb-6 text-justify">
{paragraph}
</p>
)
))}
</div>
</article>
{/* Join Party Group CTA */}
<div className="mt-12 bg-gradient-to-r from-[#38bdac]/10 to-[#1a3a4a]/50 rounded-2xl p-6 border border-[#38bdac]/30">
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-[#38bdac]/20 flex items-center justify-center flex-shrink-0">
<MessageCircle className="w-6 h-6 text-[#38bdac]" />
{/* 进群引导 CTA */}
<div className="mt-16 glass-card-heavy p-6 overflow-hidden relative">
<div className="absolute top-0 right-0 w-24 h-24 bg-[var(--app-brand)] opacity-[0.1] blur-[40px] rounded-full" />
<div className="relative flex items-center gap-4">
<div className="w-14 h-14 rounded-2xl bg-[var(--app-brand-light)] flex items-center justify-center flex-shrink-0">
<MessageCircle className="w-7 h-7 text-[var(--app-brand)]" />
</div>
<div className="flex-1">
<div className="flex-1 min-w-0">
<h3 className="text-white font-semibold mb-1">?</h3>
<p className="text-gray-400 text-sm">6-9,Soul派对房分享真实案例</p>
<p className="text-[var(--app-text-tertiary)] text-sm">6-9,Soul派对房分享真实案例</p>
</div>
<Button
<button
onClick={() => setIsQRModalOpen(true)}
className="bg-[#38bdac] hover:bg-[#2da396] text-white whitespace-nowrap"
className="btn-ios whitespace-nowrap flex-shrink-0"
>
</Button>
</button>
</div>
</div>
</>
) : (
<div>
<article className="prose prose-invert prose-lg max-w-none relative">
<div className="text-gray-300 leading-relaxed whitespace-pre-line">
{/* 预览内容 */}
<article className="book-content relative">
<div className="text-[var(--app-text-secondary)] leading-[1.9] text-[17px]">
{previewContent.split("\n").map((paragraph, index) => (
<p key={index} className="mb-4">
{paragraph}
</p>
paragraph.trim() && (
<p key={index} className="mb-6 text-justify">
{paragraph}
</p>
)
))}
</div>
<div className="absolute bottom-0 left-0 right-0 h-40 bg-gradient-to-t from-[#0a1628] to-transparent" />
<div className="absolute bottom-0 left-0 right-0 h-48 bg-gradient-to-t from-black to-transparent" />
</article>
{/* Purchase prompt */}
<div className="mt-8 bg-[#0f2137]/80 backdrop-blur-xl rounded-2xl p-8 border border-gray-700/50 text-center">
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-gray-800/50 flex items-center justify-center">
<Lock className="w-8 h-8 text-gray-500" />
</div>
<h3 className="text-xl font-semibold text-white mb-2"></h3>
<p className="text-gray-400 mb-6">
{isLoggedIn ? "购买本节或整本书以阅读完整内容" : "登录后购买即可阅读完整内容"}
</p>
{section.unlockAfterDays && (
<p className="text-orange-400 text-sm mb-4 flex items-center justify-center gap-1">
<Clock className="w-4 h-4" />
{section.unlockAfterDays}
{/* 购买提示 - 毛玻璃风格 */}
<div className="mt-8 glass-card-heavy p-8 text-center overflow-hidden relative">
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-40 h-40 bg-[var(--app-brand)] opacity-[0.08] blur-[60px] rounded-full" />
<div className="relative">
<div className="w-20 h-20 mx-auto mb-6 rounded-2xl bg-[var(--app-bg-secondary)] flex items-center justify-center">
<Lock className="w-10 h-10 text-[var(--app-text-tertiary)]" />
</div>
<h3 className="text-2xl font-semibold text-white mb-2"></h3>
<p className="text-[var(--app-text-secondary)] mb-8">
{isLoggedIn ? "购买本节或整本书以阅读完整内容" : "登录后购买即可阅读完整内容"}
</p>
)}
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button
onClick={() => handlePurchaseClick("section")}
variant="outline"
className="border-gray-600 text-white hover:bg-gray-700/50 px-6 py-5"
>
¥{section.price}
</Button>
<Button
onClick={() => handlePurchaseClick("fullbook")}
className="bg-[#38bdac] hover:bg-[#2da396] text-white px-6 py-5"
>
¥{fullBookPrice.toFixed(1)}
<span className="ml-2 text-xs opacity-80">82%</span>
</Button>
{section.unlockAfterDays && (
<div className="inline-flex items-center gap-2 px-4 py-2 mb-6 rounded-full bg-[var(--ios-orange)]/10 text-[var(--ios-orange)]">
<Clock className="w-4 h-4" />
<span className="text-sm">{section.unlockAfterDays}</span>
</div>
)}
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-6">
<button
onClick={() => handlePurchaseClick("section")}
className="btn-ios-secondary px-8 py-4"
>
¥{section.price}
</button>
<button
onClick={() => handlePurchaseClick("fullbook")}
className="btn-ios px-8 py-4 glow flex items-center justify-center gap-2"
>
<Sparkles className="w-4 h-4" />
¥{fullBookPrice.toFixed(1)}
<span className="text-xs opacity-80 ml-1">82%</span>
</button>
</div>
<p className="text-[var(--app-text-tertiary)] text-sm">
, <span className="text-[var(--app-brand)]">{distributorShare}%</span>
</p>
</div>
<p className="text-gray-500 text-sm mt-4">
, <span className="text-[#38bdac]">{distributorShare}%</span>
</p>
</div>
{/* Join Party Group */}
<div className="mt-8 bg-gradient-to-r from-[#38bdac]/10 to-[#1a3a4a]/50 rounded-2xl p-6 border border-[#38bdac]/30">
{/* 进群引导 */}
<div className="mt-8 glass-card p-6">
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-[#38bdac]/20 flex items-center justify-center flex-shrink-0">
<MessageCircle className="w-6 h-6 text-[#38bdac]" />
<div className="w-14 h-14 rounded-2xl bg-[var(--app-brand-light)] flex items-center justify-center flex-shrink-0">
<MessageCircle className="w-7 h-7 text-[var(--app-brand)]" />
</div>
<div className="flex-1">
<div className="flex-1 min-w-0">
<h3 className="text-white font-semibold mb-1">?!</h3>
<p className="text-gray-400 text-sm">6-9,Soul派对房免费分享</p>
<p className="text-[var(--app-text-tertiary)] text-sm">6-9,Soul派对房免费分享</p>
</div>
<Button
<button
onClick={() => setIsQRModalOpen(true)}
className="bg-[#38bdac] hover:bg-[#2da396] text-white whitespace-nowrap"
className="btn-ios whitespace-nowrap flex-shrink-0"
>
</Button>
</button>
</div>
</div>
</div>
)}
{/* Navigation */}
<div className="mt-12 pt-8 border-t border-gray-800 flex justify-between">
<Link href="/chapters" className="text-gray-400 hover:text-white transition-colors">
{/* 底部导航 */}
<div className="mt-16 pt-8 border-t border-[var(--app-separator)] flex justify-between items-center">
<Link
href="/chapters"
className="flex items-center gap-2 text-[var(--app-text-secondary)] hover:text-white transition-colors touch-feedback"
>
<ChevronLeft className="w-4 h-4" />
<span></span>
</Link>
<button
onClick={handleShare}
className="text-[#38bdac] hover:text-[#4fd4c4] transition-colors flex items-center gap-2"
className="flex items-center gap-2 text-[var(--app-brand)] touch-feedback"
>
<Share2 className="w-4 h-4" />
¥{((section.price * distributorShare) / 100).toFixed(1)}
<span> ¥{((section.price * distributorShare) / 100).toFixed(1)}</span>
<ChevronRight className="w-4 h-4" />
</button>
</div>
</main>
{/* Modals */}
{/* 弹窗 */}
<AuthModal isOpen={isAuthOpen} onClose={() => setIsAuthOpen(false)} />
<PaymentModal
isOpen={isPaymentOpen}

View File

@@ -2,10 +2,39 @@
export function Footer() {
return (
<footer className="py-6 px-4 border-t border-gray-800 pb-24">
<div className="max-w-xs mx-auto text-center">
<p className="text-white text-sm font-medium mb-1">SOUL的创业实验场</p>
<p className="text-gray-500 text-xs">© 2025 · 06:00-09:00</p>
<footer className="py-8 px-4 pb-28">
<div className="max-w-md mx-auto">
{/* 分隔线 */}
<div className="separator-ios mx-0 mb-6" />
<div className="text-center">
{/* Logo/品牌 */}
<div className="inline-flex items-center gap-2 mb-3">
<div className="w-6 h-6 rounded-lg bg-[var(--app-brand-light)] flex items-center justify-center">
<span className="text-[var(--app-brand)] text-xs font-bold">S</span>
</div>
<span className="text-white font-semibold">SOUL的创业实验场</span>
</div>
{/* 信息 */}
<p className="text-[var(--app-text-tertiary)] text-xs mb-4">
06:00-09:00 · Soul派对房
</p>
{/* 链接 */}
<div className="flex items-center justify-center gap-6 text-xs text-[var(--app-text-tertiary)] mb-4">
<a href="/about" className="hover:text-white transition-colors"></a>
<span className="w-1 h-1 rounded-full bg-[var(--app-separator)]" />
<a href="#" className="hover:text-white transition-colors"></a>
<span className="w-1 h-1 rounded-full bg-[var(--app-separator)]" />
<a href="#" className="hover:text-white transition-colors"></a>
</div>
{/* 版权 */}
<p className="text-[var(--app-text-tertiary)] text-xs">
© {new Date().getFullYear()} · All rights reserved
</p>
</div>
</div>
</footer>
)

View File

@@ -2,8 +2,7 @@
import type React from "react"
import { useState, useEffect } from "react"
import { X, CheckCircle, Bitcoin, Globe, Copy, Check, QrCode } from "lucide-react"
import { Button } from "@/components/ui/button"
import { X, CheckCircle, Bitcoin, Globe, Copy, Check, QrCode, Shield, Users } from "lucide-react"
import { useStore } from "@/lib/store"
const WechatIcon = () => (
@@ -65,10 +64,8 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
}
const handlePayment = async () => {
// 直接显示支付二维码/详情页面
setShowQRCode(true)
// 如果有跳转链接,尝试打开
if (paymentMethod === "wechat" && paymentConfig.wechat?.qrCode) {
const link = paymentConfig.wechat.qrCode
if (link.startsWith("http") || link.startsWith("weixin://")) {
@@ -98,7 +95,6 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
if (success) {
setIsSuccess(true)
// 支付成功后跳转微信群
const groupUrl = paymentConfig.wechat?.groupQrCode
if (groupUrl) {
setTimeout(() => {
@@ -122,6 +118,7 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
name: string
icon: React.ReactNode
color: string
iconBg: string
enabled: boolean
extra?: string
}[] = [
@@ -129,21 +126,24 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
id: "wechat",
name: "微信支付",
icon: <WechatIcon />,
color: "bg-[#07C160]",
color: "#07C160",
iconBg: "rgba(7, 193, 96, 0.15)",
enabled: paymentConfig.wechat?.enabled ?? true,
},
{
id: "alipay",
name: "支付宝",
icon: <AlipayIcon />,
color: "bg-[#1677FF]",
color: "#1677FF",
iconBg: "rgba(22, 119, 255, 0.15)",
enabled: paymentConfig.alipay?.enabled ?? true,
},
{
id: "usdt",
name: `USDT (${paymentConfig.usdt?.network || "TRC20"})`,
icon: <Bitcoin className="w-5 h-5" />,
color: "bg-[#26A17B]",
color: "#26A17B",
iconBg: "rgba(38, 161, 123, 0.15)",
enabled: paymentConfig.usdt?.enabled ?? true,
extra: `$${usdtAmount}`,
},
@@ -151,7 +151,8 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
id: "paypal",
name: "PayPal",
icon: <Globe className="w-5 h-5" />,
color: "bg-[#003087]",
color: "#003087",
iconBg: "rgba(0, 48, 135, 0.15)",
enabled: paymentConfig.paypal?.enabled ?? false,
extra: `$${paypalAmount}`,
},
@@ -159,7 +160,7 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
const availableMethods = paymentMethods.filter((m) => m.enabled)
// 二维码/详情页面
// 二维码/详情页面 - iOS毛玻璃风格
if (showQRCode) {
const isCrypto = paymentMethod === "usdt"
const isPaypal = paymentMethod === "paypal"
@@ -169,7 +170,7 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
let address = ""
let displayAmount = `¥${amount.toFixed(2)}`
let title = "扫码支付"
let hint = "支付完成后,请点击下方已完成支付按钮"
let hint = "支付完成后,请点击下方按钮确认"
let qrCodeUrl = ""
if (isCrypto) {
@@ -193,87 +194,110 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm" onClick={onClose} />
<div className="relative w-full max-w-md bg-[#0f2137] rounded-2xl border border-gray-700/50 shadow-2xl overflow-hidden">
<button onClick={onClose} className="absolute top-4 right-4 p-2 text-gray-400 hover:text-white z-10">
<X className="w-5 h-5" />
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center">
<div className="absolute inset-0 bg-black/60 backdrop-blur-md modal-overlay" onClick={onClose} />
<div className="relative w-full sm:max-w-md glass-modal overflow-hidden safe-bottom modal-content sm:m-4">
{/* 顶部把手 - 仅移动端 */}
<div className="flex justify-center pt-3 pb-2 sm:hidden">
<div className="w-9 h-1 rounded-full bg-[var(--app-text-tertiary)]" />
</div>
<button
onClick={onClose}
className="absolute top-4 right-4 w-8 h-8 rounded-full bg-[var(--app-bg-secondary)] flex items-center justify-center text-[var(--app-text-secondary)] hover:text-white z-10 touch-feedback"
>
<X className="w-4 h-4" />
</button>
<div className="p-6 pt-12">
<div className="p-6 pt-4 sm:pt-10">
{/* 标题 */}
<h3 className="text-xl font-semibold text-white text-center mb-4">{title}</h3>
<h3 className="text-xl font-semibold text-white text-center mb-2">{title}</h3>
{/* 金额显示 */}
<div className="text-center mb-6">
<p className="text-3xl font-bold text-[#38bdac]">{displayAmount}</p>
<p className="text-4xl font-bold text-[var(--app-brand)] glow-text">{displayAmount}</p>
</div>
{/* QR Code Display */}
{(isWechat || isAlipay) && (
<div className="flex flex-col items-center mb-6">
<div className="w-48 h-48 bg-white rounded-xl p-3 mb-4 flex items-center justify-center">
<div className="w-52 h-52 bg-white rounded-2xl p-4 mb-4 flex items-center justify-center shadow-lg">
{qrCodeUrl ? (
<img
src={qrCodeUrl || "/placeholder.svg"}
alt="支付二维码"
className="w-full h-full object-contain"
className="w-full h-full object-contain rounded-lg"
/>
) : (
<div className="flex flex-col items-center text-gray-400">
<QrCode className="w-16 h-16 mb-2" />
<span className="text-sm"></span>
<span className="text-sm text-center"></span>
</div>
)}
</div>
<p className="text-gray-400 text-sm">{hint}</p>
<p className="text-[var(--app-text-tertiary)] text-sm">{hint}</p>
</div>
)}
{/* Crypto/PayPal Address */}
{(isCrypto || isPaypal) && (
<div className="mb-6">
<div className="bg-[#0a1628] rounded-xl p-4 border border-gray-700/30">
<p className="text-gray-400 text-sm mb-2">
<div className="glass-card p-4">
<p className="text-[var(--app-text-tertiary)] text-xs mb-2">
{isCrypto ? `收款地址 (${paymentConfig.usdt?.network})` : "PayPal账户"}
</p>
<div className="flex items-center gap-2">
<p className="text-white text-sm break-all flex-1 font-mono">{address || "请在后台配置收款地址"}</p>
<p className="text-white text-sm break-all flex-1 font-mono bg-[var(--app-bg-secondary)] p-3 rounded-lg">
{address || "请在后台配置收款地址"}
</p>
{address && (
<button
onClick={() => handleCopyAddress(address)}
className="text-[#38bdac] hover:text-[#4fd4c4]"
className="w-10 h-10 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>
</div>
<p className="text-gray-500 text-sm mt-2 text-center">{hint}</p>
<p className="text-[var(--app-text-tertiary)] text-sm mt-3 text-center">{hint}</p>
</div>
)}
{/* 提示信息 */}
<div className="bg-[#38bdac]/10 border border-[#38bdac]/30 rounded-xl p-3 mb-4">
<p className="text-[#38bdac] text-sm text-center"></p>
<div className="glass-card p-4 mb-6 border-[var(--app-brand)]/20">
<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">
<Shield className="w-5 h-5 text-[var(--app-brand)]" />
</div>
<p className="text-[var(--app-text-secondary)] text-sm flex-1">
</p>
</div>
</div>
{/* Action Buttons */}
<div className="flex gap-3">
<Button
<button
onClick={() => setShowQRCode(false)}
variant="outline"
className="flex-1 border-gray-600 text-white hover:bg-gray-700/50 bg-transparent"
className="btn-ios-secondary flex-1"
>
</Button>
<Button
</button>
<button
onClick={confirmPayment}
disabled={isProcessing}
className="flex-1 bg-[#38bdac] hover:bg-[#2da396] text-white"
className="btn-ios flex-1 glow disabled:opacity-50"
>
{isProcessing ? "处理中..." : "已完成支付"}
</Button>
{isProcessing ? (
<div className="flex items-center justify-center gap-2">
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
...
</div>
) : (
"已完成支付"
)}
</button>
</div>
</div>
</div>
@@ -285,98 +309,133 @@ export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, a
if (isSuccess) {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm" />
<div className="relative w-full max-w-md bg-[#0f2137] rounded-2xl border border-gray-700/50 overflow-hidden">
<div className="absolute inset-0 bg-black/60 backdrop-blur-md modal-overlay" />
<div className="relative w-full max-w-sm glass-modal overflow-hidden modal-content">
<div className="p-8 text-center">
<div className="w-20 h-20 mx-auto mb-4 rounded-full bg-[#38bdac]/20 flex items-center justify-center">
<CheckCircle className="w-10 h-10 text-[#38bdac]" />
{/* 成功动画 */}
<div className="w-24 h-24 mx-auto mb-6 rounded-full bg-[var(--app-brand-light)] flex items-center justify-center">
<CheckCircle className="w-12 h-12 text-[var(--app-brand)]" />
</div>
<h3 className="text-xl font-semibold text-white mb-2"></h3>
<p className="text-gray-400">{type === "fullbook" ? "您已解锁全部内容" : "您已解锁本节内容"}</p>
{paymentConfig.wechat?.groupQrCode && <p className="text-[#07C160] text-sm mt-4">...</p>}
<h3 className="text-2xl font-semibold text-white mb-2"></h3>
<p className="text-[var(--app-text-secondary)] mb-4">
{type === "fullbook" ? "您已解锁全部内容" : "您已解锁本节内容"}
</p>
{paymentConfig.wechat?.groupQrCode && (
<div className="glass-card p-4 mt-4">
<div className="flex items-center justify-center gap-2 text-[#07C160]">
<Users className="w-5 h-5" />
<span className="text-sm">...</span>
</div>
</div>
)}
</div>
</div>
</div>
)
}
// 主支付选择页面
// 主支付选择页面 - iOS风格
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm" onClick={onClose} />
<div className="relative w-full max-w-md bg-[#0f2137] rounded-2xl border border-gray-700/50 shadow-2xl overflow-hidden max-h-[90vh] overflow-y-auto">
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center">
<div className="absolute inset-0 bg-black/60 backdrop-blur-md modal-overlay" onClick={onClose} />
<div className="relative w-full sm:max-w-md glass-modal overflow-hidden safe-bottom modal-content sm:m-4 max-h-[90vh] overflow-y-auto scrollbar-hide">
{/* 顶部把手 - 仅移动端 */}
<div className="flex justify-center pt-3 pb-2 sm:hidden sticky top-0 bg-transparent">
<div className="w-9 h-1 rounded-full bg-[var(--app-text-tertiary)]" />
</div>
<button
onClick={onClose}
className="absolute top-4 right-4 p-2 text-gray-400 hover:text-white transition-colors z-10"
className="absolute top-4 right-4 w-8 h-8 rounded-full bg-[var(--app-bg-secondary)] flex items-center justify-center text-[var(--app-text-secondary)] hover:text-white z-10 touch-feedback"
>
<X className="w-5 h-5" />
<X className="w-4 h-4" />
</button>
{/* Header */}
<div className="p-6 pt-12 border-b border-gray-700/50">
<h3 className="text-lg font-semibold text-white mb-1"></h3>
<p className="text-gray-400 text-sm">
<div className="px-6 pt-2 sm:pt-10 pb-4">
<h3 className="text-xl font-semibold text-white mb-1"></h3>
<p className="text-[var(--app-text-tertiary)] text-sm">
{type === "fullbook" ? "购买整本书,解锁全部内容" : `购买: ${sectionTitle}`}
</p>
</div>
{/* Amount */}
<div className="p-6 border-b border-gray-700/50 text-center">
<p className="text-gray-400 text-sm mb-1"></p>
<p className="text-4xl font-bold text-white">¥{amount.toFixed(2)}</p>
<div className="px-6 py-6 text-center border-y border-[var(--app-separator)]">
<p className="text-[var(--app-text-tertiary)] text-sm mb-2"></p>
<p className="text-5xl font-bold text-white">¥{amount.toFixed(2)}</p>
{(paymentMethod === "usdt" || paymentMethod === "paypal") && (
<p className="text-[#38bdac] text-sm mt-1"> ${paymentMethod === "usdt" ? usdtAmount : paypalAmount} USD</p>
<p className="text-[var(--app-brand)] text-sm mt-2">
${paymentMethod === "usdt" ? usdtAmount : paypalAmount} USD
</p>
)}
{user?.referredBy && (
<p className="text-[#38bdac] text-sm mt-2">
{settings?.distributorShare || 90}%
</p>
<div className="inline-flex items-center gap-2 mt-4 px-4 py-2 rounded-full bg-[var(--app-brand-light)]">
<Users className="w-4 h-4 text-[var(--app-brand)]" />
<span className="text-[var(--app-brand)] text-sm">
{settings?.distributorShare || 90}%
</span>
</div>
)}
</div>
{/* Payment Methods */}
<div className="p-6 space-y-3">
<p className="text-gray-400 text-sm mb-3"></p>
{availableMethods.map((method) => (
<button
key={method.id}
onClick={() => setPaymentMethod(method.id)}
className={`w-full p-4 rounded-xl border flex items-center gap-4 transition-all ${
paymentMethod === method.id
? "border-[#38bdac] bg-[#38bdac]/10"
: "border-gray-700 hover:border-gray-600 hover:bg-[#162840]"
}`}
>
<div className={`w-10 h-10 rounded-lg ${method.color} flex items-center justify-center text-white`}>
{method.icon}
</div>
<div className="flex-1 text-left">
<span className="text-white">{method.name}</span>
{method.extra && <span className="text-gray-400 text-sm ml-2">{method.extra}</span>}
</div>
{paymentMethod === method.id && <CheckCircle className="w-5 h-5 text-[#38bdac]" />}
</button>
))}
{availableMethods.length === 0 && <p className="text-gray-500 text-center py-4"></p>}
<div className="p-6">
<p className="text-[var(--app-text-tertiary)] text-sm mb-4"></p>
<div className="space-y-3">
{availableMethods.map((method) => (
<button
key={method.id}
onClick={() => setPaymentMethod(method.id)}
className={`w-full p-4 rounded-xl flex items-center gap-4 transition-all touch-feedback ${
paymentMethod === method.id
? "glass-card-light border-[var(--app-brand)]/30"
: "glass-card hover:border-[var(--glass-border-light)]"
}`}
>
<div
className="w-12 h-12 rounded-xl flex items-center justify-center"
style={{ backgroundColor: method.iconBg, color: method.color }}
>
{method.icon}
</div>
<div className="flex-1 text-left">
<span className="text-white font-medium">{method.name}</span>
{method.extra && (
<span className="text-[var(--app-text-tertiary)] text-sm ml-2">{method.extra}</span>
)}
</div>
{paymentMethod === method.id && (
<div className="w-6 h-6 rounded-full bg-[var(--app-brand)] flex items-center justify-center">
<Check className="w-4 h-4 text-white" />
</div>
)}
</button>
))}
{availableMethods.length === 0 && (
<p className="text-[var(--app-text-tertiary)] text-center py-8"></p>
)}
</div>
</div>
{/* Submit Button */}
<div className="p-6 pt-0">
<Button
<button
onClick={handlePayment}
disabled={isProcessing || availableMethods.length === 0}
className="w-full bg-[#38bdac] hover:bg-[#2da396] text-white py-6 text-lg"
className="btn-ios w-full glow text-lg disabled:opacity-50"
>
{isProcessing ? (
<div className="flex items-center gap-2">
<div className="flex items-center justify-center gap-2">
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
...
</div>
) : (
`确认支付 ¥${amount.toFixed(2)}`
)}
</Button>
<p className="text-gray-500 text-xs text-center mt-3"></p>
</button>
<p className="text-[var(--app-text-tertiary)] text-xs text-center mt-4">
</p>
</div>
</div>
</div>

View File

@@ -1,8 +1,7 @@
"use client"
import { useState, useEffect } from "react"
import { Button } from "@/components/ui/button"
import { Zap, BookOpen } from "lucide-react"
import { Zap, BookOpen, Check, TrendingUp } from "lucide-react"
import { getFullBookPrice, getAllSections } from "@/lib/book-data"
import { useStore } from "@/lib/store"
import { AuthModal } from "./modules/auth/auth-modal"
@@ -30,57 +29,111 @@ export function PurchaseSection() {
}
return (
<section className="py-8 px-4 bg-app-bg">
<div className="max-w-sm mx-auto">
{/* Pricing cards - stacked on mobile */}
<div className="space-y-3">
{/* Single section */}
<div className="bg-app-card/60 backdrop-blur-xl rounded-xl p-4 border border-app-border">
<section className="py-12 px-4">
<div className="max-w-md mx-auto">
{/* 区域标题 */}
<div className="text-center mb-8">
<h2 className="text-xl font-semibold text-white mb-2"></h2>
<p className="text-[var(--app-text-tertiary)] text-sm"></p>
</div>
{/* 定价卡片 */}
<div className="space-y-4">
{/* 单节购买 */}
<div className="glass-card p-5 touch-feedback cursor-pointer">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<BookOpen className="w-5 h-5 text-app-text-muted" />
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-xl bg-[var(--app-bg-secondary)] flex items-center justify-center">
<BookOpen className="w-6 h-6 text-[var(--app-text-secondary)]" />
</div>
<div>
<h3 className="text-app-text font-medium text-sm"></h3>
<p className="text-app-text-muted text-xs"></p>
<h3 className="text-white font-semibold mb-0.5"></h3>
<p className="text-[var(--app-text-tertiary)] text-sm"></p>
</div>
</div>
<div className="text-right">
<span className="text-xl font-bold text-app-text">¥1</span>
<span className="text-app-text-muted text-xs">/</span>
<p className="text-2xl font-bold text-white">¥1</p>
<p className="text-[var(--app-text-tertiary)] text-xs">/</p>
</div>
</div>
</div>
{/* Full book - highlighted */}
<div className="bg-gradient-to-br from-app-brand/20 to-app-card backdrop-blur-xl rounded-xl p-4 border border-app-brand/30 relative">
<span className="absolute -top-2 right-3 bg-app-brand text-white text-xs px-2 py-0.5 rounded-full">
</span>
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3">
<Zap className="w-5 h-5 text-app-brand" />
<div>
<h3 className="text-app-text font-medium text-sm"></h3>
<p className="text-app-text-muted text-xs">{sectionsCount} · </p>
{/* 整本购买 - 推荐 */}
<div className="relative">
{/* 推荐标签 */}
<div className="absolute -top-3 left-1/2 -translate-x-1/2 z-10">
<span className="inline-flex items-center gap-1 px-3 py-1 rounded-full bg-[var(--app-brand)] text-white text-xs font-medium shadow-lg">
<Zap className="w-3 h-3" />
</span>
</div>
<div
onClick={handlePurchase}
className="glass-card-heavy p-5 cursor-pointer touch-feedback border-[var(--app-brand)]/30 relative overflow-hidden"
>
{/* 背景光效 */}
<div className="absolute top-0 right-0 w-32 h-32 bg-[var(--app-brand)] opacity-[0.08] blur-[60px] rounded-full" />
<div className="relative">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-xl bg-[var(--app-brand-light)] flex items-center justify-center">
<Zap className="w-6 h-6 text-[var(--app-brand)]" />
</div>
<div>
<h3 className="text-white font-semibold mb-0.5"></h3>
<p className="text-[var(--app-text-tertiary)] text-sm">
{sectionsCount}
</p>
</div>
</div>
<div className="text-right">
<p className="text-3xl font-bold text-[var(--app-brand)]">¥{fullBookPrice.toFixed(1)}</p>
<p className="text-[var(--app-text-tertiary)] text-xs line-through">¥{sectionsCount}</p>
</div>
</div>
</div>
<div className="text-right">
<span className="text-xl font-bold text-app-text">¥{fullBookPrice.toFixed(1)}</span>
{/* 权益列表 */}
<div className="space-y-2 mb-5">
{[
"解锁全部 " + sectionsCount + " 节商业案例",
"后续更新内容免费阅读",
"专属读者社群入群资格",
"分销返佣最高90%"
].map((benefit, idx) => (
<div key={idx} className="flex items-center gap-2">
<div className="w-4 h-4 rounded-full bg-[var(--app-brand-light)] flex items-center justify-center">
<Check className="w-2.5 h-2.5 text-[var(--app-brand)]" />
</div>
<span className="text-[var(--app-text-secondary)] text-sm">{benefit}</span>
</div>
))}
</div>
{/* 购买按钮 */}
<button className="btn-ios w-full glow flex items-center justify-center gap-2">
<span></span>
</button>
</div>
</div>
<Button
onClick={handlePurchase}
className="w-full bg-app-brand hover:bg-app-brand-hover text-white rounded-lg h-10 text-sm"
>
</Button>
</div>
</div>
{/* Dynamic pricing note */}
<p className="mt-3 text-center text-app-text-muted text-xs">动态定价: 每新增一章节,+¥1</p>
{/* 动态定价说明 */}
<div className="mt-6 glass-card p-4">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-[var(--ios-orange)]/20 flex items-center justify-center">
<TrendingUp className="w-5 h-5 text-[var(--ios-orange)]" />
</div>
<div className="flex-1">
<p className="text-white text-sm font-medium mb-0.5"></p>
<p className="text-[var(--app-text-tertiary)] text-xs">
+¥1
</p>
</div>
</div>
</div>
</div>
<AuthModal isOpen={isAuthOpen} onClose={() => setIsAuthOpen(false)} />

View File

@@ -1,7 +1,7 @@
"use client"
import Link from "next/link"
import { ChevronRight } from "lucide-react"
import { ChevronRight, BookOpen, FileText } from "lucide-react"
import { Part } from "@/lib/book-data"
interface TableOfContentsProps {
@@ -10,54 +10,79 @@ interface TableOfContentsProps {
export function TableOfContents({ parts }: TableOfContentsProps) {
return (
<section className="py-16 px-4">
<section className="py-12 px-4">
<div className="max-w-2xl mx-auto">
{/* Section title */}
<h2 className="text-gray-400 text-sm mb-8"> {parts.length} </h2>
{/* 区域标题 */}
<div className="flex items-center justify-between mb-6">
<h2 className="text-[var(--app-text-secondary)] text-sm font-medium">
</h2>
<span className="text-[var(--app-text-tertiary)] text-xs">
{parts.length}
</span>
</div>
{/* Parts list */}
<div className="space-y-6">
{parts.map((part) => (
<Link key={part.id} href={`/chapters?part=${part.id}`} className="block group">
<div className="bg-[#0f2137]/60 backdrop-blur-md rounded-xl p-6 border border-transparent hover:border-[#38bdac]/30 transition-all duration-300">
<div className="flex items-start justify-between">
<div className="flex gap-4">
<span className="text-[#38bdac] font-mono text-lg">{part.number}</span>
<div>
<h3 className="text-white text-xl font-semibold mb-1 group-hover:text-[#38bdac] transition-colors">
{part.title}
</h3>
<p className="text-gray-400">{part.subtitle}</p>
<p className="text-gray-500 text-sm mt-2">
{part.chapters.length} · {part.chapters.reduce((acc, c) => acc + c.sections.length, 0)}
</p>
</div>
{/* 章节列表 - iOS列表风格 */}
<div className="glass-card overflow-hidden mb-8">
{parts.map((part, index) => (
<Link
key={part.id}
href={`/chapters?part=${part.id}`}
className="block touch-feedback"
>
<div className={`list-item-ios ${index === 0 ? 'rounded-t-xl' : ''} ${index === parts.length - 1 ? 'rounded-b-xl border-b-0' : ''}`}>
<div className="flex items-center gap-4 flex-1 min-w-0">
{/* 序号标识 */}
<div className="w-10 h-10 rounded-xl bg-[var(--app-brand-light)] flex items-center justify-center flex-shrink-0">
<span className="text-[var(--app-brand)] font-bold text-sm">{part.number}</span>
</div>
{/* 内容 */}
<div className="flex-1 min-w-0">
<h3 className="text-white font-semibold text-base truncate mb-0.5">
{part.title}
</h3>
<p className="text-[var(--app-text-tertiary)] text-sm truncate">
{part.subtitle}
</p>
<p className="text-[var(--app-text-tertiary)] text-xs mt-1">
{part.chapters.length} · {part.chapters.reduce((acc, c) => acc + c.sections.length, 0)}
</p>
</div>
<ChevronRight className="w-5 h-5 text-gray-500 group-hover:text-[#38bdac] transition-colors" />
</div>
{/* 箭头 */}
<ChevronRight className="w-5 h-5 text-[var(--app-text-tertiary)] flex-shrink-0" />
</div>
</Link>
))}
</div>
{/* Additional content */}
<div className="mt-8 pt-8 border-t border-gray-700/50">
<div className="grid grid-cols-2 gap-4">
<Link href="/chapters?section=preface" className="block group">
<div className="bg-[#0f2137]/40 backdrop-blur-md rounded-xl p-4 border border-transparent hover:border-[#38bdac]/30 transition-all">
<p className="text-gray-400 text-sm"></p>
<p className="text-white group-hover:text-[#38bdac] transition-colors">
6Soul开播?
</p>
{/* 附加内容 - 序言和尾声 */}
<div className="grid grid-cols-2 gap-3">
<Link href="/chapters?section=preface" className="block touch-feedback">
<div className="glass-card p-4 h-full">
<div className="flex items-center gap-2 mb-2">
<BookOpen className="w-4 h-4 text-[var(--ios-blue)]" />
<span className="text-[var(--app-text-tertiary)] text-xs"></span>
</div>
</Link>
<Link href="/chapters?section=epilogue" className="block group">
<div className="bg-[#0f2137]/40 backdrop-blur-md rounded-xl p-4 border border-transparent hover:border-[#38bdac]/30 transition-all">
<p className="text-gray-400 text-sm"></p>
<p className="text-white group-hover:text-[#38bdac] transition-colors">,</p>
<p className="text-white text-sm leading-relaxed text-ellipsis-2">
6Soul开播?
</p>
</div>
</Link>
<Link href="/chapters?section=epilogue" className="block touch-feedback">
<div className="glass-card p-4 h-full">
<div className="flex items-center gap-2 mb-2">
<FileText className="w-4 h-4 text-[var(--ios-purple)]" />
<span className="text-[var(--app-text-tertiary)] text-xs"></span>
</div>
</Link>
</div>
<p className="text-white text-sm leading-relaxed text-ellipsis-2">
</p>
</div>
</Link>
</div>
</div>
</section>