import { Permission, type PermissionCode } from '@lingxi/shared'; import { IconApps, IconAsset, IconCheck, IconClock, IconCopy, IconDelivery, IconDocs, IconExport, IconFilter, IconGlobal, IconGrid, IconHome, IconImageAdd, IconMaterial, IconPlay, IconRobot, IconScript, IconSettings, IconShield, IconStack, IconTask, IconUser, IconVideo, IconView, } from '@lingxi/ui/icons'; import type { ComponentType } from 'react'; export type NavIcon = ComponentType<{ size?: number | string; className?: string }>; export interface NavItem { key: string; label: string; path: string; icon: NavIcon; /** 未配置权限点的菜单对所有登录用户可见 */ permission?: PermissionCode; description?: string; } export interface NavModule { key: string; label: string; path: string; icon: NavIcon; permission?: PermissionCode; items: NavItem[]; } /** * 顶部模块菜单 + 左侧功能菜单的唯一配置源(A0-FE-03)。 * 新增页面时在这里登记;侧边栏与面包屑都从这里派生,禁止在页面内写死导航。 */ export const NAV_MODULES: readonly NavModule[] = [ { key: 'dashboard', label: '工作台', path: '/', icon: IconHome, items: [ { key: 'overview', label: '工作台', path: '/', icon: IconHome, description: '今天的生产任务、待审核脚本与最近素材', }, ], }, { key: 'scripts', label: '脚本', path: '/scripts/library', icon: IconScript, permission: Permission.SCRIPT_READ, items: [ { key: 'library', label: '脚本库', path: '/scripts/library', icon: IconDocs, permission: Permission.SCRIPT_READ, description: '按客户、行业与状态检索已生成脚本', }, { key: 'framework-cards', label: '要素卡', path: '/scripts/framework-cards', icon: IconTask, permission: Permission.SCRIPT_WRITE, description: '维护行业包的要素卡字段与填写规则', }, { key: 'templates', label: '脚本模板', path: '/scripts/templates', icon: IconStack, permission: Permission.SCRIPT_WRITE, description: '结构模板、镜头模板与提示词模板', }, { key: 'batch', label: '批量生成', path: '/scripts/batch', icon: IconRobot, permission: Permission.SCRIPT_WRITE, description: '按要素卡批量生成脚本并排队审核', }, { key: 'review', label: '脚本审核', path: '/scripts/review', icon: IconCheck, permission: Permission.SCRIPT_REVIEW, description: '审核生成结果,标记低质量脚本', }, ], }, { key: 'material', label: '素材', path: '/material/image-compose', icon: IconMaterial, permission: Permission.MATERIAL_READ, items: [ { key: 'image-compose', label: '图片合成', path: '/material/image-compose', icon: IconImageAdd, permission: Permission.MATERIAL_RENDER, description: 'PSD 套版、抠图与确定性合成', }, { key: 'video', label: '视频生产', path: '/material/video', icon: IconVideo, permission: Permission.MATERIAL_RENDER, description: '逐镜生成、合成与成片预览', }, { key: 'tasks', label: '生产任务', path: '/material/tasks', icon: IconTask, permission: Permission.MATERIAL_READ, description: '任务进度、失败原因与重试', }, { key: 'quick-review', label: '快筛', path: '/material/quick-review', icon: IconFilter, permission: Permission.MATERIAL_REVIEW, description: '按快捷键快速筛选候选素材', }, { key: 'preview', label: '成片预览', path: '/material/preview', icon: IconPlay, permission: Permission.MATERIAL_READ, description: '播放成片并核对规格与标识', }, ], }, { key: 'assets', label: '资产', path: '/assets/library', icon: IconAsset, permission: Permission.ASSET_READ, items: [ { key: 'library', label: '资产库', path: '/assets/library', icon: IconAsset, permission: Permission.ASSET_READ, description: '图片、视频、音频与模板资产', }, { key: 'templates', label: '模板库', path: '/assets/templates', icon: IconCopy, permission: Permission.ASSET_READ, description: 'PSD 模板与版式配置', }, { key: 'pools', label: '人物与场景', path: '/assets/pools', icon: IconUser, permission: Permission.ASSET_READ, description: '人物、场景与风格池', }, { key: 'prompts', label: '提示词模板', path: '/assets/prompts', icon: IconRobot, permission: Permission.ASSET_READ, description: '视频与图片生成的提示词版本', }, ], }, { key: 'delivery', label: '交付', path: '/delivery/export', icon: IconDelivery, permission: Permission.DELIVERY_READ, items: [ { key: 'export', label: '导出任务', path: '/delivery/export', icon: IconExport, permission: Permission.DELIVERY_EXPORT, description: '勾选素材导出并按批次归档', }, { key: 'records', label: '导出记录', path: '/delivery/records', icon: IconClock, permission: Permission.DELIVERY_READ, description: '导出批次、签名链接与归档状态', }, { key: 'manifests', label: '交付清单', path: '/delivery/manifests', icon: IconTask, permission: Permission.DELIVERY_READ, description: '交付清单、客户签收与留痕', }, ], }, { key: 'system', label: '管理中心', path: '/system/users', icon: IconSettings, permission: Permission.USER_VIEW, items: [ { key: 'users', label: '用户管理', path: '/system/users', icon: IconUser, permission: Permission.USER_VIEW, description: '成员账号、部门归属与登录状态', }, { key: 'departments', label: '部门管理', path: '/system/departments', icon: IconApps, permission: Permission.USER_VIEW, description: '两级组织架构、负责人、排序与停用状态', }, { key: 'roles', label: '角色管理', path: '/system/roles', icon: IconShield, permission: Permission.ROLE_VIEW, description: '角色、权限点与职责互斥', }, { key: 'customers', label: '客户', path: '/system/customers', icon: IconGlobal, permission: Permission.CUSTOMER_MANAGE, description: '客户、行业归属与数据范围', }, { key: 'industries', label: '行业树', path: '/system/industries', icon: IconStack, permission: Permission.INDUSTRY_MANAGE, description: '行业分类树与行业包绑定', }, { key: 'dicts', label: '字典', path: '/system/dicts', icon: IconDocs, permission: Permission.DICT_MANAGE, description: '核心字典与业务枚举', }, { key: 'audit', label: '审计日志', path: '/system/audit-logs', icon: IconView, permission: Permission.AUDIT_READ, description: '关键操作留痕与导出记录', }, { key: 'ai', label: '模型接入', path: '/system/ai', icon: IconRobot, permission: Permission.AI_CONFIGURE, description: '供应商、路由与预算阈值', }, { key: 'settings', label: '系统配置', path: '/system/settings', icon: IconSettings, permission: Permission.SYSTEM_CONFIGURE, description: '阈值、上限与功能开关', }, { key: 'industry-packs', label: '行业包', path: '/system/industry-packs', icon: IconGrid, permission: Permission.INDUSTRY_MANAGE, description: '行业包版本、发布与灰度', }, { key: 'forms', label: '表单设计器', path: '/system/forms', icon: IconGrid, permission: Permission.FORM_DESIGN, description: '受控字段白名单与表单布局', }, { key: 'customer-fields', label: '客户字段', path: '/system/customer-fields', icon: IconTask, permission: Permission.CUSTOMER_MANAGE, description: '客户级扩展字段与审批', }, { key: 'compliance', label: '合规规则', path: '/system/compliance', icon: IconShield, permission: Permission.SYSTEM_CONFIGURE, description: '禁忌词、极限词与合规规则集', }, ], }, ]; export function filterNavByPermissions( modules: readonly NavModule[], permissions: ReadonlySet, ): NavModule[] { return modules .map((module) => { if (module.permission && !permissions.has(module.permission)) { return null; } const items = module.items.filter( (item) => item.permission === undefined || permissions.has(item.permission), ); return items.length > 0 ? { ...module, items } : null; }) .filter((module): module is NavModule => module !== null); } export function findNavModule( modules: readonly NavModule[], moduleKey: string, ): NavModule | undefined { return modules.find((module) => module.key === moduleKey); } export interface ActiveNav { module: NavModule; item: NavItem | undefined; } export function findActiveNav(modules: readonly NavModule[], pathname: string): ActiveNav | null { const normalized = pathname !== '/' ? pathname.replace(/\/+$/, '') : pathname; for (const module of modules) { const item = module.items.find( (candidate) => candidate.path === normalized || (candidate.path !== '/' && normalized.startsWith(`${candidate.path}/`)), ); if (item) { return { module, item }; } } return null; } export interface BreadcrumbEntry { key: string; label: string; path?: string; } export function getBreadcrumbs(modules: readonly NavModule[], pathname: string): BreadcrumbEntry[] { const active = findActiveNav(modules, pathname); if (!active) { return [{ key: 'home', label: '工作台', path: '/' }]; } const entries: BreadcrumbEntry[] = [{ key: 'home', label: '工作台', path: '/' }]; if (active.module.key !== 'dashboard') { entries.push({ key: active.module.key, label: active.module.label }); } if (active.item && active.item.path !== '/' && active.item.key !== active.module.items[0]?.key) { entries.push({ key: active.item.key, label: active.item.label, path: active.item.path }); } else if (active.item && active.item.path !== '/') { entries.push({ key: active.item.key, label: active.item.label, path: active.item.path }); } return entries; }