fix: 修复管理后台样式丢失 + CSP inline style 违规

- 评论管理页:创建 comments.css,controller 传递 ExtraCSS/ExtraJS,模板移除所有 inline style,JS 改用 CSS 类
- 修复 comments.js 加载顺序:通过 ExtraJS 在 common.js 之后加载,解决 api is not defined
- base.html:SVG inline style 移至 common.css
- dashboard/index.html:3 处 inline style 替换为 CSS 类
- audit/index.html:modal 移除冗余 style="display:none"
- fund_logs/logs:label inline style 移至 energy.css
- common.css 新增多个 CSS 规则
- energy.css 新增 .filter-bar label 规则
This commit is contained in:
2026-06-02 21:37:52 +08:00
parent 983f539268
commit 543325105c
11 changed files with 174 additions and 20 deletions

View File

@ -23,7 +23,9 @@ func NewAdminCommentController(cs adminCommentUseCase) *AdminCommentController {
// CommentsPage SSR 页面 // CommentsPage SSR 页面
func (ctrl *AdminCommentController) CommentsPage(c *gin.Context) { func (ctrl *AdminCommentController) CommentsPage(c *gin.Context) {
c.HTML(http.StatusOK, "admin/comments/index.html", common.BuildAdminPageData(c, gin.H{ c.HTML(http.StatusOK, "admin/comments/index.html", common.BuildAdminPageData(c, gin.H{
"Title": "评论管理", "Title": "评论管理",
"ExtraCSS": "/admin/static/css/comments.css",
"ExtraJS": "/admin/static/js/comments.js",
})) }))
} }

View File

@ -86,7 +86,7 @@
</div> </div>
<!-- 拒绝理由弹窗 --> <!-- 拒绝理由弹窗 -->
<div class="modal-overlay" id="rejectModal" style="display:none;"> <div class="modal-overlay" id="rejectModal">
<div class="modal-box"> <div class="modal-box">
<h3>拒绝审核</h3> <h3>拒绝审核</h3>
<p>请填写拒绝理由:</p> <p>请填写拒绝理由:</p>

View File

@ -7,19 +7,19 @@
<div class="admin-toolbar"> <div class="admin-toolbar">
<input type="text" id="commentSearch" class="admin-search-input" placeholder="搜索评论内容、作者或文章标题..." /> <input type="text" id="commentSearch" class="admin-search-input" placeholder="搜索评论内容、作者或文章标题..." />
<button id="commentSearchBtn" class="btn btn-secondary">搜索</button> <button id="commentSearchBtn" class="btn btn-secondary">搜索</button>
<button id="commentClearBtn" class="btn btn-secondary" style="display:none">清除</button> <button id="commentClearBtn" class="btn btn-secondary admin-hidden">清除</button>
</div> </div>
<div class="admin-table-wrap" id="commentTableWrap"> <div class="admin-table-wrap" id="commentTableWrap">
<table class="admin-table"> <table class="admin-table">
<thead> <thead>
<tr> <tr>
<th style="width:60px">ID</th> <th class="th-id">ID</th>
<th style="width:100px">作者</th> <th class="th-author">作者</th>
<th>评论内容</th> <th>评论内容</th>
<th style="width:160px">所属文章</th> <th class="th-post">所属文章</th>
<th style="width:140px">时间</th> <th class="th-time">时间</th>
<th style="width:100px">操作</th> <th class="th-actions">操作</th>
</tr> </tr>
</thead> </thead>
<tbody id="commentTableBody"></tbody> <tbody id="commentTableBody"></tbody>
@ -27,7 +27,6 @@
</div> </div>
<div class="admin-pagination" id="commentPagination"></div> <div class="admin-pagination" id="commentPagination"></div>
<div class="admin-loading" id="commentLoading" style="display:none">加载中...</div> <div class="admin-loading admin-hidden" id="commentLoading">加载中...</div>
{{template "admin/layout/footer.html" .}} {{template "admin/layout/footer.html" .}}
<script src="/admin/static/js/comments.js?v={{assetV "/admin/static/js/comments.js"}}" nonce="{{.CSPNonce}}"></script>

View File

@ -14,7 +14,7 @@
<div class="stat-info"> <div class="stat-info">
<div class="stat-label">帖子总数</div> <div class="stat-label">帖子总数</div>
<div class="stat-value">{{.PostTotal}}</div> <div class="stat-value">{{.PostTotal}}</div>
<div class="stat-desc">已发布 {{.PostApproved}}<span class="stat-badge badge-warn" style="margin-left: 12px;">待审核 {{.PostPending}}</span></div> <div class="stat-desc">已发布 {{.PostApproved}}<span class="stat-badge badge-warn">待审核 {{.PostPending}}</span></div>
</div> </div>
</div> </div>
<div class="stat-card"> <div class="stat-card">
@ -23,8 +23,8 @@
</div> </div>
</div> </div>
<div class="stats-grid" style="margin-top: 1rem;"> <div class="stats-grid mt-grid">
<div class="stat-card" style="grid-column: span 2;"> <div class="stat-card span-2">
<div class="stat-icon {{if .StoreDegraded}}stat-red{{else if eq .StoreType "redis"}}stat-green{{else}}stat-blue{{end}}"> <div class="stat-icon {{if .StoreDegraded}}stat-red{{else if eq .StoreType "redis"}}stat-green{{else}}stat-blue{{end}}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>
</div> </div>

View File

@ -5,7 +5,7 @@
</div> </div>
<div class="filter-bar"> <div class="filter-bar">
<label for="fund-log-type-filter" style="font-size:0.85rem;color:#4a5568;font-weight:500;">类型筛选:</label> <label for="fund-log-type-filter">类型筛选:</label>
<select id="fund-log-type-filter"> <select id="fund-log-type-filter">
<option value="">全部类型</option> <option value="">全部类型</option>
{{range $type, $name := .LogTypes}}<option value="{{$type}}">{{$name}}</option>{{end}} {{range $type, $name := .LogTypes}}<option value="{{$type}}">{{$name}}</option>{{end}}

View File

@ -5,7 +5,7 @@
</div> </div>
<div class="filter-bar"> <div class="filter-bar">
<label for="log-type-filter" style="font-size:0.85rem;color:#4a5568;font-weight:500;">类型筛选:</label> <label for="log-type-filter">类型筛选:</label>
<select id="log-type-filter"> <select id="log-type-filter">
<option value="">全部类型</option> <option value="">全部类型</option>
{{range $type, $name := .LogTypes}}<option value="{{$type}}">{{$name}}</option>{{end}} {{range $type, $name := .LogTypes}}<option value="{{$type}}">{{$name}}</option>{{end}}

View File

@ -16,7 +16,7 @@
<a href="/admin" class="admin-logo">MetaLab <span>管理面板</span></a> <a href="/admin" class="admin-logo">MetaLab <span>管理面板</span></a>
<div class="admin-header-right"> <div class="admin-header-right">
<span class="admin-user">{{.Username}}</span> <span class="admin-user">{{.Username}}</span>
<a href="/" class="admin-back-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14" style="vertical-align:-2px;margin-right:3px;"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>返回前台</a> <a href="/" class="admin-back-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>返回前台</a>
</div> </div>
</div> </div>
</header> </header>

View File

@ -0,0 +1,144 @@
/* ========== 管理后台评论管理 ========== */
/* --- 页面标题 --- */
.admin-page-header {
margin-bottom: 24px;
}
.admin-page-header h2 {
font-size: 22px;
font-weight: 600;
color: var(--text);
}
/* --- 工具栏/搜索 --- */
.admin-toolbar {
display: flex;
gap: 10px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.admin-search-input {
flex: 1;
min-width: 200px;
max-width: 400px;
padding: 8px 12px;
border: 1px solid var(--border);
border-radius: var(--radius);
font-size: 13px;
outline: none;
transition: border-color 0.2s;
}
.admin-search-input:focus {
border-color: var(--primary);
}
/* --- 表格 --- */
.admin-table-wrap {
background: var(--bg-card);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow-x: auto;
}
.admin-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.admin-table th {
text-align: left;
padding: 12px 16px;
background: #f9fafb;
color: var(--text-muted);
font-weight: 600;
font-size: 12px;
border-bottom: 2px solid var(--border);
white-space: nowrap;
}
.admin-table td {
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
font-size: 13px;
color: #374151;
vertical-align: middle;
}
.admin-table tbody tr:hover {
background: #f8f9ff;
}
/* 列宽 */
.th-id { width: 60px; }
.th-author { width: 100px; }
.th-post { width: 160px; }
.th-time { width: 140px; }
.th-actions { width: 100px; }
/* 表格空状态 / 加载失败 */
.admin-table-empty {
text-align: center;
padding: 40px;
color: #9ca3af;
}
.admin-table-error {
text-align: center;
padding: 40px;
color: #dc2626;
}
/* 评论内容列 */
.comment-body-cell {
max-width: 320px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* --- 分页 --- */
.admin-pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
margin-top: 20px;
}
.admin-page-link {
padding: 6px 14px;
border: 1px solid var(--border);
background: #fff;
border-radius: 4px;
font-size: 13px;
cursor: pointer;
color: var(--text);
text-decoration: none;
transition: all 0.15s;
}
.admin-page-link:hover {
border-color: var(--primary);
color: var(--primary);
}
.admin-page-info {
padding: 6px 14px;
font-size: 13px;
color: var(--text-muted);
}
/* --- 加载状态 --- */
.admin-loading {
text-align: center;
padding: 20px;
color: var(--text-muted);
font-size: 13px;
}
/* --- 按钮适配 --- */
.btn-secondary {
background: #e5e7eb;
color: #374151;
}
.btn-secondary:hover {
background: #d1d5db;
}
/* --- 工具类 --- */
.admin-hidden {
display: none;
}

View File

@ -71,6 +71,7 @@ a:hover { color: var(--primary-hover); }
padding: 4px 10px; border: 1px solid #636e72; padding: 4px 10px; border: 1px solid #636e72;
border-radius: 4px; transition: all 0.2s; border-radius: 4px; transition: all 0.2s;
} }
.admin-back-btn svg { vertical-align: -2px; margin-right: 3px; }
.admin-back-btn:hover { color: #fff; border-color: #fff; } .admin-back-btn:hover { color: #fff; border-color: #fff; }
/* --- 整体布局 --- */ /* --- 整体布局 --- */
@ -123,6 +124,7 @@ a:hover { color: var(--primary-hover); }
gap: 16px; gap: 16px;
margin-bottom: 24px; margin-bottom: 24px;
} }
.stats-grid.mt-grid { margin-top: 1rem; }
.stat-card { .stat-card {
background: var(--bg-card); background: var(--bg-card);
border-radius: var(--radius); border-radius: var(--radius);
@ -130,6 +132,7 @@ a:hover { color: var(--primary-hover); }
box-shadow: var(--shadow); box-shadow: var(--shadow);
display: flex; align-items: center; gap: 16px; display: flex; align-items: center; gap: 16px;
} }
.stat-card.span-2 { grid-column: span 2; }
.stat-icon { .stat-icon {
width: 44px; height: 44px; border-radius: 10px; width: 44px; height: 44px; border-radius: 10px;
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
@ -142,7 +145,7 @@ a:hover { color: var(--primary-hover); }
.stat-label { font-size: 12px; color: var(--text-muted); } .stat-label { font-size: 12px; color: var(--text-muted); }
.stat-value { font-size: 22px; font-weight: 600; color: var(--text); } .stat-value { font-size: 22px; font-weight: 600; color: var(--text); }
.stat-desc { font-size: 12px; color: var(--text-muted); margin-top: 4px; } .stat-desc { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
.stat-badge { display: inline-block; font-size: 11px; padding: 1px 8px; border-radius: 10px; margin-left: 8px; vertical-align: middle; } .stat-badge { display: inline-block; font-size: 11px; padding: 1px 8px; border-radius: 10px; margin-left: 12px; vertical-align: middle; }
.badge-warn { background: #fff3e0; color: var(--warning); } .badge-warn { background: #fff3e0; color: var(--warning); }
/* --- 信息卡片 --- */ /* --- 信息卡片 --- */

View File

@ -172,6 +172,12 @@
align-items: center; align-items: center;
} }
.filter-bar label {
font-size: 0.85rem;
font-weight: 500;
color: #4a5568;
}
.filter-bar select { .filter-bar select {
padding: 0.4rem 0.75rem; padding: 0.4rem 0.75rem;
border: 1px solid #cbd5e0; border: 1px solid #cbd5e0;

View File

@ -26,7 +26,7 @@
tbody.innerHTML = ''; tbody.innerHTML = '';
var items = d.data.items || []; var items = d.data.items || [];
if (items.length === 0) { if (items.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;padding:40px;color:#9ca3af">暂无评论</td></tr>'; tbody.innerHTML = '<tr><td colspan="6" class="admin-table-empty">暂无评论</td></tr>';
pagination.innerHTML = ''; pagination.innerHTML = '';
return; return;
} }
@ -48,7 +48,7 @@
}).catch(function() { }).catch(function() {
loading.style.display = 'none'; loading.style.display = 'none';
showToast('加载失败', 'error'); showToast('加载失败', 'error');
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;padding:40px;color:#dc2626">加载失败</td></tr>'; tbody.innerHTML = '<tr><td colspan="6" class="admin-table-error">加载失败</td></tr>';
}); });
} }