import { useEffect, useState, type ReactNode } from "react"; import { AnimatePresence, motion } from "motion/react"; import { Bell, BriefcaseBusiness, ChevronRight, CircleUserRound, HelpCircle, LogOut, Mail, MapPinned, Moon, Eye, EyeOff, Phone, Settings, Shield, Sun, User, X, } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { useTheme } from "@/components/ThemeProvider"; import { clearAuth, getCurrentUser, getProfileOverview, updateCurrentUserProfile, updateCurrentUserPassword, type ProfileOverview, type UpdateCurrentUserProfilePayload, type UpdateCurrentUserPasswordPayload, type UserProfile, } from "@/lib/auth"; type MenuItem = { key: "personal" | "notice" | "security" | "help"; icon: typeof User; label: string; color: string; bg: string; }; type EditableProfileForm = UpdateCurrentUserProfilePayload; const MENU_ITEMS: MenuItem[] = [ { key: "personal", icon: User, label: "个人资料", color: "text-blue-500 dark:text-blue-400", bg: "bg-blue-50 dark:bg-blue-500/10" }, { key: "notice", icon: Bell, label: "消息通知", color: "text-amber-500 dark:text-amber-400", bg: "bg-amber-50 dark:bg-amber-500/10" }, { key: "security", icon: Shield, label: "账号安全", color: "text-emerald-500 dark:text-emerald-400", bg: "bg-emerald-50 dark:bg-emerald-500/10" }, { key: "help", icon: HelpCircle, label: "帮助中心", color: "text-violet-500 dark:text-violet-400", bg: "bg-violet-50 dark:bg-violet-500/10" }, ]; const EMPTY_FORM: EditableProfileForm = { displayName: "", email: "", phone: "", }; const EMPTY_PASSWORD_FORM: SecurityForm = { oldPassword: "", newPassword: "", confirmPassword: "", }; type SecurityForm = UpdateCurrentUserPasswordPayload & { confirmPassword: string; }; function displayText(value?: string | null) { return value && value.trim() ? value : "无"; } function numericValue(value?: number | null) { return typeof value === "number" && Number.isFinite(value) ? value : 0; } function PageModal({ title, subtitle, onClose, children, footer, }: { title: string; subtitle?: string; onClose: () => void; children: ReactNode; footer?: ReactNode; }) { return ( <>

{title}

{subtitle ?

{subtitle}

: null}
{children}
{footer ?
{footer}
: null}
); } export default function Profile() { const { theme, setTheme } = useTheme(); const navigate = useNavigate(); const [overview, setOverview] = useState(null); const [currentUser, setCurrentUser] = useState(null); const [profileDrawerOpen, setProfileDrawerOpen] = useState(false); const [securityDrawerOpen, setSecurityDrawerOpen] = useState(false); const [form, setForm] = useState(EMPTY_FORM); const [detailLoading, setDetailLoading] = useState(false); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const [securityForm, setSecurityForm] = useState(EMPTY_PASSWORD_FORM); const [securitySaving, setSecuritySaving] = useState(false); const [securityError, setSecurityError] = useState(""); const [securitySuccess, setSecuritySuccess] = useState(""); const [showOldPassword, setShowOldPassword] = useState(false); const [showNewPassword, setShowNewPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); useEffect(() => { let ignore = false; async function loadProfileData() { try { const me = await getCurrentUser(); const overviewData = await getProfileOverview().catch(() => null); if (ignore) { return; } setCurrentUser(me); setOverview(overviewData); } catch { if (ignore) { return; } setOverview(null); setCurrentUser(null); } } void loadProfileData(); return () => { ignore = true; }; }, []); const handleLogout = () => { clearAuth(); navigate("/login", { replace: true }); }; const handleNavigateToMonthlyOpportunity = () => { navigate("/opportunities"); }; const handleNavigateToMonthlyExpansion = () => { navigate("/expansion"); }; const handleOpenProfile = async () => { setDetailLoading(true); setError(""); try { const me = currentUser ?? await getCurrentUser(); const latestOverview = overview ?? await getProfileOverview().catch(() => null); setCurrentUser(me); setOverview(latestOverview); setForm({ userId: me.userId, username: me.username, displayName: me.displayName || "", email: me.email || "", phone: me.phone || "", pwdResetRequired: me.pwdResetRequired, isPlatformAdmin: me.isPlatformAdmin, orgId: undefined, }); setProfileDrawerOpen(true); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : "获取个人资料失败"); setProfileDrawerOpen(true); } finally { setDetailLoading(false); } }; const handleCloseProfile = () => { if (saving) { return; } setProfileDrawerOpen(false); setError(""); }; const handleSave = async () => { if (saving) { return; } setSaving(true); setError(""); try { await updateCurrentUserProfile({ ...form, email: form.email || undefined, phone: form.phone || undefined, }); const me = await getCurrentUser(); const overviewData = await getProfileOverview().catch(() => overview); setCurrentUser(me); setOverview(overviewData); setForm({ userId: me.userId, username: me.username, displayName: me.displayName || "", email: me.email || "", phone: me.phone || "", pwdResetRequired: me.pwdResetRequired, isPlatformAdmin: me.isPlatformAdmin, orgId: undefined, }); sessionStorage.setItem("userProfile", JSON.stringify(me)); setProfileDrawerOpen(false); } catch (saveError) { setError(saveError instanceof Error ? saveError.message : "保存失败"); } finally { setSaving(false); } }; const handleOpenSecurity = () => { setSecurityForm(EMPTY_PASSWORD_FORM); setSecurityError(""); setSecuritySuccess(""); setShowOldPassword(false); setShowNewPassword(false); setShowConfirmPassword(false); setSecurityDrawerOpen(true); }; const handleCloseSecurity = () => { if (securitySaving) { return; } setSecurityDrawerOpen(false); setSecurityError(""); setSecuritySuccess(""); }; const handleUpdatePassword = async () => { if (securitySaving) { return; } if (!securityForm.oldPassword || !securityForm.newPassword || !securityForm.confirmPassword) { setSecurityError("请完整填写旧密码、新密码和确认密码"); setSecuritySuccess(""); return; } if (securityForm.newPassword !== securityForm.confirmPassword) { setSecurityError("两次输入的新密码不一致"); setSecuritySuccess(""); return; } if (securityForm.newPassword.length < 6) { setSecurityError("新密码长度不能少于6位"); setSecuritySuccess(""); return; } setSecuritySaving(true); setSecurityError(""); setSecuritySuccess(""); try { await updateCurrentUserPassword({ oldPassword: securityForm.oldPassword, newPassword: securityForm.newPassword, }); clearAuth(); navigate("/login", { replace: true }); } catch (saveError) { setSecurityError(saveError instanceof Error ? saveError.message : "密码修改失败"); } finally { setSecuritySaving(false); } }; const roleNames = displayText(overview?.jobTitle); const orgNames = displayText(overview?.deptName); const displayName = displayText(currentUser?.displayName || overview?.realName); const avatarText = displayName === "无" ? "无" : displayName.slice(0, 1); const currentEmail = displayText(currentUser?.email); const currentPhone = displayText(currentUser?.phone); return (

我的

{avatarText}

{displayName}

部门:{orgNames}

岗位:{roleNames}

{numericValue(overview?.averageScore)}

平均分

  • {MENU_ITEMS.map((item) => (
  • ))}
退出登录 {profileDrawerOpen ? (
)} > {error && !currentUser ? (
{error}
) : (
{(form.displayName || currentUser?.displayName || "无").slice(0, 1)}
{displayText(currentUser?.displayName)}

部门:{orgNames}

岗位:{roleNames}

账号:{displayText(currentUser?.username)}
岗位信息

岗位:{roleNames}

状态:{displayText(overview?.accountStatus)}

组织信息

部门:{orgNames}

入职天数:{numericValue(overview?.onboardingDays)}

当前手机号:{currentPhone}
当前邮箱:{currentEmail}
{detailLoading ? (
正在加载个人资料...
) : null} {error ? (
{error}
) : null}
)} ) : null} {securityDrawerOpen ? ( )} >

为了账号安全,请先输入当前密码,再设置新密码。

{securityError ? (
{securityError}
) : null} {securitySuccess ? (
{securitySuccess}
) : null}
) : null} ); }