Initial commit: OmniAI Web Frontend
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
import { CheckCircleOutlined, FlagOutlined, MailOutlined, PhoneOutlined } from "@ant-design/icons";
|
||||
import { useState, type FormEvent } from "react";
|
||||
import { reportClient, type ReportInput } from "../../api/reportClient";
|
||||
|
||||
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 canSubmit =
|
||||
submitState !== "loading" && reportType !== "" && title.trim() !== "" && description.trim() !== "";
|
||||
|
||||
const resetForm = () => {
|
||||
setReportType("");
|
||||
setTargetType("");
|
||||
setTargetId("");
|
||||
setTitle("");
|
||||
setDescription("");
|
||||
setContactName("");
|
||||
setContactEmail("");
|
||||
setContactPhone("");
|
||||
setSubmitState("idle");
|
||||
setErrorMsg("");
|
||||
};
|
||||
|
||||
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 /> {import.meta.env.VITE_REPORT_EMAIL || "support@omniai.com"}</span>
|
||||
<span><PhoneOutlined /> {import.meta.env.VITE_REPORT_PHONE || "请在环境变量配置客服电话"}</span>
|
||||
<span>{import.meta.env.VITE_ICP_RECORD || "ICP备案信息待配置"}</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;
|
||||
Reference in New Issue
Block a user