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() {
+
+ navigate("/page/strategy/business-chains")}
+ className={cn(
+ "relative z-10 inline-flex shrink-0 items-center gap-1.5 rounded-[5px] border px-3.5 py-2 text-xs font-semibold transition-all active:scale-95",
+ isDark
+ ? "border-white/10 bg-white/5 text-slate-300 hover:bg-white/10 hover:text-slate-100"
+ : "border-slate-200 bg-white text-slate-600 hover:bg-slate-50 hover:text-slate-900",
+ )}
+ >
+
+ 返回业务链条
+
{/* ── 表格区 ── */}
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() {
- goConfigure()}
- className={cn(
- "flex shrink-0 items-center gap-2.5 self-start rounded-xl px-4 py-2.5 text-xs font-bold shadow-sm shadow-[#CF1322]/10 transition active:scale-95 md:self-auto",
- BRAND,
- )}
- >
-
- 新建链条
-
+
+
navigate("/page/strategy/business-chain-mapping")}
+ className="flex items-center gap-2 rounded-xl border border-zinc-200 bg-white px-4 py-2.5 text-xs font-bold text-zinc-700 shadow-sm transition hover:bg-zinc-50 active:scale-95 dark:border-[#1A2F5A] dark:bg-[#122347] dark:text-[#C5CFE0] dark:hover:bg-[#1A2F5A]"
+ >
+
+ 配置管理
+
+
goConfigure()}
+ className={cn(
+ "flex items-center gap-2.5 rounded-xl px-4 py-2.5 text-xs font-bold shadow-sm shadow-[#CF1322]/10 transition active:scale-95",
+ BRAND,
+ )}
+ >
+
+ 新建链条
+
+
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 (
{
};
const renderLightTopItem = (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 (
{