--- import SiteHeader from "@/components/site-header.astro" import Footer from "@/components/footer.astro" import { cn } from "@/lib/utils" import booksData from "../../public/data/books.json" import { formatReadingDate, getReadingData } from "@/lib/reading" // 微信读书数据。数据缺失时 reading 为 null,页面退化成空态,不报错。 const reading = getReadingData() const readingBooks = reading?.books ?? [] // 读完的挪到「书籍」区,剩下的才留在「最近在读」 const inProgressReading = readingBooks.filter((book) => !book.finished) const finishedReading = readingBooks.filter((book) => book.finished) const readingCount = inProgressReading.length /** 「书籍」区里给微信读书已读书记一个分类名,同时作为可筛选的领域 */ const WEREAD_CATEGORY = "微信读书" // ---- Types interface BookFields { 书名: string 作者?: string[] 书籍简介?: string 书评?: string 封面?: { local_path: string }[] 推荐状态?: "推荐" | "中庸" | "不行" 阅读进度?: string 完成阅读时期?: string 领域?: string } interface BookRecord { fields: BookFields id: string record_id: string } // ---- Books const allBooks = (booksData.data.items as BookRecord[]).filter( (b) => b.fields.阅读进度 === "1", ) const booksByCategory = allBooks.reduce>((acc, book) => { const cat = book.fields.领域 || "未分类" ;(acc[cat] = acc[cat] || []).push(book) return acc }, {}) const bookCategories = ["全部", ...Object.keys(booksByCategory), ...(finishedReading.length > 0 ? [WEREAD_CATEGORY] : [])] const bookStatuses = ["全部", "推荐", "中庸", "不行"] const bookStats = { total: allBooks.length, recommended: allBooks.filter((b) => b.fields.推荐状态 === "推荐").length, categories: Object.keys(booksByCategory).length, } const getBookCover = (b: BookRecord): string | null => b.fields.封面?.[0]?.local_path ?? null // ---- Data for client scripts (modal, filters) const booksForClient = allBooks.map((b) => ({ id: b.id, title: b.fields.书名, authors: b.fields.作者?.join(", ") ?? "", category: b.fields.领域 || "未分类", status: b.fields.推荐状态 ?? "", cover: getBookCover(b) ?? "", review: b.fields.书评 ?? "", description: b.fields.书籍简介 ?? "", finished: b.fields.完成阅读时期 ?? "", })) // Status badge color helpers const recommendBadge = (status?: string) => { switch (status) { case "推荐": return "bg-status-positive-bg text-status-positive-fg" case "中庸": return "bg-status-neutral-bg text-status-neutral-fg" case "不行": return "bg-status-negative-bg text-status-negative-fg" default: return "bg-muted text-muted-foreground" } } ---
{/* ========================= Sidebar ========================= */} {/* ========================= Main ========================= */}
{/* ========================= 最近在读(微信读书) ========================= 数据在构建期由 scripts/fetch-reading.mjs 抓到 src/data/reading.json, 这里只负责渲染,页面本身不发任何请求。 */}
Collection · 01

最近在读

翻开的书,都还热着
{reading ? (

来自微信读书 · 更新于 {formatReadingDate(reading.updatedAt)}

) : null}
{inProgressReading.length > 0 ? ( {/* ---------- Header ---------- */}
Collection · 02

书籍

博学而笃志,切问而近思
{/* ---------- Stats ---------- */}
{bookStats.total}本已读 {bookStats.recommended}本推荐 {bookStats.categories}个领域
{/* ---------- Search + filters ---------- */}
{/* Books filters */}
领域 {bookCategories.map((cat) => ( ))}
{/* ========================= Views ========================= */} {/* ---- Books ---- */}
{Object.entries(booksByCategory).map(([category, books]) => (

{category}

({books.length})
{books.map((book) => { const badgeClass = recommendBadge(book.fields.推荐状态) const coverUrl = getBookCover(book) const authors = book.fields.作者?.join(", ") ?? "" return ( ) })}
))} {/* 微信读书里已读完的书,并入「书籍」区。 卡片结构和上面的书一样,但没有书评/简介,所以点击直接跳微信读书,不弹窗。 data-weread-card 让搜索框和领域/评价筛选同样作用于这一组。 */} {finishedReading.length > 0 && (

微信读书 · 已读完

({finishedReading.length})
)}
{/* Empty state (shared, shown when filters return nothing) */}