From 1e25c7134a1cce3cd172c794d718e49792ba0257 Mon Sep 17 00:00:00 2001 From: v0 Date: Wed, 14 Jan 2026 05:24:13 +0000 Subject: [PATCH] feat: complete product overhaul Refactor homepage, match feature, data storage, and my page; implement paid reading logic. #VERCEL_SKIP Co-authored-by: undefined --- app/login/page.tsx | 153 ++++++++++ app/page.tsx | 352 +++++++++++++++++++++- app/read/[id]/page.tsx | 30 +- components/chapter-content.tsx | 325 +++++++------------- components/home-screen.tsx | 532 --------------------------------- components/matching-circle.tsx | 171 ----------- components/reading-modal.tsx | 125 -------- lib/book-data.ts | 307 ++++++++++--------- 8 files changed, 783 insertions(+), 1212 deletions(-) create mode 100644 app/login/page.tsx delete mode 100644 components/home-screen.tsx delete mode 100644 components/matching-circle.tsx delete mode 100644 components/reading-modal.tsx diff --git a/app/login/page.tsx b/app/login/page.tsx new file mode 100644 index 00000000..08e33836 --- /dev/null +++ b/app/login/page.tsx @@ -0,0 +1,153 @@ +"use client" + +import { useState } from "react" +import { useRouter } from "next/navigation" +import { useStore } from "@/lib/store" +import { ChevronLeft, Phone, User, Hash } from "lucide-react" + +export default function LoginPage() { + const router = useRouter() + const { login, register, adminLogin } = useStore() + const [mode, setMode] = useState<"login" | "register">("login") + const [phone, setPhone] = useState("") + const [code, setCode] = useState("") + const [nickname, setNickname] = useState("") + const [referralCode, setReferralCode] = useState("") + const [error, setError] = useState("") + const [loading, setLoading] = useState(false) + + const handleSubmit = async () => { + setError("") + setLoading(true) + + try { + // 管理员登录 + if (phone.toLowerCase() === "admin") { + if (adminLogin(phone, code)) { + router.push("/admin") + return + } else { + setError("管理员密码错误") + return + } + } + + if (mode === "login") { + const success = await login(phone, code) + if (success) { + router.push("/") + } else { + setError("验证码错误或用户不存在") + } + } else { + if (!nickname.trim()) { + setError("请输入昵称") + return + } + const success = await register(phone, nickname, referralCode || undefined) + if (success) { + router.push("/") + } else { + setError("该手机号已注册") + } + } + } finally { + setLoading(false) + } + } + + return ( +
+ {/* 顶部导航 */} +
+ +
+ + {/* 主内容 */} +
+

{mode === "login" ? "登录" : "注册"}

+

+ {mode === "login" ? "登录后查看购买记录和收益" : "注册后开始阅读真实商业故事"} +

+ +
+ {/* 手机号 */} +
+ + setPhone(e.target.value)} + placeholder="手机号" + className="w-full pl-12 pr-4 py-3.5 bg-[#1c1c1e] rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#30d158]/50" + /> +
+ + {/* 昵称(注册时显示) */} + {mode === "register" && ( +
+ + setNickname(e.target.value)} + placeholder="昵称" + className="w-full pl-12 pr-4 py-3.5 bg-[#1c1c1e] rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#30d158]/50" + /> +
+ )} + + {/* 验证码/密码 */} +
+ + setCode(e.target.value)} + placeholder={mode === "login" ? "验证码(测试:123456)" : "设置密码"} + className="w-full pl-12 pr-4 py-3.5 bg-[#1c1c1e] rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#30d158]/50" + /> +
+ + {/* 邀请码(注册时显示) */} + {mode === "register" && ( +
+ + setReferralCode(e.target.value)} + placeholder="邀请码(选填)" + className="w-full pl-12 pr-4 py-3.5 bg-[#1c1c1e] rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#30d158]/50" + /> +
+ )} + + {/* 错误提示 */} + {error &&

{error}

} + + {/* 提交按钮 */} + + + {/* 切换模式 */} +
+ +
+
+
+
+ ) +} diff --git a/app/page.tsx b/app/page.tsx index f2e9ad5d..4daa3177 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,9 +1,349 @@ -import { HomeScreen } from "@/components/home-screen" -import { getBookStructure } from "@/lib/book-file-system" +"use client" -export const dynamic = "force-dynamic" +import { useState } from "react" +import { useRouter } from "next/navigation" +import { useStore } from "@/lib/store" +import { bookData, getTotalSectionCount, specialSections } from "@/lib/book-data" +import { Book, Lock, Unlock, ChevronRight, User, BookOpen } from "lucide-react" -export default async function HomePage() { - const parts = getBookStructure() - return +export default function HomePage() { + const router = useRouter() + const { user, isLoggedIn, hasPurchased } = useStore() + const [activeTab, setActiveTab] = useState<"home" | "me">("home") + const [expandedPart, setExpandedPart] = useState("part-1") + + const totalSections = getTotalSectionCount() + const purchasedCount = user?.purchasedSections?.length || 0 + const hasFullBook = user?.hasFullBook || false + + // 点击章节 + const handleSectionClick = (sectionId: string, isFree: boolean) => { + if (isFree || hasFullBook || hasPurchased(sectionId)) { + router.push(`/read/${sectionId}`) + } else { + router.push(`/read/${sectionId}`) + } + } + + return ( +
+ {/* 顶部固定区域 - 书籍信息 */} +
+
+
+ +
+
+

一场SOUL的创业实验场

+

来自Soul派对房的真实商业故事

+
+
+
{totalSections}
+
章节
+
+
+
+ + {/* 主内容区域 - 可滚动的目录 */} + {activeTab === "home" ? ( +
+ {/* 序言入口 */} + + + {/* 五篇目录 */} + {bookData.map((part) => ( +
+ {/* 篇标题 */} + + + {/* 展开的章节列表 */} + {expandedPart === part.id && ( +
+ {part.chapters.map((chapter) => ( +
+
+ {chapter.title} +
+ {chapter.sections.map((section) => { + const isPurchased = hasFullBook || hasPurchased(section.id) + const canRead = section.isFree || isPurchased + + return ( + + ) + })} +
+ ))} +
+ )} +
+ ))} + + {/* 尾声入口 */} + + + {/* 附录 */} +
+
附录
+ {specialSections.appendix.map((item) => ( + + ))} +
+
+ ) : ( + + )} + + {/* 底部导航 */} + +
+ ) +} + +function MyPage() { + const router = useRouter() + const { user, isLoggedIn, logout } = useStore() + const [showLogin, setShowLogin] = useState(false) + + const purchasedCount = user?.purchasedSections?.length || 0 + const hasFullBook = user?.hasFullBook || false + const earnings = user?.earnings || 0 + const totalSections = getTotalSectionCount() + + // 计算阅读进度 + const readProgress = hasFullBook ? 100 : Math.round((purchasedCount / totalSections) * 100) + + if (!isLoggedIn) { + return ( +
+
+ +
+

登录后查看更多

+

登录后可查看购买记录、阅读进度和收益

+ +
+ ) + } + + return ( +
+ {/* 用户信息卡片 */} +
+
+
+ {user?.nickname?.charAt(0) || "U"} +
+
+
{user?.nickname || "用户"}
+
{user?.phone}
+
+ {hasFullBook && ( +
+ 全书会员 +
+ )} +
+ + {/* 数据统计 */} +
+
+
{hasFullBook ? totalSections : purchasedCount}
+
已购章节
+
+
+
{readProgress}%
+
阅读进度
+
+
+
¥{earnings.toFixed(2)}
+
累计收益
+
+
+
+ + {/* 阅读进度条 */} +
+
+ 阅读进度 + + {hasFullBook ? totalSections : purchasedCount}/{totalSections}章 + +
+
+
+
+
+ + {/* 收益明细 */} +
+
+ 收益明细 + +
+
+
+ 可提现 + ¥{(user?.pendingEarnings || 0).toFixed(2)} +
+
+ 已提现 + ¥{(user?.withdrawnEarnings || 0).toFixed(2)} +
+
+ 推荐人数 + {user?.referralCount || 0}人 +
+
+
+ + {/* 购买全书入口 */} + {!hasFullBook && ( + + )} + + {/* 邀请码 */} +
+
我的邀请码
+
+
+ {user?.referralCode || "N/A"} +
+ +
+

邀请好友购买,获得90%佣金

+
+ + {/* 退出登录 */} + +
+ ) } diff --git a/app/read/[id]/page.tsx b/app/read/[id]/page.tsx index e79c8fb1..bcd996bf 100644 --- a/app/read/[id]/page.tsx +++ b/app/read/[id]/page.tsx @@ -1,7 +1,7 @@ import { notFound } from "next/navigation" import { ChapterContent } from "@/components/chapter-content" import { getSectionBySlug, getChapterBySectionSlug } from "@/lib/book-file-system" -import { specialSections } from "@/lib/book-data" +import { specialSections, getSectionById } from "@/lib/book-data" interface ReadPageProps { params: Promise<{ id: string }> @@ -14,25 +14,37 @@ export default async function ReadPage({ params }: ReadPageProps) { const { id } = await params if (id === "preface") { - return + return } if (id === "epilogue") { - return + return + } + + if (id.startsWith("appendix-")) { + const appendixSection = specialSections.appendix.find((a) => a.id === id) + if (appendixSection) { + return + } } try { + // 先从文件系统获取 const section = getSectionBySlug(id) - if (!section) { - notFound() + if (section) { + const context = getChapterBySectionSlug(id) + if (context) { + return + } } - const context = getChapterBySectionSlug(id) - if (!context) { - notFound() + // 再从book-data获取 + const bookSection = getSectionById(id) + if (bookSection) { + return } - return + notFound() } catch (error) { console.error("[v0] Error in ReadPage:", error) notFound() diff --git a/components/chapter-content.tsx b/components/chapter-content.tsx index 34382397..ec0a3a77 100644 --- a/components/chapter-content.tsx +++ b/components/chapter-content.tsx @@ -1,15 +1,11 @@ "use client" import { useState, useEffect } from "react" -import Link from "next/link" -import { ChevronLeft, Lock, Share2, BookOpen, Clock, MessageCircle, ChevronRight, Sparkles } from "lucide-react" -import { type Section, getFullBookPrice, isSectionUnlocked } from "@/lib/book-data" +import { useRouter } from "next/navigation" +import { ChevronLeft, Lock, Share2, Sparkles } from "lucide-react" +import { type Section, getFullBookPrice, getTotalSectionCount } from "@/lib/book-data" import { useStore } from "@/lib/store" -import { AuthModal } from "./modules/auth/auth-modal" -import { PaymentModal } from "./modules/payment/payment-modal" -import { UserMenu } from "./user-menu" -import { QRCodeModal } from "./modules/marketing/qr-code-modal" -import { ReferralShare } from "./modules/referral/referral-share" +import { PaymentModal } from "./payment-modal" interface ChapterContentProps { section: Section & { filePath: string } @@ -18,38 +14,34 @@ interface ChapterContentProps { } export function ChapterContent({ section, partTitle, chapterTitle }: ChapterContentProps) { + const router = useRouter() const [content, setContent] = useState("") const [isLoading, setIsLoading] = useState(true) - const [isAuthOpen, setIsAuthOpen] = useState(false) const [isPaymentOpen, setIsPaymentOpen] = useState(false) - 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 + const { user, isLoggedIn, hasPurchased } = useStore() + const fullBookPrice = getFullBookPrice() + const totalSections = getTotalSectionCount() - const isUnlocked = isSectionUnlocked(section) - const canAccess = section.isFree || isUnlocked || (isLoggedIn && hasPurchased(section.id)) - - useEffect(() => { - setFullBookPrice(getFullBookPrice()) - }, []) + const hasFullBook = user?.hasFullBook || false + const canAccess = section.isFree || hasFullBook || (isLoggedIn && hasPurchased(section.id)) // 阅读进度追踪 useEffect(() => { const handleScroll = () => { const scrollTop = window.scrollY const docHeight = document.documentElement.scrollHeight - window.innerHeight - const progress = Math.min((scrollTop / docHeight) * 100, 100) + const progress = docHeight > 0 ? Math.min((scrollTop / docHeight) * 100, 100) : 0 setReadingProgress(progress) } - window.addEventListener('scroll', handleScroll) - return () => window.removeEventListener('scroll', handleScroll) + window.addEventListener("scroll", handleScroll) + return () => window.removeEventListener("scroll", handleScroll) }, []) + // 加载内容 useEffect(() => { async function loadContent() { try { @@ -59,22 +51,10 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont return } - if (typeof window !== "undefined" && section.filePath.startsWith("custom/")) { - const customSections = JSON.parse(localStorage.getItem("custom_sections") || "[]") as Section[] - const customSection = customSections.find((s) => s.id === section.id) - if (customSection?.content) { - setContent(customSection.content) - setIsLoading(false) - return - } - } - const response = await fetch(`/api/content?path=${encodeURIComponent(section.filePath)}`) if (response.ok) { const data = await response.json() - if (!data.isCustom) { - setContent(data.content) - } + setContent(data.content || "") } } catch (error) { console.error("Failed to load content:", error) @@ -84,244 +64,162 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont } loadContent() - }, [section.filePath, section.id, section.content]) + }, [section.filePath, section.content]) const handlePurchaseClick = (type: "section" | "fullbook") => { if (!isLoggedIn) { - setIsAuthOpen(true) + router.push("/login") return } setPaymentType(type) setIsPaymentOpen(true) } - const handleShare = async () => { - const url = user?.referralCode ? `${window.location.href}?ref=${user.referralCode}` : window.location.href - const shareData = { - title: section.title, - text: `来自Soul派对房的真实商业故事: ${section.title}`, - url: url, - } - - try { - if (navigator.share && navigator.canShare && navigator.canShare(shareData)) { - await navigator.share(shareData) - } else { - navigator.clipboard.writeText(url) - alert( - `链接已复制!分享后他人购买,你可获得${distributorShare}%返利 (¥${((fullBookPrice * distributorShare) / 100).toFixed(1)})`, - ) - } - } catch (error) { - if ((error as Error).name !== "AbortError") { - navigator.clipboard.writeText(url) - alert(`链接已复制!分享后他人购买,你可获得${distributorShare}%返利`) - } - } - } - - const previewContent = content.slice(0, 500) + // 计算预览内容(前50%) + const contentLines = content.split("\n").filter((line) => line.trim()) + const previewLineCount = Math.ceil(contentLines.length * 0.5) + const previewContent = contentLines.slice(0, previewLineCount).join("\n") + const hiddenContent = contentLines.slice(previewLineCount).join("\n") return ( -
+
{/* 阅读进度条 */} -
-
+
- {/* Header - iOS风格毛玻璃 */} -
-
- +
+
-

{partTitle}

- {chapterTitle && ( -

{chapterTitle}

- )} -
-
- - + {partTitle &&

{partTitle}

} + {chapterTitle &&

{chapterTitle}

}
+
{/* 阅读内容 */} -
- {/* 标题区域 */} -
-
- +
+ {/* 标题 */} +
+
+ {section.id} - {section.unlockAfterDays && !section.isFree && ( - - - {isUnlocked ? "已免费解锁" : `${section.unlockAfterDays}天后免费`} - - )} + {section.isFree && 免费}
-

- {section.title} -

+

{section.title}

{isLoading ? ( - // 骨架屏加载
{[...Array(8)].map((_, i) => ( -
+
))}
) : canAccess ? ( - <> - {/* 正文内容 - 书籍阅读风格 */} -
-
- {content.split("\n").map((paragraph, index) => ( - paragraph.trim() && ( -

- {paragraph} -

- ) - ))} -
-
- - {/* 进群引导 CTA */} -
-
-
-
- -
-
-

想听更多商业故事?

-

每天早上6-9点,卡若在Soul派对房分享真实案例

-
- -
-
- + // 完整内容 +
+ {content.split("\n").map( + (paragraph, index) => + paragraph.trim() && ( +

+ {paragraph} +

+ ), + )} +
) : ( + // 付费墙:前半免费,后半付费
- {/* 预览内容 */} -
-
- {previewContent.split("\n").map((paragraph, index) => ( + {/* 免费预览部分 */} +
+ {previewContent.split("\n").map( + (paragraph, index) => paragraph.trim() && ( -

+

{paragraph}

- ) - ))} -
-
+ ), + )}
- {/* 购买提示 - 毛玻璃风格 */} -
-
- -
-
- + {/* 渐变遮罩 */} +
+
+
+ + {/* 付费提示卡片 */} +
+
+
+
-

解锁完整内容

-

- {isLoggedIn ? "购买本节或整本书以阅读完整内容" : "登录后购买即可阅读完整内容"} +

解锁完整内容

+

+ 已阅读50%,{isLoggedIn ? "购买后继续阅读" : "登录并购买后继续阅读"}

- {section.unlockAfterDays && ( -
- - 本节将在{section.unlockAfterDays}天后免费解锁 -
- )} - -
+ {/* 购买选项 */} +
+
-

- 分享本书,他人购买你可获得 {distributorShare}%返利 -

-
-
- - {/* 进群引导 */} -
-
-
- -
-
-

不想花钱?来派对群免费听!

-

每天早上6-9点,卡若在Soul派对房免费分享

-
- +

分享给好友购买,你可获得90%佣金

)} - - {/* 底部导航 */} -
- - - 返回目录 - - -
- {/* 弹窗 */} - setIsAuthOpen(false)} /> + {/* 支付弹窗 */} setIsPaymentOpen(false)} @@ -331,7 +229,6 @@ export function ChapterContent({ section, partTitle, chapterTitle }: ChapterCont amount={paymentType === "section" ? section.price : fullBookPrice} onSuccess={() => window.location.reload()} /> - setIsQRModalOpen(false)} />
) } diff --git a/components/home-screen.tsx b/components/home-screen.tsx deleted file mode 100644 index 49ed49c4..00000000 --- a/components/home-screen.tsx +++ /dev/null @@ -1,532 +0,0 @@ -"use client" - -import type React from "react" - -import { useState, useEffect, useRef } from "react" -import { type Part, getAllSections, getFullBookPrice, specialSections } from "@/lib/book-data" -import { useStore } from "@/lib/store" -import { BookOpen, Lock, Check, Sparkles, ChevronRight, User, TrendingUp } from "lucide-react" -import { AuthModal } from "./modules/auth/auth-modal" -import { PaymentModal } from "./modules/payment/payment-modal" -import { ReadingModal } from "./reading-modal" -import { MatchingCircle } from "./matching-circle" - -interface HomeScreenProps { - parts: Part[] -} - -export function HomeScreen({ parts }: HomeScreenProps) { - const [activeTab, setActiveTab] = useState<"home" | "match" | "my">("home") - const [selectedSection, setSelectedSection] = useState<{ id: string; title: string; filePath: string } | null>(null) - const [isAuthOpen, setIsAuthOpen] = useState(false) - const [isPaymentOpen, setIsPaymentOpen] = useState(false) - const [paymentType, setPaymentType] = useState<"section" | "fullbook">("section") - const [paymentSectionId, setPaymentSectionId] = useState("") - const [paymentSectionTitle, setPaymentSectionTitle] = useState("") - const [paymentAmount, setPaymentAmount] = useState(1) - - const { user, isLoggedIn, hasPurchased } = useStore() - const [mounted, setMounted] = useState(false) - - const allSections = getAllSections() - const fullBookPrice = getFullBookPrice() - const totalSections = allSections.length - const purchasedCount = user?.hasFullBook ? totalSections : user?.purchasedSections?.length || 0 - - useEffect(() => { - setMounted(true) - }, []) - - // 点击章节 - const handleSectionClick = (section: { - id: string - title: string - filePath: string - isFree: boolean - price: number - }) => { - const canAccess = section.isFree || (isLoggedIn && hasPurchased(section.id)) - - if (canAccess) { - // 直接打开阅读弹窗 - setSelectedSection({ id: section.id, title: section.title, filePath: section.filePath }) - } else { - // 需要购买 - if (!isLoggedIn) { - setIsAuthOpen(true) - } else { - setPaymentSectionId(section.id) - setPaymentSectionTitle(section.title) - setPaymentAmount(section.price) - setPaymentType("section") - setIsPaymentOpen(true) - } - } - } - - // 购买全书 - const handleBuyFullBook = () => { - if (!isLoggedIn) { - setIsAuthOpen(true) - return - } - setPaymentType("fullbook") - setPaymentAmount(fullBookPrice) - setIsPaymentOpen(true) - } - - if (!mounted) { - return ( -
-
-
- ) - } - - return ( -
- {/* 主内容区域 - 根据Tab切换 */} -
- {activeTab === "home" && ( - - )} - - {activeTab === "match" && } - - {activeTab === "my" && ( - setIsAuthOpen(true)} - /> - )} -
- - {/* 底部导航 - 固定三个Tab */} - - - {/* 阅读弹窗 - 原地展示内容 */} - {selectedSection && ( - setSelectedSection(null)} - onPurchase={(sectionId, title, price) => { - setPaymentSectionId(sectionId) - setPaymentSectionTitle(title) - setPaymentAmount(price) - setPaymentType("section") - setIsPaymentOpen(true) - }} - /> - )} - - {/* 弹窗 */} - setIsAuthOpen(false)} /> - setIsPaymentOpen(false)} - type={paymentType} - sectionId={paymentSectionId} - sectionTitle={paymentSectionTitle} - amount={paymentAmount} - onSuccess={() => { - setIsPaymentOpen(false) - window.location.reload() - }} - /> -
- ) -} - -// Tab按钮组件 -function TabButton({ - active, - onClick, - icon, - label, -}: { - active: boolean - onClick: () => void - icon: React.ReactNode - label: string -}) { - return ( - - ) -} - -// 首页Tab - 书籍总览+完整目录 -function HomeTab({ - parts, - totalSections, - fullBookPrice, - purchasedCount, - isLoggedIn, - hasPurchased, - onSectionClick, - onBuyFullBook, -}: { - parts: Part[] - totalSections: number - fullBookPrice: number - purchasedCount: number - isLoggedIn: boolean - hasPurchased: (id: string) => boolean - onSectionClick: (section: any) => void - onBuyFullBook: () => void -}) { - const scrollRef = useRef(null) - - return ( -
- {/* 书籍总览区 - 精简版 */} -
-
-
- - Soul · 派对房 -
-

一场SOUL的创业实验场

-

来自Soul派对房的真实商业故事

-
- - {/* 价格信息 */} -
-
-
-
-

¥{fullBookPrice}

-

整本价格

-
-
-
-

{totalSections}

-

商业案例

-
-
- -
-
-
- - {/* 完整目录 - 一次性展示所有章节 */} -
-
-

全书目录

- - 已购 {purchasedCount}/{totalSections} - -
- - {/* 序言 */} - - onSectionClick({ - id: "preface", - title: specialSections.preface.title, - filePath: specialSections.preface.filePath, - isFree: true, - price: 0, - }) - } - /> - - {/* 所有篇章和小节 */} - {parts.map((part) => ( -
- {/* 篇章标题 */} -
-
- {part.number} -
-
-

{part.title}

-

{part.subtitle}

-
-
- - {/* 该篇章下的所有小节 */} -
- {part.chapters.map((chapter) => - chapter.sections.map((section, sectionIndex) => { - const isPurchased = isLoggedIn && hasPurchased(section.id) - return ( - onSectionClick(section)} - /> - ) - }), - )} -
-
- ))} - - {/* 尾声 */} - - onSectionClick({ - id: "epilogue", - title: specialSections.epilogue.title, - filePath: specialSections.epilogue.filePath, - isFree: true, - price: 0, - }) - } - /> -
-
- ) -} - -// 章节列表项 -function SectionItem({ - id, - number, - title, - isFree, - isPurchased, - price = 1, - isLast = false, - onClick, -}: { - id: string - number: string - title: string - isFree: boolean - isPurchased: boolean - price?: number - isLast?: boolean - onClick: () => void -}) { - const canAccess = isFree || isPurchased - - return ( - - ) -} - -// 匹配Tab - 圆形UI,高级感 -function MatchTab() { - return ( -
- -
- ) -} - -// 我的Tab - 数据中心 -function MyTab({ - user, - isLoggedIn, - totalSections, - purchasedCount, - onLogin, -}: { - user: any - isLoggedIn: boolean - totalSections: number - purchasedCount: number - onLogin: () => void -}) { - if (!isLoggedIn) { - return ( -
-
- -
-

登录查看更多

-

查看购买记录、阅读进度、分销收益

- -
- ) - } - - const readingProgress = user?.hasFullBook ? 100 : Math.round((purchasedCount / totalSections) * 100) - const earnings = user?.earnings || 0 - - return ( -
- {/* 用户信息 */} -
-
- -
-
-

{user?.nickname || "用户"}

-

{user?.phone}

-
-
- - {/* 数据卡片 - 清晰可视化 */} -
- {/* 已购章节 */} -
-
- - 已购章节 -
-

{user?.hasFullBook ? "全部" : purchasedCount}

-

共 {totalSections} 章

-
- - {/* 累计收益 */} -
-
- - 累计收益 -
-

¥{earnings.toFixed(1)}

-

分销所得

-
-
- - {/* 阅读进度 */} -
-
- 阅读进度 - {readingProgress}% -
-
-
-
-

- {user?.hasFullBook ? "已拥有全书" : `还差 ${totalSections - purchasedCount} 章解锁全部内容`} -

-
- - {/* 邀请码 */} -
-
-
-

我的邀请码

- {user?.referralCode} -
- -
-

分享给好友,他人购买你可获得 90% 返利

-
- - {/* 退出登录 */} - -
- ) -} diff --git a/components/matching-circle.tsx b/components/matching-circle.tsx deleted file mode 100644 index 320a27b6..00000000 --- a/components/matching-circle.tsx +++ /dev/null @@ -1,171 +0,0 @@ -"use client" - -import { useState, useEffect } from "react" -import { Sparkles, Users, BookOpen } from "lucide-react" -import { getAllSections } from "@/lib/book-data" -import { useStore } from "@/lib/store" - -export function MatchingCircle() { - const [isMatching, setIsMatching] = useState(false) - const [matchProgress, setMatchProgress] = useState(0) - const [matchResult, setMatchResult] = useState<{ - section: { id: string; title: string } - reason: string - compatibility: number - } | null>(null) - - const { user, isLoggedIn } = useStore() - const allSections = getAllSections() - - // 开始匹配 - const startMatching = () => { - if (isMatching) return - - setIsMatching(true) - setMatchProgress(0) - setMatchResult(null) - - // 模拟匹配进度 - const interval = setInterval(() => { - setMatchProgress((prev) => { - if (prev >= 100) { - clearInterval(interval) - // 匹配完成,生成结果 - const randomSection = allSections[Math.floor(Math.random() * allSections.length)] - const reasons = [ - "与你的创业方向高度匹配", - "适合你当前的发展阶段", - "契合你的商业思维模式", - "与你的行业背景相关", - "符合你的学习偏好", - ] - setMatchResult({ - section: { id: randomSection.id, title: randomSection.title }, - reason: reasons[Math.floor(Math.random() * reasons.length)], - compatibility: Math.floor(Math.random() * 20) + 80, - }) - setIsMatching(false) - return 100 - } - return prev + 2 - }) - }, 50) - } - - // 保存匹配结果到本地 - useEffect(() => { - if (matchResult && isLoggedIn) { - const savedResults = JSON.parse(localStorage.getItem("match_results") || "[]") - savedResults.unshift({ - ...matchResult, - userId: user?.id, - matchedAt: new Date().toISOString(), - }) - // 只保留最近10条 - localStorage.setItem("match_results", JSON.stringify(savedResults.slice(0, 10))) - } - }, [matchResult, isLoggedIn, user?.id]) - - return ( -
- {/* 匹配圆环 */} -
- {/* 外圈装饰 */} -
-
-
- - {/* 进度圆环 */} - - - - - - {/* 中心内容 */} -
- {isMatching ? ( - <> - -

{matchProgress}%

-

正在匹配...

- - ) : matchResult ? ( - <> -
{matchResult.compatibility}%
-

匹配度

-

{matchResult.reason}

- - ) : ( - <> - -

寻找合作伙伴

-

智能匹配商业故事

- - )} -
- - {/* 浮动装饰点 */} - {isMatching && ( - <> -
-
-
-
- - )} -
- - {/* 匹配结果 */} - {matchResult && ( -
-
-
- -
-
-

{matchResult.section.id}

-

{matchResult.section.title}

-
-
-
- )} - - {/* 匹配按钮 */} - - -

基于你的阅读偏好,智能推荐适合你的商业故事

-
- ) -} diff --git a/components/reading-modal.tsx b/components/reading-modal.tsx deleted file mode 100644 index a7ed900a..00000000 --- a/components/reading-modal.tsx +++ /dev/null @@ -1,125 +0,0 @@ -"use client" - -import { useState, useEffect } from "react" -import { X, Lock, Sparkles } from "lucide-react" -import { useStore } from "@/lib/store" -import { getFullBookPrice } from "@/lib/book-data" - -interface ReadingModalProps { - section: { id: string; title: string; filePath: string } - onClose: () => void - onPurchase: (sectionId: string, title: string, price: number) => void -} - -export function ReadingModal({ section, onClose, onPurchase }: ReadingModalProps) { - const [content, setContent] = useState("") - const [isLoading, setIsLoading] = useState(true) - const { isLoggedIn, hasPurchased } = useStore() - - const isFree = section.id === "preface" || section.id === "epilogue" - const canAccess = isFree || (isLoggedIn && hasPurchased(section.id)) - const fullBookPrice = getFullBookPrice() - - useEffect(() => { - async function loadContent() { - try { - const response = await fetch(`/api/content?path=${encodeURIComponent(section.filePath)}`) - if (response.ok) { - const data = await response.json() - setContent(data.content || "") - } - } catch (error) { - console.error("Failed to load content:", error) - } finally { - setIsLoading(false) - } - } - loadContent() - }, [section.filePath]) - - // 计算显示内容 - const displayContent = canAccess ? content : content.slice(0, Math.floor(content.length * 0.3)) - const showPaywall = !canAccess && content.length > 0 - - return ( -
-
- {/* Header */} -
- -

{section.title}

-
-
- - {/* Content */} -
-
- {isLoading ? ( -
- {[...Array(10)].map((_, i) => ( -
- ))} -
- ) : ( - <> -
-
- {displayContent.split("\n").map( - (paragraph, index) => - paragraph.trim() && ( -

- {paragraph} -

- ), - )} -
- - {/* 付费墙渐变 */} - {showPaywall && ( -
- )} -
- - {/* 付费提示 - 在阅读中途触发 */} - {showPaywall && ( -
-
- -
-

解锁完整内容

-

您已阅读30%,解锁后可阅读完整内容

- -
- - -
- -

- 分享给好友,他人购买你可获得 90% 返利 -

-
- )} - - )} -
-
-
-
- ) -} diff --git a/lib/book-data.ts b/lib/book-data.ts index d734bd1a..35f460a2 100644 --- a/lib/book-data.ts +++ b/lib/book-data.ts @@ -24,12 +24,9 @@ export interface Part { } export const BASE_BOOK_PRICE = 9.9 -export const PRICE_INCREMENT_PER_SECTION = 1 export const SECTION_PRICE = 1 -export const AUTHOR_SHARE = 0.9 -export const DISTRIBUTOR_SHARE = 0.1 -export function getFullBookPrice(sectionsCount?: number): number { +export function getFullBookPrice(): number { return 9.9 } @@ -46,40 +43,39 @@ export const bookData: Part[] = [ sections: [ { id: "1.1", - title: "自行车荷总:一个行业做到极致是什么样", + title: "荷包:电动车出租的被动收入模式", price: 1, isFree: true, - filePath: "book/_第一篇|真实的人/第1章|人与人之间的底层逻辑/1.1 自行车荷总:一个行业做到极致是什么样.md", + filePath: "book/第一篇|真实的人/第1章|人与人之间的底层逻辑/1.1 荷包:电动车出租的被动收入模式.md", }, { id: "1.2", title: "老墨:资源整合高手的社交方法", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/_第一篇|真实的人/第1章|人与人之间的底层逻辑/1.2 老墨:资源整合高手的社交方法.md", + filePath: "book/第一篇|真实的人/第1章|人与人之间的底层逻辑/1.2 老墨:资源整合高手的社交方法.md", }, { id: "1.3", - title: "笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统", + title: "笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统", price: 1, isFree: false, filePath: - "book/_第一篇|真实的人/第1章|人与人之间的底层逻辑/1.3 笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统.md", + "book/第一篇|真实的人/第1章|人与人之间的底层逻辑/1.3 笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统.md", }, { id: "1.4", - title: "人性的三角结构:情绪、价值、利益", + title: "人性的三角结构:利益、情感、价值观", price: 1, isFree: false, - filePath: "book/_第一篇|真实的人/第1章|人与人之间的底层逻辑/1.4 人性的三角结构:情绪、价值、利益.md", + filePath: "book/第一篇|真实的人/第1章|人与人之间的底层逻辑/1.4 人性的三角结构:利益、情感、价值观.md", }, { id: "1.5", - title: "为什么99%的合作死在沟通差而不是能力差", + title: "沟通差的问题:为什么你说的别人听不懂", price: 1, isFree: false, - filePath: "book/_第一篇|真实的人/第1章|人与人之间的底层逻辑/1.5 为什么99%的合作死在沟通差而不是能力差.md", + filePath: "book/第一篇|真实的人/第1章|人与人之间的底层逻辑/1.5 沟通差的问题:为什么你说的别人听不懂.md", }, ], }, @@ -92,42 +88,36 @@ export const bookData: Part[] = [ title: "相亲故事:你以为找的是人,实际是在找模式", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/_第一篇|真实的人/第2章|人性困境案例/2.1 相亲故事:你以为找的是人,实际是在找模式.md", + filePath: "book/第一篇|真实的人/第2章|人性困境案例/2.1 相亲故事:你以为找的是人,实际是在找模式.md", }, { id: "2.2", title: "找工作迷茫者:为什么简历解决不了人生", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/_第一篇|真实的人/第2章|人性困境案例/2.2 找工作迷茫者:为什么简历解决不了人生.md", + filePath: "book/第一篇|真实的人/第2章|人性困境案例/2.2 找工作迷茫者:为什么简历解决不了人生.md", }, { id: "2.3", title: "撸运费险:小钱困住大脑的真实心理", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/_第一篇|真实的人/第2章|人性困境案例/2.3 撸运费险:小钱困住大脑的真实心理.md", + filePath: "book/第一篇|真实的人/第2章|人性困境案例/2.3 撸运费险:小钱困住大脑的真实心理.md", }, { id: "2.4", title: "游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: - "book/_第一篇|真实的人/第2章|人性困境案例/2.4 游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力.md", + filePath: "book/第一篇|真实的人/第2章|人性困境案例/2.4 游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力.md", }, { id: "2.5", title: "健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒", price: 1, isFree: false, - unlockAfterDays: 3, filePath: - "book/_第一篇|真实的人/第2章|人性困境案例/2.5 健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒.md", + "book/第一篇|真实的人/第2章|人性困境案例/2.5 健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒.md", }, ], }, @@ -145,43 +135,31 @@ export const bookData: Part[] = [ sections: [ { id: "3.1", - title: "电商财税窗口:我错过的第一桶金", - price: 1, - isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第3章|电商篇/3.1 电商财税窗口:我错过的第一桶金.md", - }, - { - id: "3.2", title: "3000万流水如何跑出来(退税模式解析)", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第3章|电商篇/3.2 3000万流水如何跑出来(退税模式解析).md", + filePath: "book/第二篇|真实的行业/第3章|电商篇/3.1 3000万流水如何跑出来(退税模式解析).md", + }, + { + id: "3.2", + title: "供应链之王 vs 打工人:利润不在前端", + price: 1, + isFree: false, + filePath: "book/第二篇|真实的行业/第3章|电商篇/3.2 供应链之王 vs 打工人:利润不在前端.md", }, { id: "3.3", - title: "供应链之王vs打工人:利润不在前端", - price: 1, - isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第3章|电商篇/3.3 供应链之王 vs 打工人:利润不在前端.md", - }, - { - id: "3.4", title: "社区团购的底层逻辑", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第3章|电商篇/3.4 社区团购的底层逻辑.md", + filePath: "book/第二篇|真实的行业/第3章|电商篇/3.3 社区团购的底层逻辑.md", }, { - id: "3.5", + id: "3.4", title: "跨境电商与退税套利", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第3章|电商篇/3.5 跨境电商与退税套利.md", + filePath: "book/第二篇|真实的行业/第3章|电商篇/3.4 跨境电商与退税套利.md", }, ], }, @@ -194,7 +172,6 @@ export const bookData: Part[] = [ title: "旅游号:30天10万粉的真实逻辑", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第二篇|真实的行业/第4章|内容商业篇/4.1 旅游号:30天10万粉的真实逻辑.md", }, { @@ -202,7 +179,6 @@ export const bookData: Part[] = [ title: "做号工厂:如何让一个号变成一个机器", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第二篇|真实的行业/第4章|内容商业篇/4.2 做号工厂:如何让一个号变成一个机器.md", }, { @@ -210,7 +186,6 @@ export const bookData: Part[] = [ title: "情绪内容为什么比专业内容更赚钱", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第二篇|真实的行业/第4章|内容商业篇/4.3 情绪内容为什么比专业内容更赚钱.md", }, { @@ -218,7 +193,6 @@ export const bookData: Part[] = [ title: "猫与宠物号:为什么宠物赛道永不过时", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第二篇|真实的行业/第4章|内容商业篇/4.4 猫与宠物号:为什么宠物赛道永不过时.md", }, { @@ -226,7 +200,6 @@ export const bookData: Part[] = [ title: "直播间里的三种人:演员、技术工、系统流", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第二篇|真实的行业/第4章|内容商业篇/4.5 直播间里的三种人:演员、技术工、系统流.md", }, ], @@ -237,43 +210,38 @@ export const bookData: Part[] = [ sections: [ { id: "5.1", - title: "羽毛球馆:为什么体育培训是最稳定的现金流", + title: "拍卖行抱朴:一天240万的摇号生意", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.1 羽毛球馆:为什么体育培训是最稳定的现金流.md", + filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.1 拍卖行抱朴:一天240万的摇号生意.md", }, { id: "5.2", - title: "旅游供应链:资源越老越值钱", + title: "土地拍卖:招拍挂背后的游戏规则", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.2 旅游供应链:资源越老越值钱.md", + filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.2 土地拍卖:招拍挂背后的游戏规则.md", }, { id: "5.3", - title: "景区联盟:门票不是目的,是流量入口", + title: "地摊经济数字化:一个月900块的餐车生意", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.3 景区联盟:门票不是目的,是流量入口.md", + filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.3 地摊经济数字化:一个月900块的餐车生意.md", }, { id: "5.4", - title: "拍卖行抱朴:我人生错过的4件大钱机会(完整版)", + title: "不良资产拍卖:我错过的一个亿佣金", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.4 拍卖行抱朴:我人生错过的4件大钱机会(完整版).md", + filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.4 不良资产拍卖:我错过的一个亿佣金.md", }, { id: "5.5", - title: "飞机票供应链:为什么越便宜越亏", + title: "桶装水李总:跟物业合作的轻资产模式", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.5 飞机票供应链:为什么越便宜越亏.md", + filePath: "book/第二篇|真实的行业/第5章|传统行业篇/5.5 桶装水李总:跟物业合作的轻资产模式.md", }, ], }, @@ -291,35 +259,31 @@ export const bookData: Part[] = [ sections: [ { id: "6.1", - title: "错过电商财税(2016-2017)", + title: "电商财税窗口:2016年的千万级机会", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第三篇|真实的错误/第6章|我人生错过的4件大钱/6.1 错过电商财税(2016-2017).md", + filePath: "book/第三篇|真实的错误/第6章|我人生错过的4件大钱/6.1 电商财税窗口:2016年的千万级机会.md", }, { id: "6.2", - title: "错过供应链(2017-2018)", + title: "供应链金融:我不懂的杠杆游戏", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第三篇|真实的错误/第6章|我人生错过的4件大钱/6.2 错过供应链(2017-2018).md", + filePath: "book/第三篇|真实的错误/第6章|我人生错过的4件大钱/6.2 供应链金融:我不懂的杠杆游戏.md", }, { id: "6.3", - title: "错过内容红利(2018-2019)", + title: "内容红利:2019年我为什么没做抖音", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第三篇|真实的错误/第6章|我人生错过的4件大钱/6.3 错过内容红利(2018-2019).md", + filePath: "book/第三篇|真实的错误/第6章|我人生错过的4件大钱/6.3 内容红利:2019年我为什么没做抖音.md", }, { id: "6.4", - title: "错过资源资产化(2019-2020)", + title: "数据资产化:我还在观望的未来机会", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第三篇|真实的错误/第6章|我人生错过的4件大钱/6.4 错过资源资产化(2019-2020).md", + filePath: "book/第三篇|真实的错误/第6章|我人生错过的4件大钱/6.4 数据资产化:我还在观望的未来机会.md", }, ], }, @@ -329,10 +293,9 @@ export const bookData: Part[] = [ sections: [ { id: "7.1", - title: "投资房年轻人的迷茫:资金vs能力", + title: "投资房年轻人的迷茫:资金 vs 能力", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第三篇|真实的错误/第7章|别人犯的错误/7.1 投资房年轻人的迷茫:资金 vs 能力.md", }, { @@ -340,7 +303,6 @@ export const bookData: Part[] = [ title: "信息差骗局:永远有人靠卖学习赚钱", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第三篇|真实的错误/第7章|别人犯的错误/7.2 信息差骗局:永远有人靠卖学习赚钱.md", }, { @@ -348,7 +310,6 @@ export const bookData: Part[] = [ title: "在Soul找恋爱但想赚钱的人", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第三篇|真实的错误/第7章|别人犯的错误/7.3 在Soul找恋爱但想赚钱的人.md", }, { @@ -356,7 +317,6 @@ export const bookData: Part[] = [ title: "创业者的三种死法:冲动、轻信、没结构", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第三篇|真实的错误/第7章|别人犯的错误/7.4 创业者的三种死法:冲动、轻信、没结构.md", }, { @@ -364,7 +324,6 @@ export const bookData: Part[] = [ title: "人情生意的终点:关系越多亏得越多", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第三篇|真实的错误/第7章|别人犯的错误/7.5 人情生意的终点:关系越多亏得越多.md", }, ], @@ -386,7 +345,6 @@ export const bookData: Part[] = [ title: "流量杠杆:抖音、Soul、飞书", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第8章|底层结构/8.1 流量杠杆:抖音、Soul、飞书.md", }, { @@ -394,15 +352,13 @@ export const bookData: Part[] = [ title: "价格杠杆:供应链与信息差", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第8章|底层结构/8.2 价格杠杆:供应链与信息差.md", }, { id: "8.3", - title: "时间杠杆:自动化+AI", + title: "时间杠杆:自动化 + AI", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第8章|底层结构/8.3 时间杠杆:自动化 + AI.md", }, { @@ -410,7 +366,6 @@ export const bookData: Part[] = [ title: "情绪杠杆:咨询、婚恋、生意场", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第8章|底层结构/8.4 情绪杠杆:咨询、婚恋、生意场.md", }, { @@ -418,7 +373,6 @@ export const bookData: Part[] = [ title: "社交杠杆:认识谁比你会什么更重要", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第8章|底层结构/8.5 社交杠杆:认识谁比你会什么更重要.md", }, { @@ -426,7 +380,6 @@ export const bookData: Part[] = [ title: "云阿米巴:分不属于自己的钱", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第8章|底层结构/8.6 云阿米巴:分不属于自己的钱.md", }, ], @@ -440,7 +393,6 @@ export const bookData: Part[] = [ title: "游戏账号私域:账号即资产", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.1 游戏账号私域:账号即资产.md", }, { @@ -448,7 +400,6 @@ export const bookData: Part[] = [ title: "健康包模式:高复购、高毛利", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.2 健康包模式:高复购、高毛利.md", }, { @@ -456,15 +407,13 @@ export const bookData: Part[] = [ title: "药物私域:长期关系赛道", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.3 药物私域:长期关系赛道.md", }, { id: "9.4", - title: "残疾机构合作:退税×AI×人力成本", + title: "残疾机构合作:退税 × AI × 人力成本", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.4 残疾机构合作:退税 × AI × 人力成本.md", }, @@ -473,7 +422,6 @@ export const bookData: Part[] = [ title: "私域银行:粉丝即小股东", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.5 私域银行:粉丝即小股东.md", }, { @@ -481,7 +429,6 @@ export const bookData: Part[] = [ title: "Soul派对房:陌生人成交的最快场景", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.6 Soul派对房:陌生人成交的最快场景.md", }, { @@ -489,10 +436,62 @@ export const bookData: Part[] = [ title: "飞书中台:从聊天到成交的流程化体系", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.7 飞书中台:从聊天到成交的流程化体系.md", }, + { + id: "9.8", + title: "餐饮女孩:6万营收、1万利润的死撑生意", + price: 1, + isFree: false, + filePath: + "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.8 餐饮女孩:6万营收、1万利润的死撑生意.md", + }, + { + id: "9.9", + title: "电竞生态:从陪玩到签约到酒店的完整链条", + price: 1, + isFree: false, + filePath: + "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.9 电竞生态:从陪玩到签约到酒店的完整链条.md", + }, + { + id: "9.10", + title: "淘客大佬:损耗30%的白色通道", + price: 1, + isFree: false, + filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.10 淘客大佬:损耗30%的白色通道.md", + }, + { + id: "9.11", + title: "蔬菜供应链:农户才是最赚钱的人", + price: 1, + isFree: false, + filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.11 蔬菜供应链:农户才是最赚钱的人.md", + }, + { + id: "9.12", + title: "美业整合:一个人的公司如何月入十万", + price: 1, + isFree: false, + filePath: + "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.12 美业整合:一个人的公司如何月入十万.md", + }, + { + id: "9.13", + title: "AI工具推广:一个隐藏的高利润赛道", + price: 1, + isFree: false, + filePath: + "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.13 AI工具推广:一个隐藏的高利润赛道.md", + }, + { + id: "9.14", + title: "大健康私域:一个月150万的70后", + price: 1, + isFree: false, + filePath: "book/第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例/9.14 大健康私域:一个月150万的70后.md", + }, ], }, ], @@ -509,26 +508,23 @@ export const bookData: Part[] = [ sections: [ { id: "10.1", - title: "AI代聊与岗位替换", + title: "AI时代:哪些工作会消失,哪些会崛起", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第五篇|真实的社会/第10章|未来职业的变化趋势/10.1 AI代聊与岗位替换.md", + filePath: "book/第五篇|真实的社会/第10章|未来职业的变化趋势/10.1 AI时代:哪些工作会消失,哪些会崛起.md", }, { id: "10.2", - title: "系统化工作vs杂乱工作", + title: "一人公司:为什么越来越多人选择单干", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第五篇|真实的社会/第10章|未来职业的变化趋势/10.2 系统化工作 vs 杂乱工作.md", + filePath: "book/第五篇|真实的社会/第10章|未来职业的变化趋势/10.2 一人公司:为什么越来越多人选择单干.md", }, { id: "10.3", title: "为什么链接能力会成为第一价值", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第五篇|真实的社会/第10章|未来职业的变化趋势/10.3 为什么链接能力会成为第一价值.md", }, { @@ -536,7 +532,6 @@ export const bookData: Part[] = [ title: "新型公司:Soul-飞书-线下的三位一体", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第五篇|真实的社会/第10章|未来职业的变化趋势/10.4 新型公司:Soul-飞书-线下的三位一体.md", }, ], @@ -547,34 +542,31 @@ export const bookData: Part[] = [ sections: [ { id: "11.1", - title: "城市之间的模式差", + title: "私域经济:为什么流量越来越贵", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第五篇|真实的社会/第11章|中国社会商业生态的未来/11.1 城市之间的模式差.md", + filePath: "book/第五篇|真实的社会/第11章|中国社会商业生态的未来/11.1 私域经济:为什么流量越来越贵.md", }, { id: "11.2", - title: "厦门样本:低成本高效率经济", + title: "银发经济与孤独经济:两个被忽视的万亿市场", price: 1, isFree: false, - unlockAfterDays: 3, - filePath: "book/第五篇|真实的社会/第11章|中国社会商业生态的未来/11.2 厦门样本:低成本高效率经济.md", + filePath: + "book/第五篇|真实的社会/第11章|中国社会商业生态的未来/11.2 银发经济与孤独经济:两个被忽视的万亿市场.md", }, { id: "11.3", title: "流量红利的终局", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第五篇|真实的社会/第11章|中国社会商业生态的未来/11.3 流量红利的终局.md", }, { id: "11.4", - title: "大模型+供应链的组合拳", + title: "大模型 + 供应链的组合拳", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第五篇|真实的社会/第11章|中国社会商业生态的未来/11.4 大模型 + 供应链的组合拳.md", }, { @@ -582,7 +574,6 @@ export const bookData: Part[] = [ title: "社会分层的最终逻辑", price: 1, isFree: false, - unlockAfterDays: 3, filePath: "book/第五篇|真实的社会/第11章|中国社会商业生态的未来/11.5 社会分层的最终逻辑.md", }, ], @@ -591,6 +582,7 @@ export const bookData: Part[] = [ }, ] +// 特殊章节:序言、尾声、附录 export const specialSections = { preface: { id: "preface", @@ -601,21 +593,49 @@ export const specialSections = { }, epilogue: { id: "epilogue", - title: "尾声|终极答案:努力不是关键,选择才是", + title: "尾声|这本书的真实目的", price: 0, isFree: true, - filePath: "book/尾声|终极答案:努力不是关键,选择才是.md", + filePath: "book/尾声|这本书的真实目的.md", }, + appendix: [ + { + id: "appendix-1", + title: "附录1|Soul派对房精选对话", + price: 0, + isFree: true, + filePath: "book/附录/附录1|Soul派对房精选对话.md", + }, + { + id: "appendix-2", + title: "附录2|创业者自检清单", + price: 0, + isFree: true, + filePath: "book/附录/附录2|创业者自检清单.md", + }, + { + id: "appendix-3", + title: "附录3|本书提到的工具和资源", + price: 0, + isFree: true, + filePath: "book/附录/附录3|本书提到的工具和资源.md", + }, + ], } -export const FULL_BOOK_PRICE = getFullBookPrice() +// 获取总章节数 +export function getTotalSectionCount(): number { + let count = 0 + bookData.forEach((part) => { + part.chapters.forEach((chapter) => { + count += chapter.sections.length + }) + }) + return count // 64章 +} export function getAllSections(): Section[] { const sections: Section[] = [] - if (typeof window !== "undefined") { - const customSections = JSON.parse(localStorage.getItem("custom_sections") || "[]") - sections.push(...customSections) - } bookData.forEach((part) => { part.chapters.forEach((chapter) => { sections.push(...chapter.sections) @@ -625,18 +645,17 @@ export function getAllSections(): Section[] { } export function getSectionById(id: string): Section | undefined { - if (typeof window !== "undefined") { - const customSections = JSON.parse(localStorage.getItem("custom_sections") || "[]") as Section[] - const customSection = customSections.find((s) => s.id === id) - if (customSection) return customSection - } - for (const part of bookData) { for (const chapter of part.chapters) { const section = chapter.sections.find((s) => s.id === id) if (section) return section } } + // 检查特殊章节 + if (id === "preface") return specialSections.preface as Section + if (id === "epilogue") return specialSections.epilogue as Section + const appendix = specialSections.appendix.find((a) => a.id === id) + if (appendix) return appendix as Section return undefined } @@ -651,26 +670,4 @@ export function getChapterBySection(sectionId: string): { part: Part; chapter: C return undefined } -export function isSectionUnlocked(section: Section): boolean { - if (section.isFree) return true - if (!section.unlockAfterDays || !section.createdAt) return false - - const createdDate = new Date(section.createdAt) - const unlockDate = new Date(createdDate.getTime() + section.unlockAfterDays * 24 * 60 * 60 * 1000) - return new Date() >= unlockDate -} - -export function addCustomSection(section: Omit): Section { - const newSection: Section = { - ...section, - createdAt: new Date().toISOString(), - } - - if (typeof window !== "undefined") { - const customSections = JSON.parse(localStorage.getItem("custom_sections") || "[]") as Section[] - customSections.push(newSection) - localStorage.setItem("custom_sections", JSON.stringify(customSections)) - } - - return newSection -} +export const FULL_BOOK_PRICE = getFullBookPrice()