页脚改版:删掉重复的导航栏,桌面补站点信息;我的介绍可点去关于页
页脚「导航」栏 - 它是顶部导航的逐项复制(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 栏整块包 <a href="/about/">,加 hover 反馈(头像描边转主色、 站名转主色、右上箭头位移)和常显的「关于我 →」提示(手机没有 hover, 不能只在 hover 才出现) - 侧栏作者卡片同样处理。注意这里**只包到签名那一段** —— 下面的社交图标 本身是 <a>,<a> 不能嵌套 <a>;页脚 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
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { getCollection } from "astro:content"
|
||||
import { site } from "@/data/site"
|
||||
import { buildSidebarMeta, getSortedPosts, toPostMeta } from "./posts"
|
||||
|
||||
export type LatestPostLink = {
|
||||
title: string
|
||||
href: string
|
||||
date: string
|
||||
}
|
||||
|
||||
export type SiteStats = {
|
||||
/** 建站日期,YYYY-MM-DD */
|
||||
startDate: string
|
||||
/** 已运行天数(构建时算的兜底值,页面加载后由内联脚本按真实时间刷新) */
|
||||
runningDays: number
|
||||
postCount: number
|
||||
thoughtCount: number
|
||||
categoryCount: number
|
||||
tagCount: number
|
||||
totalWords: number
|
||||
/** 最后一篇的发布时间(ISO),用于前端算「最近更新」 */
|
||||
lastUpdatedIso: string | null
|
||||
/** 「最近更新」的构建时文案,作为无 JS 时的兜底 */
|
||||
lastUpdatedText: string
|
||||
/** 最近文章,按发布时间倒序(不把置顶提前) */
|
||||
latestPosts: LatestPostLink[]
|
||||
}
|
||||
|
||||
/** 把字数说成人话:12345 -> 1.2 万 */
|
||||
export const formatWords = (n: number) => {
|
||||
if (n >= 10000) return `${(n / 10000).toFixed(1)} 万`
|
||||
return String(n)
|
||||
}
|
||||
|
||||
const startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate())
|
||||
|
||||
export async function getSiteStats(latestCount = 4): Promise<SiteStats> {
|
||||
const posts = await getSortedPosts()
|
||||
const metas = posts.map(toPostMeta)
|
||||
const { categories, tags } = buildSidebarMeta(metas)
|
||||
const thoughts = await getCollection("thoughts")
|
||||
|
||||
const totalWords = metas.reduce((sum, meta) => sum + (meta.wordCount || 0), 0)
|
||||
|
||||
// 分类里有一条 id 为 "all" 的合集项,统计时要去掉
|
||||
const categoryCount = categories.filter((category) => category.id !== "all").length
|
||||
|
||||
const startDate = site.startDate
|
||||
const runningDays = Math.max(
|
||||
0,
|
||||
Math.floor((Date.now() - startOfDay(new Date(`${startDate}T00:00:00+08:00`)).getTime()) / 86_400_000),
|
||||
)
|
||||
|
||||
// 「最近更新」看的是最后动过的时间,有 updated 就用它
|
||||
const latestDate = posts.reduce<Date | null>((acc, post) => {
|
||||
const d = new Date(post.data.updated ?? post.data.published)
|
||||
return !acc || d > acc ? d : acc
|
||||
}, null)
|
||||
|
||||
const latestPosts = [...posts]
|
||||
.sort((a, b) => new Date(b.data.published).getTime() - new Date(a.data.published).getTime())
|
||||
.slice(0, latestCount)
|
||||
.map((post) => ({
|
||||
title: post.data.title,
|
||||
href: `/posts/${post.slug}/`,
|
||||
date: new Date(post.data.published).toISOString().slice(0, 10),
|
||||
}))
|
||||
|
||||
const sinceUpdate = latestDate
|
||||
? Math.max(0, Math.floor((Date.now() - latestDate.getTime()) / 86_400_000))
|
||||
: null
|
||||
const lastUpdatedText =
|
||||
sinceUpdate === null ? "—" : sinceUpdate === 0 ? "今天" : sinceUpdate === 1 ? "昨天" : `${sinceUpdate} 天前`
|
||||
|
||||
return {
|
||||
startDate,
|
||||
runningDays,
|
||||
postCount: posts.length,
|
||||
thoughtCount: thoughts.length,
|
||||
categoryCount,
|
||||
tagCount: tags.length,
|
||||
totalWords,
|
||||
lastUpdatedIso: latestDate ? latestDate.toISOString() : null,
|
||||
lastUpdatedText,
|
||||
latestPosts,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user