完善UI
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { AlertCircle, Boxes, Check, ChevronDown, Info, LogOut, Settings, UsersRound } from "lucide-react";
|
||||
import { AlertCircle, Boxes, Check, ChevronDown, ChevronRight, Info, LogOut, Settings, UsersRound } from "lucide-react";
|
||||
import { IconKitSvg } from "./IconKitSvg";
|
||||
import { ConfirmModal, useBodyScrollLock, useOverlayTransition } from "./overlays";
|
||||
import type { Product, Project, Team, User } from "../types";
|
||||
@@ -101,9 +101,9 @@ function CommandPalette({ open, onClose, navigate, canManageBilling = true }: {
|
||||
|
||||
type Navigate = (page: Page) => void;
|
||||
|
||||
// 账户下拉菜单 —— 忠实搬设计稿 shell.js 的 accountMenuHtml + toggleAccountMenu 定位逻辑。
|
||||
// 触发器(侧栏 .user / 顶栏头像)传入自己的 DOMRect 当锚点;菜单 fixed 定位、右对齐锚点、
|
||||
// 越界自动翻到锚点上方,并钳进视口。点外部 / Esc 收起。各项 navigate,退出走 logout。
|
||||
// 账户下拉菜单 —— 触发器为侧栏左下角 .org-switcher;菜单 fixed 定位。
|
||||
// 锚点在视口左半边时左对齐并向上展开,否则右对齐向下展开;越界翻转并钳进视口。
|
||||
// 点外部 / Esc 收起。各项 navigate,退出走 logout。
|
||||
// 认证定调:头像下方第二行展示「用户名」(user.username),本项目不用邮箱。
|
||||
const ACCOUNT_ITEMS: { act: Page; icon: string; label: string }[] = [
|
||||
{ act: "settings", icon: "settings", label: "个人设置" },
|
||||
@@ -137,11 +137,15 @@ export function AccountMenu({ open, anchorRect, onClose, navigate, logout, user,
|
||||
const menu = menuRef.current;
|
||||
const width = menu?.offsetWidth || 232;
|
||||
const height = menu?.offsetHeight || 260;
|
||||
let left = rect.right - width;
|
||||
if (left < 12) left = rect.left;
|
||||
const fromLeft = rect.left < window.innerWidth / 2;
|
||||
let left = fromLeft ? rect.left : rect.right - width;
|
||||
left = Math.min(Math.max(12, left), window.innerWidth - width - 12);
|
||||
let top = rect.bottom + 8;
|
||||
if (top + height > window.innerHeight - 12) top = Math.max(12, rect.top - height - 8);
|
||||
let top = fromLeft ? rect.top - height - 8 : rect.bottom + 8;
|
||||
if (top < 12 || top + height > window.innerHeight - 12) {
|
||||
top = fromLeft
|
||||
? Math.min(rect.bottom + 8, window.innerHeight - height - 12)
|
||||
: Math.max(12, rect.top - height - 8);
|
||||
}
|
||||
setPos({ left, top });
|
||||
}, [rect]);
|
||||
|
||||
@@ -151,7 +155,7 @@ export function AccountMenu({ open, anchorRect, onClose, navigate, logout, user,
|
||||
const onDown = (event: MouseEvent) => {
|
||||
const target = event.target as Node;
|
||||
if (menuRef.current?.contains(target)) return;
|
||||
if ((target as Element).closest?.(".user, .topbar-avatar")) return;
|
||||
if ((target as Element).closest?.(".org-switcher")) return;
|
||||
onClose();
|
||||
};
|
||||
const onKey = (event: KeyboardEvent) => { if (event.key === "Escape") onClose(); };
|
||||
@@ -403,10 +407,11 @@ const PAGE_TO_NAV: Partial<Record<Page, Page>> = {
|
||||
settingsNotify: "settings"
|
||||
};
|
||||
|
||||
export function Sidebar({ page, navigate, user, canManageBilling = true, products, projects, productTotal, projectTotal, aiUnread, onOpenAdmin }: {
|
||||
export function Sidebar({ page, navigate, user, team, canManageBilling = true, products, projects, productTotal, projectTotal, aiUnread, onOpenAdmin, onOpenAccount, accountOpen = false }: {
|
||||
page: Page;
|
||||
navigate: Navigate;
|
||||
user: User;
|
||||
team?: Team | null;
|
||||
// 主账号(owner=超管)才看得到「团队」「消费」入口;子账号隐藏(PMC#3)。默认 true 兼容旧调用。
|
||||
canManageBilling?: boolean;
|
||||
products: Product[];
|
||||
@@ -417,6 +422,8 @@ export function Sidebar({ page, navigate, user, canManageBilling = true, product
|
||||
aiUnread?: number;
|
||||
// 平台超管入口:仅 user.is_platform_admin 时显示,点了进 /admin 后台
|
||||
onOpenAdmin?: () => void;
|
||||
onOpenAccount?: (rect: DOMRect) => void;
|
||||
accountOpen?: boolean;
|
||||
}) {
|
||||
const activeNav = PAGE_TO_NAV[page];
|
||||
// 子账号:导航里去掉「团队」「消费」两项(命令面板、账户菜单同步过滤)。
|
||||
@@ -658,6 +665,20 @@ export function Sidebar({ page, navigate, user, canManageBilling = true, product
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className="org-switcher"
|
||||
type="button"
|
||||
aria-label="打开账户菜单"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={accountOpen}
|
||||
onClick={(event) => onOpenAccount?.(event.currentTarget.getBoundingClientRect())}
|
||||
>
|
||||
<span className="org-badge">
|
||||
{user.avatar_url ? <img src={user.avatar_url} alt="" /> : (team?.name || user.username || "A").slice(0, 1).toUpperCase()}
|
||||
</span>
|
||||
<span className="org-switcher-name">{team?.name || user.username}</span>
|
||||
<ChevronRight size={16} strokeWidth={1.8} />
|
||||
</button>
|
||||
</aside>
|
||||
<CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} navigate={navigate} canManageBilling={canManageBilling} />
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user