/** * AKSRT Report — 前台统一举报弹窗 * * 监听 [data-aksrt-report] 点击,弹出对齐参考主题的举报弹窗(像素级同构): * edit-header 红色警示头 + 举报原因徽章(active 状态走 .active.badg 焦点色) * + 违规链接 + 详细描述 + 图片举证(后台开启 user_report_img_s 时) * + 举报说明(后台 user_report_desc,支持 HTML)+ but-average modal-buts 底栏。 * 提交走 REST /aksrt/v1/report/submit(user/comment/post 三类目标统一入口), * 举证图片走 /aksrt/v1/report/upload-image(最多 3 张,URL 拼入举报内容)。 * * @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: '举报评论', }; var MAX_IMAGES = 3; function getReportCategories() { // 预置原因:后台“用户&互动 → 禁封/举报 → 预置原因”(ban_preset_reason, // 由 UserRuntime::ban_settings 解析,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)) || ''), name: String((cat && (cat.name || cat.label || cat.t)) || ''), }; }) .filter(function (cat) { return cat.name !== ''; }); if (items.length) return items; } return [ { slug: 'spam', name: '垃圾信息' }, { slug: 'abuse', name: '人身攻击' }, { slug: 'copyright', name: '版权投诉' }, ]; } function reportDescHtml() { // 举报说明:后台富文本(服务端已 wp_kses_post),直接注入。 var d = window.aksrtData && window.aksrtData.report; return (d && typeof d.desc === 'string') ? d.desc : ''; } function imgUploadEnabled() { var d = window.aksrtData && window.aksrtData.report; return !!(d && d.imgEnabled); } function escapeHtml(value) { return String(value == null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function buildReportModalHtml(data) { var categories = getReportCategories(); var title = TARGET_TITLES[data.target_type] || '举报不良信息'; var html = ''; // 弹窗头:参考主题 edit-header 同构(关闭钮 + 红色警示徽标 + 标题)。 html += '
'; html += ''; html += '' + escapeHtml(title) + ''; html += '
'; html += '
'; html += '
'; html += '
'; // 被举报对象(头像 + 名称),由服务端渲染进 data-aksrt-report。 if (data.target_avatar || data.target_name) { html += '
'; if (data.target_avatar) { html += ''; } html += '' + escapeHtml(data.target_name || '') + ''; html += '
'; } html += '
'; // 举报原因(徽章单选,active 走参考主题焦点色;含“其它原因”展开自定义文本)。 html += '
'; html += '
举报原因
'; categories.forEach(function (cat) { html += '' + escapeHtml(cat.name) + ''; }); html += '其它原因'; html += ''; html += '
'; // 违规链接。 html += '
'; html += '
违规链接
'; html += ''; html += '
'; // 详细描述。 html += '
'; html += '
详细描述
'; html += ''; html += '
'; // 图片举证(后台 user_report_img_s 开启时渲染,最多 3 张)。 if (imgUploadEnabled()) { html += '
'; html += '
添加图片举证
'; html += '
'; html += '
'; html += '
'; html += ''; html += '
'; } // 举报说明(后台 user_report_desc,支持 HTML)。 var desc = reportDescHtml(); if (desc) { html += '
' + desc + '
'; } html += '
'; // 底栏:参考主题 but-average modal-buts 双按钮。 html += ''; html += '
'; return html; } function selectedReason(shell) { var active = shell.querySelector('[data-aksrt-report-reason].active'); if (!active) return { label: '', slug: '' }; return { label: active.getAttribute('data-aksrt-report-reason') || '', slug: active.getAttribute('data-reason-slug') || '', }; } function collectImages(shell) { return Array.prototype.map.call( shell.querySelectorAll('.preview .preview-item img'), function (img) { return img.getAttribute('src') || ''; } ).filter(function (url) { return url !== ''; }); } function uploadImage(shell, file) { var preview = shell.querySelector('.form-upload .preview'); if (!preview) return Promise.resolve(); var existing = shell.querySelectorAll('.preview .preview-item').length; if (existing + collectImages(shell).length >= MAX_IMAGES) { notyf('最多上传 ' + MAX_IMAGES + ' 张图片', 'danger'); return Promise.resolve(); } var body = new FormData(); body.append('image', file); return window.fetch(API_ROOT + '/report/upload-image', { method: 'POST', headers: { 'X-WP-Nonce': NONCE }, body: body, }) .then(function (res) { return res.json(); }) .then(function (result) { var url = result && result.success && result.data && result.data.url; if (!url) { notyf((result && result.message) || '图片上传失败', 'danger'); return; } var item = document.createElement('div'); item.className = 'preview-item'; var img = document.createElement('img'); img.src = url; img.alt = ''; var remove = document.createElement('button'); remove.type = 'button'; remove.className = 'remove'; remove.setAttribute('aria-label', '删除图片'); remove.innerHTML = '×'; remove.addEventListener('click', function () { item.remove(); }); item.appendChild(img); item.appendChild(remove); preview.insertBefore(item, preview.querySelector('.add')); }) .catch(function () { notyf('图片上传失败,请重试', 'danger'); }); } function submitReport(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 selected = selectedReason(shell); var other = (otherInput && otherInput.value || '').trim(); if (!selected.label && !other) { notyf('请选择或输入举报原因', 'danger'); return; } // “其它原因”以自定义文本为举报原因;预置徽章取徽章文案(参考主题同语义)。 var reason = selected.label === 'other' || !selected.label ? other : selected.label; var slug = selected.label && selected.label !== 'other' ? selected.slug : ''; if (!reason) { notyf('请填写举报原因', 'danger'); return; } var link = (urlInput && urlInput.value || '').trim(); if (link) { reason += (reason ? '\n' : '') + '违规链接:' + link; } var images = collectImages(shell); if (images.length) { reason += '\n图片举证:'; images.forEach(function (url) { reason += '\n' + url; }); } 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 bindAutoHeight(textarea) { // 对齐参考主题 autoHeight 行为:随内容自动增高,最小保持初始行数高度。 // 隐藏中的 textarea(如“其它原因”)跳过首次计算,待可见后按实际高度增长。 if (!textarea || textarea.dataset.aksrtAutoHeight === '1') return; textarea.dataset.aksrtAutoHeight = '1'; var minHeight = 0; var grow = function () { if (!minHeight) minHeight = textarea.offsetHeight || 0; textarea.style.height = 'auto'; textarea.style.height = Math.max(textarea.scrollHeight, minHeight) + 'px'; }; textarea.addEventListener('input', grow); if (textarea.offsetHeight > 0) grow(); } function bindReportModal(shell) { shell.querySelectorAll('textarea.form-control').forEach(bindAutoHeight); shell.addEventListener('click', function (event) { // 原因徽章:单选 active(参考主题 .active.badg 焦点色),其它原因展开文本框。 // 参考主题行为:重复点击已选徽章保持选中(先全清再激活),不做反选。 var chip = event.target.closest('[data-aksrt-report-reason]'); if (chip) { event.preventDefault(); shell.querySelectorAll('[data-aksrt-report-reason].active').forEach(function (el) { el.classList.remove('active'); }); chip.classList.add('active'); var otherBox = shell.querySelector('.aksrt-report-other'); if (otherBox) otherBox.style.display = chip.getAttribute('data-aksrt-report-reason') === 'other' ? '' : 'none'; return; } // 图片举证:点击加号或预览补位触发文件选择。 var addBtn = event.target.closest('[data-aksrt-report-add]'); if (addBtn) { event.preventDefault(); var fileInput = shell.querySelector('[data-aksrt-report-file]'); if (fileInput) fileInput.click(); return; } var submitBtn = event.target.closest('[data-aksrt-report-submit]'); if (submitBtn) { event.preventDefault(); submitReport(shell.getAttribute('data-aksrt-report-target') ? JSON.parse(shell.getAttribute('data-aksrt-report-target')) : {}, shell); } }); shell.addEventListener('change', function (event) { var fileInput = event.target.closest('[data-aksrt-report-file]'); if (!fileInput || !fileInput.files || !fileInput.files.length) return; Array.prototype.forEach.call(fileInput.files, function (file) { uploadImage(shell, file); }); fileInput.value = ''; }); } 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', kind: 'report', mobileBottom: true, }); shell.setAttribute('data-aksrt-report-target', JSON.stringify({ target_type: data.target_type, target_id: data.target_id, })); bindReportModal(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); })();