143 lines
4.0 KiB
TypeScript
143 lines
4.0 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useState } from "react";
|
||
import Modal from "@/components/Modal";
|
||
import StaffDeleteFields from "@/components/StaffDeleteFields";
|
||
|
||
export type DeleteCommentPayload = {
|
||
delete_type: string;
|
||
delete_reason: string;
|
||
};
|
||
|
||
/**
|
||
* 评论软删弹窗:
|
||
* - 评论者自删:必填理由(仅管理可见)
|
||
* - 管理删:与删帖共用类型表单;仅「其他」须填理由
|
||
*/
|
||
export default function DeleteCommentModal({
|
||
open,
|
||
mode,
|
||
busy,
|
||
onClose,
|
||
onConfirm,
|
||
}: {
|
||
open: boolean;
|
||
/** self=评论者自删;staff=版主/后台删除 */
|
||
mode: "self" | "staff";
|
||
busy?: boolean;
|
||
onClose: () => void;
|
||
onConfirm: (opts: DeleteCommentPayload) => void;
|
||
}) {
|
||
const [deleteType, setDeleteType] = useState("spam");
|
||
const [reason, setReason] = useState("");
|
||
const [err, setErr] = useState("");
|
||
|
||
useEffect(() => {
|
||
if (open) {
|
||
setDeleteType("spam");
|
||
setReason("");
|
||
setErr("");
|
||
}
|
||
}, [open]);
|
||
|
||
if (!open) return null;
|
||
|
||
const isStaff = mode === "staff";
|
||
const reasonRequired = !isStaff || deleteType === "other";
|
||
|
||
const submit = () => {
|
||
const r = reason.trim();
|
||
if (reasonRequired) {
|
||
if (!r) {
|
||
setErr("请填写删除理由");
|
||
return;
|
||
}
|
||
if ([...r].length > 200) {
|
||
setErr("删除理由不能超过 200 字");
|
||
return;
|
||
}
|
||
}
|
||
onConfirm({
|
||
delete_type: isStaff ? deleteType : "",
|
||
delete_reason: reasonRequired ? r : "",
|
||
});
|
||
};
|
||
|
||
return (
|
||
<Modal
|
||
open
|
||
title={isStaff ? "删除评论" : "删除我的评论"}
|
||
description={
|
||
isStaff
|
||
? "评论区保留匿名马赛克占位,读者看不到评论者头像与名称;可见删除类型,「其他」时还会展示补充理由。子回复不受影响。"
|
||
: "评论区保留马赛克占位,读者仅见「评论者已删除此评论」。你填写的理由仅管理员可见。"
|
||
}
|
||
onClose={onClose}
|
||
busy={busy}
|
||
maxWidthClass="max-w-[440px]"
|
||
panelClassName="p-5"
|
||
ariaLabel={isStaff ? "删除评论" : "删除我的评论"}
|
||
footer={
|
||
<>
|
||
<button type="button" className="btn btn-line" onClick={onClose} disabled={busy}>
|
||
取消
|
||
</button>
|
||
<button type="button" className="btn btn-danger" onClick={submit} disabled={busy}>
|
||
{busy ? "删除中…" : "确认删除"}
|
||
</button>
|
||
</>
|
||
}
|
||
>
|
||
{isStaff ? (
|
||
<StaffDeleteFields
|
||
deleteType={deleteType}
|
||
onTypeChange={(value) => {
|
||
setDeleteType(value);
|
||
setErr("");
|
||
if (value !== "other") setReason("");
|
||
}}
|
||
reason={reason}
|
||
onReasonChange={(value) => {
|
||
setReason(value);
|
||
setErr("");
|
||
}}
|
||
reasonId="cmt-delete-reason"
|
||
reasonPlaceholder="向读者说明删除原因(将展示在占位中)"
|
||
typeHint="选「其他」时需补充理由;其余类型直接按标签展示给读者。"
|
||
reasonHint="将公开显示在评论占位旁。"
|
||
/>
|
||
) : (
|
||
<>
|
||
<label
|
||
className="block mb-1.5 text-[12.5px] font-medium"
|
||
style={{ color: "var(--ink)" }}
|
||
htmlFor="cmt-delete-reason"
|
||
>
|
||
删除理由 <span style={{ color: "var(--danger)" }}>*</span>
|
||
</label>
|
||
<textarea
|
||
id="cmt-delete-reason"
|
||
className="field"
|
||
rows={3}
|
||
value={reason}
|
||
onChange={(e) => {
|
||
setReason(e.target.value);
|
||
setErr("");
|
||
}}
|
||
placeholder="例如:发错了 / 不想保留(仅管理员可见)"
|
||
maxLength={200}
|
||
autoFocus
|
||
/>
|
||
<p className="meta text-[11.5px] mt-1.5">普通读者看不到这条理由。</p>
|
||
</>
|
||
)}
|
||
|
||
{err ? (
|
||
<p className="mt-2 text-[12px]" style={{ color: "var(--danger)" }}>
|
||
{err}
|
||
</p>
|
||
) : null}
|
||
</Modal>
|
||
);
|
||
}
|