From 2a46c01c85a465adaa13d6221218ee957716eb5f Mon Sep 17 00:00:00 2001 From: freefire Date: Sat, 29 Aug 2026 06:53:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20SSR=20=E5=85=AC=E5=BC=80=E6=9D=BF?= =?UTF-8?q?=E5=9D=97=E7=B4=A2=E5=BC=95=20/boards?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EOF Co-authored-by: Cursor --- docs/rebuild-spec/02-features.md | 2 +- docs/rebuild-spec/06-pages-ux.md | 1 + .../rebuild-spec/08-gitea-ssr-architecture.md | 2 + modules/webrender/render.go | 1 + public/assets/site.css | 56 +++++++++++++++++++ routers/web/boards.go | 53 ++++++++++++++++++ routers/web/chrome.go | 6 ++ routers/web/home.go | 2 +- templates/base/navbar.tmpl | 1 + templates/boards/list.tmpl | 28 ++++++++++ templates/embed.go | 2 +- templates/shared/board_aside.tmpl | 3 +- web_src/css/site.css | 56 +++++++++++++++++++ 13 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 routers/web/boards.go create mode 100644 templates/boards/list.tmpl diff --git a/docs/rebuild-spec/02-features.md b/docs/rebuild-spec/02-features.md index b977172..35c6f42 100644 --- a/docs/rebuild-spec/02-features.md +++ b/docs/rebuild-spec/02-features.md @@ -44,7 +44,7 @@ ## C. 板块 -- [x] 列出板块(含帖数等展示字段)— 前台侧栏 + Admin +- [x] 列出板块(含帖数等展示字段)— SSR `/boards` + 侧栏 + Admin - [x] 管理员:创建 / 改 / 删板块 — SSR `/admin/boards` - [x] 板块名称、描述、图标、色板索引、排序 - [x] 默认板块保障(空站可引导创建) diff --git a/docs/rebuild-spec/06-pages-ux.md b/docs/rebuild-spec/06-pages-ux.md index b9db134..78f8cdb 100644 --- a/docs/rebuild-spec/06-pages-ux.md +++ b/docs/rebuild-spec/06-pages-ux.md @@ -26,6 +26,7 @@ |------|------|-----| | `/` | Feed | 已迁 | | `/board/:id` | 板块 Feed | 已迁 | +| `/boards` | 板块索引 | 已迁 | | `/post/:id` | 帖详情 + 评论(回复/赞/私密)/赞/藏 | 已迁 | | `/compose` | 发帖(normal;textarea + 图片 + 门控插入) | 已迁 | | `/post/:id/edit` | 编辑帖 | 已迁 | diff --git a/docs/rebuild-spec/08-gitea-ssr-architecture.md b/docs/rebuild-spec/08-gitea-ssr-architecture.md index ea55ec8..2b41825 100644 --- a/docs/rebuild-spec/08-gitea-ssr-architecture.md +++ b/docs/rebuild-spec/08-gitea-ssr-architecture.md @@ -88,6 +88,8 @@ web_src/ → public/assets/ 私信:`/messages`、`/messages/with/:peerId`(系统 peer=0 只读;打开会话标已读)。 +公开浏览:`/boards` 板块索引(链到 `/board/:id`)。 + 友链:`/links`(列表、登录申请/取消、Logo 上传);Admin `/admin/friend-links`(品牌增删、审核、入口开关)。 Admin:`/admin/dashboard`、`/admin/boards`、`/admin/moderation`、`/admin/settings`(品牌/限流/敏感词/SMTP)、`/admin/friend-links`。 \ No newline at end of file diff --git a/modules/webrender/render.go b/modules/webrender/render.go index 062c0a1..07b089b 100644 --- a/modules/webrender/render.go +++ b/modules/webrender/render.go @@ -71,6 +71,7 @@ var parseGlobs = []string{ "favorites/*.tmpl", "messages/*.tmpl", "links/*.tmpl", + "boards/*.tmpl", } // Load 解析全部模板(进程内一次) diff --git a/public/assets/site.css b/public/assets/site.css index 7c972b6..d65ecae 100644 --- a/public/assets/site.css +++ b/public/assets/site.css @@ -610,3 +610,59 @@ points-only[data-locked="false"] { margin-right: 0.25rem; } +.j13-boards { max-width: 800px; margin: 0 auto; padding: 1rem 1rem 2.5rem; } +.j13-boards h1 { margin: 0 0 1rem; font-size: 1.4rem; } +.j13-boards__grid { + list-style: none; + margin: 0; + padding: 0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 0.85rem; +} +.j13-boards__card { + display: flex; + gap: 0.75rem; + align-items: flex-start; + padding: 0.85rem 0.9rem; + border: 1px solid var(--j13-border); + border-radius: var(--j13-radius); + background: var(--j13-bg); + color: inherit; + text-decoration: none; + min-height: 5rem; +} +.j13-boards__card:hover { border-color: var(--j13-accent); } +.j13-boards__dot { + width: 0.65rem; + height: 0.65rem; + border-radius: 50%; + margin-top: 0.35rem; + flex-shrink: 0; + background: var(--j13-accent); +} +.j13-boards__card[data-color="0"] .j13-boards__dot { background: #0f766e; } +.j13-boards__card[data-color="1"] .j13-boards__dot { background: #b45309; } +.j13-boards__card[data-color="2"] .j13-boards__dot { background: #1d4ed8; } +.j13-boards__card[data-color="3"] .j13-boards__dot { background: #be123c; } +.j13-boards__card[data-color="4"] .j13-boards__dot { background: #7c3aed; } +.j13-boards__card[data-color="5"] .j13-boards__dot { background: #047857; } +.j13-boards__card[data-color="6"] .j13-boards__dot { background: #c2410c; } +.j13-boards__card[data-color="7"] .j13-boards__dot { background: #0369a1; } +.j13-boards__body { + display: flex; + flex-direction: column; + gap: 0.2rem; + min-width: 0; +} +.j13-boards__body strong { font-size: 1rem; } +.j13-boards__icon { font-size: 0.75rem; } +.j13-boards__desc { + font-size: 0.85rem; + color: var(--j13-muted); + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + diff --git a/routers/web/boards.go b/routers/web/boards.go new file mode 100644 index 0000000..70719a0 --- /dev/null +++ b/routers/web/boards.go @@ -0,0 +1,53 @@ +package web + +import ( + "net/http" + + "git.iioio.com/freefire/jiang13-forum/modules/webctx" + "github.com/gin-gonic/gin" +) + +type boardsItemView struct { + ID uint + Name string + Description string + Icon string + ColorIndex int + PostCount int +} + +type boardsPageData struct { + PageChrome + Items []boardsItemView +} + +// BoardsGet 公开板块索引 +func (d Deps) BoardsGet(c *gin.Context) { + ctx := d.ctx(c) + d.renderBoards(ctx) +} + +func (d Deps) renderBoards(ctx *webctx.Context) { + brand := d.Settings.SiteBranding() + chrome := d.chrome(ctx, "板块 · "+brand.Name, "", "") + list, _ := d.Board.ListWithStats() + items := make([]boardsItemView, 0, len(list)) + for _, b := range list { + color := b.ColorIndex + if color < 0 { + color = int(b.ID % 8) + } + items = append(items, boardsItemView{ + ID: b.ID, + Name: b.Name, + Description: b.Description, + Icon: b.Icon, + ColorIndex: color, + PostCount: b.PostCount, + }) + } + ctx.HTML(http.StatusOK, "boards/list", boardsPageData{ + PageChrome: chrome, + Items: items, + }) +} diff --git a/routers/web/chrome.go b/routers/web/chrome.go index cbc81e0..9d6fc72 100644 --- a/routers/web/chrome.go +++ b/routers/web/chrome.go @@ -46,6 +46,7 @@ type PageChrome struct { ViewerName string Boards []BoardView ActiveBoard uint + Path string // 当前请求路径,供侧栏高亮 Inner string // 保留字段;入口模板已固定组合,不再动态 template CSRF string Flash string @@ -88,6 +89,10 @@ func (d Deps) chrome(ctx *webctx.Context, title, desc, inner string) PageChrome if ctx.IsSigned() && ctx.Doer != nil { viewerPoints = ctx.Doer.Points } + path := "" + if ctx.C != nil && ctx.C.Request != nil && ctx.C.Request.URL != nil { + path = ctx.C.Request.URL.Path + } return PageChrome{ Title: title, Description: desc, @@ -98,6 +103,7 @@ func (d Deps) chrome(ctx *webctx.Context, title, desc, inner string) PageChrome IsAdmin: ctx.IsAdmin(), ViewerName: name, Boards: bv, + Path: path, Inner: inner, CSRF: ctx.EnsureCSRF(), Flash: ctx.TakeFlash(), diff --git a/routers/web/home.go b/routers/web/home.go index 8ce508f..7883272 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -83,7 +83,7 @@ func Register(r *gin.Engine, deps Deps, authMW *auth.AuthMiddleware) { g.POST("/links/apply/:id/cancel", authMW.RequireAuth(), deps.LinksApplyCancelPost) g.POST("/links/logo", authMW.RequireAuth(), deps.LinksLogoUpload) g.GET("/projects", deps.PendingPage) - g.GET("/boards", deps.PendingPage) + g.GET("/boards", deps.BoardsGet) } // HomePageData Feed diff --git a/templates/base/navbar.tmpl b/templates/base/navbar.tmpl index 2e53c76..d1913fc 100644 --- a/templates/base/navbar.tmpl +++ b/templates/base/navbar.tmpl @@ -9,6 +9,7 @@