Files
wordpress-theme-aksrt/assets/js/report.js
T
kate522 7c730b83d6 fix(report): unify the frontend report entry and modal
- enqueue report.js on singular pages too: the single meta box and
  comment report buttons rendered without their script handler, so
  clicking the report item did nothing
- align the report runtime default with the admin schema and the
  reference theme (user_report_s default true) so the public user
  home cover menu shows the report entry
- rebuild report.js on top of AksrtModalRuntime following the
  reference theme modal format: red warning header, preset reason
  badges with an other-reason textarea, violation link field, detail
  description and a cancel/submit footer; submit goes through the
  unified /report/submit endpoint
- keep report inside the comment three-dot dropdown and enrich the
  payload (target name/avatar) for the modal; inject configured
  report categories via aksrtData.reportCategories
- update the report contract test accordingly
2026-09-20 13:40:05 +08:00

254 lines
9.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* AKSRT Report — 前台统一举报弹窗
*
* 监听 [data-aksrt-report] 点击,弹出对齐参考主题格式的举报弹窗:
* 红色警示头 + 举报原因徽章(含“其它原因”)+ 违规链接 + 详细描述 + 取消/确认提交。
* 提交走 REST /aksrt/v1/report/submit(user/comment/post 三类目标统一入口)。
*
* @package AKSRT
*/
(function () {
'use strict';
var API_ROOT = (function () {
var d = window.aksrtData || {};
var raw = d.apiUrl || d.restUrl || d.rest_url || '/index.php?rest_route=/aksrt/v1';
return String(raw).replace(/\/$/, '');
})();
var NONCE = (window.aksrtData && window.aksrtData.restNonce) || '';
var TARGET_TITLES = {
user: '举报用户',
post: '举报文章',
comment: '举报评论',
};
function getReportCategories() {
// 预置原因:后台“用户&互动 → 禁封/举报”配置(aksrtData.reportCategories)。
var list = window.aksrtData && window.aksrtData.reportCategories;
if (Array.isArray(list)) {
var items = list
.map(function (cat) {
return {
slug: String((cat && (cat.slug || cat.value)) || ''),
label: String((cat && (cat.label || cat.name)) || ''),
};
})
.filter(function (cat) { return cat.slug !== '' && cat.label !== ''; });
if (items.length) return items;
}
return [
{ slug: 'spam', label: '垃圾信息' },
{ slug: 'abuse', label: '人身攻击' },
{ slug: 'copyright', label: '版权投诉' },
];
}
function escapeHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function buildReportModalHtml(data) {
var categories = getReportCategories();
var title = TARGET_TITLES[data.target_type] || '举报不良信息';
var html = '';
// 弹窗头:参考主题红色警示样式。
html += '<div class="flex jsb ac mb10">';
html += '<b class="flex ac c-red em12"><span class="toggle-radius mr10 c-red"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i></span>' + escapeHtml(title) + '</b>';
html += '<button type="button" class="aksrt-modal-runtime__close" data-aksrt-modal-close aria-label="关闭">&times;</button>';
html += '</div>';
html += '<form class="aksrt-report-form">';
html += '<div class="scroll-y mini-scrollbar max-vh7">';
// 被举报对象(头像 + 名称),由服务端渲染进 data-aksrt-report。
if (data.target_avatar || data.target_name) {
html += '<div class="flex ac mb10">';
if (data.target_avatar) {
html += '<img class="avatar-mini mr6" src="' + escapeHtml(data.target_avatar) + '" alt="">';
}
html += '<b>' + escapeHtml(data.target_name || '') + '</b>';
html += '</div>';
}
// 举报原因(徽章单选,含“其它原因”)。
html += '<div class="mb20">';
html += '<div class="em09 muted-2-color mb6">举报原因</div>';
html += '<div class="aksrt-report-reasons">';
categories.forEach(function (cat) {
html += '<button type="button" class="mr6 mb6 badg p2-10 pointer" data-report-reason="' + escapeHtml(cat.slug) + '">' + escapeHtml(cat.label) + '</button>';
});
html += '<button type="button" class="mr6 mb6 badg p2-10 pointer" data-report-reason="other">其它原因</button>';
html += '</div>';
html += '<div class="aksrt-report-other mt6" hidden>';
html += '<textarea class="form-control" name="reason_other" rows="2" placeholder="请填写举报原因"></textarea>';
html += '</div>';
html += '</div>';
// 违规链接。
html += '<div class="mb20">';
html += '<div class="em09 muted-2-color mb6">违规链接</div>';
html += '<input type="url" class="form-control" name="report_url" placeholder="请输入违规链接(选填)">';
html += '</div>';
// 详细描述。
html += '<div class="mb20">';
html += '<div class="em09 muted-2-color mb6">详细描述</div>';
html += '<textarea class="form-control" name="report_desc" rows="3" placeholder="请详细描述违规事项"></textarea>';
html += '</div>';
html += '</div>';
// 底栏:参考主题 but-average modal-buts 双按钮。
html += '<div class="but-average modal-buts">';
html += '<button type="button" class="but" data-aksrt-modal-close>取消</button>';
html += '<button type="button" class="but c-blue" data-aksrt-report-submit><i class="fa fa-check" aria-hidden="true"></i>确认提交</button>';
html += '</div>';
html += '</form>';
return html;
}
function selectedReason(shell) {
var active = shell.querySelector('[data-report-reason].is-active');
return active ? active.getAttribute('data-report-reason') : '';
}
function submitReport(session, data, shell) {
var form = shell.querySelector('.aksrt-report-form');
if (!form) return;
var submitBtn = form.querySelector('[data-aksrt-report-submit]');
var descInput = form.querySelector('[name="report_desc"]');
var otherInput = form.querySelector('[name="reason_other"]');
var urlInput = form.querySelector('[name="report_url"]');
var slug = selectedReason(shell);
var reason = (descInput && descInput.value || '').trim();
var other = (otherInput && otherInput.value || '').trim();
if (!slug && !reason) {
notyf('请选择或输入举报原因', 'danger');
return;
}
// “其它原因”时以自定义文本为举报原因。
if (slug === 'other') {
slug = '';
reason = other || reason;
}
if (!reason) {
// 徽章已选但没有描述时,将徽章文案作为举报原因(参考主题同款语义)。
var activeBtn = shell.querySelector('[data-report-reason].is-active');
reason = activeBtn ? (activeBtn.textContent || '').trim() : '';
}
var link = (urlInput && urlInput.value || '').trim();
if (link) {
reason += (reason ? '\n' : '') + '违规链接:' + link;
}
if (!reason) {
notyf('请选择或输入举报原因', 'danger');
return;
}
submitBtn.disabled = true;
notyf('正在提交举报…', 'load', 3000, 'aksrt-report-submit');
window.fetch(API_ROOT + '/report/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': NONCE,
},
body: JSON.stringify({
target_type: data.target_type,
target_id: data.target_id,
category: slug,
reason: reason,
}),
})
.then(function (res) { return res.json(); })
.then(function (result) {
if (result && result.success) {
window.AksrtModalRuntime.close('submit');
notyf(result.message || '举报已提交,我们会尽快处理', 'success');
} else {
submitBtn.disabled = false;
notyf((result && result.message) || '举报提交失败', 'danger');
}
})
.catch(function () {
submitBtn.disabled = false;
notyf('网络错误,请重试', 'danger');
})
.then(function () {
var toast = document.getElementById('aksrt-report-submit');
if (toast) toast.remove();
});
}
function openReportModal(data) {
if (!window.AksrtModalRuntime || typeof window.AksrtModalRuntime.open !== 'function') {
notyf('举报组件加载失败,请刷新页面后重试', 'danger');
return;
}
var shell = window.AksrtModalRuntime.open({
html: buildReportModalHtml(data),
className: 'aksrt-report-modal modal-mini',
kind: 'report',
mobileBottom: true,
});
shell.addEventListener('click', function (event) {
var chip = event.target.closest('[data-report-reason]');
if (chip) {
event.preventDefault();
var wasActive = chip.classList.contains('is-active');
shell.querySelectorAll('[data-report-reason].is-active').forEach(function (el) {
el.classList.remove('is-active');
});
if (!wasActive) chip.classList.add('is-active');
var otherBox = shell.querySelector('.aksrt-report-other');
if (otherBox) otherBox.hidden = chip.getAttribute('data-report-reason') !== 'other' || wasActive;
return;
}
var submitBtn = event.target.closest('[data-aksrt-report-submit]');
if (submitBtn) {
event.preventDefault();
submitReport({ target_type: data.target_type, target_id: data.target_id }, data, shell);
}
});
}
function initReportButtons() {
if (document.documentElement.dataset.aksrtReportInitialized === '1') return;
document.documentElement.dataset.aksrtReportInitialized = '1';
document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-aksrt-report]');
if (!btn) return;
e.preventDefault();
try {
var data = JSON.parse(btn.getAttribute('data-aksrt-report'));
if (data && data.target_type && data.target_id) {
openReportModal(data);
}
} catch {
// invalid data attribute
}
});
}
jQuery(document).off('auto_fun.aksrt-report').on('auto_fun.aksrt-report', initReportButtons);
})();