"use client"; import Link from "next/link"; import { FormEvent, useEffect, useState } from "react"; import { ArrowRight } from "@/components/icons"; import { BrandIcon } from "@/components/brand-icon"; import { PreviewNotice, StatusCard } from "@/components/ui"; const apiBase = process.env.NEXT_PUBLIC_API_BASE_URL ?? "/api/v1"; type User = { email: string | null; phone: string | null; username: string; role: "user" | "admin"; plan: "free" | "vip"; status: string; email_verified: boolean; phone_verified: boolean }; type Usage = { period_start: string; period_end: string; limit: number; adjustment: number; used: number; reserved: number; available: number; plan: string }; export function AccountPanel() { const [csrf, setCsrf] = useState(""); const [user, setUser] = useState(null); const [usage, setUsage] = useState(null); const [error, setError] = useState(""); const [passwordMessage, setPasswordMessage] = useState(""); const [profileMessage, setProfileMessage] = useState(""); async function load() { const csrfResponse = await fetch(`${apiBase}/auth/csrf`, { credentials: "include" }); const csrfBody = await csrfResponse.json(); setCsrf(csrfBody.csrf_token ?? ""); const [meResponse, usageResponse] = await Promise.all([ fetch(`${apiBase}/auth/me`, { credentials: "include" }), fetch(`${apiBase}/account/usage`, { credentials: "include" }), ]); if (!meResponse.ok) throw new Error("请先登录后查看账户"); setUser(await meResponse.json()); if (usageResponse.ok) setUsage(await usageResponse.json()); } useEffect(() => { load().catch((loadError) => setError(loadError instanceof Error ? loadError.message : "无法加载账户")); }, []); async function logout() { await fetch(`${apiBase}/auth/logout`, { method: "POST", credentials: "include", headers: { "X-CSRF-Token": csrf } }); window.location.assign("/login"); } async function changePassword(event: FormEvent) { event.preventDefault(); const form = event.currentTarget; const data = new FormData(form); const response = await fetch(`${apiBase}/auth/password/change`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", "X-CSRF-Token": csrf }, body: JSON.stringify({ current_password: data.get("current_password"), new_password: data.get("new_password") }) }); const body = await response.json().catch(() => ({})); if (!response.ok) setPasswordMessage(body.error?.message ?? body.detail?.message ?? "密码修改失败"); else { setPasswordMessage("密码已修改,其他会话已撤销"); form.reset(); } } async function updateProfile(event: FormEvent) { event.preventDefault(); const data = new FormData(event.currentTarget); const response = await fetch(`${apiBase}/auth/profile`, { method: "PATCH", credentials: "include", headers: { "Content-Type": "application/json", "X-CSRF-Token": csrf }, body: JSON.stringify({ username: data.get("username"), email: data.get("email") || null, phone: data.get("phone") || null }) }); const body = await response.json().catch(() => ({})); if (!response.ok) setProfileMessage(body.error?.message ?? body.detail?.message ?? "资料保存失败"); else { setUser(body); setProfileMessage("资料已保存;如修改邮箱或手机号,请重新完成验证"); } } if (error) return
{error}。前往登录
; if (!user || !usage) return
; return <>

Profile

基本资料

{profileMessage ?

{profileMessage}

: null}
{user.role === "admin" ?
业务后台
进入管理后台
内容管理
进入 CMS
: null}

Usage

额度明细

打开工作台

Security

修改密码

{passwordMessage ?

{passwordMessage}

: null}
; }