feat: 帖/评待审与被拒状态对作者可见
详情页横幅与楼层标签,避免作者误以为内容未保存。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -63,7 +63,7 @@
|
||||
- [x] 点赞切换;收藏切换;收藏列表 — SSR `/favorites`
|
||||
- [x] 浏览量 — 详情页计数
|
||||
- [x] 举报帖子 — SSR 表单 + `/admin/reports`
|
||||
- [ ] 内容审核状态展示(作者可见待审/被拒)
|
||||
- [x] 内容审核状态展示(作者可见待审/被拒)— SSR 帖详情横幅 + 评论楼层标签
|
||||
|
||||
### D.1 帖子类型
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
- [x] 评论举报 — SSR
|
||||
- [x] @ 提及 → 通知 — SSR 发评/审通调用 `NotifyCommentMentions`
|
||||
- [x] 回复提醒(站内信 + 可选邮件)— SSR 发评/审通调用 `NotifyCommentPublished`
|
||||
- [ ] 审核中 / 被拒评论可见性规则
|
||||
- [x] 审核中 / 被拒评论可见性规则 — 服务层已有;SSR 楼层标签(作者/管理员)
|
||||
- [x] 管理员:通过 / 拒绝待审评论 — SSR `/admin/moderation`(回收站/修订未迁)
|
||||
|
||||
---
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
|
||||
**Feed**:排序条(最新发帖 / 最新回复 / 热门)+ 搜索面板(关键词、标签、作者、仅标题;已迁 SSR GET)+ 列表项(标题、作者、板块徽章、回复数、置顶/精华)。虚拟滚动 / `feed_list_style` 未迁。
|
||||
|
||||
**帖子详情**:标题区操作(赞、藏、举报、编辑、管理操作)→ 特殊组件(投票卡 / 悬赏条 / 抽奖卡)→ 正文 `PostContent`(门控块 UI)→ 文章目录 → 作者卡片 → 修订入口 → 评论线程 + 评论框。
|
||||
**帖子详情**:标题区操作(赞、藏、举报、编辑、管理操作)→ 待审/被拒横幅(作者/管理员)→ 特殊组件(投票卡 / 悬赏条 / 抽奖卡)→ 正文 `PostContent`(门控块 UI)→ 文章目录 → 作者卡片 → 修订入口 → 评论线程 + 评论框。
|
||||
|
||||
### 2.3 右栏 RightPanel
|
||||
|
||||
|
||||
@@ -298,6 +298,25 @@ body.j13-body {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.j13-tag--pin { background: #fef3c7; color: #92400e; }
|
||||
.j13-tag--pending { background: #fff7ed; color: #9a3412; }
|
||||
.j13-tag--rejected { background: #fef2f2; color: #991b1b; }
|
||||
.j13-mod-banner {
|
||||
margin: 0.5rem 0 0.75rem;
|
||||
padding: 0.65rem 0.85rem;
|
||||
border-radius: var(--j13-radius);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.j13-mod-banner--pending {
|
||||
background: #fff7ed;
|
||||
color: #9a3412;
|
||||
border: 1px solid #fed7aa;
|
||||
}
|
||||
.j13-mod-banner--rejected {
|
||||
background: #fef2f2;
|
||||
color: #991b1b;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
.j13-tag--feat { background: #ede9fe; color: #5b21b6; }
|
||||
|
||||
.j13-empty { color: var(--j13-muted); }
|
||||
|
||||
@@ -39,10 +39,13 @@ type PostPageData struct {
|
||||
CommentCount int
|
||||
Comments []CommentView
|
||||
CommentsLocked bool
|
||||
CanEdit bool
|
||||
CanEdit bool
|
||||
CanViewRevisions bool
|
||||
CanReportPost bool
|
||||
IsAdmin bool
|
||||
CanReportPost bool
|
||||
IsAdmin bool
|
||||
Status string
|
||||
StatusLabel string
|
||||
ShowModerationBanner bool
|
||||
}
|
||||
|
||||
// CommentView 评论
|
||||
@@ -63,6 +66,8 @@ type CommentView struct {
|
||||
CanReport bool
|
||||
CanEdit bool
|
||||
CanDelete bool
|
||||
Status string
|
||||
StatusLabel string
|
||||
}
|
||||
|
||||
// PostView GET /post/:id
|
||||
@@ -101,6 +106,12 @@ func (d Deps) PostView(c *gin.Context) {
|
||||
CanReport: ctx.IsSigned() && (cm.UserID == 0 || cm.UserID != ctx.UserID()),
|
||||
CanEdit: !cm.ContentHidden && d.Comment.CanUserEditComment(&cm, ctx.UserID(), ctx.IsAdmin()),
|
||||
CanDelete: !cm.ContentHidden && d.Comment.CanUserDeleteComment(&cm, ctx.UserID(), ctx.IsAdmin()),
|
||||
Status: cm.Status,
|
||||
}
|
||||
if cm.Status == models.ContentStatusPending || cm.Status == models.ContentStatusRejected {
|
||||
if ctx.IsAdmin() || (cm.UserID > 0 && cm.UserID == ctx.UserID()) {
|
||||
view.StatusLabel = contentStatusLabel(cm.Status)
|
||||
}
|
||||
}
|
||||
if cm.ReplyTarget != nil {
|
||||
view.ReplyToID = cm.ReplyTarget.ID
|
||||
@@ -131,6 +142,15 @@ func (d Deps) PostView(c *gin.Context) {
|
||||
chrome := d.chrome(ctx, post.Title+" · "+d.Settings.SiteBranding().Name, "", "post/body")
|
||||
chrome.ActiveBoard = post.BoardID
|
||||
|
||||
statusLabel := ""
|
||||
showBanner := false
|
||||
if post.Status == models.ContentStatusPending || post.Status == models.ContentStatusRejected {
|
||||
if ctx.IsAdmin() || post.UserID == ctx.UserID() {
|
||||
statusLabel = contentStatusLabel(post.Status)
|
||||
showBanner = true
|
||||
}
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, "post", PostPageData{
|
||||
PageChrome: chrome, PostID: post.ID,
|
||||
PostPath: url.QueryEscape(fmt.Sprintf("/post/%d", post.ID)),
|
||||
@@ -143,13 +163,27 @@ func (d Deps) PostView(c *gin.Context) {
|
||||
Liked: d.Post.IsLiked(ctx.UserID(), post.ID), Favorited: d.Post.IsFavorited(ctx.UserID(), post.ID),
|
||||
BodyHTML: body, CommentCount: len(cv), Comments: cv,
|
||||
CommentsLocked: post.CommentsLocked,
|
||||
CanEdit: d.Post.CanUserEdit(post, ctx.UserID(), ctx.IsAdmin()),
|
||||
CanEdit: d.Post.CanUserEdit(post, ctx.UserID(), ctx.IsAdmin()),
|
||||
CanViewRevisions: canViewPostRevisions(post, ctx.UserID(), ctx.IsAdmin()),
|
||||
CanReportPost: ctx.IsSigned() && post.UserID != ctx.UserID(),
|
||||
IsAdmin: ctx.IsAdmin(),
|
||||
CanReportPost: ctx.IsSigned() && post.UserID != ctx.UserID(),
|
||||
IsAdmin: ctx.IsAdmin(),
|
||||
Status: post.Status,
|
||||
StatusLabel: statusLabel,
|
||||
ShowModerationBanner: showBanner,
|
||||
})
|
||||
}
|
||||
|
||||
func contentStatusLabel(status string) string {
|
||||
switch status {
|
||||
case models.ContentStatusPending:
|
||||
return "审核中"
|
||||
case models.ContentStatusRejected:
|
||||
return "已拒绝"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func postTypeLabel(t string) string {
|
||||
switch t {
|
||||
case models.PostTypeQuestion:
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
<article class="j13-post" data-post-id="{{.PostID}}" data-csrf="{{.CSRF}}" data-logged-in="{{if .LoggedIn}}1{{else}}0{{end}}">
|
||||
<header class="j13-post__header">
|
||||
<h1 class="j13-post__title">{{.PostTitle}}</h1>
|
||||
{{if .ShowModerationBanner}}
|
||||
<p class="j13-mod-banner j13-mod-banner--{{.Status}}" role="status">
|
||||
{{if eq .Status "pending"}}本帖正在审核中,仅你与管理员可见。{{else if eq .Status "rejected"}}本帖未通过审核,仅你与管理员可见。{{else}}{{.StatusLabel}}{{end}}
|
||||
</p>
|
||||
{{end}}
|
||||
<div class="j13-post__meta">
|
||||
{{if .StatusLabel}}<span class="j13-tag j13-tag--{{.Status}}">{{.StatusLabel}}</span>{{end}}
|
||||
{{if .Pinned}}<span class="j13-tag j13-tag--pin">置顶</span>{{end}}
|
||||
{{if .Featured}}<span class="j13-tag j13-tag--feat">精华</span>{{end}}
|
||||
{{if .EditLocked}}<span class="j13-tag">禁编</span>{{end}}
|
||||
@@ -98,6 +104,7 @@
|
||||
<span class="j13-comment__floor">#{{.Floor}}</span>
|
||||
{{if .AuthorID}}<a href="/user/{{.AuthorID}}">{{.AuthorName}}</a>{{else}}<span>{{.AuthorName}}</span>{{end}}
|
||||
<span>{{.CreatedLabel}}</span>
|
||||
{{if .StatusLabel}}<span class="j13-tag j13-tag--{{.Status}}">{{.StatusLabel}}</span>{{end}}
|
||||
{{if .IsPrivate}}<span class="j13-tag">私密</span>{{end}}
|
||||
{{if .ReplyToFloor}}
|
||||
<span class="j13-comment__reply-to">回复 <a href="#floor-{{.ReplyToFloor}}">#{{.ReplyToFloor}}</a>{{if .ReplyToAuthor}} {{.ReplyToAuthor}}{{end}}</span>
|
||||
|
||||
@@ -298,6 +298,25 @@ body.j13-body {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.j13-tag--pin { background: #fef3c7; color: #92400e; }
|
||||
.j13-tag--pending { background: #fff7ed; color: #9a3412; }
|
||||
.j13-tag--rejected { background: #fef2f2; color: #991b1b; }
|
||||
.j13-mod-banner {
|
||||
margin: 0.5rem 0 0.75rem;
|
||||
padding: 0.65rem 0.85rem;
|
||||
border-radius: var(--j13-radius);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.j13-mod-banner--pending {
|
||||
background: #fff7ed;
|
||||
color: #9a3412;
|
||||
border: 1px solid #fed7aa;
|
||||
}
|
||||
.j13-mod-banner--rejected {
|
||||
background: #fef2f2;
|
||||
color: #991b1b;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
.j13-tag--feat { background: #ede9fe; color: #5b21b6; }
|
||||
|
||||
.j13-empty { color: var(--j13-muted); }
|
||||
|
||||
Reference in New Issue
Block a user