主要更新: 1. 按H5网页端完全重构匹配功能(match页面) - 4种匹配类型: 创业合伙/资源对接/导师顾问/团队招募 - 资源对接等类型弹出手机号/微信号输入框 - 去掉重新匹配按钮,改为返回按钮 2. 修复所有卡片对齐和宽度问题 - 目录页附录卡片居中 - 首页阅读进度卡片满宽度 - 我的页面菜单卡片对齐 - 推广中心分享卡片统一宽度 3. 修复目录页图标和文字对齐 - section-icon固定40rpx宽高 - section-title与图标垂直居中 4. 更新真实完整文章标题(62篇) - 从book目录读取真实markdown文件名 - 替换之前的简化标题 5. 新增文章数据API - /api/db/chapters - 获取完整书籍结构 - 支持按ID获取单篇文章内容
228 lines
11 KiB
TypeScript
228 lines
11 KiB
TypeScript
"use client"
|
||
|
||
import { useState } from "react"
|
||
import { useRouter } from "next/navigation"
|
||
import { ChevronRight, Lock, Unlock, Book, BookOpen, Home, List, Sparkles, User, Users, Zap, Crown } from "lucide-react"
|
||
import { useStore } from "@/lib/store"
|
||
import { bookData, getTotalSectionCount, specialSections, getPremiumBookPrice, getExtraSectionsCount, BASE_SECTIONS_COUNT } from "@/lib/book-data"
|
||
|
||
export default function ChaptersPage() {
|
||
const router = useRouter()
|
||
const { user, hasPurchased } = useStore()
|
||
const [expandedPart, setExpandedPart] = useState<string | null>("part-1")
|
||
const [bookVersion, setBookVersion] = useState<"basic" | "premium">("basic")
|
||
const [showPremiumTab, setShowPremiumTab] = useState(false) // 控制是否显示最新完整版标签
|
||
|
||
const totalSections = getTotalSectionCount()
|
||
const hasFullBook = user?.hasFullBook || false
|
||
const premiumPrice = getPremiumBookPrice()
|
||
const extraSections = getExtraSectionsCount()
|
||
|
||
const handleSectionClick = (sectionId: string) => {
|
||
router.push(`/read/${sectionId}`)
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-black text-white pb-24">
|
||
<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 justify-center">
|
||
<h1 className="text-lg font-semibold text-[#00CED1]">目录</h1>
|
||
</div>
|
||
</header>
|
||
|
||
<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">
|
||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-[#00CED1] to-[#20B2AA] flex items-center justify-center">
|
||
<Book className="w-6 h-6 text-white" />
|
||
</div>
|
||
<div className="flex-1">
|
||
<h2 className="text-white font-semibold">一场SOUL的创业实验场</h2>
|
||
<p className="text-gray-500 text-xs mt-0.5">来自Soul派对房的真实商业故事</p>
|
||
</div>
|
||
<div className="text-right">
|
||
<div className="text-xl font-bold text-[#00CED1]">{totalSections}</div>
|
||
<div className="text-[10px] text-gray-500">章节</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 版本标签切换 - 仅在showPremiumTab为true时显示 */}
|
||
{showPremiumTab && extraSections > 0 && (
|
||
<div className="mx-4 mt-3 flex gap-2">
|
||
<button
|
||
onClick={() => setBookVersion("basic")}
|
||
className={`flex-1 py-2.5 rounded-xl text-sm font-medium transition-all ${
|
||
bookVersion === "basic"
|
||
? "bg-[#00CED1]/20 text-[#00CED1] border border-[#00CED1]/30"
|
||
: "bg-[#1c1c1e] text-white/60 border border-white/5"
|
||
}`}
|
||
>
|
||
基础版 ¥9.9
|
||
</button>
|
||
<button
|
||
onClick={() => setBookVersion("premium")}
|
||
className={`flex-1 py-2.5 rounded-xl text-sm font-medium transition-all flex items-center justify-center gap-1 ${
|
||
bookVersion === "premium"
|
||
? "bg-[#FFD700]/20 text-[#FFD700] border border-[#FFD700]/30"
|
||
: "bg-[#1c1c1e] text-white/60 border border-white/5"
|
||
}`}
|
||
>
|
||
<Crown className="w-4 h-4" />
|
||
最新版 ¥{premiumPrice.toFixed(1)}
|
||
</button>
|
||
</div>
|
||
)}
|
||
|
||
{/* 目录内容 */}
|
||
<main className="px-4 py-4">
|
||
<button
|
||
onClick={() => handleSectionClick("preface")}
|
||
className="w-full mb-3 p-3 rounded-xl bg-[#1c1c1e] flex items-center justify-between active:bg-[#2c2c2e] transition-colors border border-white/5"
|
||
>
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-8 h-8 rounded-lg bg-[#00CED1]/20 flex items-center justify-center">
|
||
<BookOpen className="w-4 h-4 text-[#00CED1]" />
|
||
</div>
|
||
<span className="text-sm font-medium text-white">序言|为什么我每天早上6点在Soul开播?</span>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-xs text-[#00CED1]">免费</span>
|
||
<ChevronRight className="w-4 h-4 text-gray-500" />
|
||
</div>
|
||
</button>
|
||
|
||
{bookData.map((part) => (
|
||
<div key={part.id} className="mb-3">
|
||
<button
|
||
onClick={() => setExpandedPart(expandedPart === part.id ? null : part.id)}
|
||
className="w-full p-3 rounded-xl bg-[#1c1c1e] flex items-center justify-between active:bg-[#2c2c2e] transition-colors border border-white/5"
|
||
>
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#00CED1] to-[#20B2AA] flex items-center justify-center text-sm font-bold text-white">
|
||
{part.number}
|
||
</div>
|
||
<div className="text-left">
|
||
<div className="text-sm font-semibold text-white">{part.title}</div>
|
||
<div className="text-[10px] text-gray-500">{part.subtitle}</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-xs text-gray-500">
|
||
{part.chapters.reduce((acc, ch) => acc + ch.sections.length, 0)}章
|
||
</span>
|
||
<ChevronRight
|
||
className={`w-4 h-4 text-gray-500 transition-transform ${expandedPart === part.id ? "rotate-90" : ""}`}
|
||
/>
|
||
</div>
|
||
</button>
|
||
|
||
{expandedPart === part.id && (
|
||
<div className="mt-2 ml-2 space-y-1">
|
||
{part.chapters.map((chapter) => (
|
||
<div key={chapter.id} className="rounded-lg bg-[#1c1c1e]/50 overflow-hidden border border-white/5">
|
||
<div className="px-3 py-2 text-xs font-medium text-gray-400 border-b border-white/5">
|
||
{chapter.title}
|
||
</div>
|
||
{chapter.sections.map((section) => {
|
||
const isPurchased = hasFullBook || hasPurchased(section.id)
|
||
const canRead = section.isFree || isPurchased
|
||
|
||
return (
|
||
<button
|
||
key={section.id}
|
||
onClick={() => handleSectionClick(section.id)}
|
||
className="w-full px-3 py-2.5 flex items-center justify-between border-b border-white/5 last:border-0 active:bg-white/5 transition-colors"
|
||
>
|
||
<div className="flex items-center gap-2 flex-1 min-w-0">
|
||
{canRead ? (
|
||
<Unlock className="w-3.5 h-3.5 text-[#00CED1] flex-shrink-0" />
|
||
) : (
|
||
<Lock className="w-3.5 h-3.5 text-gray-500 flex-shrink-0" />
|
||
)}
|
||
<span className={`text-xs truncate ${canRead ? "text-white" : "text-gray-400"}`}>
|
||
{section.id} {section.title}
|
||
</span>
|
||
</div>
|
||
<div className="flex items-center gap-2 flex-shrink-0 ml-2">
|
||
{section.isFree ? (
|
||
<span className="text-[10px] text-[#00CED1] px-1.5 py-0.5 rounded bg-[#00CED1]/10">
|
||
免费
|
||
</span>
|
||
) : isPurchased ? (
|
||
<span className="text-[10px] text-[#00CED1]">已购</span>
|
||
) : (
|
||
<span className="text-[10px] text-gray-500">¥{section.price}</span>
|
||
)}
|
||
<ChevronRight className="w-3.5 h-3.5 text-gray-600" />
|
||
</div>
|
||
</button>
|
||
)
|
||
})}
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
))}
|
||
|
||
<button
|
||
onClick={() => handleSectionClick("epilogue")}
|
||
className="w-full mb-3 p-3 rounded-xl bg-[#1c1c1e] flex items-center justify-between active:bg-[#2c2c2e] transition-colors border border-white/5"
|
||
>
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-8 h-8 rounded-lg bg-[#00CED1]/20 flex items-center justify-center">
|
||
<BookOpen className="w-4 h-4 text-[#00CED1]" />
|
||
</div>
|
||
<span className="text-sm font-medium text-white">尾声|这本书的真实目的</span>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-xs text-[#00CED1]">免费</span>
|
||
<ChevronRight className="w-4 h-4 text-gray-500" />
|
||
</div>
|
||
</button>
|
||
|
||
{/* 附录 */}
|
||
<div className="mb-3 p-3 rounded-xl bg-[#1c1c1e] border border-white/5">
|
||
<div className="text-xs font-medium text-gray-400 mb-2">附录</div>
|
||
{specialSections.appendix.map((item) => (
|
||
<button
|
||
key={item.id}
|
||
onClick={() => handleSectionClick(item.id)}
|
||
className="w-full py-2 flex items-center justify-between border-b border-white/5 last:border-0 active:bg-white/5"
|
||
>
|
||
<span className="text-xs text-gray-300">{item.title}</span>
|
||
<ChevronRight className="w-3.5 h-3.5 text-gray-600" />
|
||
</button>
|
||
))}
|
||
</div>
|
||
</main>
|
||
|
||
<nav className="fixed bottom-0 left-0 right-0 bg-[#1c1c1e]/95 backdrop-blur-xl border-t border-white/5 pb-safe-bottom">
|
||
<div className="px-4 py-2">
|
||
<div className="flex items-center justify-around">
|
||
<button onClick={() => router.push("/")} className="flex flex-col items-center py-2 px-4">
|
||
<Home className="w-5 h-5 text-gray-500 mb-1" />
|
||
<span className="text-gray-500 text-xs">首页</span>
|
||
</button>
|
||
<button className="flex flex-col items-center py-2 px-4">
|
||
<List className="w-5 h-5 text-[#00CED1] mb-1" />
|
||
<span className="text-[#00CED1] text-xs font-medium">目录</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">
|
||
<Users className="w-7 h-7 text-white" />
|
||
</div>
|
||
<span className="text-gray-500 text-xs mt-1">找伙伴</span>
|
||
</button>
|
||
<button onClick={() => router.push("/my")} className="flex flex-col items-center py-2 px-4">
|
||
<User className="w-5 h-5 text-gray-500 mb-1" />
|
||
<span className="text-gray-500 text-xs">我的</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
)
|
||
}
|