feat:账号设置+密码重置
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { ChevronRight, LayoutGrid } from "lucide-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { ChevronRight, LayoutGrid, LogOut, Settings } from "lucide-react";
|
||||
import type { AuthUser, AuthWorkspace } from "../../context/AuthContext";
|
||||
import { AccountSettingsDialog } from "./AccountSettingsDialog";
|
||||
|
||||
type TopbarProps = {
|
||||
pageTitle: string;
|
||||
@@ -24,6 +26,26 @@ export function Topbar({
|
||||
onSetCurrentWorkspace,
|
||||
onLogout,
|
||||
}: TopbarProps) {
|
||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const userMenuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userMenuOpen) return;
|
||||
const onPointerDown = (event: MouseEvent) => {
|
||||
if (!userMenuRef.current?.contains(event.target as Node)) {
|
||||
setUserMenuOpen(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener("mousedown", onPointerDown);
|
||||
return () => document.removeEventListener("mousedown", onPointerDown);
|
||||
}, [userMenuOpen]);
|
||||
|
||||
const openUserMenu = () => {
|
||||
onSetWorkspaceMenuOpen(false);
|
||||
setUserMenuOpen((open) => !open);
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="relative z-[2] flex h-[70px] min-h-[70px] items-center justify-between border-b border-[#dfe6ee] bg-white px-[22px]">
|
||||
<div className="flex items-center gap-[9px]">
|
||||
@@ -57,7 +79,10 @@ export function Topbar({
|
||||
<button
|
||||
className="flex h-[46px] cursor-pointer items-center gap-[9px] rounded-lg border border-line bg-white px-[11px] hover:border-[#b9cce0]"
|
||||
type="button"
|
||||
onClick={() => onSetWorkspaceMenuOpen(!workspaceMenuOpen)}
|
||||
onClick={() => {
|
||||
setUserMenuOpen(false);
|
||||
onSetWorkspaceMenuOpen(!workspaceMenuOpen);
|
||||
}}
|
||||
>
|
||||
<span className="grid size-[30px] place-items-center rounded-[7px] bg-gradient-to-br from-[#23aa73] to-[#158d5d] text-white">
|
||||
<LayoutGrid size={18} />
|
||||
@@ -104,10 +129,13 @@ export function Topbar({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex items-center gap-[9px]">
|
||||
<div className="relative" ref={userMenuRef}>
|
||||
<button
|
||||
className="flex h-[46px] cursor-pointer items-center gap-[9px] rounded-lg border border-transparent bg-white px-[11px] hover:border-[#b9cce0]"
|
||||
type="button"
|
||||
aria-expanded={userMenuOpen}
|
||||
aria-haspopup="menu"
|
||||
onClick={openUserMenu}
|
||||
>
|
||||
<span className="grid size-[34px] place-items-center rounded-full bg-gradient-to-br from-[#3b92ed] to-[#1869c9] text-sm font-bold text-white">
|
||||
{user?.display_name?.slice(0, 1) ?? "?"}
|
||||
@@ -120,18 +148,52 @@ export function Topbar({
|
||||
{user?.role_code === "admin" ? "管理员" : "开发人员"}
|
||||
</small>
|
||||
</span>
|
||||
<ChevronRight size={15} className="text-[#97a3b1]" />
|
||||
</button>
|
||||
<button
|
||||
className="inline-flex cursor-pointer items-center gap-0.5 border-0 bg-transparent px-px py-[5px] text-xs font-semibold text-[#1474d4] hover:text-[#0d58a2]"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void onLogout();
|
||||
}}
|
||||
>
|
||||
登出
|
||||
</button>
|
||||
{userMenuOpen && (
|
||||
<div
|
||||
className="absolute top-[calc(100%+7px)] right-0 z-50 w-[240px] rounded-lg border border-line bg-white p-1.5 shadow-lg"
|
||||
role="menu"
|
||||
>
|
||||
<div className="border-b border-line px-2.5 py-2.5">
|
||||
<strong className="block truncate text-[12px] text-[#243a50]">
|
||||
{user?.display_name ?? "未知用户"}
|
||||
</strong>
|
||||
<small className="mt-0.5 block truncate text-[10px] text-[#8797a7]">
|
||||
{user?.username}
|
||||
{user?.role_code === "admin" ? " · 管理员" : " · 开发人员"}
|
||||
</small>
|
||||
</div>
|
||||
<button
|
||||
className="mt-1 flex w-full cursor-pointer items-center gap-2 rounded-md border-0 bg-transparent px-2.5 py-2 text-left text-[12px] text-[#40576d] hover:bg-brand-soft"
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={() => {
|
||||
setUserMenuOpen(false);
|
||||
setSettingsOpen(true);
|
||||
}}
|
||||
>
|
||||
<Settings size={14} />
|
||||
账号设置
|
||||
</button>
|
||||
<button
|
||||
className="flex w-full cursor-pointer items-center gap-2 rounded-md border-0 bg-transparent px-2.5 py-2 text-left text-[12px] text-[#c14a4a] hover:bg-[#fdf2f2]"
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={() => {
|
||||
setUserMenuOpen(false);
|
||||
void onLogout();
|
||||
}}
|
||||
>
|
||||
<LogOut size={14} />
|
||||
登出
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AccountSettingsDialog open={settingsOpen} onOpenChange={setSettingsOpen} />
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user