Files
jiang13-forum/templates/post/body.tmpl
freefire 1f777eebb1 feat: Markdown 工具栏编辑器与服务端预览
共用 md_editor 于发帖/改帖/评论,扩展 ComposeBodyToHTML,并提供 /compose/preview。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 04:50:07 +08:00

249 lines
11 KiB
Cheetah
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "post/body"}}
<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}}
{{if .CommentsLocked}}<span class="j13-tag">已结贴</span>{{end}}
{{if .PostTypeLabel}}<span class="j13-tag">{{.PostTypeLabel}}</span>{{end}}
{{if .IsQuestion}}
{{if .QuestionResolved}}<span class="j13-tag j13-tag--resolved">已解决</span>{{else}}<span class="j13-tag j13-tag--unresolved">未解决</span>{{end}}
{{end}}
{{if .BoardName}}<a class="j13-tag" href="{{.BoardHref}}">{{.BoardName}}</a>{{end}}
{{if .AuthorID}}<a href="{{.AuthorHref}}">{{.AuthorName}}</a>{{else}}<span>{{.AuthorName}}</span>{{end}}
<span>{{.CreatedLabel}}</span>
<span>{{.ViewCount}} 阅读</span>
</div>
{{if .LoggedIn}}
<div class="j13-post__actions">
{{if .CanToggleQuestion}}
<form method="post" action="/post/{{.PostID}}/question/resolve" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
{{if .QuestionResolved}}
<input type="hidden" name="resolved" value="0"/>
<button type="submit" class="j13-btn-secondary">标为未解决</button>
{{else}}
<input type="hidden" name="resolved" value="1"/>
<button type="submit">标为已解决</button>
{{end}}
</form>
{{end}}
<form method="post" action="/post/{{.PostID}}/like" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<button type="submit">{{if .Liked}}取消赞{{else}}赞{{end}} ({{.LikeCount}})</button>
</form>
<form method="post" action="/post/{{.PostID}}/favorite" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<button type="submit">{{if .Favorited}}取消收藏{{else}}收藏{{end}}</button>
</form>
{{if .CanEdit}}<a class="j13-linkbtn" href="{{.PostHref}}/edit">编辑</a>{{end}}
{{if .CanViewRevisions}}<a class="j13-linkbtn" href="{{.PostHref}}/revisions">修订历史</a>{{end}}
{{if .CanReportPost}}
<details class="j13-report">
<summary class="j13-linkbtn">举报</summary>
<form method="post" action="/post/{{.PostID}}/report" class="j13-report__form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<label>原因
<select name="reason" required>
<option value="">请选择</option>
<option value="spam">垃圾广告</option>
<option value="abuse">人身攻击 / 辱骂</option>
<option value="illegal">违法违规</option>
<option value="irrelevant">内容无关 / 灌水</option>
<option value="other">其他</option>
</select>
</label>
<label>补充说明(可选)
<input name="detail" maxlength="500" placeholder="可选"/>
</label>
<button type="submit" class="j13-btn j13-btn--sm">提交举报</button>
</form>
</details>
{{end}}
</div>
{{end}}
{{if .IsAdmin}}
<div class="j13-post__admin" role="group" aria-label="管理操作">
<form method="post" action="/post/{{.PostID}}/admin/pin" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<input type="hidden" name="enable" value="{{if .GlobalPinned}}0{{else}}1{{end}}"/>
<button type="submit" class="j13-linkbtn">{{if .GlobalPinned}}取消全局置顶{{else}}全局置顶{{end}}</button>
</form>
<form method="post" action="/post/{{.PostID}}/admin/board-pin" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<input type="hidden" name="enable" value="{{if .BoardPinned}}0{{else}}1{{end}}"/>
<button type="submit" class="j13-linkbtn">{{if .BoardPinned}}取消版内置顶{{else}}版内置顶{{end}}</button>
</form>
<form method="post" action="/post/{{.PostID}}/admin/feature" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<input type="hidden" name="enable" value="{{if .Featured}}0{{else}}1{{end}}"/>
<button type="submit" class="j13-linkbtn">{{if .Featured}}取消精华{{else}}设为精华{{end}}</button>
</form>
<form method="post" action="/post/{{.PostID}}/admin/edit-lock" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<input type="hidden" name="enable" value="{{if .EditLocked}}0{{else}}1{{end}}"/>
<button type="submit" class="j13-linkbtn">{{if .EditLocked}}允许编辑{{else}}禁止编辑{{end}}</button>
</form>
<form method="post" action="/post/{{.PostID}}/admin/comments-lock" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<input type="hidden" name="enable" value="{{if .CommentsLocked}}0{{else}}1{{end}}"/>
<button type="submit" class="j13-linkbtn">{{if .CommentsLocked}}解锁评论{{else}}锁定评论{{end}}</button>
</form>
<form method="post" action="/post/{{.PostID}}/admin/delete" class="j13-inline-form" onsubmit="return confirm('确认移入回收站?');">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<button type="submit" class="j13-linkbtn">移入回收站</button>
</form>
</div>
{{end}}
</header>
{{if .Bounty}}
<section class="j13-bounty" aria-label="悬赏">
<h2 class="j13-bounty__title">悬赏</h2>
<p class="j13-muted">
{{if eq .Bounty.Status "refunded"}}已退回{{else}}{{.Bounty.Points}} 积分{{end}}
· {{.Bounty.StatusLabel}}
</p>
{{if .Bounty.CanRefund}}
<form method="post" action="/post/{{.PostID}}/bounty/refund" class="j13-inline-form" onsubmit="return confirm('确认取消悬赏并退回积分?');">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<button type="submit" class="j13-btn-secondary">取消悬赏并退回</button>
</form>
{{else if .Bounty.RefundBlockReason}}
<p class="j13-muted">{{.Bounty.RefundBlockReason}}</p>
{{end}}
</section>
{{end}}
{{if .Lottery}}
<section class="j13-lottery" aria-label="抽奖">
<h2 class="j13-lottery__title">抽奖</h2>
<p class="j13-muted">
中奖 {{.Lottery.WinnerCount}} 人 · 参与 {{.Lottery.ParticipantCount}} 人 · {{.Lottery.StatusLabel}}
</p>
{{if .Lottery.CanDraw}}
<form method="post" action="/post/{{.PostID}}/lottery/draw" class="j13-inline-form" onsubmit="return confirm('确认开奖?人数不足会失败。');">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<button type="submit">开奖</button>
</form>
{{end}}
{{if .Lottery.Winners}}
<ul class="j13-lottery__winners">
{{range .Lottery.Winners}}
<li>{{if .UserID}}<a href="/user/{{.UserID}}">{{.Name}}</a>{{else}}{{.Name}}{{end}}</li>
{{end}}
</ul>
{{end}}
</section>
{{end}}
{{if .Poll}}
<section class="j13-poll" aria-label="投票">
<h2 class="j13-poll__title">投票</h2>
<p class="j13-muted">
{{if .Poll.Multi}}多选(最多 {{.Poll.MaxChoices}} 项){{else}}单选{{end}}
· 共 {{.Poll.TotalVotes}} 票
{{if .Poll.EndsLabel}} · 截止 {{.Poll.EndsLabel}}{{end}}
{{if .Poll.Closed}} · 已结束{{else if .Poll.HasVoted}} · 你已投票{{end}}
</p>
{{if .Poll.CanVote}}
<form method="post" action="/post/{{.PostID}}/poll/vote" class="j13-form j13-poll__form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<ul class="j13-poll__options">
{{range .Poll.Options}}
<li>
<label class="j13-check">
{{if $.Poll.Multi}}
<input type="checkbox" name="option_ids" value="{{.ID}}"/>
{{else}}
<input type="radio" name="option_id" value="{{.ID}}" required/>
{{end}}
{{.Text}}
</label>
</li>
{{end}}
</ul>
<button type="submit">提交投票</button>
</form>
{{else}}
<ul class="j13-poll__options">
{{range .Poll.Options}}
<li class="j13-poll__option{{if .Selected}} is-selected{{end}}">
<span class="j13-poll__opt-text">{{.Text}}</span>
{{if $.Poll.ShowResults}}
<span class="j13-poll__bar" style="--pct: {{.Percent}}%"></span>
<span class="j13-muted">{{.Percent}}%{{.VoteCount}}</span>
{{end}}
</li>
{{end}}
</ul>
{{if and (not $.LoggedIn) (not .Poll.Closed)}}
<p class="j13-muted"><a href="/login?redirect={{.PostPath}}">登录</a> 后可投票。</p>
{{end}}
{{end}}
{{if .Poll.CanClose}}
<form method="post" action="/post/{{.PostID}}/poll/close" class="j13-inline-form" onsubmit="return confirm('确认结束投票?');">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<button type="submit" class="j13-linkbtn">结束投票</button>
</form>
{{end}}
</section>
{{end}}
<div class="j13-post__content post-detail-content">{{safeHTML .BodyHTML}}</div>
</article>
<section class="j13-comments" id="comments">
<h2>评论 ({{.CommentCount}})</h2>
{{if not .Comments}}
<p class="j13-empty">暂无评论。</p>
{{else}}
<ul class="j13-comment-list">
{{range .Comments}}
{{template "post/comment_item" .}}
{{end}}
</ul>
{{end}}
{{if .LoggedIn}}
{{if .CommentsLocked}}
<p class="j13-muted">评论已锁定。</p>
{{else}}
<form class="j13-comment-form" method="post" action="/post/{{.PostID}}/comments" id="comment-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<input type="hidden" name="reply_to" id="comment-reply-to" value=""/>
<p class="j13-muted j13-comment-reply-hint" id="comment-reply-hint" hidden></p>
<label for="comment-content">发表评论</label>
{{template "shared/md_editor" (dict
"Name" "content"
"ID" "comment-content"
"Content" ""
"Rows" 5
"MaxLength" 8000
"Placeholder" "支持 Markdown可回复楼上"
"CSRF" .CSRF
"ShowGates" 0
"ShowUpload" 1
"Unsaved" 0
"Required" 1
)}}
<label class="j13-check">
<input type="checkbox" name="is_private" value="1"/>
私密评论(仅相关可见)
</label>
<div class="j13-form__row">
<button type="submit">提交</button>
<button type="button" class="j13-btn-secondary" id="comment-reply-clear" hidden>取消回复</button>
</div>
</form>
{{end}}
{{else}}
<p><a href="/login?redirect={{.PostPath}}">登录</a> 后回复。</p>
{{end}}
</section>
{{end}}