Files
omniai-web/src/features/report/ReportPage.tsx
T

197 lines
6.9 KiB
TypeScript
Raw Normal View History

2026-06-02 12:38:01 +08:00
import { CheckCircleOutlined, FlagOutlined, MailOutlined, PhoneOutlined } from "@ant-design/icons";
import { useEffect, useState, type FormEvent } from "react";
import { publicConfigClient, type WebPublicConfig } from "../../api/publicConfigClient";
2026-06-02 12:38:01 +08:00
import { reportClient, type ReportInput } from "../../api/reportClient";
2026-06-08 16:32:16 +08:00
import "../../styles/pages/compliance.css";
2026-06-02 12:38:01 +08:00
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>({});
2026-06-02 12:38:01 +08:00
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;
};
}, []);
2026-06-02 12:38:01 +08:00
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>
2026-06-02 12:38:01 +08:00
</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;