fix: 修复审核管理页面 CSP 违规 — inline onclick/onerror 移入事件委托
- 审核操作按钮(通过/拒绝)从 onclick 替换为 data-audit-action 属性 + document 级事件委托 - 分页按钮从 onclick 替换为 data-go-page 属性 + 事件委托 - 头像加载失败处理从 inline onerror 替换为 data-fallback 标记 + addEventListener 绑定
This commit is contained in:
@ -13,6 +13,34 @@ const pages = {
|
||||
};
|
||||
let pendingRejectId = null;
|
||||
|
||||
// 审核操作按钮事件委托(避免 inline onclick)
|
||||
function handleAuditClick(e) {
|
||||
const btn = e.target.closest('[data-audit-action]');
|
||||
if (!btn) return;
|
||||
const action = btn.dataset.auditAction;
|
||||
const id = parseInt(btn.dataset.auditId);
|
||||
if (action === 'approve') approveAudit(id);
|
||||
else if (action === 'reject') openRejectModal(id);
|
||||
}
|
||||
|
||||
// 分页按钮事件委托
|
||||
function handlePaginationClick(e) {
|
||||
const btn = e.target.closest('[data-go-page]');
|
||||
if (!btn) return;
|
||||
goPage(parseInt(btn.dataset.goPage));
|
||||
}
|
||||
|
||||
// 为动态插入的图片绑定 error 处理
|
||||
function attachImageErrorHandlers(container) {
|
||||
if (!container) return;
|
||||
container.querySelectorAll('img[data-fallback]').forEach(img => {
|
||||
img.addEventListener('error', function handler() {
|
||||
this.src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80"><rect fill="#eee" width="80" height="80"/><text x="40" y="45" text-anchor="middle" fill="#999" font-size="12">加载失败</text></svg>');
|
||||
this.removeEventListener('error', handler);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// TAB 切换
|
||||
document.querySelectorAll('.audit-tab').forEach(tab => {
|
||||
@ -26,6 +54,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// 全局事件委托:审核操作 & 分页
|
||||
document.addEventListener('click', handleAuditClick);
|
||||
document.addEventListener('click', handlePaginationClick);
|
||||
|
||||
// 初始加载
|
||||
loadCurrentTab();
|
||||
});
|
||||
@ -122,8 +154,8 @@ function renderUsernameRow(item) {
|
||||
<td><strong>${escapeHtml(item.new_value)}</strong></td>
|
||||
<td>${formatTime(item.created_at)}</td>
|
||||
<td class="col-actions">
|
||||
<button class="btn btn-primary btn-sm" onclick="approveAudit(${item.id})">通过</button>
|
||||
<button class="btn btn-outline btn-sm" onclick="openRejectModal(${item.id})">拒绝</button>
|
||||
<button class="btn btn-primary btn-sm" data-audit-action="approve" data-audit-id="${item.id}">通过</button>
|
||||
<button class="btn btn-outline btn-sm" data-audit-action="reject" data-audit-id="${item.id}">拒绝</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
@ -137,8 +169,8 @@ function renderBioRow(item) {
|
||||
<td title="${escapeHtml(text)}">${escapeHtml(display)}</td>
|
||||
<td>${formatTime(item.created_at)}</td>
|
||||
<td class="col-actions">
|
||||
<button class="btn btn-primary btn-sm" onclick="approveAudit(${item.id})">通过</button>
|
||||
<button class="btn btn-outline btn-sm" onclick="openRejectModal(${item.id})">拒绝</button>
|
||||
<button class="btn btn-primary btn-sm" data-audit-action="approve" data-audit-id="${item.id}">通过</button>
|
||||
<button class="btn btn-outline btn-sm" data-audit-action="reject" data-audit-id="${item.id}">拒绝</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
@ -161,14 +193,16 @@ function renderAvatarCards(items) {
|
||||
</div>
|
||||
<div class="avatar-compare">
|
||||
<span>新头像 →</span>
|
||||
<img src="${escapeHtml(item.new_value)}" alt="新头像" onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 80 80%22><rect fill=%22%23eee%22 width=%2280%22 height=%2280%22/><text x=%2240%22 y=%2245%22 text-anchor=%22middle%22 fill=%22%23999%22 font-size=%2212%22>加载失败</text></svg>'">
|
||||
<img src="${escapeHtml(item.new_value)}" alt="新头像" data-fallback="true">
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-primary btn-sm" onclick="approveAudit(${item.id})">通过</button>
|
||||
<button class="btn btn-outline btn-sm" onclick="openRejectModal(${item.id})">拒绝</button>
|
||||
<button class="btn btn-primary btn-sm" data-audit-action="approve" data-audit-id="${item.id}">通过</button>
|
||||
<button class="btn btn-outline btn-sm" data-audit-action="reject" data-audit-id="${item.id}">拒绝</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
// 为动态插入的图片绑定 error 处理器(CSP 不允许 inline onerror)
|
||||
attachImageErrorHandlers(grid);
|
||||
}
|
||||
|
||||
/* =========================================
|
||||
@ -258,9 +292,9 @@ function renderPagination(total, page, barId) {
|
||||
const bar = document.getElementById(barId);
|
||||
if (totalPages <= 1) { bar.innerHTML = ''; return; }
|
||||
|
||||
let html = `<button onclick="goPage(${page - 1})" ${page <= 1 ? 'disabled' : ''}>上一页</button>`;
|
||||
let html = `<button data-go-page="${page - 1}" ${page <= 1 ? 'disabled' : ''}>上一页</button>`;
|
||||
html += `<span class="page-current">第 ${page} / ${totalPages} 页(共 ${total} 条)</span>`;
|
||||
html += `<button onclick="goPage(${page + 1})" ${page >= totalPages ? 'disabled' : ''}>下一页</button>`;
|
||||
html += `<button data-go-page="${page + 1}" ${page >= totalPages ? 'disabled' : ''}>下一页</button>`;
|
||||
bar.innerHTML = html;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user