feat(admin): review reports and ban appeals in dialogs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
32ac2112b0
commit
59ddf677b6
13 files changed
+2122
-704
No files matched your search
@@ -486,15 +486,6 @@ a.aksrt-native-stat:focus { outline: 2px solid color-mix(in srgb, var(--an-tone)
|
||||
.aksrt-native-text--info { color: var(--an-info); }
|
||||
.aksrt-native-text--warning { color: #c27c0e; }
|
||||
.aksrt-native-text--danger { color: var(--an-danger); }
|
||||
.aksrt-native-clamp { max-height: 40px; overflow: hidden; }
|
||||
/* 举报&禁封申诉页:宽表 + 排序箭头 */
|
||||
.aksrt-native-ban .aksrt-native-table-wrap > table { min-width: 1000px; }
|
||||
.aksrt-native-ban .orderby-but { position: relative; display: inline-block; width: 0; height: 0; margin-left: 4px; }
|
||||
.aksrt-native-ban .orderby-but > a { position: absolute; left: 0; bottom: .725em; padding-right: 1em; opacity: .4; transform: translateY(50%); transition: opacity .3s; }
|
||||
.aksrt-native-ban .orderby-but > a + a { bottom: .075em; }
|
||||
.aksrt-native-ban .orderby-but > a:hover { opacity: 1; }
|
||||
.aksrt-native-ban .aksrt-native-toolbar + .subsubsub { float: none; margin: 10px 0 0; }
|
||||
.aksrt-native-ban textarea { width: 95%; max-width: 500px; }
|
||||
.aksrt-native-amount { color: var(--an-text); font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||
.aksrt-native-amount.is-positive { color: var(--an-primary); }
|
||||
.aksrt-native-amount.is-negative { color: var(--an-danger); }
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/* 举报与禁封申诉运营页。继承 native-ui.css 的后台颜色与表单令牌。 */
|
||||
.aksrt-ban-page [hidden],
|
||||
.aksrt-ban-dialog [hidden] { display: none !important; }
|
||||
|
||||
.aksrt-ban-toolbar { padding: 14px 20px; border-bottom: 1px solid var(--an-border-soft); }
|
||||
.aksrt-ban-toolbar__top,
|
||||
.aksrt-ban-toolbar__bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 18px; }
|
||||
.aksrt-ban-toolbar__bottom { margin-top: 14px; }
|
||||
.aksrt-ban-types { display: inline-flex; flex-wrap: wrap; gap: 4px; padding: 3px; background: var(--an-bg-soft); border-radius: var(--an-radius-sm); }
|
||||
.aksrt-ban-types a { padding: 6px 12px; color: var(--an-text-2); border-radius: 5px; text-decoration: none; }
|
||||
.aksrt-ban-types a:hover { color: var(--an-primary); }
|
||||
.aksrt-ban-types a[aria-current="page"] { color: var(--an-primary); background: var(--an-card); box-shadow: 0 1px 3px rgba(15, 23, 42, .08); font-weight: 600; }
|
||||
.aksrt-ban-toolbar .subsubsub { margin: 0; padding: 0; box-shadow: none; }
|
||||
.aksrt-ban-toolbar .aksrt-native-filters { margin: 0; }
|
||||
.aksrt-ban-toolbar .aksrt-native-filters input[type="search"] { min-width: 240px; }
|
||||
.aksrt-ban-filter-hint { margin: 12px 0 0; color: var(--an-text-2); font-size: 12px; }
|
||||
|
||||
.aksrt-ban-page .aksrt-ban-table { min-width: 1160px; table-layout: fixed; }
|
||||
.aksrt-ban-table .column-type { width: 11%; }
|
||||
.aksrt-ban-table .column-user { width: 16%; }
|
||||
.aksrt-ban-table .column-summary { width: 27%; }
|
||||
.aksrt-ban-table .column-status { width: 10%; }
|
||||
.aksrt-ban-table .column-time,
|
||||
.aksrt-ban-table .column-updated { width: 13%; }
|
||||
.aksrt-ban-table .column-actions { width: 10%; }
|
||||
.aksrt-ban-page .aksrt-ban-table td { overflow-wrap: anywhere; }
|
||||
.aksrt-ban-sort { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
|
||||
.aksrt-ban-sort span { font-size: 14px; }
|
||||
.aksrt-ban-page .aksrt-ban-table th[aria-sort] a,
|
||||
.aksrt-ban-page .aksrt-ban-table th a:hover { color: var(--an-primary); }
|
||||
.aksrt-ban-sub { margin-top: 5px; color: var(--an-text-2); font-size: 12px; line-height: 1.6; }
|
||||
.aksrt-ban-summary { display: -webkit-box; max-height: 3.1em; margin-top: 5px; color: var(--an-text-2); line-height: 1.55; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
|
||||
.aksrt-ban-user-filter { display: inline-block; margin: 5px 0 0 36px; font-size: 12px; }
|
||||
.aksrt-ban-table .column-actions .button { white-space: nowrap; }
|
||||
.aksrt-ban-page :is(a, button, input):focus-visible,
|
||||
.aksrt-ban-dialog :is(a, button, input, textarea):focus-visible { outline: 2px solid var(--an-primary); outline-offset: 2px; }
|
||||
|
||||
html.aksrt-ban-modal-open { overflow: hidden; scrollbar-gutter: stable; }
|
||||
.aksrt-ban-dialog {
|
||||
width: min(780px, calc(100vw - 32px));
|
||||
max-width: none;
|
||||
max-height: calc(100vh - 40px);
|
||||
max-height: calc(100dvh - 40px);
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
color: var(--an-text-2);
|
||||
background: var(--an-card);
|
||||
border: 1px solid var(--an-border);
|
||||
border-radius: var(--an-radius);
|
||||
box-shadow: var(--an-shadow-hover);
|
||||
}
|
||||
.aksrt-ban-dialog::backdrop { background: rgba(15, 23, 42, .45); }
|
||||
.aksrt-ban-dialog__frame { display: flex; flex-direction: column; min-height: 0; max-height: inherit; margin: 0; }
|
||||
.aksrt-ban-dialog__head { display: flex; flex: none; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 19px 24px 17px; border-bottom: 1px solid var(--an-border-soft); }
|
||||
.aksrt-ban-dialog .aksrt-ban-dialog__head h2 { margin: 0; padding: 0; color: var(--an-text); font-size: 18px; font-weight: 600; line-height: 1.45; }
|
||||
.aksrt-ban-dialog__head p { max-width: 52ch; margin: 5px 0 0; font-size: 13px; line-height: 1.6; }
|
||||
.aksrt-ban-dialog__close { display: inline-flex; flex: none; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; color: var(--an-text-2); background: transparent; border: 0; border-radius: var(--an-radius-sm); cursor: pointer; }
|
||||
.aksrt-ban-dialog__close:hover { color: var(--an-text); background: var(--an-bg-soft); }
|
||||
.aksrt-ban-dialog__body { min-height: 0; padding: 20px 24px; overflow-y: auto; overscroll-behavior: contain; }
|
||||
.aksrt-ban-dialog__foot { display: flex; flex: none; justify-content: flex-end; gap: 8px; padding: 14px 24px; background: var(--an-card); border-top: 1px solid var(--an-border-soft); }
|
||||
.aksrt-ban-dialog__foot .button { min-width: 120px; }
|
||||
.aksrt-ban-dialog .aksrt-ban-dialog__foot .button.is-danger { color: var(--an-card); background: var(--an-danger); border-color: var(--an-danger); }
|
||||
.aksrt-ban-dialog .button:disabled,
|
||||
.aksrt-ban-dialog__close:disabled { opacity: .65; cursor: not-allowed; }
|
||||
|
||||
.aksrt-ban-applicant { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; margin-bottom: 18px; }
|
||||
.aksrt-ban-meta { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 18px; margin: 0; padding: 14px 16px; background: var(--an-bg-soft); border-radius: var(--an-radius-sm); }
|
||||
.aksrt-ban-meta > div,
|
||||
.aksrt-ban-materials > div { min-width: 0; }
|
||||
.aksrt-ban-meta dt,
|
||||
.aksrt-ban-materials dt { color: var(--an-text-2); font-size: 12px; line-height: 1.6; }
|
||||
.aksrt-ban-meta dd,
|
||||
.aksrt-ban-materials dd { margin: 3px 0 0; color: var(--an-text); white-space: pre-wrap; overflow-wrap: anywhere; }
|
||||
.aksrt-ban-section { margin-top: 20px; }
|
||||
.aksrt-ban-dialog .aksrt-ban-section h3,
|
||||
.aksrt-ban-dialog .aksrt-ban-review > h3 { margin: 0 0 9px; color: var(--an-text); font-size: 14px; font-weight: 600; line-height: 1.5; }
|
||||
.aksrt-ban-copy { color: var(--an-text-2); line-height: 1.75; white-space: pre-wrap; overflow-wrap: anywhere; }
|
||||
.aksrt-ban-target { color: var(--an-text); font-weight: 600; overflow-wrap: anywhere; }
|
||||
.aksrt-ban-materials { display: grid; gap: 12px; margin: 0; padding-left: 14px; border-left: 2px solid var(--an-border); }
|
||||
.aksrt-ban-restrictions { display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; }
|
||||
.aksrt-ban-restrictions li { margin: 0; padding: 12px 14px; background: var(--an-bg-soft); border-radius: var(--an-radius-sm); }
|
||||
.aksrt-ban-restriction__title { display: flex; align-items: center; gap: 10px; margin-bottom: 7px; font-size: 12px; }
|
||||
.aksrt-ban-normal { margin: 0; color: var(--an-text-2); }
|
||||
.aksrt-ban-profile-link { margin: 12px 0 18px; }
|
||||
.aksrt-ban-history { margin: 0; padding-left: 14px; list-style: none; border-left: 2px solid var(--an-border); }
|
||||
.aksrt-ban-history li { margin: 0 0 14px; }
|
||||
.aksrt-ban-history li:last-child { margin-bottom: 0; }
|
||||
.aksrt-ban-history__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; margin-bottom: 5px; font-size: 12px; }
|
||||
.aksrt-ban-history__meta strong { color: var(--an-text); }
|
||||
.aksrt-ban-dialog__body > .aksrt-native-callout { margin-top: 18px; }
|
||||
|
||||
.aksrt-ban-review { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--an-border); }
|
||||
.aksrt-ban-decisions { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 16px; padding: 0; border: 0; }
|
||||
.aksrt-ban-decisions legend { width: 100%; margin-bottom: 8px; padding: 0; color: var(--an-text); font-weight: 600; }
|
||||
.aksrt-ban-decisions label { position: relative; flex: 1 1 180px; cursor: pointer; }
|
||||
.aksrt-ban-decisions input[type="radio"] { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); }
|
||||
.aksrt-ban-decisions label > span { display: flex; align-items: center; min-height: 42px; padding: 9px 12px; color: var(--an-text-2); background: var(--an-card); border: 1px solid var(--an-border); border-radius: var(--an-radius-sm); }
|
||||
.aksrt-ban-decisions label:hover > span { border-color: var(--an-primary); }
|
||||
.aksrt-ban-decisions input:checked + span { color: var(--an-primary); background: var(--an-primary-soft); border-color: var(--an-primary); font-weight: 600; }
|
||||
.aksrt-ban-decisions input:focus-visible + span { outline: 2px solid var(--an-primary); outline-offset: 2px; }
|
||||
.aksrt-ban-review textarea { resize: vertical; }
|
||||
.aksrt-ban-review .aksrt-native-field__hint { margin-bottom: 0; color: var(--an-text-2); }
|
||||
|
||||
@media (max-width: 782px) {
|
||||
.aksrt-ban-toolbar { padding: 12px 14px; }
|
||||
.aksrt-ban-toolbar__top,
|
||||
.aksrt-ban-toolbar__bottom { align-items: flex-start; }
|
||||
.aksrt-ban-toolbar .aksrt-native-filters { width: 100%; }
|
||||
.aksrt-ban-toolbar .aksrt-native-filters input[type="search"] { min-width: 0; flex: 1 1 220px; }
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.aksrt-ban-dialog { width: calc(100vw - 24px); max-height: calc(100dvh - 24px); }
|
||||
.aksrt-ban-dialog__head,
|
||||
.aksrt-ban-dialog__body,
|
||||
.aksrt-ban-dialog__foot { padding-left: 16px; padding-right: 16px; }
|
||||
.aksrt-ban-meta { grid-template-columns: 1fr; gap: 10px; }
|
||||
.aksrt-ban-decisions { flex-direction: column; }
|
||||
.aksrt-ban-decisions label { flex-basis: auto; }
|
||||
.aksrt-ban-dialog__foot .button { flex: 1; min-width: 0; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.aksrt-ban-page *,
|
||||
.aksrt-ban-dialog * { transition: none !important; }
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/** 举报与禁封申诉审核。仅在对应 native 后台页入队。 */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var root = document.querySelector('.aksrt-ban-page');
|
||||
var dialog = document.getElementById('aksrt-ban-dialog');
|
||||
if (!root || !dialog || typeof dialog.showModal !== 'function') return;
|
||||
|
||||
var opener = null;
|
||||
var activeForm = null;
|
||||
var submitting = false;
|
||||
var pointerStartedOutside = false;
|
||||
|
||||
function lockScroll() {
|
||||
document.documentElement.classList.toggle('aksrt-ban-modal-open', !!dialog.open);
|
||||
}
|
||||
|
||||
function attribute(element, name) {
|
||||
return element ? element.getAttribute(name) || '' : '';
|
||||
}
|
||||
|
||||
function selectedDecision(form) {
|
||||
var radios = form.querySelectorAll('input[type="radio"]');
|
||||
for (var index = 0; index < radios.length; index += 1) {
|
||||
if (radios[index].checked) return radios[index].value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function syncReview(form) {
|
||||
if (submitting) return;
|
||||
var decision = selectedDecision(form);
|
||||
var rejected = decision === 'reject' || decision === 'dismissed';
|
||||
var note = form.querySelector('textarea[name="note"]');
|
||||
var label = form.querySelector('[data-aksrt-ban-note-label]');
|
||||
var submit = form.querySelector('[data-aksrt-ban-submit]');
|
||||
if (note) {
|
||||
note.required = rejected;
|
||||
note.setCustomValidity(rejected && !note.value.trim() ? attribute(note, 'data-required-error') : '');
|
||||
}
|
||||
if (label) label.textContent = attribute(label, rejected ? 'data-required' : 'data-optional');
|
||||
if (submit) {
|
||||
submit.disabled = !decision;
|
||||
submit.textContent = attribute(submit, decision ? (rejected ? 'data-reject-label' : 'data-approve-label') : 'data-idle-label');
|
||||
submit.classList.toggle('is-danger', rejected);
|
||||
}
|
||||
}
|
||||
|
||||
function setActionsDisabled(disabled) {
|
||||
dialog.querySelectorAll('[data-aksrt-ban-submit], [data-aksrt-dialog-close]').forEach(function (button) {
|
||||
button.disabled = disabled;
|
||||
});
|
||||
}
|
||||
|
||||
function setupReview(form) {
|
||||
form.querySelectorAll('input[type="radio"]').forEach(function (radio) { radio.checked = false; });
|
||||
var note = form.querySelector('textarea[name="note"]');
|
||||
if (note) note.value = '';
|
||||
syncReview(form);
|
||||
form.addEventListener('input', function () { syncReview(form); });
|
||||
form.addEventListener('change', function () { syncReview(form); });
|
||||
form.addEventListener('submit', function (event) {
|
||||
if (submitting || form.dataset.submitting === '1') {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
syncReview(form);
|
||||
if (!selectedDecision(form) || !form.reportValidity()) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
submitting = true;
|
||||
form.dataset.submitting = '1';
|
||||
form.setAttribute('aria-busy', 'true');
|
||||
setActionsDisabled(true);
|
||||
var submit = form.querySelector('[data-aksrt-ban-submit]');
|
||||
if (submit) submit.textContent = attribute(submit, 'data-busy-label');
|
||||
});
|
||||
}
|
||||
|
||||
root.querySelectorAll('[data-aksrt-ban-view]').forEach(function (button) {
|
||||
button.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
if (submitting || dialog.open) return;
|
||||
var template = document.getElementById('aksrt-ban-detail-' + attribute(button, 'data-aksrt-ban-view'));
|
||||
if (!template || !template.content) return;
|
||||
dialog.replaceChildren(template.content.cloneNode(true));
|
||||
opener = button;
|
||||
pointerStartedOutside = false;
|
||||
activeForm = dialog.querySelector('form[data-aksrt-ban-review]');
|
||||
if (activeForm) setupReview(activeForm);
|
||||
dialog.showModal();
|
||||
lockScroll();
|
||||
});
|
||||
});
|
||||
|
||||
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('pointercancel', function () { pointerStartedOutside = false; });
|
||||
dialog.addEventListener('click', function (event) {
|
||||
var close = event.target.closest('[data-aksrt-dialog-close]');
|
||||
if (close || (pointerStartedOutside && event.target === dialog && isOutside(event))) {
|
||||
event.preventDefault();
|
||||
if (!submitting) dialog.close();
|
||||
}
|
||||
pointerStartedOutside = false;
|
||||
});
|
||||
dialog.addEventListener('cancel', function (event) {
|
||||
event.preventDefault();
|
||||
if (!submitting) dialog.close();
|
||||
});
|
||||
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;
|
||||
activeForm.removeAttribute('aria-busy');
|
||||
setActionsDisabled(false);
|
||||
syncReview(activeForm);
|
||||
}
|
||||
lockScroll();
|
||||
});
|
||||
})();
|
||||
Reference in new issue
Block a user