feat: 公告系统 — Phase 2 完成

- Model/Repo/Service/Controller 全栈实现
- CRUD + ListActive(生效时间范围过滤)
- 管理后台独立页面 /admin/announcements(列表+弹窗CRUD)
- 前台首页 Hero 下方公告栏(info/success/warning/error 四色)
- Moderator+ 权限可管理公告
This commit is contained in:
2026-06-22 00:00:21 +08:00
parent 2d1dd5d401
commit fc57ed17d4
15 changed files with 837 additions and 8 deletions

View File

@ -0,0 +1,89 @@
{{template "admin/layout/base.html" .}}
<div class="page-header">
<h1>公告管理</h1>
<p class="page-desc">管理前台首页公告栏展示内容</p>
</div>
<div class="toolbar">
<button class="btn btn-primary" id="btnNewAnnouncement">+ 新建公告</button>
</div>
<table class="data-table" id="announcementTable">
<thead>
<tr>
<th style="width:60px">ID</th>
<th>标题</th>
<th style="width:80px">类型</th>
<th style="width:80px">状态</th>
<th style="width:140px">生效时间</th>
<th style="width:60px">排序</th>
<th style="width:160px">创建时间</th>
<th style="width:120px">操作</th>
</tr>
</thead>
<tbody id="announcementList">
<tr><td colspan="8" class="table-empty">加载中...</td></tr>
</tbody>
</table>
<div class="pagination" id="pagination" style="display:none"></div>
<!-- 编辑/新建弹窗 -->
<div class="modal-overlay" id="modalOverlay">
<div class="modal">
<div class="modal-header">
<h3 id="modalTitle">新建公告</h3>
<button class="modal-close" id="modalClose">&times;</button>
</div>
<div class="modal-body">
<form id="announcementForm">
<input type="hidden" id="announcementId">
<div class="form-group">
<label for="announcementTitle">标题</label>
<input type="text" id="announcementTitle" class="form-input" maxlength="200" required>
</div>
<div class="form-group">
<label for="announcementContent">内容</label>
<textarea id="announcementContent" class="form-textarea" rows="3" required></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label for="announcementType">类型</label>
<select id="announcementType" class="form-select">
{{range $k, $v := .Types}}
<option value="{{$k}}">{{$v.Label}}</option>
{{end}}
</select>
</div>
<div class="form-group">
<label for="announcementSort">排序(越小越前)</label>
<input type="number" id="announcementSort" class="form-input" value="0" min="0" style="width:100px">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="announcementStartAt">生效开始(可选)</label>
<input type="datetime-local" id="announcementStartAt" class="form-input">
</div>
<div class="form-group">
<label for="announcementEndAt">生效结束(可选)</label>
<input type="datetime-local" id="announcementEndAt" class="form-input">
</div>
</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="announcementIsActive" checked>
启用
</label>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" id="modalCancel">取消</button>
<button class="btn btn-primary" id="modalSave">保存</button>
</div>
</div>
</div>
<div class="toast" id="toast"></div>
{{template "admin/layout/footer.html" .}}

View File

@ -55,6 +55,10 @@
<span class="sidebar-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/></svg></span>
公户流水
</a>
<a href="/admin/announcements" class="sidebar-item {{if eq .CurrentPath "/admin/announcements"}}active{{end}}">
<span class="sidebar-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg></span>
公告管理
</a>
<a href="/admin/comments" class="sidebar-item {{if eq .CurrentPath "/admin/comments"}}active{{end}}">
<span class="sidebar-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></span>
评论管理