"use client"; import { useCallback, useState } from "react"; import { useRouter } from "next/navigation"; import { Check } from "lucide-react"; import { apiUpdateProfile, type User } from "@/lib/api"; import AvatarPicker from "@/components/AvatarPicker"; interface ProfileFormProps { user: User; } /** * 个人资料编辑表单:头像(上传/裁剪/历史选择)、昵称、邮箱、签名。 * 头像由独立上传链路处理,不随本表单提交;保存成功后 router.refresh() * 让 Header 等 SSR 区域同步最新昵称。 */ export default function ProfileForm({ user }: ProfileFormProps) { const router = useRouter(); const [nickname, setNickname] = useState(user.nickname || ""); const [email, setEmail] = useState(user.email || ""); const [signature, setSignature] = useState(user.signature || ""); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const [avatarDragOver, setAvatarDragOver] = useState(false); const [incomingAvatarFile, setIncomingAvatarFile] = useState(null); const clearIncomingAvatar = useCallback(() => setIncomingAvatarFile(null), []); const handleAvatarDragOver = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); if (e.dataTransfer.types.includes("Files")) setAvatarDragOver(true); }; const handleAvatarDragLeave = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); // 仅在离开整块卡片时取消高亮(避免子元素冒泡闪烁) const related = e.relatedTarget as Node | null; if (related && e.currentTarget.contains(related)) return; setAvatarDragOver(false); }; const handleAvatarDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setAvatarDragOver(false); const file = e.dataTransfer.files?.[0]; if (!file) return; setIncomingAvatarFile(file); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); setSuccess(""); const trimmed = nickname.trim(); if (!trimmed) { setError("昵称不能为空"); return; } if (trimmed.length > 64) { setError("昵称不能超过 64 个字符"); return; } if (signature.length > 255) { setError("签名不能超过 255 个字符"); return; } setSubmitting(true); try { const res = await apiUpdateProfile({ nickname: trimmed, email: email.trim(), signature, }); if (res.user) { setSuccess("资料已保存"); router.refresh(); } else { setError(res.error || "保存失败"); } } catch { setError("网络错误,请重试"); } finally { setSubmitting(false); } }; return (
{success && (
{success}
)} {error &&
{error}
} {/* 头像:点击上传/裁剪,或拖放图片到本区域 */}

头像

{avatarDragOver ? "松开即可上传并裁剪" : "点击头像或拖放图片到此处;支持拖动裁剪,统一转为 WebP,裁剪后不超过 2MB"}

{/* 昵称 + 邮箱:桌面双列高密度排布 */}
setNickname(e.target.value)} required maxLength={64} className="field" placeholder="请输入昵称" />
setEmail(e.target.value)} maxLength={128} className="field" placeholder="your@email.com" />
{/* 个性签名 */}