diff --git a/frontend-web/src/components/page-sidebar-dark.tsx b/frontend-web/src/components/page-sidebar-dark.tsx index af274aff..4851a5d5 100644 --- a/frontend-web/src/components/page-sidebar-dark.tsx +++ b/frontend-web/src/components/page-sidebar-dark.tsx @@ -6,6 +6,8 @@ import { getStoredAuth } from "@/core/auth"; import { isWujieEnv } from "@/core/embed/wujie"; import { defaultExpandedIds, + menuItemContainsPath, + menuItemMatchesPath, type MenuItem, } from "@/core/page-layout/sidebar-menu"; import { useSidebarMenuItems } from "@/core/page-layout/use-sidebar-menu"; @@ -53,13 +55,7 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar }, 180); }; - const hasActiveChild = (item: MenuItem): boolean => { - if (item.path && (pathname === item.path || pathname.startsWith(item.path + "/"))) { - return true; - } - - return item.children?.some(hasActiveChild) ?? false; - }; + const hasActiveChild = (item: MenuItem): boolean => menuItemContainsPath(item, pathname); // lazy init:仅在首次 render 计算一次 active id;之后由下面的 useEffect 跟随 pathname 同步 const [activeTopId, setActiveTopId] = useState( @@ -152,9 +148,9 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar const isSidebarItemActive = (item: MenuItem) => inWujieEnv ? item.id === activeTopId - : item.path - ? pathname === item.path || pathname.startsWith(item.path + "/") - : hasActiveChild(item); + : item.path || item.aliasPaths?.length + ? menuItemMatchesPath(item, pathname) + : menuItemContainsPath(item, pathname); const handleSidebarItemClick = (item: MenuItem) => { if (inWujieEnv) { handleTopItemClick(item); @@ -176,7 +172,9 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar {items.map((item) => { const hasChildren = !!item.children?.length; const isExpanded = expandedIds.has(item.id); - const isSelected = item.path ? pathname === item.path : hasActiveChild(item); + const isSelected = item.path || item.aliasPaths?.length + ? menuItemMatchesPath(item, pathname) + : hasActiveChild(item); return (
{ - if (item.path && (pathname === item.path || pathname.startsWith(item.path + "/"))) { - return true; - } - return item.children?.some((child) => hasActiveChild(child, pathname)) ?? false; -}; +const hasActiveChild = (item: MenuItem, pathname: string): boolean => + menuItemContainsPath(item, pathname); const findFirstRoutePath = (items?: MenuItem[]): string | null => { if (!items?.length) { @@ -116,9 +114,9 @@ function SecondaryNavSidebar() { const secondaryItems = activeTopItem?.children ?? []; const isItemActive = (item: MenuItem) => - item.path - ? pathname === item.path || pathname.startsWith(item.path + "/") - : hasActiveChild(item, pathname); + item.path || item.aliasPaths?.length + ? menuItemMatchesPath(item, pathname) + : menuItemContainsPath(item, pathname); const handleItemClick = (item: MenuItem) => { const path = item.path ?? findFirstRoutePath(item.children); @@ -211,9 +209,8 @@ function SecondaryNavSidebar() {
+ + {/* ── 表格区 ── */} diff --git a/frontend-web/src/roundtable-planning/pages/BusinessChainListPage.tsx b/frontend-web/src/roundtable-planning/pages/BusinessChainListPage.tsx index aab9004e..cc7bc3cf 100644 --- a/frontend-web/src/roundtable-planning/pages/BusinessChainListPage.tsx +++ b/frontend-web/src/roundtable-planning/pages/BusinessChainListPage.tsx @@ -43,6 +43,7 @@ import { SearchIcon, SendIcon, SettingsIcon, + SlidersHorizontalIcon, Trash2Icon, UserIcon, UsersIcon, @@ -252,17 +253,27 @@ export default function BusinessChainListPage() {

- +
+ + +
diff --git a/frontend-web/src/strategy-components/Header.tsx b/frontend-web/src/strategy-components/Header.tsx index 27c617ba..4d75ccbd 100644 --- a/frontend-web/src/strategy-components/Header.tsx +++ b/frontend-web/src/strategy-components/Header.tsx @@ -1,23 +1,24 @@ import { Bell, ChevronDown, FileText, Shirt, User } from "lucide-react"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import { Button } from "@/components/ui/button"; import "@/components/page-sdebar.css"; import { NotificationDialog } from "@/components/workspace/notification-dialog"; -import { getStoredAuth } from "@/core/auth"; +import { getStoredAuth, getAccountDisplayName } from "@/core/auth"; import { isWujieEnv } from "@/core/embed/wujie"; import { useUnreadCount } from "@/core/notifications"; import { useMyPosition } from "@/core/positions"; import { getActiveThirdLevelItems, + menuItemContainsPath, + menuItemMatchesPath, type MenuItem, } from "@/core/page-layout/sidebar-menu"; import { useSidebarMenuItems } from "@/core/page-layout/use-sidebar-menu"; import { cn } from "@/lib/utils"; import { useTheme, type ThemeType } from "../contexts/ThemeContext"; -import { readUserInfo } from "./lib/light-app"; const iconButtonClass = (isDark: boolean) => cn( @@ -135,28 +136,14 @@ const Header = ({ collapsed }: HeaderProps) => { const { data: myPosition } = useMyPosition(); const positionName = myPosition?.name?.trim() || ""; - // 登录时写入的真实用户名(外部登录存于 localStorage.userInfo.userName); - // 缺省回退到 stored auth 的邮箱前缀。 - const displayName = useMemo(() => { - const info = readUserInfo(); - const name = typeof info.userName === "string" ? info.userName.trim() : ""; - if (name) return name; - const email = auth?.email?.trim(); - return email ? email.split("@")[0] : "用户"; - }, [auth?.email]); + const displayName = getAccountDisplayName(auth); const showTopNav = isDark && !collapsed && !isWujieEnv(); // 浅色(红/蓝)顶栏:一级导航正常横排进顶栏,不做深色那种围绕基座的特殊处理。 // 与 collapsed 解耦(折叠只影响侧边栏,一级导航始终可见);wujie 嵌入下隐藏, // 此时一级导航回落到侧边栏(见 page-sidebar.tsx)。 const showLightTopNav = !isDark && !isWujieEnv(); - const hasActiveChild = (item: MenuItem): boolean => { - if (item.path && (pathname === item.path || pathname.startsWith(item.path + "/"))) { - return true; - } - - return item.children?.some(hasActiveChild) ?? false; - }; + const hasActiveChild = (item: MenuItem): boolean => menuItemContainsPath(item, pathname); const findFirstRoutePath = (items?: MenuItem[]): string | null => { if (!items?.length) { @@ -189,7 +176,9 @@ const Header = ({ collapsed }: HeaderProps) => { }; const renderTopItem = (item: MenuItem) => { - const isSelected = item.path ? (pathname === item.path || pathname.startsWith(item.path + "/")) : hasActiveChild(item); + const isSelected = item.path || item.aliasPaths?.length + ? menuItemMatchesPath(item, pathname) + : hasActiveChild(item); return (