From 6e7b46a2e9f3715907b3f4cb6244ceff820ffad3 Mon Sep 17 00:00:00 2001 From: freefire Date: Sun, 30 Aug 2026 05:55:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A6=96=E9=A1=B5=E9=97=A8=E9=9D=A2=20?= =?UTF-8?q?Feed=20=E5=AF=86=E5=BA=A6=E4=B8=8E=E4=BE=A7=E6=A0=8F=E5=88=86?= =?UTF-8?q?=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对齐 SPA 信息密度:头像/摘要/互动、高级搜索收起、浏览/板块分区。 Co-authored-by: Cursor --- docs/rebuild-spec/09-ssr-progress.md | 8 +- docs/rebuild-spec/10-design-system.md | 12 ++- public/assets/site.css | 146 ++++++++++++++++++++++++-- public/assets/site.js | 12 ++- routers/web/home.go | 55 +++++++++- templates/base/navbar.tmpl | 11 +- templates/home/feed.tmpl | 81 ++++++++------ templates/shared/board_aside.tmpl | 7 +- web_src/css/site.css | 146 ++++++++++++++++++++++++-- web_src/js/site.js | 12 ++- 10 files changed, 415 insertions(+), 75 deletions(-) diff --git a/docs/rebuild-spec/09-ssr-progress.md b/docs/rebuild-spec/09-ssr-progress.md index c31ee81..0dc9103 100644 --- a/docs/rebuild-spec/09-ssr-progress.md +++ b/docs/rebuild-spec/09-ssr-progress.md @@ -10,15 +10,15 @@ | 项 | 值 | |----|-----| -| **上一刀** | 青主题产品化重绘 + 静态资源 `?v=` 版本号 | -| **下一刀** | 需点名(移动侧栏抽屉 / OIDC Admin / S3 / Gitea / 表格表情…) | +| **上一刀** | 首页门面精修(Feed 头像/摘要/互动 + 高级搜索收起 + 侧栏分组) | +| **下一刀** | 需点名(移动侧栏抽屉 / 帖详情抛光 / OIDC Admin / S3…) | | **工作区** | 应干净 | --- ## 一句话状态 -核心论坛、Admin、Limits/伪静态、嵌套评论、Markdown 编辑器、主题、公开页青主题产品壳与资源版本号已齐;本分支 `routers/api` 仅保留机器入口。余量多为后置与体验打磨。 +核心论坛、Admin、Limits/伪静态、嵌套评论、Markdown 编辑器、主题、清青产品壳与资源版本号、首页门面密度已齐;本分支 `routers/api` 仅保留机器入口。余量多为后置与体验打磨。 --- @@ -32,7 +32,7 @@ | Markdown 工具栏 + preview | 已迁 | | 浅色 / 暗色 / 跟随系统 | 已迁(CSS 媒体查询,无 head 防闪脚本) | | 未挂载论坛 JSON API | 已删(对照 `main`) | -| 公开页设计系统 | 已迁(清青产品 chrome + `?v=`;[10-design-system.md](10-design-system.md)) | +| 公开页设计系统 | 已迁(清青 chrome + `?v=` + 首页 Feed 门面密度) | --- diff --git a/docs/rebuild-spec/10-design-system.md b/docs/rebuild-spec/10-design-system.md index 339ffb5..ff4bd6c 100644 --- a/docs/rebuild-spec/10-design-system.md +++ b/docs/rebuild-spec/10-design-system.md @@ -82,16 +82,18 @@ | 组件 | 规则 | |------|------| -| 顶栏 | 品牌 \| 搜索条 \| 发帖主按钮 + 主题 + 私信 + 用户 +「更多」菜单;告别链接墙 | -| 侧栏链 | sm;active = soft 底 + 左边青条 | -| Feed | 排序分段控件;搜索扁平工具条;细线列表;回复数右对齐强调 | -| 标签 / 按钮 / 评论 / 门控 | 同青 token;列表仍非卡片墙 | +| 顶栏 | 品牌 \| 搜索条 \|「+ 发帖」主按钮 + 主题 + 私信 + 用户 +「更多」;告别链接墙 | +| 左栏 | 分区「浏览 / 板块」;active = soft 底 + 左边青条 | +| Feed 行 | 头像 · 作者+相对时间 / 标题 / 摘要(最多 2 行)/ 标签 · 右栏回复/赞/阅读;细线分隔非卡片墙 | +| 高级搜索 | `
` 默认收起;有筛选条件或错误时展开;顶栏搜索仍 `/?keyword=` | +| 排序 | 分段控件 | +| 标签 / 按钮 / 评论 / 门控 | 同青 token | --- ## 5. 范围边界 -- **覆盖**:公开页壳层、资源 `?v=`、浅/暗色 +- **覆盖**:公开页壳层(含首页门面密度)、资源 `?v=`、浅/暗色 - **不覆盖**:Admin 深重绘、移动抽屉、虚拟滚动、TipTap、改站名 交叉:[06-pages-ux.md](06-pages-ux.md) · [09-ssr-progress.md](09-ssr-progress.md) diff --git a/public/assets/site.css b/public/assets/site.css index 9eb0542..d31a68f 100644 --- a/public/assets/site.css +++ b/public/assets/site.css @@ -303,14 +303,21 @@ html[data-theme="dark"] .j13-brand__mark { background: transparent; color: var(--j13-muted); border-radius: var(--j13-radius); - padding: 0.3rem 0.55rem; + padding: 0.3rem 0.5rem; font: inherit; font-size: 0.75rem; font-weight: 500; cursor: pointer; line-height: 1.3; + display: inline-flex; + align-items: center; + gap: 0.3rem; transition: color var(--j13-ease), border-color var(--j13-ease), background-color var(--j13-ease); } +.j13-theme-toggle__icon { + flex-shrink: 0; + opacity: 0.9; +} .j13-theme-toggle:hover { color: var(--j13-accent-ink); border-color: var(--j13-accent); @@ -415,6 +422,16 @@ html[data-theme="dark"] .j13-brand__mark { position: sticky; top: calc(var(--j13-header-h) + 0.75rem); } +.j13-aside__section { + margin: 0.85rem 0 0.35rem; + padding: 0 0.55rem; + font-size: 0.7rem; + font-weight: 650; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--j13-muted); +} +.j13-aside__section:first-child { margin-top: 0.15rem; } .j13-aside__link { padding: 0.45rem 0.6rem; border-radius: var(--j13-radius); @@ -450,7 +467,7 @@ html[data-theme="dark"] .j13-brand__mark { align-items: center; justify-content: space-between; gap: 0.75rem 1rem; - margin-bottom: 0.75rem; + margin-bottom: 0.65rem; } .j13-feed__title { margin: 0; @@ -485,13 +502,44 @@ html[data-theme="dark"] .j13-brand__mark { background: var(--j13-hover); } +.j13-search-advanced { + margin: 0 0 0.75rem; + scroll-margin-top: 4.5rem; +} +.j13-search-advanced__summary { + list-style: none; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 0.35rem; + color: var(--j13-muted); + font-size: 0.8125rem; + font-weight: 500; + padding: 0.25rem 0; + user-select: none; +} +.j13-search-advanced__summary::-webkit-details-marker { display: none; } +.j13-search-advanced__summary::before { + content: ""; + width: 0.4rem; + height: 0.4rem; + border-right: 1.5px solid currentColor; + border-bottom: 1.5px solid currentColor; + transform: rotate(-45deg); + transition: transform var(--j13-ease); +} +.j13-search-advanced[open] > .j13-search-advanced__summary::before { + transform: rotate(45deg); +} +.j13-search-advanced__summary:hover { color: var(--j13-accent); } +.j13-search-advanced .j13-search { margin-top: 0.45rem; } + .j13-search { margin: 0 0 0.85rem; padding: 0; border: 0; border-radius: 0; background: transparent; - scroll-margin-top: 4.5rem; } .j13-search--toolbar .j13-search__row { display: flex; @@ -541,38 +589,114 @@ html[data-theme="dark"] .j13-brand__mark { .j13-post-list { list-style: none; margin: 0; padding: 0; } .j13-post-item { - padding: 0.85rem 0.4rem; - margin: 0 -0.4rem; + display: grid; + grid-template-columns: 2.5rem minmax(0, 1fr) auto; + gap: 0.75rem 0.85rem; + padding: 1rem 0.45rem; + margin: 0 -0.45rem; border-top: 1px solid var(--j13-border); border-radius: var(--j13-radius); transition: background-color var(--j13-ease); + align-items: start; } .j13-post-item:first-child { border-top: 0; } .j13-post-item:hover { background: var(--j13-hover); } +.j13-post-item__avatar { + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; + overflow: hidden; + flex-shrink: 0; + background: var(--j13-soft); +} +.j13-post-item__avatar img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} +.j13-post-item__mark { + width: 100%; + height: 100%; + display: grid; + place-items: center; + font-weight: 700; + font-size: 0.95rem; + color: var(--j13-accent-ink); + background: var(--j13-soft); +} +.j13-post-item__body { min-width: 0; } +.j13-post-item__byline { + display: flex; + flex-wrap: wrap; + gap: 0.4rem 0.65rem; + align-items: baseline; + font-size: 0.75rem; + color: var(--j13-muted); + margin-bottom: 0.2rem; +} +.j13-post-item__byline a { + color: var(--j13-accent-ink); + text-decoration: none; + font-weight: 600; +} +.j13-post-item__byline a:hover { color: var(--j13-accent); } .j13-post-item__title { + display: inline-block; color: var(--j13-text); text-decoration: none; font-weight: 700; - font-size: 1.15rem; + font-size: 1.1rem; letter-spacing: -0.02em; line-height: 1.35; transition: color var(--j13-ease); } .j13-post-item__title:hover { color: var(--j13-accent); } +.j13-post-item__excerpt { + margin: 0.3rem 0 0; + font-size: 0.875rem; + line-height: 1.5; + color: var(--j13-muted); + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} .j13-post-item__meta { - margin-top: 0.35rem; + margin-top: 0.4rem; font-size: 0.75rem; color: var(--j13-muted); display: flex; flex-wrap: wrap; - gap: 0.45rem; + gap: 0.4rem; align-items: center; } -.j13-post-item__replies { - margin-left: auto; +.j13-post-item__stats { + display: flex; + flex-direction: column; + gap: 0.2rem; + align-items: flex-end; + font-size: 0.72rem; + color: var(--j13-muted); + font-variant-numeric: tabular-nums; + padding-top: 0.15rem; + white-space: nowrap; +} +.j13-post-item__stats strong { font-weight: 650; color: var(--j13-accent-ink); - font-variant-numeric: tabular-nums; +} +@media (max-width: 700px) { + .j13-post-item { + grid-template-columns: 2.25rem minmax(0, 1fr); + } + .j13-post-item__stats { + grid-column: 2; + flex-direction: row; + gap: 0.75rem; + align-items: center; + justify-content: flex-start; + } } .j13-widget__title { diff --git a/public/assets/site.js b/public/assets/site.js index db2c642..c95e977 100644 --- a/public/assets/site.js +++ b/public/assets/site.js @@ -27,9 +27,15 @@ document.documentElement.dataset.j13Ssr = "1"; } catch (e) {} var btn = document.getElementById("j13-theme-toggle"); if (btn) { - btn.textContent = LABELS[pref] || LABELS.system; - btn.setAttribute("aria-label", "主题:" + (LABELS[pref] || "")); - btn.title = "当前:" + (LABELS[pref] || "") + "(点击切换)"; + var label = btn.querySelector(".j13-theme-toggle__label"); + var text = LABELS[pref] || LABELS.system; + if (label) { + label.textContent = text; + } else { + btn.textContent = text; + } + btn.setAttribute("aria-label", "主题:" + text); + btn.title = "当前:" + text + "(点击切换)"; } } diff --git a/routers/web/home.go b/routers/web/home.go index d36bc90..b36d763 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -169,11 +169,18 @@ type PostListItem struct { ID uint Title string Href string + AuthorID uint AuthorName string + AuthorHref string + AvatarURL string + AuthorMark string // 无头像时的单字占位 BoardName string + Excerpt string Pinned bool Featured bool CommentCount int + LikeCount int + ViewCount int CreatedLabel string } @@ -329,10 +336,26 @@ func (d Deps) Home(c *gin.Context) { if it.Board.ID > 0 { bname = it.Board.Name } + excerptSrc := strings.TrimSpace(it.ContentPlain) + if excerptSrc == "" { + excerptSrc = services.ExcerptFromHTML(it.Content, 100) + } else { + excerptSrc = services.TruncateRunes(excerptSrc, 100) + } + avatar := strings.TrimSpace(it.User.Avatar) + authorID := it.UserID + authorHref := "" + if authorID > 0 { + authorHref = pl.UserPath(authorID) + } posts = append(posts, PostListItem{ - ID: it.ID, Title: it.Title, Href: pl.PostPath(it.ID), AuthorName: authorName, BoardName: bname, - Pinned: it.Pinned, Featured: it.Featured, CommentCount: it.CommentCount, - CreatedLabel: it.CreatedAt.Local().Format("2006-01-02 15:04"), + ID: it.ID, Title: it.Title, Href: pl.PostPath(it.ID), + AuthorID: authorID, AuthorName: authorName, AuthorHref: authorHref, + AvatarURL: avatar, AuthorMark: firstRuneOr(authorName, "姜"), + BoardName: bname, Excerpt: excerptSrc, + Pinned: it.Pinned, Featured: it.Featured, + CommentCount: it.CommentCount, LikeCount: it.LikeCount, ViewCount: it.ViewCount, + CreatedLabel: formatRelativeTime(it.CreatedAt), }) } @@ -353,3 +376,29 @@ func normalizeSort(s string) string { func formatTime(t time.Time) string { return t.Local().Format("2006-01-02 15:04") } + +// formatRelativeTime 列表用相对时间 +func formatRelativeTime(t time.Time) string { + if t.IsZero() { + return "" + } + now := time.Now() + d := now.Sub(t) + if d < 0 { + d = 0 + } + switch { + case d < time.Minute: + return "刚刚" + case d < time.Hour: + return fmt.Sprintf("%d 分钟前", int(d.Minutes())) + case d < 24*time.Hour: + return fmt.Sprintf("%d 小时前", int(d.Hours())) + case d < 48*time.Hour: + return "昨天" + case d < 30*24*time.Hour: + return fmt.Sprintf("%d 天前", int(d.Hours()/24)) + default: + return t.Local().Format("2006-01-02") + } +} diff --git a/templates/base/navbar.tmpl b/templates/base/navbar.tmpl index aeff96e..43bdb60 100644 --- a/templates/base/navbar.tmpl +++ b/templates/base/navbar.tmpl @@ -20,15 +20,20 @@
{{if .LoggedIn}} - 发帖 + + 发帖 {{else}} 登录 注册 {{end}} - + {{if .LoggedIn}} 私信{{if gt .UnreadCount 0}} {{.UnreadCount}}{{end}} - {{.ViewerName}} · {{.ViewerPoints}} + {{.ViewerName}} {{end}}
更多 diff --git a/templates/home/feed.tmpl b/templates/home/feed.tmpl index 7ae1662..8bce785 100644 --- a/templates/home/feed.tmpl +++ b/templates/home/feed.tmpl @@ -8,43 +8,62 @@ 热门讨论
- + {{if not .Posts}}

{{if .SearchError}}请调整搜索条件后重试。{{else if .HasSearch}}未找到匹配帖子。{{else}}暂无帖子。{{end}}

{{else}}
    {{range .Posts}}
  • - {{.Title}} -
    - {{if .Pinned}}置顶{{end}} - {{if .Featured}}精华{{end}} - {{if .BoardName}}{{.BoardName}}{{end}} - {{.AuthorName}} - {{.CreatedLabel}} - {{.CommentCount}} 回复 +
    + {{if .AvatarURL}} + + {{else}} + + {{end}} +
    +
    + + {{.Title}} + {{if .Excerpt}}

    {{.Excerpt}}

    {{end}} +
    + {{if .Pinned}}置顶{{end}} + {{if .Featured}}精华{{end}} + {{if .BoardName}}{{.BoardName}}{{end}} +
    +
    +
    + {{.CommentCount}} 回复 + {{.LikeCount}} + {{.ViewCount}} 阅读
  • {{end}} diff --git a/templates/shared/board_aside.tmpl b/templates/shared/board_aside.tmpl index 5426ba5..0107d21 100644 --- a/templates/shared/board_aside.tmpl +++ b/templates/shared/board_aside.tmpl @@ -1,7 +1,12 @@ {{define "shared/board_aside"}}