399 lines
12 KiB
TypeScript
399 lines
12 KiB
TypeScript
/**
|
||
* 全局侧边栏菜单数据与权限过滤逻辑。
|
||
*
|
||
* 浅色 ``PageSidebar`` 和深色 ``PageSidebarDark`` 共用这份数据 —— 菜单结构与
|
||
* adminOnly 权限策略对两种皮肤完全一致;UI 渲染(icon 显示/折叠样式/hover flyout)
|
||
* 在各自组件里实现。
|
||
*
|
||
* 修改菜单只需改这一处,两个 sidebar 自动同步。
|
||
*/
|
||
import type { ElementType } from "react";
|
||
import {
|
||
Activity,
|
||
BookOpen,
|
||
Bot,
|
||
Brain,
|
||
Briefcase,
|
||
CalendarClock,
|
||
CircleHelp,
|
||
ClipboardList,
|
||
Database,
|
||
FileBox,
|
||
FileText,
|
||
Gauge,
|
||
Grid3X3,
|
||
HelpCircle,
|
||
History,
|
||
Layers,
|
||
Layout,
|
||
Library,
|
||
List,
|
||
ListChecks,
|
||
ListTodo,
|
||
Network,
|
||
Radar,
|
||
Settings,
|
||
Sparkles,
|
||
Target,
|
||
Telescope,
|
||
Trophy,
|
||
Users,
|
||
} from "lucide-react";
|
||
|
||
export type MenuItem = {
|
||
id: string;
|
||
label: string;
|
||
icon: ElementType;
|
||
path?: string;
|
||
/** 额外视为「当前页属于本菜单项」的路由(不在侧栏展示,仅用于高亮)。 */
|
||
aliasPaths?: string[];
|
||
/** 非 admin 用户使用的路由(菜单对普通用户可见时生效,与简洁模式公开排行榜一致) */
|
||
userPath?: string;
|
||
adminOnly?: boolean;
|
||
children?: MenuItem[];
|
||
};
|
||
|
||
export const sidebarMenuItems: MenuItem[] = [
|
||
{
|
||
id: "qa-center",
|
||
label: "问答管理",
|
||
icon: HelpCircle,
|
||
children: [
|
||
{ id: "general-qa", label: "通用问答", icon: CircleHelp, path: "/page/workspace/chats/new" },
|
||
{
|
||
id: "multi-agent-roundtable",
|
||
label: "多智能体会商",
|
||
icon: Users,
|
||
path: "/page/strategy/qa/multi-agent-roundtable",
|
||
},
|
||
// {
|
||
// id: "position-roundtable",
|
||
// label: "岗位会商",
|
||
// icon: Briefcase,
|
||
// path: "/page/strategy/qa/position-roundtable",
|
||
// },
|
||
{
|
||
id: "dashboard-agent",
|
||
label: "大屏绘制",
|
||
icon: Gauge,
|
||
path: "/page/strategy/dashboard-agent",
|
||
},
|
||
// { id: "expert-qa", label: "专家问答", icon: Brain, path: "/page/strategy/qa/expert-qa" },
|
||
{
|
||
id: "qa-zzjc",
|
||
label: "JC支持",
|
||
icon: Bot,
|
||
path: "/page/strategy/qa/zzjc",
|
||
},
|
||
{ id: "qa-zzxd", label: "XD应用", icon: Layout, path: "/page/strategy/qa/zzxd" },
|
||
{ id: "qa-research", label: "课题研究", icon: FileText, path: "/page/canvas/ai-writing" },
|
||
{
|
||
id: "qa-research-chat",
|
||
label: "课题研究(对话版)",
|
||
icon: FileText,
|
||
path: "/page/canvas/ai-writing-chat",
|
||
},
|
||
{
|
||
id: "qa-deep-research",
|
||
label: "深度研究",
|
||
icon: Telescope,
|
||
path: "/page/strategy/qa/deep-research",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "task-management",
|
||
label: "任务管理",
|
||
icon: Target,
|
||
children: [
|
||
{
|
||
id: "xk-center-task",
|
||
label: "XK中任务",
|
||
icon: ClipboardList,
|
||
children: [
|
||
// {
|
||
// id: "xk-task-jc",
|
||
// label: "任务JC",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/xk-task-jc",
|
||
// },
|
||
// {
|
||
// id: "xk-task-dispatch",
|
||
// label: "任务调度",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/xk-task-dispatch",
|
||
// },
|
||
],
|
||
},
|
||
{
|
||
id: "coh-center-task",
|
||
label: "COH中任务",
|
||
icon: Activity,
|
||
children: [
|
||
// {
|
||
// id: "coh-task-3qfx",
|
||
// label: "3QFX",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/plan-task-sentiment",
|
||
// },
|
||
// {
|
||
// id: "coh-task-fx",
|
||
// label: "任务FX",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/plan-task/plan-task-fx",
|
||
// },
|
||
// {
|
||
// id: "coh-task-xdfx",
|
||
// label: "XDFX",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/plan-task/plan-task-action",
|
||
// },
|
||
],
|
||
},
|
||
{
|
||
id: "pg-center-task",
|
||
label: "PG中任务",
|
||
icon: Gauge,
|
||
children: [
|
||
// {
|
||
// id: "pg-task-pg",
|
||
// label: "任务PG",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/effect-task-pg",
|
||
// },
|
||
// {
|
||
// id: "pg-task-xdpg",
|
||
// label: "XDPG",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/effect-action-pg",
|
||
// },
|
||
// {
|
||
// id: "pg-task-info-pg",
|
||
// label: "信息PG",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/effect-info-pg",
|
||
// },
|
||
],
|
||
},
|
||
{
|
||
id: "ty-center-task",
|
||
label: "TY中任务",
|
||
icon: Radar,
|
||
children: [
|
||
// {
|
||
// id: "ty-task-xdty",
|
||
// label: "XDTY任务",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/mnty-task-mechanism",
|
||
// },
|
||
// {
|
||
// id: "ty-task-plan",
|
||
// label: "方案TY任务",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/mnty-task-plan",
|
||
// },
|
||
// {
|
||
// id: "ty-task-xxty",
|
||
// label: "XXTY任务",
|
||
// icon: ListTodo,
|
||
// path: "/page/strategy/task-management/mnty-task-zsty",
|
||
// },
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "light-app-center",
|
||
label: "应用中心",
|
||
icon: Grid3X3,
|
||
children: [
|
||
{
|
||
id: "light-app-management",
|
||
label: "应用管理",
|
||
icon: Layout,
|
||
path: "/page/strategy/light-app/management",
|
||
adminOnly: true,
|
||
},
|
||
{
|
||
id: "light-app-apps",
|
||
label: "应用列表",
|
||
icon: List,
|
||
path: "/page/strategy/light-app/apps",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "comprehensive-management",
|
||
label: "综合管理",
|
||
icon: Settings,
|
||
children: [
|
||
{
|
||
id: "mgmt-user-leaderboard",
|
||
label: "用户排行榜",
|
||
icon: Trophy,
|
||
path: "/page/strategy/admin/user-leaderboard",
|
||
userPath: "/page/strategy/user-leaderboard",
|
||
},
|
||
{ id: "mgmt-agents", label: "智能体管理", icon: Bot, path: "/page/strategy/agents" },
|
||
// {
|
||
// id: "mgmt-business-chain-mapping",
|
||
// label: "配置管理",
|
||
// icon: Network,
|
||
// path: "/page/strategy/business-chain-mapping",
|
||
// },
|
||
{
|
||
id: "mgmt-business-chains",
|
||
label: "业务链条配置",
|
||
icon: Layers,
|
||
path: "/page/strategy/business-chains",
|
||
aliasPaths: ["/page/strategy/business-chain-mapping"],
|
||
},
|
||
{ id: "mgmt-skills", label: "技能管理", icon: Sparkles, path: "/page/strategy/skills" },
|
||
{ id: "mgmt-memory", label: "记忆管理", icon: Brain, path: "/page/strategy/memory" },
|
||
{
|
||
id: "mgmt-tasks",
|
||
label: "定时任务",
|
||
icon: ListChecks,
|
||
path: "/page/strategy/scheduled/tasks",
|
||
},
|
||
{
|
||
id: "mgmt-llm-metrics",
|
||
label: "大模型调用统计",
|
||
icon: Gauge,
|
||
path: "/page/strategy/settings/llm-metrics",
|
||
adminOnly: true,
|
||
},
|
||
{
|
||
id: "mgmt-users",
|
||
label: "用户管理",
|
||
icon: Users,
|
||
path: "/page/strategy/admin/users",
|
||
adminOnly: true,
|
||
},
|
||
{
|
||
id: "mgmt-positions",
|
||
label: "岗位管理",
|
||
icon: Briefcase,
|
||
path: "/page/strategy/admin/positions",
|
||
adminOnly: true,
|
||
},
|
||
{
|
||
id: "mgmt-position-roles",
|
||
label: "岗位角色配置",
|
||
icon: Briefcase,
|
||
path: "/page/strategy/admin/position-roles",
|
||
adminOnly: true,
|
||
},
|
||
{
|
||
id: "mgmt-admin-scheduled-tasks",
|
||
label: "定时任务管理",
|
||
icon: CalendarClock,
|
||
path: "/page/strategy/admin/scheduled-tasks",
|
||
adminOnly: true,
|
||
},
|
||
{
|
||
id: "mgmt-user-activity",
|
||
label: "用户活动审计",
|
||
icon: History,
|
||
path: "/page/strategy/admin/user-activity",
|
||
adminOnly: true,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: "resource-management",
|
||
label: "资源管理",
|
||
icon: Database,
|
||
children: [
|
||
// {
|
||
// id: "resource-network-xwt",
|
||
// label: "网络xwt资源管理",
|
||
// icon: Network,
|
||
// path: "/page/strategy/resource-management/resource-network-actor",
|
||
// },
|
||
{
|
||
id: "resource-knowledge-native",
|
||
label: "问答梳理",
|
||
icon: BookOpen,
|
||
path: "/page/workspace/knowledge",
|
||
},
|
||
{
|
||
id: "resource-knowledge-base",
|
||
label: "知识库资源管理",
|
||
icon: Library,
|
||
path: "/page/strategy/resource-management/resource-knowledge-base",
|
||
},
|
||
// {
|
||
// id: "resource-evaluation-index",
|
||
// label: "作战资源管理",
|
||
// icon: FileBox,
|
||
// path: "/page/strategy/resource-management/resource-evaluation-index",
|
||
// },
|
||
],
|
||
},
|
||
];
|
||
|
||
/** 默认展开的菜单 id 集合(浅色版可折叠交互的初始状态) */
|
||
export const defaultExpandedIds: ReadonlySet<string> = new Set([
|
||
"qa-center",
|
||
"light-app-center",
|
||
"light-app-note-management",
|
||
"task-management",
|
||
"xk-center-task",
|
||
"comprehensive-management",
|
||
]);
|
||
|
||
/** 单条路由是否与 pathname 精确匹配或为其子路径。 */
|
||
function routeMatchesPathname(pathname: string, route?: string): boolean {
|
||
if (!route) return false;
|
||
return pathname === route || pathname.startsWith(route + "/");
|
||
}
|
||
|
||
/** 菜单项自身 path / aliasPaths 是否匹配当前路由。 */
|
||
export function menuItemMatchesPath(item: MenuItem, pathname: string): boolean {
|
||
if (routeMatchesPathname(pathname, item.path)) return true;
|
||
return item.aliasPaths?.some((p) => routeMatchesPathname(pathname, p)) ?? false;
|
||
}
|
||
|
||
/** 该菜单项自身或任意后代的 path 是否匹配当前路由(支持子路径,如 /knowledge/:id) */
|
||
export function menuItemContainsPath(item: MenuItem, pathname: string): boolean {
|
||
if (menuItemMatchesPath(item, pathname)) return true;
|
||
return item.children?.some((child) => menuItemContainsPath(child, pathname)) ?? false;
|
||
}
|
||
|
||
/**
|
||
* 在「一级 → 二级 → 三级」菜单中,取当前路由所在的「激活二级项」的三级子项。
|
||
* 浅色侧边栏据此在二级右侧展开三级列;深色顶栏据此渲染三级横条。
|
||
* 全量模式:三级仅一项时不返回(无选择必要,侧边栏保持窄栏 / 不渲染横条)。
|
||
*/
|
||
export function getActiveThirdLevelItems(
|
||
items: MenuItem[],
|
||
pathname: string,
|
||
): MenuItem[] {
|
||
const activeTop =
|
||
items.find((item) => menuItemContainsPath(item, pathname)) ?? items[0];
|
||
const secondary = activeTop?.children ?? [];
|
||
const activeSecond = secondary.find(
|
||
(item) => (item.children?.length ?? 0) > 0 && menuItemContainsPath(item, pathname),
|
||
);
|
||
const third = activeSecond?.children ?? [];
|
||
return third.length <= 1 ? [] : third;
|
||
}
|
||
|
||
/** 按 admin 角色过滤菜单:非 admin 不展示 ``adminOnly: true`` 的项;``userPath`` 替换 ``path``。 */
|
||
export function filterMenuItemsByRole(items: MenuItem[], isAdmin: boolean): MenuItem[] {
|
||
return items
|
||
.filter((item) => !item.adminOnly || isAdmin)
|
||
.map((item) => {
|
||
const children = item.children
|
||
? filterMenuItemsByRole(item.children, isAdmin)
|
||
: undefined;
|
||
const base = children ? { ...item, children } : item;
|
||
if (!isAdmin && base.userPath) {
|
||
const { userPath: _userPath, ...rest } = base;
|
||
return { ...rest, path: base.userPath };
|
||
}
|
||
return base;
|
||
});
|
||
}
|