feat: 首页 Go SSR 与 React hydrate 同构,消壳层与帖行闪动

补齐侧栏/右栏图标与徽章、鉴权种子、StaticFeedList,并修正嵌套 a 与标题徽章对齐。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-01 07:07:38 +08:00
parent 9e134916a4
commit 44eed97fe0
26 changed files with 2530 additions and 159 deletions

View File

@@ -9,7 +9,7 @@ function FooterSep() {
return <span className="site-footer__sep" aria-hidden>·</span>;
}
/** 站点页脚:版权、友链/展柜入口、单页、备案号 */
/** 站点页脚:版权、友链/展柜入口、单页、备案号(结构与 Go writeSSRFooter 对齐) */
export default function SiteFooter() {
const { branding } = useSiteBranding();
const { footerPages } = useSitePages();
@@ -19,8 +19,42 @@ export default function SiteFooter() {
const icpURL = branding.icp_beian_url?.trim() || 'https://beian.miit.gov.cn/';
const showFriendLinks = limits.footer_show_friend_links !== false;
const showShowcase = !!limits.footer_show_showcase;
const hasNavBeforePages = showFriendLinks || showShowcase;
const hasNavBeforeIcp = hasNavBeforePages || footerPages.length > 0;
const navItems: React.ReactNode[] = [];
if (showFriendLinks) {
navItems.push(
<span key="links" className="site-footer__friend">
<Link to="/links"></Link>
</span>,
);
}
if (showShowcase) {
navItems.push(
<span key="showcase" className="site-footer__friend">
<Link to="/showcase"></Link>
</span>,
);
}
for (const p of footerPages) {
navItems.push(
<span key={p.slug} className="site-footer__friend">
<Link to={pagePath(p.slug, limits)}>{p.title}</Link>
</span>,
);
}
if (icp) {
navItems.push(
<a
key="icp"
href={icpURL}
target="_blank"
rel="noopener noreferrer"
className="site-footer__icp"
>
{icp}
</a>,
);
}
return (
<footer className="site-footer">
@@ -38,35 +72,8 @@ export default function SiteFooter() {
</div>
<nav className="site-footer__nav" aria-label="站点链接">
{showFriendLinks && (
<span className="site-footer__friend">
<Link to="/links"></Link>
</span>
)}
{showShowcase && (
<span className="site-footer__friend">
{showFriendLinks && <FooterSep />}
<Link to="/showcase"></Link>
</span>
)}
{footerPages.map((p, i) => (
<span key={p.slug} className="site-footer__friend">
{(hasNavBeforePages || i > 0) && <FooterSep />}
<Link to={pagePath(p.slug, limits)}>{p.title}</Link>
</span>
))}
{icp && (
<>
{hasNavBeforeIcp && <FooterSep />}
<a
href={icpURL}
target="_blank"
rel="noopener noreferrer"
className="site-footer__icp"
>
{icp}
</a>
</>
{navItems.flatMap((node, i) =>
i === 0 ? [node] : [<FooterSep key={`sep-${i}`} />, node],
)}
</nav>
</div>