feat(admin): review reports and ban appeals in dialogs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-10-30 23:06:04 +08:00
1 parent 32ac2112b0
commit 59ddf677b6
13 files changed
+2122 -704

No files matched your search

-9
View File
@@ -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); }
+125
View File
@@ -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; }
}
+139
View File
@@ -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();
});
})();