"use client"; import { useCallback, useState, useEffect, useRef } from "react"; import { Profile } from "@/features/agent/types"; import { updateProfile as updateProfileApi, uploadAvatar as uploadAvatarApi, UpdateProfilePayload, } from "@/features/agent/services/profileApi"; import { getAvatarUrl, getAvatarColor, getAvatarInitial } from "@/utils/avatar"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; interface ProfileModalProps { profile: Profile | null; open: boolean; onClose: () => void; onUpdate: (profile: Profile) => void; } export function ProfileModal({ profile, open, onClose, onUpdate, }: ProfileModalProps) { const [editingNickname, setEditingNickname] = useState(false); const [editingEmail, setEditingEmail] = useState(false); const [nickname, setNickname] = useState(""); const [email, setEmail] = useState(""); const [avatarPreview, setAvatarPreview] = useState(null); const [saving, setSaving] = useState(false); const [uploading, setUploading] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const fileInputRef = useRef(null); // 当弹窗打开或 profile 变化时,初始化表单 useEffect(() => { if (open && profile) { setNickname(profile.nickname || ""); setEmail(profile.email || ""); setAvatarPreview(profile.avatar_url || null); setEditingNickname(false); setEditingEmail(false); setErrorMessage(""); } }, [open, profile]); // 选择头像文件 const handleAvatarSelect = useCallback( async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file || !profile) { return; } // 验证文件类型 const allowedTypes = ["image/jpeg", "image/jpg", "image/png", "image/gif"]; if (!allowedTypes.includes(file.type)) { setErrorMessage("只支持上传图片文件(jpg、png、gif)"); return; } // 验证文件大小(10MB) if (file.size > 10 * 1024 * 1024) { setErrorMessage("头像文件大小不能超过10MB"); return; } // 预览头像 const reader = new FileReader(); reader.onload = (e) => { setAvatarPreview(e.target?.result as string); }; reader.readAsDataURL(file); // 上传头像 setUploading(true); setErrorMessage(""); try { const updated = await uploadAvatarApi(profile.id, file); onUpdate(updated); setAvatarPreview(updated.avatar_url); } catch (error) { setErrorMessage((error as Error).message || "上传头像失败,请稍后重试"); // 恢复原头像 setAvatarPreview(profile.avatar_url || null); } finally { setUploading(false); } }, [profile, onUpdate] ); // 保存昵称 const handleSaveNickname = useCallback(async () => { if (!profile || !nickname.trim()) { return; } setSaving(true); setErrorMessage(""); try { const payload: UpdateProfilePayload = { nickname: nickname.trim() || undefined, }; const updated = await updateProfileApi(profile.id, payload); onUpdate(updated); setEditingNickname(false); } catch (error) { setErrorMessage((error as Error).message || "保存失败,请稍后重试"); } finally { setSaving(false); } }, [profile, nickname, onUpdate]); // 保存邮箱 const handleSaveEmail = useCallback(async () => { if (!profile) { return; } setSaving(true); setErrorMessage(""); try { const payload: UpdateProfilePayload = { email: email.trim() || undefined, }; const updated = await updateProfileApi(profile.id, payload); onUpdate(updated); setEditingEmail(false); } catch (error) { setErrorMessage((error as Error).message || "保存失败,请稍后重试"); } finally { setSaving(false); } }, [profile, email, onUpdate]); if (!profile) { return null; } const displayName = profile.nickname || profile.username; const avatarColor = getAvatarColor(profile.id); const displayInitial = getAvatarInitial(profile.username, profile.nickname); const fullAvatarUrl = getAvatarUrl(profile.avatar_url); return ( !isOpen && onClose()}> 个人资料 {/* 错误提示 */} {errorMessage && (
{errorMessage}
)} {/* 头像区域 */}
{avatarPreview || fullAvatarUrl ? ( {displayName} ) : (
{displayInitial}
)} {uploading && (
)}
{/* 用户名(只读) */}
用户名
{profile.username}
{/* 角色(只读) */}
角色
{profile.role}
{/* 昵称(可编辑) */}
昵称 {!editingNickname ? ( ) : (
)}
{editingNickname ? ( setNickname(e.target.value)} placeholder="请输入昵称" disabled={saving} /> ) : (
{profile.nickname || "未设置"}
)}
{/* 邮箱(可编辑) */}
邮箱 {!editingEmail ? ( ) : (
)}
{editingEmail ? ( setEmail(e.target.value)} placeholder="请输入邮箱" disabled={saving} /> ) : (
{profile.email || "未设置"}
)}
); }