fix: 修复帖子系统5项缺陷 + 代码块插入位置/language-class/分割线占位

- 作者显示 用户名(UID): 修正Post.AuthorName GORM标签从 -:all 改为 -:migration;<-:false;column:author_name
- 审核流程: 帖子详情页新增"提交审核"按钮(draft/rejected时作者可见)
- 图片上传: 新增 POST /api/posts/upload-image 端点,限制5MB/jpg/png/gif/webp
- 代码块语言标注: 工具栏插入代码块时弹出语言输入框,生成 language-xxx class
- 状态中文显示: 管理后台/帖子详情页状态改为 PostStatusDisplayNames 映射
- 修复代码块插入位置错误: init时调用switchMode同步DOM; 无选区时appendChild; 防<pre>嵌套
- 修复 bluemonday.UGCPolicy() 剥离 code/pre 的 class 属性,显式 AllowAttrs("class")
- 修复分割线工具栏插入多余text占位符: hr/link/image以外不强制填占位文本
This commit is contained in:
2026-05-27 19:22:47 +08:00
parent 1830f10e24
commit 8a8d801187
11 changed files with 281 additions and 28 deletions

View File

@ -29,7 +29,7 @@
<a href="/posts/{{.ID}}">{{.Title}}</a>
</h2>
<div class="post-card-meta">
<span class="post-author">{{.AuthorName}}</span>
<span class="post-author">{{if .AuthorName}}{{.AuthorName}}({{.UserID}}){{else}}UID{{.UserID}}{{end}}</span>
<span class="post-time">{{.CreatedAt.Format "2006-01-02 15:04"}}</span>
</div>
<p class="post-card-summary">{{printf "%.200s" .Body}}</p>

View File

@ -77,6 +77,9 @@
<button type="submit" class="btn btn-primary">{{if .Post}}保存修改{{else}}发布帖子{{end}}</button>
</div>
</form>
{{/* 隐藏的图片上传控件 */}}
<input type="file" id="imageFileInput" accept="image/jpeg,image/png,image/gif,image/webp" style="display:none;">
</div>
{{template "layout/footer.html" .}}

View File

@ -8,10 +8,10 @@
<header class="post-detail-header">
<h1 class="post-detail-title">{{.Post.Title}}</h1>
<div class="post-detail-meta">
<span class="post-author">{{.Post.AuthorName}}</span>
<span class="post-author">{{if .Post.AuthorName}}{{.Post.AuthorName}}({{.Post.UserID}}){{else}}UID{{.Post.UserID}}{{end}}</span>
<span class="post-time">{{.Post.CreatedAt.Format "2006-01-02 15:04"}}</span>
{{if ne .Post.Status "approved"}}
<span class="post-status post-status-{{.Post.Status}}">{{printf "%s" .Post.Status}}</span>
<span class="post-status post-status-{{.Post.Status}}">{{index $.StatusNames .Post.Status}}</span>
{{end}}
</div>
</header>
@ -36,6 +36,9 @@
{{if and $.Post (or (eq $.Post.UserID $.UID) $.CanAccessAdmin) (ne $.Post.Status "pending") (ne $.Post.Status "locked")}}
<a href="/posts/{{$.Post.ID}}/edit" class="btn btn-secondary">编辑</a>
{{end}}
{{if and $.Post (eq $.Post.UserID $.UID) (or (eq $.Post.Status "draft") (eq $.Post.Status "rejected"))}}
<button class="btn btn-primary" id="submitAuditBtn" data-id="{{$.Post.ID}}">提交审核</button>
{{end}}
{{if $.CanAccessAdmin}}
<a href="/admin/posts" class="btn btn-secondary">管理</a>
{{end}}
@ -46,5 +49,28 @@
{{template "layout/footer.html" .}}
<script src="/static/js/common.js"></script>
<script>
(function() {
var btn = document.getElementById('submitAuditBtn');
if (!btn) return;
btn.addEventListener('click', function() {
if (!confirm('确认提交审核?审核通过后将公开发布。')) return;
var csrf = document.querySelector('meta[name="csrf-token"]');
fetch('/api/posts/' + btn.dataset.id + '/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrf ? csrf.getAttribute('content') : ''
}
})
.then(function(r) { return r.json(); })
.then(function(d) {
if (d.success) { window.location.reload(); }
else { alert(d.message || '提交失败'); }
})
.catch(function() { alert('请求失败'); });
});
})();
</script>
</body>
</html>