Files
wordpress-theme-aksrt/assets/js/admin-vip.js
T
kate522andClaude Fable 5 1be52d75a0 feat(admin): redesign VIP membership page with modal editing
- Replace per-row inline <details> forms and the separate bulk form with a
  single <dialog> shared by single-user and bulk adjustments
- Add stats row, styled tabs, date range filter with reset, sortable expiry
- Show fallback tier, remaining time, and disable unavailable operations
- History tab: labelled operation/status badges, avatars, local time,
  batch marker and error message
- Move inline script/onclick to assets/js/admin-vip.js; add vip.css
- Surface the service error message when every user fails

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-30 16:12:12 +08:00

162 lines
6.0 KiB
JavaScript

/**
* 用户会员后台页(users.php?page=aksrt_user_vip)的调整弹窗。
* - 行内「调整」:单人打开弹窗;工具栏「批量调整」:对勾选用户打开弹窗;
* - 按操作类型切换字段,隐藏区域的输入被禁用,不参与校验与提交;
* - 单人时按用户状态禁用不可用的操作(延长 / 恢复);
* - 提交后禁用按钮防重复提交。
* 标记由 src/Admin/Native/UsersMenusVipView.php 输出。
*/
(function () {
'use strict';
var dialog = document.getElementById('aksrt-vip-dialog');
if (!dialog || typeof dialog.showModal !== 'function') return;
var form = dialog.querySelector('form');
var ids = dialog.querySelector('[data-aksrt-vip-ids]');
var title = dialog.querySelector('[data-aksrt-vip-title]');
var target = dialog.querySelector('[data-aksrt-vip-target]');
var opHint = dialog.querySelector('[data-aksrt-vip-op-hint]');
var bulkNote = dialog.querySelector('[data-aksrt-vip-bulk-note]');
var submit = dialog.querySelector('[data-aksrt-vip-submit]');
var permanent = dialog.querySelector('[data-aksrt-vip-permanent]');
var dateInput = dialog.querySelector('[data-aksrt-vip-date]');
var levelSelect = dialog.querySelector('[data-aksrt-vip-level]');
var radios = dialog.querySelectorAll('input[name="vip_operation"]');
var panes = dialog.querySelectorAll('[data-aksrt-vip-pane]');
var bulkButton = document.querySelector('[data-aksrt-vip-bulk]');
var selection = document.querySelector('[data-aksrt-vip-selection]');
var checkAll = document.querySelector('[data-aksrt-vip-check-all]');
var checks = document.querySelectorAll('.aksrt-vip-check');
function t(key, value) {
var text = dialog.getAttribute('data-i18n-' + key) || '';
return value === undefined ? text : text.replace(/%[sd]/, String(value));
}
function operation() {
var checked = dialog.querySelector('input[name="vip_operation"]:checked');
return checked ? checked.value : 'set';
}
function sync() {
var op = operation();
panes.forEach(function (pane) {
var on = pane.getAttribute('data-aksrt-vip-pane') === op;
pane.hidden = !on;
pane.querySelectorAll('input, select').forEach(function (field) { field.disabled = !on; });
});
if (op === 'set' && dateInput) {
dateInput.disabled = permanent.checked;
dateInput.required = !permanent.checked;
}
submit.classList.toggle('is-danger', op === 'disable');
submit.classList.toggle('button-primary', op !== 'disable');
}
function setOperation(value) {
radios.forEach(function (radio) { radio.checked = radio.value === value; });
sync();
}
function setAvailability(row) {
var hints = [];
radios.forEach(function (radio) {
var off = false;
if (row && radio.value === 'extend' && row.getAttribute('data-can-extend') !== '1') {
off = true;
hints.push(t('extend-off'));
}
if (row && radio.value === 'restore' && row.getAttribute('data-can-restore') !== '1') {
off = true;
hints.push(t('restore-off'));
}
radio.disabled = off;
radio.closest('label').classList.toggle('is-disabled', off);
});
opHint.textContent = hints.join(' ');
opHint.hidden = hints.length === 0;
}
function open(rows, bulk) {
ids.textContent = '';
rows.forEach(function (row) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'user_ids[]';
input.value = row.getAttribute('data-user-id');
ids.appendChild(input);
});
form.reset();
if (bulk) {
title.textContent = t('bulk', rows.length);
target.textContent = rows.map(function (row) { return row.getAttribute('data-user-name'); }).slice(0, 8).join('、') + (rows.length > 8 ? ' …' : '');
setAvailability(null);
bulkNote.textContent = t('bulk-note');
bulkNote.hidden = false;
} else {
var row = rows[0];
title.textContent = t('single', row.getAttribute('data-user-name'));
target.textContent = '#' + row.getAttribute('data-user-id') + ' · ' + row.getAttribute('data-current');
if (levelSelect) levelSelect.value = row.getAttribute('data-level') || '1';
setAvailability(row);
bulkNote.hidden = true;
}
setOperation('set');
submit.disabled = false;
dialog.showModal();
var reason = form.querySelector('input[name="reason"]');
if (reason) reason.focus();
}
function selected() {
return Array.prototype.filter.call(checks, function (check) { return check.checked; });
}
function syncSelection() {
var count = selected().length;
if (bulkButton) bulkButton.disabled = count === 0;
if (selection) {
selection.hidden = count === 0;
selection.textContent = t('selected', count);
}
if (checkAll) {
checkAll.checked = count > 0 && count === checks.length;
checkAll.indeterminate = count > 0 && count < checks.length;
}
}
document.querySelectorAll('[data-aksrt-vip-edit]').forEach(function (button) {
button.addEventListener('click', function () { open([button], false); });
});
if (bulkButton) {
bulkButton.addEventListener('click', function () {
var rows = selected();
if (rows.length) open(rows, true);
});
}
if (checkAll) {
checkAll.addEventListener('change', function () {
checks.forEach(function (check) { check.checked = checkAll.checked; });
syncSelection();
});
}
checks.forEach(function (check) { check.addEventListener('change', syncSelection); });
radios.forEach(function (radio) { radio.addEventListener('change', sync); });
if (permanent) permanent.addEventListener('change', sync);
dialog.querySelectorAll('[data-aksrt-vip-close]').forEach(function (button) {
button.addEventListener('click', function () { dialog.close(); });
});
// 点击遮罩(dialog 自身而非内容区)关闭。
dialog.addEventListener('click', function (event) {
if (event.target === dialog) dialog.close();
});
form.addEventListener('submit', function () {
submit.disabled = true;
});
syncSelection();
})();