Replace inline identity review forms with focused detail and review dialogs, improve list filters and caching, and align evidence permissions and deletion feedback. Add contract and runtime regression coverage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
256 lines
8.4 KiB
JavaScript
256 lines
8.4 KiB
JavaScript
/** 认证记录审核详情与删除确认。仅在认证 native 后台页入队。 */
|
|
(function () {
|
|
'use strict';
|
|
|
|
var root = document.querySelector('.aksrt-verify-page');
|
|
var dialog = document.getElementById('aksrt-verify-dialog');
|
|
if (!root || !dialog || typeof dialog.showModal !== 'function') return;
|
|
|
|
var opener = null;
|
|
var activeForm = null;
|
|
var submitting = false;
|
|
var pointerStartedOutside = false;
|
|
|
|
function prevent(event) {
|
|
if (event && typeof event.preventDefault === 'function') event.preventDefault();
|
|
}
|
|
|
|
function valueOf(element, name, fallback) {
|
|
var value = element ? element.getAttribute(name) : null;
|
|
return value === null ? (fallback || '') : value;
|
|
}
|
|
|
|
function lockScroll() {
|
|
document.documentElement.classList.toggle('aksrt-verify-modal-open', !!dialog.open);
|
|
}
|
|
|
|
function closestAttribute(target, attribute, boundary) {
|
|
var node = target;
|
|
while (node) {
|
|
if (node.nodeType === 1 && node.hasAttribute(attribute)) return node;
|
|
if (node === boundary) break;
|
|
node = node.parentElement;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function isOutside(event) {
|
|
var rect = dialog.getBoundingClientRect();
|
|
return event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom;
|
|
}
|
|
|
|
function selectedStatus(form) {
|
|
var radios = form.querySelectorAll('input[name="status"]');
|
|
for (var index = 0; index < radios.length; index += 1) {
|
|
if (radios[index].checked) return radios[index].value;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function getProfileName(profile) {
|
|
return profile ? profile.querySelector('input[name="name"]') : null;
|
|
}
|
|
|
|
function setTrimValidity(field, active) {
|
|
if (!field || typeof field.setCustomValidity !== 'function') return;
|
|
if (!active || String(field.value || '').trim() !== '') {
|
|
field.setCustomValidity('');
|
|
return;
|
|
}
|
|
field.setCustomValidity(valueOf(field, 'data-required-error', '必填项不能为空'));
|
|
}
|
|
|
|
function syncReview(form) {
|
|
var decision = selectedStatus(form);
|
|
var profile = form.querySelector('[data-aksrt-verify-profile]');
|
|
var name = getProfileName(profile);
|
|
var note = form.querySelector('textarea[name="note"]');
|
|
var noteLabel = form.querySelector('[data-aksrt-verify-note-label]');
|
|
var submit = form.querySelector('[data-aksrt-verify-submit]');
|
|
var rejected = decision === 'rejected';
|
|
var approved = decision === 'verified';
|
|
|
|
if (profile) {
|
|
profile.hidden = !approved;
|
|
profile.disabled = !approved;
|
|
}
|
|
if (note) {
|
|
note.required = rejected;
|
|
setTrimValidity(note, rejected);
|
|
}
|
|
setTrimValidity(name, approved);
|
|
if (noteLabel) {
|
|
noteLabel.textContent = valueOf(noteLabel, rejected ? 'data-required' : 'data-optional');
|
|
}
|
|
if (submit) {
|
|
var idleLabel = valueOf(submit, 'data-idle-label');
|
|
submit.disabled = !decision;
|
|
submit.textContent = approved
|
|
? valueOf(submit, 'data-approve-label', idleLabel)
|
|
: rejected
|
|
? valueOf(submit, 'data-reject-label', idleLabel)
|
|
: idleLabel;
|
|
submit.classList.toggle('is-danger', rejected);
|
|
}
|
|
}
|
|
|
|
function resetReview(form) {
|
|
var radios = form.querySelectorAll('input[name="status"]');
|
|
radios.forEach(function (radio) { radio.checked = false; });
|
|
|
|
var note = form.querySelector('textarea[name="note"]');
|
|
if (note) note.value = '';
|
|
syncReview(form);
|
|
}
|
|
|
|
function actionButtons() {
|
|
var buttons = [];
|
|
var submitButtons = dialog.querySelectorAll('[data-aksrt-verify-submit]');
|
|
var closeButtons = dialog.querySelectorAll('[data-aksrt-dialog-close]');
|
|
submitButtons.forEach(function (button) { buttons.push(button); });
|
|
closeButtons.forEach(function (button) {
|
|
if (buttons.indexOf(button) === -1) buttons.push(button);
|
|
});
|
|
return buttons;
|
|
}
|
|
|
|
function setActionsDisabled(disabled) {
|
|
actionButtons().forEach(function (button) { button.disabled = disabled; });
|
|
}
|
|
|
|
function setBusyLabel(form) {
|
|
var submit = form.querySelector('[data-aksrt-verify-submit]');
|
|
if (submit) submit.textContent = valueOf(submit, 'data-busy-label', submit.textContent);
|
|
}
|
|
|
|
function syncReviewIfIdle(form) {
|
|
if (!submitting && form.dataset.submitting !== '1') syncReview(form);
|
|
}
|
|
|
|
function setupForm(form) {
|
|
var review = form.hasAttribute('data-aksrt-verify-review');
|
|
var deletion = form.hasAttribute('data-aksrt-verify-delete-form');
|
|
var submit = form.querySelector('[data-aksrt-verify-submit]');
|
|
if (!review && !deletion) return;
|
|
|
|
if (review) {
|
|
resetReview(form);
|
|
form.addEventListener('change', function () { syncReviewIfIdle(form); });
|
|
form.addEventListener('input', function () { syncReviewIfIdle(form); });
|
|
} else if (submit) {
|
|
submit.disabled = false;
|
|
submit.textContent = valueOf(submit, 'data-idle-label', submit.textContent);
|
|
submit.classList.add('is-danger');
|
|
}
|
|
|
|
form.addEventListener('submit', function (event) {
|
|
if (submitting || form.dataset.submitting === '1') {
|
|
prevent(event);
|
|
return;
|
|
}
|
|
|
|
if (review) syncReview(form);
|
|
var valid = typeof form.reportValidity !== 'function' || form.reportValidity();
|
|
if (!valid || (review && !selectedStatus(form))) {
|
|
prevent(event);
|
|
return;
|
|
}
|
|
|
|
submitting = true;
|
|
form.dataset.submitting = '1';
|
|
setActionsDisabled(true);
|
|
setBusyLabel(form);
|
|
lockScroll();
|
|
});
|
|
}
|
|
|
|
function openTemplate(button, prefix) {
|
|
if (submitting || dialog.open) return;
|
|
var id = button.getAttribute(prefix);
|
|
if (id === null || id === '') return;
|
|
var template = document.getElementById('aksrt-verify-' + (prefix === 'data-aksrt-verify-view' ? 'detail-' : 'delete-') + id);
|
|
if (!template || !template.content || typeof template.content.cloneNode !== 'function') return;
|
|
|
|
pointerStartedOutside = false;
|
|
dialog.replaceChildren(template.content.cloneNode(true));
|
|
opener = button;
|
|
activeForm = dialog.querySelector('form[data-aksrt-verify-review]')
|
|
|| dialog.querySelector('form[data-aksrt-verify-delete-form]');
|
|
if (activeForm) setupForm(activeForm);
|
|
|
|
dialog.showModal();
|
|
lockScroll();
|
|
}
|
|
|
|
root.querySelectorAll('[data-aksrt-verify-view]').forEach(function (button) {
|
|
button.addEventListener('click', function (event) {
|
|
prevent(event);
|
|
openTemplate(button, 'data-aksrt-verify-view');
|
|
});
|
|
});
|
|
root.querySelectorAll('[data-aksrt-verify-delete]').forEach(function (button) {
|
|
button.addEventListener('click', function (event) {
|
|
prevent(event);
|
|
openTemplate(button, 'data-aksrt-verify-delete');
|
|
});
|
|
});
|
|
|
|
dialog.addEventListener('click', function (event) {
|
|
var closeButton = closestAttribute(event.target, 'data-aksrt-dialog-close', dialog);
|
|
if (closeButton) {
|
|
prevent(event);
|
|
if (!submitting) dialog.close();
|
|
pointerStartedOutside = false;
|
|
return;
|
|
}
|
|
if (pointerStartedOutside && event.target === dialog && isOutside(event) && !submitting) dialog.close();
|
|
pointerStartedOutside = false;
|
|
});
|
|
|
|
dialog.addEventListener('pointerdown', function (event) {
|
|
pointerStartedOutside = event.target === dialog && isOutside(event);
|
|
});
|
|
|
|
dialog.addEventListener('cancel', function (event) {
|
|
prevent(event);
|
|
if (!submitting) dialog.close();
|
|
});
|
|
|
|
dialog.addEventListener('keydown', function (event) {
|
|
if (event.key === 'Escape' && submitting) {
|
|
prevent(event);
|
|
if (typeof event.stopPropagation === 'function') event.stopPropagation();
|
|
}
|
|
});
|
|
|
|
dialog.addEventListener('close', function () {
|
|
dialog.replaceChildren();
|
|
activeForm = null;
|
|
submitting = false;
|
|
pointerStartedOutside = false;
|
|
lockScroll();
|
|
var previousOpener = opener;
|
|
opener = null;
|
|
if (previousOpener && previousOpener.isConnected) previousOpener.focus();
|
|
});
|
|
|
|
window.addEventListener('pageshow', function () {
|
|
submitting = false;
|
|
if (activeForm) {
|
|
delete activeForm.dataset.submitting;
|
|
setActionsDisabled(false);
|
|
if (activeForm.hasAttribute('data-aksrt-verify-review')) {
|
|
syncReview(activeForm);
|
|
} else {
|
|
var submit = activeForm.querySelector('[data-aksrt-verify-submit]');
|
|
if (submit) {
|
|
submit.disabled = false;
|
|
submit.textContent = valueOf(submit, 'data-idle-label', submit.textContent);
|
|
submit.classList.add('is-danger');
|
|
}
|
|
}
|
|
}
|
|
lockScroll();
|
|
});
|
|
})();
|