Files
wordpress-theme-aksrt/assets/js/admin-pay-products.js
T
kate522andClaude Fable 5 dc6dbc05d8 feat(admin): quick-edit paid post settings in a dialog on the pay products page
- Rebuild aksrt_pay_products as list + in-page quick settings dialog
  (pay type, limit, cash/points prices, sales float, coupon, order expiry, copy)
- Query by _aksrt_pay_type with real pagination; drop order-summary fallback
  and the duplicate "Meta 销量" column
- Centralize pay meta writes in DomainModel::persist_pay_meta(); stop writing
  unread _aksrt_pay_modo / _aksrt_pay_limit keys

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-31 17:28:45 +08:00

197 lines
6.7 KiB
JavaScript

/** 付费文章:快速设置弹窗(填充、条件显隐、会员价校验、未保存提醒、防重复提交)。仅本页入队。 */
(function () {
'use strict';
var root = document.querySelector('.aksrt-products-page');
var dialog = document.getElementById('aksrt-product-editor');
if (!root || !dialog || typeof dialog.showModal !== 'function') return;
var form = dialog.querySelector('[data-aksrt-product-form]');
var submit = form.querySelector('[data-aksrt-product-submit]');
var subject = form.querySelector('[data-aksrt-product-subject]');
var editLink = form.querySelector('[data-aksrt-product-edit-link]');
var submitLabel = submit.textContent;
var opener = null;
var snapshot = '';
var pointerStartedOutside = false;
function field(key) {
return form.querySelectorAll('[name="aksrt_pay[' + key + ']"]');
}
function value(key) {
var nodes = field(key);
for (var i = 0; i < nodes.length; i += 1) {
var node = nodes[i];
if (node.type === 'radio') {
if (node.checked) return node.value;
} else if (node.type === 'checkbox') {
return node.checked ? node.value : '';
} else {
return node.value;
}
}
return '';
}
function paneActive(name) {
switch (name) {
case 'enabled': return value('pay_type') !== 'no';
case 'disabled': return value('pay_type') === 'no';
case 'cash': return value('pay_modo') !== 'points';
case 'points': return value('pay_modo') === 'points';
case 'expire': return value('order_expired_s') === '1';
default: return true;
}
}
/** 隐藏的分区连同其中的控件一起禁用:既不提交,也不参与必填校验。 */
function syncPanes() {
form.querySelectorAll('[data-aksrt-product-pane]').forEach(function (pane) {
var active = paneActive(pane.getAttribute('data-aksrt-product-pane'));
pane.hidden = !active;
if (pane.tagName === 'FIELDSET') pane.disabled = !active;
});
form.querySelectorAll('input:not([type="hidden"]), select, textarea').forEach(function (control) {
var node = control.parentElement;
var disabled = false;
while (node && node !== form) {
if (node.hasAttribute('data-aksrt-product-pane') && node.hidden) {
disabled = true;
break;
}
node = node.parentElement;
}
control.disabled = disabled;
});
syncVip();
}
/** 现金按「分」比较,避免浮点误差。 */
function amount(raw, mode) {
var text = String(raw || '').trim();
if (mode === 'points') return /^\d+$/.test(text) ? Number(text) : NaN;
var match = text.match(/^(\d+)(?:\.(\d{1,2}))?$/);
return match ? Number(match[1]) * 100 + Number((match[2] || '').padEnd(2, '0')) : NaN;
}
function syncVip() {
['cash', 'points'].forEach(function (mode) {
var base = form.querySelector('[data-aksrt-product-base="' + mode + '"]');
var message = form.getAttribute(mode === 'points' ? 'data-vip-points-error' : 'data-vip-error');
form.querySelectorAll('[data-aksrt-product-vip="' + mode + '"]').forEach(function (input) {
var vip = amount(input.value, mode);
var limit = amount(base && base.value, mode);
input.setCustomValidity(!input.disabled && vip > limit ? message : '');
});
});
}
function serialize() {
return new URLSearchParams(new FormData(form)).toString();
}
function fill(payload) {
var fields = payload.fields || {};
form.reset();
form.elements.post_id.value = String(payload.id || '');
Object.keys(fields).forEach(function (key) {
var current = String(fields[key]);
field(key).forEach(function (node) {
if (node.type === 'radio') {
node.checked = node.value === current;
} else if (node.type === 'checkbox') {
node.checked = current === '1';
} else {
node.value = key === 'order_expired_time' && current === '0' ? '' : current;
}
});
});
subject.textContent = (payload.title || '') + ' · #' + String(payload.id || '');
editLink.hidden = !payload.editUrl;
editLink.href = payload.editUrl || '#';
syncPanes();
}
function lockScroll() {
document.documentElement.classList.toggle('aksrt-products-modal-open', dialog.open);
}
function requestClose() {
if (form.dataset.submitting) return;
if (serialize() !== snapshot && !window.confirm(form.getAttribute('data-discard'))) return;
dialog.close();
}
root.addEventListener('click', function (event) {
var button = event.target.closest('[data-aksrt-product-edit]');
if (!button || dialog.open) return;
var payload;
try {
payload = JSON.parse(button.getAttribute('data-aksrt-product-edit'));
} catch (error) {
return;
}
opener = button;
fill(payload);
snapshot = serialize();
dialog.showModal();
lockScroll();
});
form.addEventListener('change', syncPanes);
form.addEventListener('input', syncVip);
function isOutside(event) {
var rect = dialog.getBoundingClientRect();
return event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom;
}
dialog.addEventListener('pointerdown', function (event) {
pointerStartedOutside = event.target === dialog && isOutside(event);
});
dialog.addEventListener('click', function (event) {
if (event.target.closest('[data-aksrt-dialog-close]')
|| (pointerStartedOutside && event.target === dialog && isOutside(event))) {
event.preventDefault();
requestClose();
}
pointerStartedOutside = false;
});
dialog.addEventListener('cancel', function (event) {
event.preventDefault();
requestClose();
});
dialog.addEventListener('close', function () {
lockScroll();
if (opener && opener.isConnected) opener.focus();
opener = null;
});
form.addEventListener('submit', function (event) {
syncVip();
if (form.dataset.submitting || !form.checkValidity()) {
event.preventDefault();
form.reportValidity();
return;
}
form.dataset.submitting = '1';
submit.textContent = form.getAttribute('data-busy-label');
// 延后禁用:同步禁用 submit 按钮会让部分浏览器丢掉这次提交。
setTimeout(function () { submit.disabled = true; }, 0);
});
// bfcache 返回时恢复按钮,避免弹窗停在保存中状态。
window.addEventListener('pageshow', function () {
delete form.dataset.submitting;
submit.disabled = false;
submit.textContent = submitLabel;
lockScroll();
});
var highlighted = root.querySelector('.aksrt-products-table tr.is-highlight');
if (highlighted && typeof highlighted.scrollIntoView === 'function') {
highlighted.scrollIntoView({ block: 'center' });
}
})();