--- /** * 书评索引(/books):方案 C「抽书阅读」(用户 2026-11-03 拍板定稿)。 * * 为什么是这一版:五个方案里它是唯一在「书少、封面少」时也不露怯的—— * 一排放得下、长书名不裁、没有 4+1 孤儿位;点一行抽出封面与简介,靠原生 * details 零 JS。 * * 置顶契约(与文章同一套,见 src/lib/post-order.ts): * - frontmatter `pinned: true` → 排在最前(置顶优先 → 日期倒序) * - **支持多本置顶**:所有 pinned 书都默认展开,不限于一本 * - 置顶标记用「↑ + 横线」的回到顶部图标,不写文字(用户要求); * 展开/收起是另一个形状的 chevron,避免两个箭头混读 */ import { getCollection } from 'astro:content'; import { isVisiblePost } from '../../lib/post-visibility'; import { Image } from 'astro:assets'; import BaseLayout from '../../layouts/BaseLayout.astro'; import { t } from '../../i18n/ui'; import type { Locale } from '../../lib/locale'; import { sortPosts } from '../../lib/post-order'; import { pageNote } from '../../lib/site-content'; interface Props { locale?: Locale; } const { locale = 'zh' } = Astro.props; // 标题下说明:文字与样式都可后台改(白名单校验,非法值回落) const note = await pageNote('books', locale); const books = sortPosts( await getCollection( 'posts', ({ data }) => isVisiblePost(data) && data.category === 'reading', ), ); /** * description 回退契约:缺省取正文第一个非空段落(去 markdown 记号)前 60 字。 * 与历史实现保持一致——书架卡不能出现空白简介。 */ const firstPara = (body: string | undefined): string => { const line = (body ?? '') .split(/\n\s*\n/) .map((l) => l.trim()) .find((l) => l && !l.startsWith('#') && !l.startsWith('---')) ?? ''; return line.replace(/[*_>`[\]]/g, '').slice(0, 60); }; const items = books.map((b) => ({ id: b.id, title: b.data.title, author: b.data.bookAuthor ?? '', desc: b.data.description ?? firstPara(b.body), cover: b.data.cover, mood: b.data.mood ?? 'var(--ink-4)', pinned: b.data.pinned === true, href: locale === 'en' ? `/en/archives/${b.id}/` : `/archives/${b.id}/`, })); const pinnedCount = items.filter((i) => i.pinned).length; const L = { shelf: t('books_title', locale), count: locale === 'zh' ? `${items.length} 本` : `${items.length} books`, read: locale === 'zh' ? '读这篇书评 →' : 'Read the review →', pinned: t('pinned_label', locale), expand: locale === 'zh' ? '展开封面与简介' : 'Expand cover and blurb', }; ---

{L.shelf}{L.count} {pinnedCount > 0 && {pinnedCount} {L.pinned}}