按 content-system.md 设计文档,实现帖子内容系统的最小可行 MVC。 新增: - Model: Post 模型,含 5 种状态(draft/pending/approved/rejected/locked) - Repository: post_repo.go,分页查询(前台 approved 列表 + 后台全状态筛选) - Service: post_service.go,核心业务逻辑(状态流转、Markdown 渲染 Goldmark) - Controller: post_controller + admin_post_controller,SSR 页面 6 个 + API 13 个 - Template: 列表/详情/编辑器/404 + 管理员列表,各配 CSS/JS - 路由: /posts, /posts/:id, /posts/new, /posts/:id/edit, REST API, Admin API - gomod: + github.com/yuin/goldmark v1.8.2 修改: - Main: AutoMigrate Post 表 - Router: deps 注册 PostService/Controller,路由注册 - Nav: 新增“帖子”导航链接 - Admin Sidebar: 新增“内容管理”入口 设计: - 审核开关复用 audit.enabled,开启时帖子为 draft,关闭后直接 approved - 仅 approved 公开可见,作者/admin 可预览非公开状态帖子 - 严格分层 ISP 接口: Controller → postUseCase, Service → postStore, Repo → *PostRepo - 状态保护: pending/locked 禁止编辑,rejected 编辑后自动重置为 draft
62 lines
2.2 KiB
JavaScript
62 lines
2.2 KiB
JavaScript
(function() {
|
|
function getCSRFToken() {
|
|
var meta = document.querySelector('meta[name="csrf-token"]');
|
|
return meta ? meta.getAttribute('content') : '';
|
|
}
|
|
|
|
function api(url, method, body) {
|
|
return fetch(url, {
|
|
method: method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': getCSRFToken()
|
|
},
|
|
body: body ? JSON.stringify(body) : undefined
|
|
}).then(function(r) { return r.json(); });
|
|
}
|
|
|
|
function refreshPage() { window.location.reload(); }
|
|
|
|
function handleClick(sel, actionFn) {
|
|
var btns = document.querySelectorAll(sel);
|
|
btns.forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
var id = btn.getAttribute('data-id');
|
|
if (confirm('确定执行此操作吗?')) {
|
|
actionFn(id);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 审核通过
|
|
handleClick('.post-approve', function(id) {
|
|
api('/api/admin/posts/' + id + '/approve', 'POST')
|
|
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
|
.catch(function() { alert('操作失败'); });
|
|
});
|
|
|
|
// 退回
|
|
handleClick('.post-reject', function(id) {
|
|
var reason = prompt('请输入退回理由(必填):');
|
|
if (!reason || !reason.trim()) return;
|
|
api('/api/admin/posts/' + id + '/reject', 'POST', { reason: reason.trim() })
|
|
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
|
.catch(function() { alert('操作失败'); });
|
|
});
|
|
|
|
// 锁定
|
|
handleClick('.post-lock', function(id) {
|
|
api('/api/admin/posts/' + id + '/lock', 'POST')
|
|
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
|
.catch(function() { alert('操作失败'); });
|
|
});
|
|
|
|
// 解锁
|
|
handleClick('.post-unlock', function(id) {
|
|
api('/api/admin/posts/' + id + '/unlock', 'POST')
|
|
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
|
.catch(function() { alert('操作失败'); });
|
|
});
|
|
})();
|