197 lines
6.9 KiB
TypeScript
197 lines
6.9 KiB
TypeScript
import { CheckCircleOutlined, FlagOutlined, MailOutlined, PhoneOutlined } from "@ant-design/icons";
|
||
import { useEffect, useState, type FormEvent } from "react";
|
||
import { publicConfigClient, type WebPublicConfig } from "../../api/publicConfigClient";
|
||
import { reportClient, type ReportInput } from "../../api/reportClient";
|
||
import "../../styles/pages/compliance.css";
|
||
|
||
type SubmitState = "idle" | "loading" | "success" | "error";
|
||
|
||
const REPORT_TYPES = [
|
||
{ value: "spam", label: "垃圾内容" },
|
||
{ value: "abuse", label: "滥用 / 骚扰" },
|
||
{ value: "copyright", label: "侵权 / 版权" },
|
||
{ value: "illegal", label: "违法违规" },
|
||
{ value: "other", label: "其他" },
|
||
];
|
||
|
||
const TARGET_TYPES = [
|
||
{ value: "project", label: "项目" },
|
||
{ value: "community_post", label: "社区作品" },
|
||
{ value: "user", label: "用户" },
|
||
{ value: "comment", label: "评论" },
|
||
{ value: "other", label: "其他" },
|
||
];
|
||
|
||
function ReportPage() {
|
||
const [reportType, setReportType] = useState("");
|
||
const [targetType, setTargetType] = useState("");
|
||
const [targetId, setTargetId] = useState("");
|
||
const [title, setTitle] = useState("");
|
||
const [description, setDescription] = useState("");
|
||
const [contactName, setContactName] = useState("");
|
||
const [contactEmail, setContactEmail] = useState("");
|
||
const [contactPhone, setContactPhone] = useState("");
|
||
const [submitState, setSubmitState] = useState<SubmitState>("idle");
|
||
const [errorMsg, setErrorMsg] = useState("");
|
||
const [publicConfig, setPublicConfig] = useState<WebPublicConfig>({});
|
||
|
||
const canSubmit =
|
||
submitState !== "loading" && reportType !== "" && title.trim() !== "" && description.trim() !== "";
|
||
|
||
const resetForm = () => {
|
||
setReportType("");
|
||
setTargetType("");
|
||
setTargetId("");
|
||
setTitle("");
|
||
setDescription("");
|
||
setContactName("");
|
||
setContactEmail("");
|
||
setContactPhone("");
|
||
setSubmitState("idle");
|
||
setErrorMsg("");
|
||
};
|
||
|
||
useEffect(() => {
|
||
let cancelled = false;
|
||
publicConfigClient
|
||
.get()
|
||
.then((config) => {
|
||
if (!cancelled) setPublicConfig(config);
|
||
})
|
||
.catch(() => {
|
||
if (!cancelled) setPublicConfig({});
|
||
});
|
||
|
||
return () => {
|
||
cancelled = true;
|
||
};
|
||
}, []);
|
||
|
||
const handleSubmit = async (event: FormEvent) => {
|
||
event.preventDefault();
|
||
if (!canSubmit) return;
|
||
setSubmitState("loading");
|
||
setErrorMsg("");
|
||
try {
|
||
const input: ReportInput = {
|
||
reportType,
|
||
targetType: targetType || undefined,
|
||
targetId: targetId.trim() || undefined,
|
||
title: title.trim(),
|
||
description: description.trim(),
|
||
contactName: contactName.trim() || undefined,
|
||
contactEmail: contactEmail.trim() || undefined,
|
||
contactPhone: contactPhone.trim() || undefined,
|
||
pageUrl: window.location.href,
|
||
};
|
||
await reportClient.submit(input);
|
||
setSubmitState("success");
|
||
} catch (error: unknown) {
|
||
setErrorMsg(error instanceof Error ? error.message : "提交失败,请稍后重试");
|
||
setSubmitState("error");
|
||
}
|
||
};
|
||
|
||
return (
|
||
<section className="report-page page-motion">
|
||
<div className="report-page__inner">
|
||
<header className="report-hero">
|
||
<span className="report-hero__icon"><FlagOutlined /></span>
|
||
<div>
|
||
<h1>投诉举报</h1>
|
||
<p>提交后会同步到服务器举报工单,工作人员会在审核台中查看处理。</p>
|
||
</div>
|
||
</header>
|
||
|
||
<div className="report-contact-strip">
|
||
<span><MailOutlined /> {publicConfig.contactEmail || "由服务器配置"}</span>
|
||
<span><PhoneOutlined /> {publicConfig.contactPhone || "由服务器配置"}</span>
|
||
<span>{publicConfig.icpRecord || "由服务器配置"}</span>
|
||
</div>
|
||
|
||
{submitState === "success" ? (
|
||
<div className="report-success">
|
||
<CheckCircleOutlined />
|
||
<h2>提交成功</h2>
|
||
<p>感谢反馈,举报内容已进入服务器工单列表。</p>
|
||
<button type="button" onClick={resetForm}>继续举报</button>
|
||
</div>
|
||
) : (
|
||
<form className="report-form" onSubmit={handleSubmit}>
|
||
<div className="report-form__grid">
|
||
<label>
|
||
举报类型 *
|
||
<select value={reportType} onChange={(event) => setReportType(event.target.value)}>
|
||
<option value="">请选择</option>
|
||
{REPORT_TYPES.map((item) => <option key={item.value} value={item.value}>{item.label}</option>)}
|
||
</select>
|
||
</label>
|
||
<label>
|
||
对象类型
|
||
<select value={targetType} onChange={(event) => setTargetType(event.target.value)}>
|
||
<option value="">请选择</option>
|
||
{TARGET_TYPES.map((item) => <option key={item.value} value={item.value}>{item.label}</option>)}
|
||
</select>
|
||
</label>
|
||
<label>
|
||
对象 ID
|
||
<input value={targetId} onChange={(event) => setTargetId(event.target.value)} placeholder="作品、项目或用户 ID" />
|
||
</label>
|
||
</div>
|
||
|
||
<label>
|
||
标题 *
|
||
<input
|
||
value={title}
|
||
onChange={(event) => setTitle(event.target.value)}
|
||
placeholder="简要描述问题"
|
||
maxLength={100}
|
||
/>
|
||
</label>
|
||
<label>
|
||
详细说明 *
|
||
<textarea
|
||
value={description}
|
||
onChange={(event) => setDescription(event.target.value)}
|
||
placeholder="请说明举报原因、相关页面或证据线索"
|
||
maxLength={2000}
|
||
/>
|
||
</label>
|
||
|
||
<div className="report-form__grid">
|
||
<label>
|
||
联系人
|
||
<input value={contactName} onChange={(event) => setContactName(event.target.value)} placeholder="可选" />
|
||
</label>
|
||
<label>
|
||
联系邮箱
|
||
<input
|
||
type="email"
|
||
value={contactEmail}
|
||
onChange={(event) => setContactEmail(event.target.value)}
|
||
placeholder="可选"
|
||
/>
|
||
</label>
|
||
<label>
|
||
联系电话
|
||
<input value={contactPhone} onChange={(event) => setContactPhone(event.target.value)} placeholder="可选" />
|
||
</label>
|
||
</div>
|
||
|
||
{submitState === "error" ? <p className="report-form__error">{errorMsg}</p> : null}
|
||
|
||
<div className="report-form__actions">
|
||
<button type="submit" disabled={!canSubmit}>
|
||
{submitState === "loading" ? "提交中..." : "提交举报"}
|
||
</button>
|
||
<span>必填字段完整后才可提交。</span>
|
||
</div>
|
||
</form>
|
||
)}
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
export default ReportPage;
|