import { Popover, Switch } from "antd"; import { ChevronRight, CircleUserRound, LogIn, LogOut, Moon, ShieldCheck, Sun } from "lucide-react"; import { useEffect, useState, type ReactNode } from "react"; import { Link } from "react-router"; import { AppChangelogButton } from "@/components/layout/app-changelog-modal"; import { IdentityProviderBadge } from "@/components/layout/identity-provider-badge"; import { useWalletBalance } from "@/hooks/use-wallet-balance"; import { useWorkspaceLogout } from "@/hooks/use-workspace-logout"; import { cn } from "@/lib/utils"; import { useThemeStore } from "@/stores/use-theme-store"; import { useUserStore, type LocalUser } from "@/stores/use-user-store"; /** 顶部栏头像账户菜单:账户信息、管理入口、版本、深色模式与退出登录。 */ export function WorkspaceAccountMenu() { const theme = useThemeStore((state) => state.theme); const setTheme = useThemeStore((state) => state.setTheme); const user = useUserStore((state) => state.user); const hydrated = useUserStore((state) => state.hydrated); const creditsEnabled = useUserStore((state) => state.features.creditsEnabled); const { availableMicrocredits } = useWalletBalance(user?.id, creditsEnabled); const { handleLogout } = useWorkspaceLogout(); const [menuOpen, setMenuOpen] = useState(false); const balance = availableMicrocredits === null ? "--" : (availableMicrocredits / 1_000_000).toLocaleString("zh-CN", { maximumFractionDigits: 2 }); if (!hydrated) { return ; } return user ? ( {user.displayName || user.username} {creditsEnabled ? 可用 {balance} 积分 : null} {user.role === "admin" ? ( } label="管理员后台" onNavigate={() => setMenuOpen(false)} /> ) : null} {theme === "dark" ? : } 深色模式 setTheme(checked ? "dark" : "light")} aria-label="深色模式" /> void handleLogout()} > 退出登录 )} > ) : ( ); } function MenuLink({ to, icon, label, onNavigate }: { to: string; icon: ReactNode; label: string; onNavigate: () => void }) { return {icon}{label}; } function UserAvatar({ user, className }: { user: LocalUser; className?: string }) { const [failed, setFailed] = useState(false); const avatarUrl = /^https?:\/\//i.test(user.avatarUrl || "") ? user.avatarUrl : ""; useEffect(() => setFailed(false), [avatarUrl]); // 结构保持 button > span > svg:占位图标自动复用顶栏图标按钮的 18px/1.8 描边与配色合同。 return ( {avatarUrl && !failed ? ( setFailed(true)} /> ) : ( )} ); }