书架拆成「在读」和「书籍」两个独立页面

问题:两块内容挤在 /bookshelf/ 一页里,往下滚就从「在读」滑进「书籍」,
不像两个分类该有的样子。

拆法
- src/pages/bookshelf.astro -> src/pages/bookshelf/index.astro(书籍页)
- 新增 src/pages/bookshelf/reading.astro(在读页)
- 两页共用 bookshelf-content.astro,只多收一个 view: "reading" | "books",
  决定渲染哪一块;外壳(页眉/侧栏/页脚/样式)完全同一份,样式不会跑偏
- 在读页标题带真实本数:「在读 20 本 | 时之刃的小站」

侧栏「馆藏」改成真导航
- 在读 -> /bookshelf/reading/,数量 = 未读完本数(20)
- 书籍 -> /bookshelf/,数量 = 手工书单 + 微信读书已读完(0 + 6 = 6)
  原来这里只算 books.json 的 total,所以显示 0,正是截图里的问题
- 当前页用 aria-current="page" + 主色底高亮

页面互相不串
- /bookshelf/        : id=books、搜索框、已读完分组 6 张、无在读区块、0 条进度条
- /bookshelf/reading/: id=reading、在读卡片 20 张、20 条进度条、无书籍区块、
                       无搜索框、无已读完分组、0 张 data-weread-card

顺带更正 README 首段还写着的「读书/论文/漫画书架」。
This commit is contained in:
dsh
2026-09-15 10:58:27 +08:00
parent 8a867132de
commit 95e5111fd8
6 changed files with 62 additions and 10 deletions
+28 -4
View File
@@ -6,6 +6,17 @@ import booksData from "../../public/data/books.json"
import ReadingCard from "@/components/reading-card.astro"
import { formatReadingDate, getReadingData } from "@/lib/reading"
/**
* view="reading" -> 在读页(/bookshelf/reading/)
* view="books" -> 书籍页(/bookshelf/)
* 两页共用这个组件和同一套外壳,只是渲染其中一块。
*/
interface Props {
view?: "reading" | "books"
}
const { view = "books" } = Astro.props
const isReadingView = view === "reading"
// 微信读书数据。数据缺失时 reading 为 null,页面退化成空态,不报错。
const reading = getReadingData()
const readingBooks = reading?.books ?? []
@@ -107,15 +118,22 @@ const recommendBadge = (status?: string) => {
</div>
<div class="text-[10px] text-muted-foreground/80 uppercase tracking-[0.18em] mb-2 pl-2">馆藏</div>
{/* 漫画和论文两个分类已移除,只剩书籍;这里改成页内锚点,不再是 tab 切换 */}
{/* 漫画和论文两个分类已移除;在读和书籍现在是两个独立页面,这里是它们的导航 */}
<nav class="flex flex-row md:flex-col gap-1 mb-6 md:mb-8 overflow-x-auto md:overflow-visible -mx-1 px-1">
{[
{ href: "#reading", label: "在读", count: readingCount, svg: "reading" },
{ href: "#books", label: "书籍", count: bookStats.total, svg: "books" },
{ href: "/bookshelf/reading/", label: "在读", count: readingCount, svg: "reading", active: isReadingView },
{ href: "/bookshelf/", label: "书籍", count: bookStats.total + finishedReading.length, svg: "books", active: !isReadingView },
].map((it) => (
<a
href={it.href}
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm transition-colors whitespace-nowrap text-foreground/80 hover:bg-muted/60 hover:text-foreground"
data-astro-prefetch
aria-current={it.active ? "page" : undefined}
class={cn(
"flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm transition-colors whitespace-nowrap",
it.active
? "bg-primary text-primary-foreground"
: "text-foreground/80 hover:bg-muted/60 hover:text-foreground",
)}
>
<span class="inline-flex h-4 w-4 items-center justify-center shrink-0">
{it.svg === "reading" && (
@@ -145,6 +163,7 @@ const recommendBadge = (status?: string) => {
{/* ========================= 最近在读(微信读书) =========================
数据在构建期由 scripts/fetch-reading.mjs 抓到 src/data/reading.json,
这里只负责渲染,页面本身不发任何请求。 */}
{isReadingView && (
<div id="reading" class="mb-16 scroll-mt-24">
<div class="flex flex-wrap items-end justify-between gap-x-6 gap-y-2 mb-6">
<div>
@@ -172,7 +191,10 @@ const recommendBadge = (status?: string) => {
</div>
)}
</div>
)}
{!isReadingView && (
<>
{/* ---------- Header ---------- */}
<div id="books" class="mb-6 scroll-mt-24">
<div class="text-[11px] text-primary tracking-[0.22em] font-semibold uppercase mb-1.5">Collection · 02</div>
@@ -344,6 +366,8 @@ const recommendBadge = (status?: string) => {
</span>
<p class="text-muted-foreground">没有找到匹配的内容</p>
</div>
</>
)}
</section>
</div>
+1 -1
View File
@@ -1,5 +1,5 @@
{
"updatedAt": "2026-09-15T02:51:38.693Z",
"updatedAt": "2026-09-15T02:58:11.129Z",
"source": "weread",
"books": [
{
@@ -3,10 +3,10 @@ import BaseLayout from "@/layouts/BaseLayout.astro"
import BookshelfContent from "@/components/bookshelf-content.astro"
import { siteTitleSuffix } from "@/data/site"
const title = `书架 | ${siteTitleSuffix}`
const title = `书籍 | ${siteTitleSuffix}`
const description = "我的阅读记录与书籍推荐"
---
<BaseLayout {title} {description} fontProfile="interface">
<BookshelfContent />
<BookshelfContent view="books" />
</BaseLayout>
+17
View File
@@ -0,0 +1,17 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro"
import BookshelfContent from "@/components/bookshelf-content.astro"
import { getReadingData } from "@/lib/reading"
import { siteTitleSuffix } from "@/data/site"
const title = `在读 | ${siteTitleSuffix}`
const description = "最近在读的书,数据来自微信读书"
// 用真实本数做标题,抓不到数据时退回通用文案
const count = getReadingData()?.books.filter((book) => !book.finished).length ?? 0
const heading = count > 0 ? `在读 ${count} 本` : "在读"
---
<BaseLayout title={`${heading} | ${siteTitleSuffix}`} {description} fontProfile="interface">
<BookshelfContent view="reading" />
</BaseLayout>