From 54d1e6b6935b3f2b7b7c14da00bbf96131e2329b Mon Sep 17 00:00:00 2001 From: sikongyue <3155157671@qq.com> Date: Wed, 19 Aug 2026 23:25:32 +0800 Subject: [PATCH] =?UTF-8?q?refactor(web):=20=E5=B7=A5=E4=BD=9C=E5=8C=BA?= =?UTF-8?q?=E5=B8=83=E5=B1=80=20-=20=E4=BE=A7=E6=A0=8F=E6=94=B9=20260px=20?= =?UTF-8?q?=E5=8F=AF=E6=8A=98=E5=8F=A0=E5=AF=BC=E8=88=AA=E3=80=81=E9=A1=B6?= =?UTF-8?q?=E6=A0=8F=E7=A7=BB=E5=85=A5=E4=B8=BB=E5=8C=BA=E5=B9=B6=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E8=B4=A6=E6=88=B7=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 侧栏由 64px 图标 rail 改为 260px 可折叠导航:分组、设置分区子项、钱包余额徽章、⌘K 命令面板入口、激活分支自动展开 - 顶栏移入主内容区(侧栏右侧):面包屑、主题切换、公告中心、账户菜单;移除冗余搜索按钮(保留侧栏搜索与 ⌘K) - 账户按钮与其余图标按钮视觉对齐:24px 圆角头像、占位图标复用统一图标合同、移除触发按钮角标 - 新增命令面板与登出 hook;浮层背景不透明化并收敛到唯一源规则 --- web/src/components/layout/app-top-nav.tsx | 184 ++------- .../layout/workspace-account-menu.tsx | 106 ++++++ .../layout/workspace-command-palette.tsx | 155 ++++++++ .../layout/workspace-sidebar-footer.tsx | 137 ------- .../layout/workspace-sidebar-nav.tsx | 352 ++++++++++++++++++ .../components/layout/workspace-top-bar.tsx | 41 +- web/src/hooks/use-workspace-logout.ts | 33 ++ web/src/styles/globals.css | 315 +++++----------- 8 files changed, 802 insertions(+), 521 deletions(-) create mode 100644 web/src/components/layout/workspace-account-menu.tsx create mode 100644 web/src/components/layout/workspace-command-palette.tsx delete mode 100644 web/src/components/layout/workspace-sidebar-footer.tsx create mode 100644 web/src/components/layout/workspace-sidebar-nav.tsx create mode 100644 web/src/hooks/use-workspace-logout.ts diff --git a/web/src/components/layout/app-top-nav.tsx b/web/src/components/layout/app-top-nav.tsx index c2660f47e..158f434e2 100644 --- a/web/src/components/layout/app-top-nav.tsx +++ b/web/src/components/layout/app-top-nav.tsx @@ -1,47 +1,32 @@ -import { Home, Infinity as InfinityIcon } from "lucide-react"; -import { Fragment, useEffect, useRef, useState, type CSSProperties, type ReactNode } from "react"; -import { Link, useLocation, useNavigate } from "react-router"; +import { useEffect, useState, type CSSProperties, type ReactNode } from "react"; +import { useLocation, useNavigate } from "react-router"; import { ModelSetupGuide } from "@/components/layout/model-setup-guide"; -import { WorkspaceSidebarFooter } from "@/components/layout/workspace-sidebar-footer"; +import { WorkspaceCommandPalette } from "@/components/layout/workspace-command-palette"; +import { WorkspaceSidebarNav } from "@/components/layout/workspace-sidebar-nav"; import { WorkspaceTopBar } from "@/components/layout/workspace-top-bar"; -import { navigationTools, type NavigationToolSlug } from "@/constant/navigation-tools"; import { cn } from "@/lib/utils"; import { refreshFeatureAvailability } from "@/lib/user-session"; import { isSpatialWorkbenchPath } from "@/lib/workspace-routes"; -import { preloadWorkspaceRoute } from "@/lib/workspace-route-modules"; import { useUserStore } from "@/stores/use-user-store"; export function AppWorkspaceShell({ children }: { children: ReactNode }) { const { pathname } = useLocation(); const navigate = useNavigate(); const user = useUserStore((state) => state.user); - const features = useUserStore((state) => state.features); const [mobileSidebarExpanded, setMobileSidebarExpanded] = useState(false); const [desktopSidebarCollapsed, setDesktopSidebarCollapsed] = useState(false); - const scrollRef = useRef(null); - const [scrollState, setScrollState] = useState({ - hasTopFade: false, - hasBottomFade: false, - }); + const [paletteOpen, setPaletteOpen] = useState(false); + const hideChrome = pathname.startsWith("/admin") || /^\/canvas\/[^/]+/.test(pathname); const showGlobalTopBar = !hideChrome; const spatialWorkbench = isSpatialWorkbenchPath(pathname); const creationWorkspace = pathname === "/create"; - const slug = pathname.split("/").filter(Boolean)[0]; - const activeToolSlug = navigationTools.some((tool) => tool.slug === slug) ? (slug as NavigationToolSlug) : undefined; - const visibleNavigationTools = (spatialWorkbench ? navigationTools : navigationTools.filter((tool) => tool.section === "创作空间")) - .filter((tool) => { - if (tool.slug === "projects") return features.shortDramaEnabled; - if (tool.slug === "tasks") return features.taskCenterEnabled; - if (tool.slug === "wallet") return features.creditsEnabled; - return true; - }); - const topRailNavTools = visibleNavigationTools.filter((tool) => tool.section === "创作空间" || tool.slug === "skills"); - const bottomRailNavTools = visibleNavigationTools.filter((tool) => tool.slug === "wallet"); + + const isMobileViewport = () => window.innerWidth < 1024; const toggleSidebar = () => { - if (window.innerWidth < 1024) { + if (isMobileViewport()) { setMobileSidebarExpanded((current) => !current); return; } @@ -49,40 +34,20 @@ export function AppWorkspaceShell({ children }: { children: ReactNode }) { }; const handleNavClick = () => { - if (window.innerWidth < 1024) setMobileSidebarExpanded(false); + if (isMobileViewport()) setMobileSidebarExpanded(false); }; - const renderSidebarLink = (tool: (typeof navigationTools)[number]) => { - const Icon = tool.icon; - const active = tool.slug === activeToolSlug; - return ( - preloadWorkspaceRoute(tool.slug)} - onPointerDown={() => preloadWorkspaceRoute(tool.slug)} - onPointerEnter={() => preloadWorkspaceRoute(tool.slug)} - className={cn( - "app-workspace-nav-link app-workspace-rail-tile grid size-10 place-items-center rounded-md text-[var(--fs-tiny)] transition-colors", - active ? "is-active font-medium" : "", - )} - > - - - ); - }; - - const handleScroll = () => { - const element = scrollRef.current; - if (!element) return; - const { scrollTop, scrollHeight, clientHeight } = element; - setScrollState({ - hasTopFade: scrollTop > 0, - hasBottomFade: scrollTop + clientHeight < scrollHeight - 1, - }); - }; + // ⌘K / Ctrl+K 全局呼出搜索面板。 + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") { + event.preventDefault(); + setPaletteOpen((open) => !open); + } + }; + window.addEventListener("keydown", handler); + return () => window.removeEventListener("keydown", handler); + }, []); useEffect(() => { const handleWorkspaceNavigation = (rawEvent: Event) => { @@ -95,10 +60,6 @@ export function AppWorkspaceShell({ children }: { children: ReactNode }) { return () => window.removeEventListener("workspace:navigate", handleWorkspaceNavigation); }, [navigate]); - useEffect(() => { - handleScroll(); - }, [visibleNavigationTools.length, mobileSidebarExpanded]); - useEffect(() => { if (!user) return; const refresh = () => void refreshFeatureAvailability().catch((error) => console.warn("功能开放状态刷新失败", error)); @@ -112,93 +73,30 @@ export function AppWorkspaceShell({ children }: { children: ReactNode }) { return ( <> -
+
{!hideChrome && mobileSidebarExpanded ?
+ setPaletteOpen(false)} />