From 0cfcbd060e61420514d0a5b2951e1a28975c54a2 Mon Sep 17 00:00:00 2001 From: dsh Date: Tue, 15 Sep 2026 09:55:26 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B5=E8=84=9A=E6=94=B9=E7=89=88=EF=BC=9A?= =?UTF-8?q?=E5=88=A0=E6=8E=89=E9=87=8D=E5=A4=8D=E7=9A=84=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E6=A0=8F=EF=BC=8C=E6=A1=8C=E9=9D=A2=E8=A1=A5=E7=AB=99=E7=82=B9?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=EF=BC=9B=E6=88=91=E7=9A=84=E4=BB=8B=E7=BB=8D?= =?UTF-8?q?=E5=8F=AF=E7=82=B9=E5=8E=BB=E5=85=B3=E4=BA=8E=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 页脚「导航」栏 - 它是顶部导航的逐项复制(9 条名字和目标完全一样),手机上白占 312px - 已整栏删除;桌面端改用「最新文章」(最近 4 篇,按发布时间倒序, 不把置顶提前),把重复变成发现 - 手机端「最新文章」隐藏(上面就是文章列表,再列一遍没意义) - 不影响 SEO:顶部导航 hidden md:flex 和移动端菜单里的同一批链接 在每页 HTML 里都还在,爬虫照样抓得到 站点信息(新增) - 新增 src/lib/site-stats.ts:文章数/偶得数/分类数/标签数/总字数 在构建期算;「已运行 N 天」「最近更新」由页脚内联脚本按真实时间刷新, 避免站点久不重建时数字停在构建那天;服务端先渲染构建时兜底值,关 JS 不空 - 页脚加一条站点信息带:建站时间 / 已运行 / 文章 / 偶得 / 总字数 / 最近更新 桌面 6 列一行,手机 3 列两行 - 实测数值:2026-07-17 建站、已运行 60 天、30 篇文章、8 条偶得、 2.9 万字、最近更新 59 天前 「我的介绍」跳关于页 - 页脚 BRAND 栏整块包 ,加 hover 反馈(头像描边转主色、 站名转主色、右上箭头位移)和常显的「关于我 →」提示(手机没有 hover, 不能只在 hover 才出现) - 侧栏作者卡片同样处理。注意这里**只包到签名那一段** —— 下面的社交图标 本身是 , 不能嵌套 ;页脚 BRAND 栏没有嵌套链接,所以可以整块包 - 两处都给 aria-label,避免读屏把「头像 alt + 站名 + 签名」念成链接名 工程改动 - footer.tsx -> footer.astro:React 组件不能在构建期 await 统计数据, 换成 Astro 组件后可以直接取;12 处引用同步改成默认导入 - README 补「页脚」一节,并修正组件路径 验证(headless Chrome 131,禁用缓存) - 桌面 1280:页脚 529px,三栏(我的介绍/最新文章/联系)+ 站点信息 + 底栏, 页脚内 nav 链接数 0,无横向溢出 - 手机 390:页脚 822px(原 957),最新文章 display:none,三栏只剩两栏 - 点页脚「我的介绍」-> /about/;点侧栏作者卡片 -> /about/,均正常 - 页面内 `a a` 嵌套数量:页脚 0、侧栏 0 --- README.md | 25 +++- src/components/bookshelf-content.astro | 2 +- src/components/footer.astro | 188 +++++++++++++++++++++++++ src/components/footer.tsx | 138 ------------------ src/components/sidebar.tsx | 40 ++++-- src/lib/site-stats.ts | 87 ++++++++++++ src/pages/[...page].astro | 2 +- src/pages/about.astro | 2 +- src/pages/friends.astro | 2 +- src/pages/gallery/[album].astro | 2 +- src/pages/gallery/index.astro | 2 +- src/pages/index.astro | 2 +- src/pages/posts/[...slug].astro | 2 +- src/pages/studies/[id].astro | 2 +- src/pages/studies/index.astro | 2 +- src/pages/thoughts/index.astro | 2 +- src/pages/timeline/index.astro | 2 +- 17 files changed, 336 insertions(+), 166 deletions(-) create mode 100644 src/components/footer.astro delete mode 100644 src/components/footer.tsx create mode 100644 src/lib/site-stats.ts diff --git a/README.md b/README.md index acf4d08..827f463 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ epigraph: 题记 # 可选 | 评论(twikoo) | `src/data/comments.ts` | | 友链列表 | `src/data/friends.json` | | 首页打字机文案 | `src/components/hero.tsx` 顶部的 `phrases` | -| 导航栏、页脚 | `src/components/site-header.astro`、`src/components/footer.tsx` | +| 导航栏、页脚 | `src/components/site-header.astro`、`src/components/footer.astro` | | 配色与全局样式 | `src/styles/global.css` | | 代码块主题 | `astro.config.mjs` 里 `expressiveCode` 的 `themes` | | 免责声明模板 | `src/data/disclaimers.ts` | @@ -233,6 +233,29 @@ prose-p:leading-[1.78] md:prose-p:leading-[1.85] `--header-h: 69px` 是固定页眉的实测高度,移动端菜单和这条胶囊条都靠它定位。**改页眉内边距或头像尺寸时记得同步改它**,否则吸附位置会错。 +### 页脚 + +`src/components/footer.astro`(原来是个 React 组件 `footer.tsx`,为了能在构建期 `await` 统计数据改成了 Astro 组件;引用方式随之从 `import { Footer } from "@/components/footer"` 变成 `import Footer from "@/components/footer.astro"`)。 + +结构: + +| 区块 | 内容 | 显示范围 | +|---|---|---| +| 我的介绍 | 头像 + 站名 + 签名,**整块可点去 `/about/`** | 全部 | +| 最新文章 | 最近 4 篇(按发布时间倒序,不把置顶提前) | 桌面 | +| 联系 | 社交图标 + 订阅 RSS | 全部 | +| 站点信息 | 建站时间 / 已运行 / 文章 / 偶得 / 总字数 / 最近更新 | 全部(想只在桌面显示就给它加 `hidden md:grid`) | +| 底栏 | 版权 / 备案 / 主题署名 | 全部 | + +**原来的「导航」栏已删除** —— 它是顶部导航的逐项复制(9 条名字和目标完全一样),手机上白占 312px。桌面端改用「最新文章」,把重复变成发现。不影响 SEO:顶部导航和移动端菜单里的链接在每页 HTML 里都还在。 + +两点需要注意: + +- **「我的介绍」整块包 `` 是合法的**,因为这一块里没有别的链接。但**侧栏的作者卡片不行** —— 它下面有社交图标(本身是 ``),`` 不能嵌套。所以那里只包到签名那一段。 +- 整块链接要给 `aria-label`,否则读屏会把「头像 alt + 站名 + 签名」整段念成链接名。 + +统计数据在 `src/lib/site-stats.ts`:文章数/偶得数/分类数/标签数/总字数在构建时算,「已运行 N 天」和「最近更新」由页脚里的内联脚本按**真实时间**刷新(站点久不重建也不会停在构建那天),服务端会先渲染一份构建时兜底值,关 JS 也不空。加减统计项改 `footer.astro` 里的 `statCells` 数组即可。 + ### 磨砂玻璃 `global.css` 里有四个 token: diff --git a/src/components/bookshelf-content.astro b/src/components/bookshelf-content.astro index fd27ae5..23a2dcf 100644 --- a/src/components/bookshelf-content.astro +++ b/src/components/bookshelf-content.astro @@ -1,6 +1,6 @@ --- import SiteHeader from "@/components/site-header.astro" -import { Footer } from "@/components/footer" +import Footer from "@/components/footer.astro" import { cn } from "@/lib/utils" import booksData from "../../public/data/books.json" import comicsData from "../../public/data/comics.json" diff --git a/src/components/footer.astro b/src/components/footer.astro new file mode 100644 index 0000000..6f46f67 --- /dev/null +++ b/src/components/footer.astro @@ -0,0 +1,188 @@ +--- +import type { ComponentType, SVGProps } from "react" +import { ArrowRight, Github, Mail, Music, Rss } from "lucide-react" +import { QqIcon } from "@/components/icons/qq-icon" +import { profile, type ExternalProfileLink, type ExternalProfileLinkType } from "@/data/profile" +import { site } from "@/data/site" +import { formatWords, getSiteStats } from "@/lib/site-stats" + +const iconMap: Record>> = { + qq: QqIcon, + music: Music, + github: Github, + email: Mail, +} + +const stats = await getSiteStats() +const contactLinks = profile.links.filter( + (link): link is ExternalProfileLink => link.type !== "wechat", +) + +const statCells = [ + { label: "建站时间", value: stats.startDate, live: undefined }, + { label: "已运行", value: String(stats.runningDays), unit: "天", live: "running-days" }, + { label: "文章", value: String(stats.postCount), unit: "篇", live: undefined }, + { label: "偶得", value: String(stats.thoughtCount), unit: "条", live: undefined }, + { label: "总字数", value: formatWords(stats.totalWords), unit: "字", live: undefined }, + { label: "最近更新", value: stats.lastUpdatedText, live: "last-update" }, +] +--- + + + + diff --git a/src/components/footer.tsx b/src/components/footer.tsx deleted file mode 100644 index db3a80d..0000000 --- a/src/components/footer.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import type { ComponentType, SVGProps } from "react" -import { Github, Mail, Music, Rss } from "lucide-react" -import { profile, type ExternalProfileLink, type ExternalProfileLinkType } from "@/data/profile" -import { site } from "@/data/site" -import { QqIcon } from "@/components/icons/qq-icon" - -const iconMap: Record>> = { - qq: QqIcon, - music: Music, - github: Github, - email: Mail, -} - -export function Footer() { - return ( -
-
-
- {/* Brand */} -
-
-
- {profile.name} -
- {site.name} -
-

- {profile.bio} -

-
- - {/* Navigation */} -
-
-

导航

- -
-
- - {/* Connect */} -
-
-

联系

-
- {profile.links.filter((link): link is ExternalProfileLink => link.type !== "wechat").map((link) => { - const Icon = iconMap[link.type] - return ( - - - - ) - })} -
-

通过 RSS 订阅,获取最新文章更新。

- - - 订阅 RSS - -
-
-
- - {/* Bottom */} -
-

© {site.startDate.slice(0, 4)} {profile.name}. All rights reserved.

- - - - 博客主题:朱墨留白 | VermilionVoid - -
-
-
- ) -} diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index aaef068..bb846a5 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -2,7 +2,7 @@ import { useState, type ReactNode } from "react" import { cn } from "@/lib/utils" -import { Calendar } from "lucide-react" +import { ArrowRight, Calendar } from "lucide-react" import { profile } from "@/data/profile" import { ProfileContactLink } from "@/components/profile-contact-link" @@ -39,23 +39,33 @@ export function Sidebar({ return (