feat: 新增创作中心(/studio),支持数据概览、内容管理、草稿箱、写文章
- 新增 StudioController,依赖 studioUseCase 接口,遵循 ISP 接口隔离 - PostRepo 新增 FindByUserIDAndStatus 方法,按用户+状态分页查询 - PostService 新增 ListByUser、GetOverview 及 StudioOverview 统计 - /studio 页面路由(概览/管理/草稿箱/写文章/数据分析)+ /api/studio API - 复用 Vditor 编辑器,studio-editor.js 对接 /api/studio/posts 端点 - 新增 studio.css(B站风格三栏布局+统计卡片+状态筛选tabs) - 模板引擎注册 add/subtract 函数,分页模板中直接使用
This commit is contained in:
105
templates/MetaLab-2026/html/studio/drafts.html
Normal file
105
templates/MetaLab-2026/html/studio/drafts.html
Normal file
@ -0,0 +1,105 @@
|
||||
{{template "layout/header.html" .}}
|
||||
<body>
|
||||
|
||||
{{template "layout/nav.html" .}}
|
||||
|
||||
<div class="studio-wrapper">
|
||||
{{template "studio/sidebar.html" .}}
|
||||
|
||||
<main class="studio-main">
|
||||
<div class="studio-header">
|
||||
<h1>草稿箱</h1>
|
||||
<span class="studio-subtitle">共 {{.Total}} 篇草稿</span>
|
||||
</div>
|
||||
|
||||
{{if .Error}}
|
||||
<div class="studio-error">{{.Error}}</div>
|
||||
{{else}}
|
||||
{{if .Posts}}
|
||||
<div class="post-list">
|
||||
{{range .Posts}}
|
||||
<div class="post-item">
|
||||
<div class="post-item-main">
|
||||
<span class="post-item-title draft-title">{{if .Title}}{{.Title}}{{else}}无标题{{end}}</span>
|
||||
<div class="post-item-meta">
|
||||
<span class="post-status-badge status-draft">草稿</span>
|
||||
<span>{{.CreatedAt.Format "2006-01-02 15:04"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="post-item-actions">
|
||||
<a href="/studio/write?id={{.ID}}" class="action-btn primary">继续编辑</a>
|
||||
<button class="action-btn" onclick="submitPost({{.ID}})" title="提交发布">提交</button>
|
||||
<button class="action-btn danger" onclick="deletePost({{.ID}})" title="删除">删除</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if gt .TotalPages 1}}
|
||||
<div class="pagination">
|
||||
{{if gt .Page 1}}
|
||||
<a href="/studio/drafts?page={{subtract .Page 1}}" class="page-btn">上一页</a>
|
||||
{{end}}
|
||||
<span class="page-info">第 {{.Page}} / {{.TotalPages}} 页</span>
|
||||
{{if lt .Page .TotalPages}}
|
||||
<a href="/studio/drafts?page={{add .Page 1}}" class="page-btn">下一页</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{else}}
|
||||
<div class="studio-empty">
|
||||
<p>✨ 草稿箱是空的</p>
|
||||
<a href="/studio/write" class="btn-primary">开始写作</a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function submitPost(id) {
|
||||
if (!confirm('确定要提交这篇草稿进行审核吗?')) return;
|
||||
|
||||
fetch('/api/studio/posts/' + id + '/submit', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': '{{.CSRFToken}}'
|
||||
}
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.message || '提交失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deletePost(id) {
|
||||
if (!confirm('确定要删除这篇草稿吗?此操作不可恢复。')) return;
|
||||
|
||||
fetch('/api/studio/posts/' + id, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': '{{.CSRFToken}}'
|
||||
}
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.message || '删除失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "layout/footer.html" .}}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user