From 3f05bd16e144ba0e38eeb1345953be5cebb073c4 Mon Sep 17 00:00:00 2001 From: Zhengen TANG Date: Fri, 7 Aug 2026 13:30:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8E=A5=E5=85=A5PIPL=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=88=A0=E9=99=A4=E6=9D=83=E7=94=B3=E8=AF=B7/?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 /privacy-policy/delete-request 页面:提交邮箱发起删除申请 - 新增 /privacy-policy/delete-confirm 页面:从邮件链接确认删除(Suspense包裹useSearchParams) - 隐私政策页面加入删除申请入口链接 --- .../delete-confirm/DeleteConfirmForm.tsx | 81 +++++++++++++++++++ app/privacy-policy/delete-confirm/page.tsx | 15 ++++ app/privacy-policy/delete-request/page.tsx | 72 +++++++++++++++++ app/privacy-policy/page.tsx | 10 +++ 4 files changed, 178 insertions(+) create mode 100644 app/privacy-policy/delete-confirm/DeleteConfirmForm.tsx create mode 100644 app/privacy-policy/delete-confirm/page.tsx create mode 100644 app/privacy-policy/delete-request/page.tsx diff --git a/app/privacy-policy/delete-confirm/DeleteConfirmForm.tsx b/app/privacy-policy/delete-confirm/DeleteConfirmForm.tsx new file mode 100644 index 0000000..acd4fb3 --- /dev/null +++ b/app/privacy-policy/delete-confirm/DeleteConfirmForm.tsx @@ -0,0 +1,81 @@ +"use client"; + +import { useState } from "react"; +import { useSearchParams } from "next/navigation"; + +const API_BASE_URL = + process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000"; + +export default function DeleteConfirmForm() { + const searchParams = useSearchParams(); + const token = searchParams.get("token"); + const [status, setStatus] = useState< + "idle" | "submitting" | "done" | "error" + >("idle"); + const [message, setMessage] = useState(""); + + async function handleConfirm() { + if (!token) return; + setStatus("submitting"); + try { + const res = await fetch( + `${API_BASE_URL}/api/v1/custom/leads/deletion-requests/confirm/`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ token }), + }, + ); + const json = await res.json(); + if (!res.ok) { + setMessage(json?.error?.message ?? "确认失败,请重新申请删除。"); + setStatus("error"); + return; + } + setMessage(json?.message ?? "已成功删除您的个人信息。"); + setStatus("done"); + } catch { + setMessage("网络错误,请稍后重试。"); + setStatus("error"); + } + } + + if (!token) { + return ( +

+ 链接无效,请从确认邮件中重新打开该链接,或前往{" "} + + 申请删除个人信息 + {" "} + 重新发起申请。 +

+ ); + } + + if (status === "done") { + return

{message}

; + } + + if (status === "error") { + return

{message}

; + } + + return ( +
+

确认删除个人信息

+

+ 点击下方按钮将永久删除您通过表单提交给我们的个人信息,此操作不可撤销。 +

+ +
+ ); +} diff --git a/app/privacy-policy/delete-confirm/page.tsx b/app/privacy-policy/delete-confirm/page.tsx new file mode 100644 index 0000000..6d9326a --- /dev/null +++ b/app/privacy-policy/delete-confirm/page.tsx @@ -0,0 +1,15 @@ +import { Suspense } from "react"; +import type { Metadata } from "next"; +import DeleteConfirmForm from "./DeleteConfirmForm"; + +export const metadata: Metadata = { title: "确认删除个人信息" }; + +export default function DeleteConfirmPage() { + return ( +
+ 加载中...

}> + +
+
+ ); +} diff --git a/app/privacy-policy/delete-request/page.tsx b/app/privacy-policy/delete-request/page.tsx new file mode 100644 index 0000000..b23ed06 --- /dev/null +++ b/app/privacy-policy/delete-request/page.tsx @@ -0,0 +1,72 @@ +"use client"; + +import { useState } from "react"; + +const API_BASE_URL = + process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000"; + +export default function DeleteRequestPage() { + const [contact, setContact] = useState(""); + const [status, setStatus] = useState< + "idle" | "submitting" | "done" | "error" + >("idle"); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + setStatus("submitting"); + try { + const res = await fetch( + `${API_BASE_URL}/api/v1/custom/leads/deletion-requests/`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ contact }), + }, + ); + if (!res.ok) throw new Error("request failed"); + setStatus("done"); + } catch { + setStatus("error"); + } + } + + if (status === "done") { + return ( +
+ 如果我们持有与该邮箱关联的信息,确认邮件将发送至该邮箱,请查收并点击链接完成删除确认。 +
+ ); + } + + return ( +
+

+ 申请删除个人信息 +

+

+ 依据《中华人民共和国个人信息保护法》(PIPL),您可以申请删除我们通过表单收集的您的个人信息。 + 请输入您提交表单时使用的邮箱,我们将向该邮箱发送确认邮件,点击邮件中的链接即可完成删除。 +

+
+ setContact(e.target.value)} + placeholder="请输入邮箱" + className="flex-1 rounded-lg border border-slate-200 px-4 py-2" + /> + +
+ {status === "error" && ( +

提交失败,请稍后重试。

+ )} +
+ ); +} diff --git a/app/privacy-policy/page.tsx b/app/privacy-policy/page.tsx index ba5925f..459b602 100644 --- a/app/privacy-policy/page.tsx +++ b/app/privacy-policy/page.tsx @@ -22,6 +22,16 @@ export default async function PrivacyPolicyPage() { className="space-y-4 text-slate-700 [&_h2]:mt-8 [&_h2]:text-2xl [&_h2]:font-semibold [&_h2]:text-slate-900 [&_ul]:list-disc [&_ul]:pl-6 [&_li]:mt-1" dangerouslySetInnerHTML={{ __html: page.body }} /> +

+ 如需删除我们持有的您的个人信息,请 + + 点击此处申请删除 + + 。 +

); }