fix: 帖行评论数勿贴右缘,手机 SSR 顶栏/页脚与 React 同构

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-01 08:06:57 +08:00
parent 37d14a22d5
commit 6048d71759
4 changed files with 46 additions and 14 deletions

View File

@@ -520,10 +520,10 @@ export default function MainLayout() {
<div className="app-frame"> <div className="app-frame">
<header className="app-header"> <header className="app-header">
<div className="header-inner"> <div className="header-inner">
{isMobile && !isCompose && ( {!isCompose && (
<button <button
type="button" type="button"
className="header-icon-btn" className="header-icon-btn header-menu-btn"
onClick={openSidebar} onClick={openSidebar}
aria-label="打开导航菜单" aria-label="打开导航菜单"
aria-expanded={sidebarOpen} aria-expanded={sidebarOpen}
@@ -536,7 +536,7 @@ export default function MainLayout() {
{/* 任意页点 Logo回首页并强制刷新不展示会话缓存 */} {/* 任意页点 Logo回首页并强制刷新不展示会话缓存 */}
<button type="button" className="header-brand" onClick={() => navigateFeed(nav, '/', { refresh: true })}> <button type="button" className="header-brand" onClick={() => navigateFeed(nav, '/', { refresh: true })}>
<SiteBrandMark branding={branding} className="header-logo-mark" /> <SiteBrandMark branding={branding} className="header-logo-mark" />
{!isMobile && <span className="header-logo-text">{branding.name}</span>} <span className="header-logo-text">{branding.name}</span>
</button> </button>
{!isCompose && isMobile && ( {!isCompose && isMobile && (
@@ -621,7 +621,7 @@ export default function MainLayout() {
aria-label="发帖" aria-label="发帖"
> >
<Plus size={16} aria-hidden /> <Plus size={16} aria-hidden />
{!isMobile && <span></span>} <span className="header-compose-btn__label"></span>
</button> </button>
)} )}
@@ -641,17 +641,15 @@ export default function MainLayout() {
</button> </button>
)} )}
{!isMobile && ( <button
<button
type="button" type="button"
className="header-icon-btn" className="header-icon-btn header-theme-btn"
onClick={toggle} onClick={toggle}
aria-label={theme === 'light' ? '切换暗色模式' : '切换亮色模式'} aria-label={theme === 'light' ? '切换暗色模式' : '切换亮色模式'}
title={theme === 'light' ? '切换暗色模式' : '切换亮色模式'} title={theme === 'light' ? '切换暗色模式' : '切换亮色模式'}
> >
{theme === 'light' ? <Moon size={18} aria-hidden /> : <Sun size={18} aria-hidden />} {theme === 'light' ? <Moon size={18} aria-hidden /> : <Sun size={18} aria-hidden />}
</button> </button>
)}
{authLoading ? ( {authLoading ? (
<span className="header-auth-slot header-auth-slot--loading" aria-hidden /> <span className="header-auth-slot header-auth-slot--loading" aria-hidden />

View File

@@ -345,6 +345,16 @@ html.dark .top-progress {
} }
} }
@media (min-width: 769px) {
/* 桌面:只保留壳层贴底页脚,隐藏列表内流入页脚 */
.post-list-scroll > .site-footer {
display: none;
}
.header-menu-btn {
display: none;
}
}
.admin-brand-sections > .admin-settings-section { .admin-brand-sections > .admin-settings-section {
margin-top: 20px; margin-top: 20px;
padding-top: 16px; padding-top: 16px;
@@ -1974,10 +1984,17 @@ html.dark .ssr-theme-icon--sun {
.sidebar { display: none; } .sidebar { display: none; }
.header-inner { padding: 0 12px; gap: 8px; } .header-inner { padding: 0 12px; gap: 8px; }
.header-brand { flex-shrink: 0; } .header-brand { flex-shrink: 0; }
.header-logo-text { display: none; }
.header-menu-btn { display: inline-flex; }
.header-theme-btn { display: none; }
.header-compose-btn__label { display: none; }
.header-search-wrap { max-width: none; } .header-search-wrap { max-width: none; }
.header-compose-btn { width: 34px; padding: 0; justify-content: center; } .header-compose-btn { width: 34px; padding: 0; justify-content: center; }
.header-action-group { gap: 4px; } .header-action-group { gap: 4px; }
/* 手机:藏壳层贴底页脚(列表内 InFlow 负责) */
.app-frame > .site-footer { display: none; }
.post-search-field__input { .post-search-field__input {
font-size: 16px; font-size: 16px;
} }
@@ -3144,7 +3161,7 @@ a.post-row {
.post-row--v2 .post-meta { .post-row--v2 .post-meta {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: flex-start;
flex-wrap: nowrap; flex-wrap: nowrap;
gap: 8px; gap: 8px;
min-width: 0; min-width: 0;
@@ -3158,7 +3175,7 @@ a.post-row {
flex-wrap: nowrap; flex-wrap: nowrap;
gap: 0; gap: 0;
min-width: 0; min-width: 0;
flex: 1; flex: 0 1 auto;
overflow: hidden; overflow: hidden;
} }
@@ -3354,6 +3371,8 @@ a.post-row {
.post-row--v2 .post-stats { .post-row--v2 .post-stats {
gap: 8px; gap: 8px;
flex-shrink: 0;
margin-left: 0;
} }
.post-row--v2 .post-stat { .post-row--v2 .post-stat {

View File

@@ -342,6 +342,10 @@ func renderHomeSSRHTML(data *homeSSRData) string {
b.WriteString(`<div class="app-shell ssr-home"><div class="app-frame">`) b.WriteString(`<div class="app-shell ssr-home"><div class="app-frame">`)
b.WriteString(`<header class="app-header"><div class="header-inner">`) b.WriteString(`<header class="app-header"><div class="header-inner">`)
// 手机端汉堡:桌面 CSS 隐藏hydrate 后由 React 接管点击
b.WriteString(`<button type="button" class="header-icon-btn header-menu-btn" aria-label="打开导航菜单" title="导航">`)
b.WriteString(ssrIconMenu())
b.WriteString(`</button>`)
b.WriteString(`<a class="header-brand" href="/">`) b.WriteString(`<a class="header-brand" href="/">`)
if logo := strings.TrimSpace(boot.Branding.Logo); logo != "" { if logo := strings.TrimSpace(boot.Branding.Logo); logo != "" {
b.WriteString(`<img class="header-logo-mark" src="` + html.EscapeString(logo) + `" alt=""/>`) b.WriteString(`<img class="header-logo-mark" src="` + html.EscapeString(logo) + `" alt=""/>`)
@@ -366,11 +370,11 @@ func renderHomeSSRHTML(data *homeSSRData) string {
} }
b.WriteString(`<a class="header-compose-btn" href="` + composeHref + `" aria-label="发帖">`) b.WriteString(`<a class="header-compose-btn" href="` + composeHref + `" aria-label="发帖">`)
b.WriteString(ssrIconPlus()) b.WriteString(ssrIconPlus())
b.WriteString(`<span>发帖</span></a>`) b.WriteString(`<span class="header-compose-btn__label">发帖</span></a>`)
b.WriteString(`<div class="header-action-group">`) b.WriteString(`<div class="header-action-group">`)
// 主题:双图标 + CSS配合 documentElement.dark // 主题:双图标 + CSS配合 documentElement.dark;手机顶栏 CSS 隐藏
b.WriteString(`<button type="button" class="header-icon-btn" aria-label="切换主题" title="切换主题">`) b.WriteString(`<button type="button" class="header-icon-btn header-theme-btn" aria-label="切换主题" title="切换主题">`)
b.WriteString(`<span class="ssr-theme-icon ssr-theme-icon--moon">` + ssrIconMoon() + `</span>`) b.WriteString(`<span class="ssr-theme-icon ssr-theme-icon--moon">` + ssrIconMoon() + `</span>`)
b.WriteString(`<span class="ssr-theme-icon ssr-theme-icon--sun">` + ssrIconSun() + `</span>`) b.WriteString(`<span class="ssr-theme-icon ssr-theme-icon--sun">` + ssrIconSun() + `</span>`)
b.WriteString(`</button>`) b.WriteString(`</button>`)
@@ -593,7 +597,10 @@ func writeSSRFeed(b *strings.Builder, boot homeBootPayload, meta homeSSRMeta) {
writeSSRPostRow(b, &boot.Posts[i], boot.BoardID, meta.permalink, boot.Sort, titleOnly, needExcerpt, needThumb) writeSSRPostRow(b, &boot.Posts[i], boot.BoardID, meta.permalink, boot.Sort, titleOnly, needExcerpt, needThumb)
} }
} }
b.WriteString(`</div></div></div></div>`) b.WriteString(`</div>`)
// 手机端流入页脚(桌面 CSS 隐藏);壳层贴底页脚见 writeSSRFooter
writeSSRFooter(b, boot, meta)
b.WriteString(`</div></div></div>`)
} }
func ssrFilterQuery(boot homeBootPayload) url.Values { func ssrFilterQuery(boot homeBootPayload) url.Values {

View File

@@ -60,6 +60,14 @@ func ssrIconPlus() string {
) )
} }
func ssrIconMenu() string {
return ssrSVG(18,
`<path d="M4 5h16"/>`,
`<path d="M4 12h16"/>`,
`<path d="M4 19h16"/>`,
)
}
func ssrIconMoon() string { func ssrIconMoon() string {
return ssrSVG(18, return ssrSVG(18,
`<path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/>`, `<path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/>`,