Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.
Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)
Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
(dropped 3 stale entries, added the 17 missing ones)
Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
when it is empty
Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.
Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
228 lines
9.6 KiB
JavaScript
228 lines
9.6 KiB
JavaScript
/** 消息管理:详情、选择与批量操作、发送计数及角色群发确认。仅本页入队。 */
|
|
(function () {
|
|
'use strict';
|
|
|
|
var root = document.querySelector('.jio-messages-admin');
|
|
if (!root) return;
|
|
var compose = document.getElementById('jio-msg-compose');
|
|
var detail = document.getElementById('jio-msg-detail');
|
|
var confirmDialog = document.getElementById('jio-msg-confirm');
|
|
if (!compose || !detail || !confirmDialog || typeof compose.showModal !== 'function') return;
|
|
|
|
function text(element, key, values) {
|
|
var value = element.getAttribute('data-i18n-' + key) || '';
|
|
return value.replace(/\{(\w+)\}/g, function (match, name) {
|
|
return values && values[name] !== undefined ? String(values[name]) : match;
|
|
});
|
|
}
|
|
|
|
function syncScroll() {
|
|
document.documentElement.classList.toggle('jio-msg-modal-open', !!root.querySelector('dialog[open]'));
|
|
}
|
|
|
|
function open(dialog) {
|
|
if (!dialog.open) dialog.showModal();
|
|
syncScroll();
|
|
}
|
|
|
|
root.querySelectorAll('.jio-msg-dialog').forEach(function (dialog) {
|
|
var outsideStart = false;
|
|
function outside(event) {
|
|
var rect = dialog.getBoundingClientRect();
|
|
return event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom;
|
|
}
|
|
dialog.querySelectorAll('[data-jio-dialog-close]').forEach(function (button) {
|
|
button.addEventListener('click', function () { dialog.close(); });
|
|
});
|
|
dialog.addEventListener('pointerdown', function (event) {
|
|
outsideStart = event.target === dialog && outside(event);
|
|
});
|
|
dialog.addEventListener('click', function (event) {
|
|
if (outsideStart && event.target === dialog && outside(event)) dialog.close();
|
|
outsideStart = false;
|
|
});
|
|
dialog.addEventListener('close', syncScroll);
|
|
});
|
|
|
|
var pending = null;
|
|
var confirmButton = confirmDialog.querySelector('[data-jio-msg-confirm]');
|
|
function ask(title, message, label, danger, callback) {
|
|
pending = callback;
|
|
confirmDialog.querySelector('[data-jio-dialog-title]').textContent = title;
|
|
confirmDialog.querySelector('#jio-msg-confirm-text').textContent = message;
|
|
confirmButton.textContent = label;
|
|
confirmButton.classList.toggle('jio-messages-admin__danger', danger);
|
|
confirmButton.classList.toggle('button-primary', !danger);
|
|
open(confirmDialog);
|
|
confirmDialog.querySelector('[data-jio-dialog-close][autofocus]').focus();
|
|
}
|
|
confirmDialog.addEventListener('cancel', function () { pending = null; });
|
|
confirmDialog.addEventListener('close', function () { pending = null; });
|
|
confirmButton.addEventListener('click', function () {
|
|
var callback = pending;
|
|
pending = null;
|
|
confirmDialog.close();
|
|
if (callback) callback();
|
|
});
|
|
|
|
root.querySelectorAll('[data-jio-msg-view]').forEach(function (button) {
|
|
button.addEventListener('click', function () {
|
|
var template = document.getElementById('jio-msg-detail-' + button.getAttribute('data-jio-msg-view'));
|
|
if (!template) return;
|
|
detail.querySelector('[data-jio-msg-detail-body]').replaceChildren(template.content.cloneNode(true));
|
|
open(detail);
|
|
});
|
|
});
|
|
|
|
var list = root.querySelector('[data-jio-msg-list]');
|
|
var listSubmitting = false;
|
|
var syncSelection;
|
|
if (list) {
|
|
var checks = Array.from(list.querySelectorAll('[data-jio-msg-check]'));
|
|
var checkAll = list.querySelector('[data-jio-msg-check-all]');
|
|
var selection = list.querySelector('[data-jio-msg-selection]');
|
|
var bulkButtons = list.querySelectorAll('[data-jio-msg-action]:not([data-id])');
|
|
|
|
function selected() { return checks.filter(function (check) { return check.checked; }); }
|
|
syncSelection = function () {
|
|
var count = selected().length;
|
|
selection.textContent = selection.getAttribute('data-label').replace('%d', String(count));
|
|
checkAll.checked = count > 0 && count === checks.length;
|
|
checkAll.indeterminate = count > 0 && count < checks.length;
|
|
checkAll.disabled = listSubmitting || checks.length === 0;
|
|
bulkButtons.forEach(function (button) { button.disabled = listSubmitting || count === 0; });
|
|
checks.forEach(function (check) { check.closest('tr').classList.toggle('is-selected', check.checked); });
|
|
};
|
|
checkAll.addEventListener('change', function () {
|
|
checks.forEach(function (check) { check.checked = checkAll.checked; });
|
|
syncSelection();
|
|
});
|
|
checks.forEach(function (check) { check.addEventListener('change', syncSelection); });
|
|
function mutate(op, id) {
|
|
if (listSubmitting) return;
|
|
list.elements.bulk_action.value = op;
|
|
list.elements.message_id.value = id || '';
|
|
list.requestSubmit();
|
|
}
|
|
list.querySelectorAll('[data-jio-msg-action]').forEach(function (button) {
|
|
button.addEventListener('click', function () {
|
|
if (listSubmitting) return;
|
|
var op = button.getAttribute('data-jio-msg-action');
|
|
var id = button.getAttribute('data-id');
|
|
var count = id ? 1 : selected().length;
|
|
if (!count) return;
|
|
if (op === 'delete') {
|
|
ask(text(confirmDialog, 'delete-title'), text(confirmDialog, 'delete-text', { count: count }), text(confirmDialog, 'delete-send'), true, function () { mutate(op, id); });
|
|
} else {
|
|
mutate(op, id);
|
|
}
|
|
});
|
|
});
|
|
list.addEventListener('submit', function (event) {
|
|
if (listSubmitting || !list.elements.bulk_action.value || (!list.elements.message_id.value && !selected().length)) {
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
listSubmitting = true;
|
|
// 只锁操作按钮,不禁用复选框,否则 ids[] 不会随表单提交。
|
|
list.querySelectorAll('button').forEach(function (button) { button.disabled = true; });
|
|
syncSelection();
|
|
});
|
|
syncSelection();
|
|
}
|
|
|
|
var form = compose.querySelector('[data-jio-msg-compose]');
|
|
var send = form.querySelector('[data-jio-msg-send]');
|
|
var role = form.elements.role;
|
|
var counters = form.querySelectorAll('[data-count-limit]');
|
|
var sending = false;
|
|
var approved = '';
|
|
|
|
function clearApproval() {
|
|
approved = '';
|
|
form.elements.confirmed_role.value = '';
|
|
form.elements.confirmed_count.value = '';
|
|
}
|
|
function syncCounters() {
|
|
counters.forEach(function (field) {
|
|
// 与 PHP mb_strlen 一致按 Unicode 码点计数,emoji 不占两个字。
|
|
var count = Array.from(field.value).length;
|
|
var limit = Number(field.getAttribute('data-count-limit'));
|
|
var counter = form.querySelector('[data-count-for="' + field.name + '"]');
|
|
counter.textContent = count + ' / ' + limit;
|
|
counter.classList.toggle('is-over', count > limit);
|
|
field.setCustomValidity(count > limit ? text(form, 'count-error', { count: limit }) : '');
|
|
});
|
|
}
|
|
function syncRole() {
|
|
var option = role.options[role.selectedIndex];
|
|
var count = option ? Number(option.getAttribute('data-count')) : 0;
|
|
form.querySelector('[data-jio-msg-role-hint]').textContent = count > 0 && !option.disabled ? text(form, 'role-hint', { count: count }) : '';
|
|
}
|
|
function mode() { return form.querySelector('input[name="recipient_mode"]:checked').value; }
|
|
function syncMode() {
|
|
clearApproval();
|
|
form.querySelectorAll('[data-jio-msg-pane]').forEach(function (pane) {
|
|
var active = pane.getAttribute('data-jio-msg-pane') === mode();
|
|
pane.hidden = !active;
|
|
pane.querySelectorAll('input, select, textarea').forEach(function (field) { field.disabled = !active; });
|
|
});
|
|
syncRole();
|
|
}
|
|
form.querySelectorAll('input[name="recipient_mode"]').forEach(function (radio) { radio.addEventListener('change', syncMode); });
|
|
role.addEventListener('change', function () { clearApproval(); syncRole(); });
|
|
form.addEventListener('input', function () { clearApproval(); syncCounters(); });
|
|
|
|
function snapshot() {
|
|
return JSON.stringify([role.value, role.options[role.selectedIndex].getAttribute('data-count'), form.elements.title.value, form.elements.content.value, form.elements.target_url.value]);
|
|
}
|
|
form.addEventListener('submit', function (event) {
|
|
syncCounters();
|
|
if (sending || !form.reportValidity()) {
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
if (mode() === 'role' && approved !== snapshot()) {
|
|
event.preventDefault();
|
|
var option = role.options[role.selectedIndex];
|
|
if (!option || option.disabled || !role.value) return;
|
|
var count = Number(option.getAttribute('data-count'));
|
|
var current = snapshot();
|
|
ask(text(form, 'role-title'), text(form, 'role-confirm', { role: option.getAttribute('data-label'), count: count }), text(form, 'role-send'), false, function () {
|
|
if (mode() !== 'role' || current !== snapshot()) return;
|
|
approved = current;
|
|
form.elements.confirmed_role.value = role.value;
|
|
form.elements.confirmed_count.value = String(count);
|
|
form.requestSubmit(send);
|
|
});
|
|
return;
|
|
}
|
|
sending = true;
|
|
send.disabled = true;
|
|
});
|
|
|
|
root.querySelectorAll('[data-jio-dialog-open]').forEach(function (button) {
|
|
button.addEventListener('click', function () {
|
|
open(compose);
|
|
var field = mode() === 'role' ? role : form.elements.users;
|
|
field.focus();
|
|
});
|
|
});
|
|
window.addEventListener('pageshow', function () {
|
|
sending = false;
|
|
send.disabled = false;
|
|
clearApproval();
|
|
if (list) {
|
|
listSubmitting = false;
|
|
list.elements.bulk_action.value = '';
|
|
list.elements.message_id.value = '';
|
|
list.querySelectorAll('button[data-id], button[data-jio-msg-view]').forEach(function (button) { button.disabled = false; });
|
|
syncSelection();
|
|
}
|
|
syncCounters();
|
|
});
|
|
syncMode();
|
|
syncCounters();
|
|
if (compose.hasAttribute('data-jio-dialog-autoopen')) open(compose);
|
|
})();
|