Files
kate522 7a3f5d7c34 fix(ban): appeal textarea squash and nested tone on the user center entry
- fix(ban): 申诉弹窗「请填写申诉说明」填写框被压成 0px——bindAppealForm
  对全部 textarea 执行 bindAutoHeight,而 grow 对无 maxHeight 属性的元素
  走 else 分支写入 height:0;参考主题申诉说明框为 rows=3 自然高度。
  现 autoHeight 仅对显式声明 autoHeight/maxHeight 的文本框生效
- fix(ban): 申诉弹窗与禁封记录弹窗滚动容器对齐参考主题 max-vh5(50vh,
  内层滚动、外层对话框不滚动);mini-scrollbar 提供细滚动条(悬停才显示,
  无常驻侧边滚动栏);禁封弹窗保持 max-vh7 不变
- fix(ban): 用户中心封禁元信息按钮两层颜色嵌套——外层 but 与内层 badg
  同时带 c-red/c-yellow;现外层 but 为默认按钮色,颜色仅由内层 badg
  承载(1:1 参考主题 zib_get_user_ban_info_link 结构)
- test: 契约测试锁定三处修复(说明框自然高度、max-vh5、外层 but 不重复着色)
2026-09-22 19:46:35 +08:00

400 lines
16 KiB
JavaScript
Raw Permalink 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 User Ban — 前台“禁封/解封用户”弹窗
*
* 1:1 复刻参考主题 Zibll 前台禁封能力:
* - zib_get_edit_user_ban_link → [data-aksrt-ban] 委托入口(三点菜单内)
* - zib_get_set_user_ban_modal → REST /user-ban/modal 拉取弹窗内容
* - zib_ajax_set_user_ban → REST /user-ban/set 提交
* - zib_get_ban_info_modal → [data-aksrt-ban-info](查看禁封状态)
* - zib_get_user_ban_appeal_modal → [data-aksrt-ban-appeal](禁封申诉)
* - zib_get_ban_log_modal → [data-aksrt-ban-log](禁封记录)
* - zib_user_ban_appeal(wp-ajax) → admin-ajax aksrt_ban_appeal_submit 提交
*
* 弹窗经 AksrtModalRuntime.openRemote 打开(与参考主题 refresh_modal 每次刷新
* 同语义):桌面 600px 宽(参考主题未传 data-class → Bootstrap .modal-dialog
* 默认宽 600px)/ min-height 360px(data-height=360 → .modal-content 初始高度),
* 移动端底部抽屉(mobileBottom)。提交成功语义与参考主题 zib_ajax_set_user_ban
* 返回值一致:禁封 → reload:true(整页刷新),解封 → hide_modal:true(仅关闭弹窗)。
*
* @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 AJAX_URL = (window.aksrtData && window.aksrtData.ajaxUrl)
|| (window._win && window._win.ajax_url)
|| '/wp-admin/admin-ajax.php';
var MODAL_HEIGHT = 360;
// 参考主题 data_class='modal-mini' + data-height=240:400px 宽、初始高 240。
var MINI_MODAL_HEIGHT = 240;
function ajaxUrl() {
return AJAX_URL;
}
function openRemoteModal(options) {
if (!window.AksrtModalRuntime || typeof window.AksrtModalRuntime.openRemote !== 'function') {
notyf('禁封组件加载失败,请刷新页面后重试', 'danger');
return;
}
window.AksrtModalRuntime.openRemote(options);
}
function openBanModal(userId) {
openRemoteModal({
url: API_ROOT + '/user-ban/modal',
method: 'GET',
query: { user_id: userId },
cache: false,
height: MODAL_HEIGHT,
mobileBottom: true,
kind: 'user-ban',
className: 'aksrt-user-ban-modal',
onContent: function (html, session) {
if (session && session.shell) bindBanModal(session.shell);
},
});
}
// 查看禁封状态弹窗(参考主题 zib_get_ban_info_modal / user_ban_info_modal)。
function openInfoModal(userId) {
openRemoteModal({
url: API_ROOT + '/user-ban/info-modal',
method: 'GET',
query: { user_id: userId },
cache: false,
height: MINI_MODAL_HEIGHT,
mobileBottom: true,
kind: 'user-ban-info',
className: 'aksrt-user-ban-info-modal',
});
}
// 禁封申诉弹窗(参考主题 zib_get_user_ban_appeal_modal;表单走 admin-ajax)。
function openAppealModal(userId) {
openRemoteModal({
url: API_ROOT + '/user-ban/appeal-modal',
method: 'GET',
query: { user_id: userId },
cache: false,
height: MINI_MODAL_HEIGHT,
mobileBottom: true,
kind: 'user-ban-appeal',
className: 'aksrt-user-ban-appeal-modal',
onContent: function (html, session) {
if (session && session.shell) bindAppealForm(session.shell);
},
});
}
// 禁封记录弹窗(参考主题 zib_get_ban_log_modal)。
function openLogModal(userId) {
openRemoteModal({
url: API_ROOT + '/user-ban/log-modal',
method: 'GET',
query: { user_id: userId },
cache: false,
height: MINI_MODAL_HEIGHT,
mobileBottom: true,
kind: 'user-ban-log',
className: 'aksrt-user-ban-log-modal',
});
}
function bindAppealForm(shell) {
var form = shell.querySelector('[data-aksrt-appeal-form]');
if (!form || form.dataset.aksrtAppealBound === '1') return;
form.dataset.aksrtAppealBound = '1';
// autoHeight 仅对显式声明 autoHeight/maxHeight 的文本框生效:
// 申诉说明框(1:1 参考主题)为 rows=3 自然高度 + 容器滚动,
// 无 maxHeight 时执行 grow 会被压成 0px。
Array.prototype.forEach.call(
form.querySelectorAll('textarea[autoHeight], textarea[maxHeight]'),
bindAutoHeight
);
form.addEventListener('submit', function (event) {
event.preventDefault();
submitAppeal(form);
});
}
function submitAppeal(form) {
var btn = form.querySelector('[data-aksrt-appeal-submit]');
if (btn) btn.disabled = true;
notyf('正在提交申诉…', 'load', 3000, 'aksrt-appeal-submit');
var fd = new FormData(form);
if (!fd.get('action')) fd.set('action', 'aksrt_ban_appeal_submit');
fetch(ajaxUrl(), { method: 'POST', body: fd, credentials: 'same-origin' })
.then(function (res) { return res.json(); })
.then(function (data) {
if (data && !data.error) {
notyf(data.msg || '申诉已提交', 'success');
if (data.reload) {
window.setTimeout(function () { window.location.reload(); }, 400);
} else if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.close === 'function') {
window.AksrtModalRuntime.close('submit');
}
} else {
if (btn) btn.disabled = false;
notyf((data && data.msg) || '提交失败', 'danger');
}
})
.catch(function () {
if (btn) btn.disabled = false;
notyf('网络错误,请重试', 'danger');
})
.then(function () {
var toast = document.getElementById('aksrt-appeal-submit');
if (toast) toast.remove();
});
}
function collectPayload(shell, userId, unban) {
var payload = { user_id: userId, ban: unban ? 0 : 1 };
if (unban) {
var descInput = shell.querySelector('[data-aksrt-ban-form] [name="desc"]');
payload.desc = (descInput && descInput.value || '').trim();
return payload;
}
var form = shell.querySelector('[data-aksrt-ban-form]');
if (!form) return payload;
var banInput = form.querySelector('input[name="ban"]:checked');
payload.ban = banInput ? parseInt(banInput.value, 10) : 1;
var timeInput = form.querySelector('input[name="time_day"]:checked');
payload.time_day = timeInput ? parseInt(timeInput.value, 10) : 7;
var reasonInput = form.querySelector('input[name="reason"]:checked');
payload.reason = reasonInput ? reasonInput.value : '';
var otherInput = form.querySelector('[name="reason_other"]');
payload.reason_other = (otherInput && otherInput.value || '').trim();
var desc = form.querySelector('[name="desc"]');
payload.desc = (desc && desc.value || '').trim();
payload.delete_content = !!(form.querySelector('[name="delete_content"]') || {}).checked;
payload.no_appeal = !!(form.querySelector('[name="no_appeal"]') || {}).checked;
var noAppealDesc = form.querySelector('[name="no_appeal_desc"]');
payload.no_appeal_desc = (noAppealDesc && noAppealDesc.value || '').trim();
return payload;
}
function submitBan(shell, userId, unban) {
var payload = collectPayload(shell, userId, unban);
if (!unban) {
var reason = payload.reason === 'other' || !payload.reason ? payload.reason_other : payload.reason;
if (!reason) {
notyf('请选择或输入禁封原因', 'danger');
return;
}
if (payload.no_appeal && !payload.no_appeal_desc) {
notyf('请输入禁止用户申诉的说明或原因', 'danger');
return;
}
}
var submitBtn = shell.querySelector(unban ? '[data-aksrt-unban-submit]' : '[data-aksrt-ban-submit]');
if (submitBtn) submitBtn.disabled = true;
notyf(unban ? '正在解封…' : '正在提交禁封…', 'load', 3000, 'aksrt-ban-submit');
window
.fetch(API_ROOT + '/user-ban/set', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': NONCE },
body: JSON.stringify(payload),
})
.then(function (res) { return res.json(); })
.then(function (result) {
if (result && result.success) {
notyf(result.message || '操作成功', 'success');
// 参考主题 zib_ajax_set_user_ban 返回值语义:
// 禁封 → reload:true(整页刷新,入口按钮状态随页面重渲染);
// 解封 → hide_modal:true(仅关闭弹窗)。
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.close === 'function') {
window.AksrtModalRuntime.close('submit');
}
if (!unban) window.location.reload();
} else {
if (submitBtn) submitBtn.disabled = false;
notyf((result && result.message) || '操作失败', 'danger');
}
})
.catch(function () {
if (submitBtn) submitBtn.disabled = false;
notyf('网络错误,请重试', 'danger');
})
.then(function () {
var toast = document.getElementById('aksrt-ban-submit');
if (toast) toast.remove();
});
}
function bindAutoHeight(textarea) {
// 1:1 复刻参考主题 textarea[autoHeight] 行为(input/focus 触发):
// 高度 = scrollHeight + 上下边框;超过 maxHeight 属性则锁定 maxHeight 并
// 打开自身滚动条(参考主题 main.js autoHeight 同语义)。
if (!textarea || textarea.dataset.aksrtAutoHeight === '1') return;
textarea.dataset.aksrtAutoHeight = '1';
var grow = function () {
var style = getComputedStyle(textarea);
var border = (parseInt(style.getPropertyValue('border-top-width'), 10) || 0)
+ (parseInt(style.getPropertyValue('border-bottom-width'), 10) || 0);
var max = parseInt(textarea.getAttribute('maxHeight'), 10) || 0;
textarea.style.height = 'auto';
if (max > textarea.scrollHeight) {
textarea.style.height = textarea.scrollHeight + border + 'px';
textarea.style.overflow = 'hidden';
} else {
textarea.style.height = max + 'px';
textarea.style.overflow = 'auto';
}
};
textarea.addEventListener('input', grow);
textarea.addEventListener('focus', grow);
if (textarea.offsetHeight > 0) grow();
}
function syncBanForm(shell) {
var form = shell.querySelector('[data-aksrt-ban-form]');
if (!form || form.getAttribute('data-aksrt-ban-mode') !== 'ban') return;
var banInput = form.querySelector('input[name="ban"]:checked');
var ban = banInput ? banInput.value : '1';
Array.prototype.forEach.call(form.querySelectorAll('[data-aksrt-ban-desc]'), function (el) {
el.style.display = el.getAttribute('data-aksrt-ban-desc') === ban ? '' : 'none';
});
var reasonInput = form.querySelector('input[name="reason"]:checked');
var otherBox = form.querySelector('[data-aksrt-ban-reason-other]');
if (otherBox) otherBox.style.display = reasonInput && reasonInput.value === 'other' ? '' : 'none';
var noAppealBox = form.querySelector('[data-aksrt-ban-noappeal-desc]');
var noAppealInput = form.querySelector('[name="no_appeal"]');
if (noAppealBox) noAppealBox.style.display = noAppealInput && noAppealInput.checked ? '' : 'none';
}
function bindBanModal(shell) {
Array.prototype.forEach.call(shell.querySelectorAll('textarea.form-control'), bindAutoHeight);
syncBanForm(shell);
}
/**
* 收起封面三点菜单面板(参考主题 author_header_drop_lists 菜单项点击后
* 不自动收起,需手动关闭;评论区与文章作者下拉由各自脚本点击项后自动收起)。
*/
function closeCoverMenu(trigger) {
var menu = trigger && trigger.closest ? trigger.closest('[data-aksrt-menu]') : null;
if (!menu) return;
var panel = menu.querySelector('.aksrt-cover-menu__panel');
if (panel) panel.hidden = true;
var menuTrigger = menu.querySelector('.aksrt-cover-menu__trigger');
if (menuTrigger) menuTrigger.setAttribute('aria-expanded', 'false');
}
function initBanBindings() {
if (document.documentElement.dataset.aksrtBanInitialized === '1') return;
document.documentElement.dataset.aksrtBanInitialized = '1';
document.addEventListener('click', function (event) {
if (!event.target || !event.target.closest) return;
// 禁封 / 解封入口(封面菜单按钮 / 评论区菜单项 / 文章作者菜单项)。
var banBtn = event.target.closest('[data-aksrt-ban]');
if (banBtn) {
event.preventDefault();
var userId = parseInt(banBtn.getAttribute('data-aksrt-ban'), 10);
if (userId > 0) {
closeCoverMenu(banBtn);
openBanModal(userId);
}
return;
}
// 查看禁封状态入口(用户中心元信息 / 评论拒绝态 / 禁封信息弹窗内申诉按钮)。
var infoBtn = event.target.closest('[data-aksrt-ban-info]');
if (infoBtn) {
event.preventDefault();
var infoUser = parseInt(infoBtn.getAttribute('data-aksrt-ban-info'), 10);
if (infoUser > 0) openInfoModal(infoUser);
return;
}
// 申诉入口(禁封信息弹窗 / 登录后禁封页)。
var appealBtn = event.target.closest('[data-aksrt-ban-appeal]');
if (appealBtn) {
event.preventDefault();
var appealUser = parseInt(appealBtn.getAttribute('data-aksrt-ban-appeal'), 10);
if (appealUser > 0) openAppealModal(appealUser);
return;
}
// 禁封记录入口(禁封/解封弹窗内的记录链接,仅管理员)。
var logBtn = event.target.closest('[data-aksrt-ban-log]');
if (logBtn) {
event.preventDefault();
var logUser = parseInt(logBtn.getAttribute('data-aksrt-ban-log'), 10);
if (logUser > 0) openLogModal(logUser);
return;
}
// 弹窗内交互(事件委托到 document,弹窗壳由 runtime portal 到 body)。
var active = window.AksrtModalRuntime && window.AksrtModalRuntime.getActive
? window.AksrtModalRuntime.getActive()
: null;
if (!active || !active.contains(event.target)) return;
var shell = active;
var form = shell.querySelector('[data-aksrt-ban-form]');
var modalUser = form ? parseInt(form.getAttribute('data-aksrt-ban-user'), 10) : 0;
var submitBtn = event.target.closest('[data-aksrt-ban-submit]');
if (submitBtn) {
event.preventDefault();
submitBtn.disabled = true;
submitBan(shell, modalUser, false);
return;
}
var unbanBtn = event.target.closest('[data-aksrt-unban-submit]');
if (unbanBtn) {
event.preventDefault();
unbanBtn.disabled = true;
submitBan(shell, modalUser, true);
return;
}
});
document.addEventListener('change', function (event) {
var active = window.AksrtModalRuntime && window.AksrtModalRuntime.getActive
? window.AksrtModalRuntime.getActive()
: null;
if (!active || !event.target || !active.contains(event.target)) return;
syncBanForm(active);
});
// 弹窗内容注入后由 onContent 绑定;bs.modal 展示契约下对 user-ban 弹窗做同步。
jQuery(document).on('loaded.bs.modal show.bs.modal', function (event) {
var shell = event.target && event.target.classList && event.target.classList.contains('aksrt-user-ban-modal')
? event.target
: null;
if (shell) syncBanForm(shell);
});
}
jQuery(document)
.off('auto_fun.aksrt-ban')
.on('auto_fun.aksrt-ban', initBanBindings);
})();