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

197 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;