/** * Admin 导航单源(D6)。 * * 背景: 侧栏(NAV_SECTIONS)、命令面板(NAV_PAGES)、面包屑(ROUTE_LABELS) * 此前各维护一份平行清单,已出现漂移 —— data-pipeline 不在侧栏、 * 「系统」vs「系统设置」命名不一致、monitoring/eval 两处顺序相反。 * * 现在只允许维护 NAV_ITEMS 一份,其余视图一律由此派生; * 禁止再新建平行导航清单(修改入口/新增页面只改这里)。 */ export interface NavItem { to: string label: string /** 命令面板中的分组名(展示原样) */ group: string /** 侧栏图标名(见 AdminLayout 的 Icon) */ icon: string /** 仅命令面板/面包屑可达,不进侧栏(深链页) */ hideFromSidebar?: boolean } /** 唯一的导航配置源。顺序 = 侧栏渲染顺序(命令面板分组内顺序与之相同)。 * * 分组按用户心智模型: 数据(往里灌) → 预测(算出来) → 系统(保证它活着)。 * 「预测历史/回测/评估」同属预测生命周期,不再挂在「数据流水线」名下。 */ export const NAV_ITEMS: NavItem[] = [ { to: '/admin', label: '仪表盘', group: '概览', icon: 'chart' }, // ── 数据:采集、核对、管线内部视图 ── { to: '/admin/collection', label: '数据采集', group: '数据', icon: 'collection' }, { to: '/admin/data-completeness', label: '数据完整性', group: '数据', icon: 'eval' }, { to: '/admin/data-pipeline', label: '数据管线', group: '数据', icon: 'chart', hideFromSidebar: true }, // ── 预测:历史 → 回测 → 评估闭环 ── { to: '/admin/predictions', label: '预测历史', group: '预测', icon: 'target' }, { to: '/admin/backtest', label: '回测', group: '预测', icon: 'repeat' }, { to: '/admin/eval', label: '评估', group: '预测', icon: 'eval' }, // ── 系统:活着吗、发生了什么、怎么调 ── { to: '/admin/monitoring', label: '监控', group: '系统', icon: 'monitor' }, { to: '/admin/logs', label: '日志', group: '系统', icon: 'logs' }, { to: '/admin/settings', label: '设置', group: '系统', icon: 'settings' }, ] /** 命令面板条目(原 NAV_PAGES 的唯一来源) */ export const COMMAND_PALETTE_PAGES = NAV_ITEMS.map(({ to, label, group }) => ({ to, label, group })) /** 路由 → 面包屑标签(原 ROUTE_LABELS 的唯一来源) */ export const ROUTE_LABELS: Record = Object.fromEntries( NAV_ITEMS.map(i => [i.to, i.label]), ) /** * 侧栏分组(原 NAV_SECTIONS 的唯一来源)。 * 仪表盘(group=概览)在 AdminLayout 中独立渲染于顶部,不进分组循环。 */ export const NAV_SECTIONS = (() => { const sidebarItems = NAV_ITEMS.filter(i => !i.hideFromSidebar && i.group !== '概览') const titles: string[] = [] for (const i of sidebarItems) { if (!titles.includes(i.group)) titles.push(i.group) } return titles.map(title => ({ title, items: sidebarItems .filter(i => i.group === title) .map(({ to, label, icon }) => ({ to, label, icon })), })) })()