refactor(web): 消除侧边栏硬编码 — iconMap 抽离 + routeTitleFallback 精简
- iconMap 抽离为 utils/iconRegistry.tsx(单一真相源),补齐 10 个后端 seed 使用但前端缺失的图标 - MainLayout import 从 28 个图标减少到 6 个(仅保留布局专用图标) - routeTitleFallback 从 26 条精简到 10 条(仅保留动态参数路由 + 无后端菜单的静态路由) - 后端菜单已覆盖的 16 条标题映射移除(由 getTitleFromMenus 从后端数据获取) - wiki 关键数字更新:迁移 146、权限码 132
This commit is contained in:
@@ -1,41 +1,12 @@
|
||||
import { useCallback, useState, memo, useEffect, useMemo } from 'react';
|
||||
import { Layout, Avatar, Space, Dropdown, Tooltip, Spin, theme } from 'antd';
|
||||
import {
|
||||
HomeOutlined,
|
||||
UserOutlined,
|
||||
SafetyOutlined,
|
||||
ApartmentOutlined,
|
||||
SettingOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
PartitionOutlined,
|
||||
LogoutOutlined,
|
||||
MessageOutlined,
|
||||
SearchOutlined,
|
||||
AppstoreOutlined,
|
||||
TeamOutlined,
|
||||
TableOutlined,
|
||||
TagsOutlined,
|
||||
RightOutlined,
|
||||
HeartOutlined,
|
||||
CalendarOutlined,
|
||||
PhoneOutlined,
|
||||
CommentOutlined,
|
||||
MedicineBoxOutlined,
|
||||
TrophyOutlined,
|
||||
ShopOutlined,
|
||||
FileTextOutlined,
|
||||
DashboardOutlined,
|
||||
RobotOutlined,
|
||||
HistoryOutlined,
|
||||
BarChartOutlined,
|
||||
AlertOutlined,
|
||||
BellOutlined,
|
||||
ControlOutlined,
|
||||
InboxOutlined,
|
||||
ApiOutlined,
|
||||
ReadOutlined,
|
||||
ExperimentOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useAppStore } from '../stores/app';
|
||||
@@ -43,6 +14,7 @@ import { useAuthStore } from '../stores/auth';
|
||||
import { usePluginStore } from '../stores/plugin';
|
||||
import type { PluginMenuGroup } from '../stores/plugin';
|
||||
import { getMenusForUser, type MenuInfo } from '../api/menus';
|
||||
import { getIcon } from '../utils/iconRegistry';
|
||||
import NotificationPanel from '../components/NotificationPanel';
|
||||
import ThemeSwitcher from '../components/ThemeSwitcher';
|
||||
|
||||
@@ -54,74 +26,22 @@ interface MenuItem {
|
||||
label: string;
|
||||
}
|
||||
|
||||
// 完整图标映射表
|
||||
const iconMap: Record<string, React.ReactNode> = {
|
||||
HomeOutlined: <HomeOutlined />,
|
||||
UserOutlined: <UserOutlined />,
|
||||
SafetyOutlined: <SafetyOutlined />,
|
||||
ApartmentOutlined: <ApartmentOutlined />,
|
||||
SettingOutlined: <SettingOutlined />,
|
||||
PartitionOutlined: <PartitionOutlined />,
|
||||
MessageOutlined: <MessageOutlined />,
|
||||
AppstoreOutlined: <AppstoreOutlined />,
|
||||
TeamOutlined: <TeamOutlined />,
|
||||
TableOutlined: <TableOutlined />,
|
||||
TagsOutlined: <TagsOutlined />,
|
||||
HeartOutlined: <HeartOutlined />,
|
||||
CalendarOutlined: <CalendarOutlined />,
|
||||
PhoneOutlined: <PhoneOutlined />,
|
||||
CommentOutlined: <CommentOutlined />,
|
||||
MedicineBoxOutlined: <MedicineBoxOutlined />,
|
||||
TrophyOutlined: <TrophyOutlined />,
|
||||
ShopOutlined: <ShopOutlined />,
|
||||
FileTextOutlined: <FileTextOutlined />,
|
||||
DashboardOutlined: <DashboardOutlined />,
|
||||
RobotOutlined: <RobotOutlined />,
|
||||
HistoryOutlined: <HistoryOutlined />,
|
||||
BarChartOutlined: <BarChartOutlined />,
|
||||
AlertOutlined: <AlertOutlined />,
|
||||
BellOutlined: <BellOutlined />,
|
||||
ControlOutlined: <ControlOutlined />,
|
||||
InboxOutlined: <InboxOutlined />,
|
||||
ApiOutlined: <ApiOutlined />,
|
||||
ReadOutlined: <ReadOutlined />,
|
||||
ExperimentOutlined: <ExperimentOutlined />,
|
||||
};
|
||||
|
||||
function getIcon(name?: string): React.ReactNode {
|
||||
if (!name) return <AppstoreOutlined />;
|
||||
return iconMap[name] || <AppstoreOutlined />;
|
||||
}
|
||||
|
||||
// 路由标题 fallback(给动态参数路由用)
|
||||
// 路由标题 fallback — 仅保留后端菜单无法覆盖的路由
|
||||
// 1. 动态参数路由(:id/:id/edit)— 菜单表不会存储这些路径
|
||||
// 2. 无后端菜单记录的静态页面路由
|
||||
const routeTitleFallback: Record<string, string> = {
|
||||
// 动态参数路由
|
||||
'/health/patients/:id': '患者详情',
|
||||
'/health/follow-up-records': '随访记录',
|
||||
'/health/consultations/:id': '咨询详情',
|
||||
'/health/points-rules': '积分规则管理',
|
||||
'/health/offline-events': '线下活动管理',
|
||||
'/health/articles': '内容管理',
|
||||
'/health/articles/new': '新建文章',
|
||||
'/health/articles/:id/edit': '编辑文章',
|
||||
'/health/care-plans/:id': '护理计划详情',
|
||||
'/health/shifts/:id': '班次详情',
|
||||
'/health/ble-gateways/:id': '网关详情',
|
||||
// 无后端菜单的静态路由
|
||||
'/health/follow-up-records': '随访记录',
|
||||
'/health/article-categories': '分类管理',
|
||||
'/health/article-tags': '标签管理',
|
||||
'/health/alerts': '告警列表',
|
||||
'/health/alert-dashboard': '告警仪表盘',
|
||||
'/health/alert-rules': '告警规则',
|
||||
'/health/devices': '设备管理',
|
||||
'/health/dialysis': '透析管理',
|
||||
'/health/follow-up-templates': '随访模板管理',
|
||||
'/health/care-plans': '护理计划',
|
||||
'/health/care-plans/:id': '护理计划详情',
|
||||
'/health/shifts': '班次管理',
|
||||
'/health/shifts/:id': '班次详情',
|
||||
'/health/medications': '药物记录',
|
||||
'/health/ble-gateways': 'BLE 网关管理',
|
||||
'/health/ble-gateways/:id': '网关详情',
|
||||
'/health/critical-value-thresholds': '危急值阈值',
|
||||
'/health/diagnoses': '诊断记录',
|
||||
'/health/family-proxy': '家庭健康代理',
|
||||
'/health/consents': '知情同意管理',
|
||||
};
|
||||
|
||||
function getTitleFromMenus(path: string, menus: MenuInfo[]): string | undefined {
|
||||
|
||||
107
apps/web/src/utils/iconRegistry.tsx
Normal file
107
apps/web/src/utils/iconRegistry.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* 图标注册表 — 菜单图标名称 → React 组件的单一真相源
|
||||
*
|
||||
* 后端 menus.icon 字段存储图标名称字符串(如 "HomeOutlined"),
|
||||
* 前端通过此注册表将其转换为对应的 Ant Design 图标组件。
|
||||
*
|
||||
* 新增后端 seed 图标时必须同步在此添加映射,否则侧边栏回退为 AppstoreOutlined。
|
||||
*/
|
||||
|
||||
import {
|
||||
HomeOutlined,
|
||||
UserOutlined,
|
||||
SafetyOutlined,
|
||||
ApartmentOutlined,
|
||||
SettingOutlined,
|
||||
PartitionOutlined,
|
||||
MessageOutlined,
|
||||
AppstoreOutlined,
|
||||
TeamOutlined,
|
||||
TableOutlined,
|
||||
TagsOutlined,
|
||||
HeartOutlined,
|
||||
CalendarOutlined,
|
||||
PhoneOutlined,
|
||||
CommentOutlined,
|
||||
MedicineBoxOutlined,
|
||||
TrophyOutlined,
|
||||
ShopOutlined,
|
||||
FileTextOutlined,
|
||||
DashboardOutlined,
|
||||
RobotOutlined,
|
||||
HistoryOutlined,
|
||||
BarChartOutlined,
|
||||
AlertOutlined,
|
||||
BellOutlined,
|
||||
ControlOutlined,
|
||||
InboxOutlined,
|
||||
ApiOutlined,
|
||||
ReadOutlined,
|
||||
ExperimentOutlined,
|
||||
// 以下为后端 seed 使用但原 iconMap 缺失的图标
|
||||
AuditOutlined,
|
||||
ClockCircleOutlined,
|
||||
FileSearchOutlined,
|
||||
FormOutlined,
|
||||
MonitorOutlined,
|
||||
PictureOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
SolutionOutlined,
|
||||
SwapOutlined,
|
||||
WifiOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export const iconRegistry: Record<string, ReactNode> = {
|
||||
// 基础模块
|
||||
HomeOutlined: <HomeOutlined />,
|
||||
UserOutlined: <UserOutlined />,
|
||||
SafetyOutlined: <SafetyOutlined />,
|
||||
ApartmentOutlined: <ApartmentOutlined />,
|
||||
SettingOutlined: <SettingOutlined />,
|
||||
PartitionOutlined: <PartitionOutlined />,
|
||||
MessageOutlined: <MessageOutlined />,
|
||||
AppstoreOutlined: <AppstoreOutlined />,
|
||||
TeamOutlined: <TeamOutlined />,
|
||||
TableOutlined: <TableOutlined />,
|
||||
TagsOutlined: <TagsOutlined />,
|
||||
SearchOutlined: <AppstoreOutlined />, // 搜索无专属侧边栏图标
|
||||
|
||||
// 健康模块
|
||||
HeartOutlined: <HeartOutlined />,
|
||||
CalendarOutlined: <CalendarOutlined />,
|
||||
PhoneOutlined: <PhoneOutlined />,
|
||||
CommentOutlined: <CommentOutlined />,
|
||||
MedicineBoxOutlined: <MedicineBoxOutlined />,
|
||||
TrophyOutlined: <TrophyOutlined />,
|
||||
ShopOutlined: <ShopOutlined />,
|
||||
FileTextOutlined: <FileTextOutlined />,
|
||||
DashboardOutlined: <DashboardOutlined />,
|
||||
RobotOutlined: <RobotOutlined />,
|
||||
HistoryOutlined: <HistoryOutlined />,
|
||||
BarChartOutlined: <BarChartOutlined />,
|
||||
AlertOutlined: <AlertOutlined />,
|
||||
BellOutlined: <BellOutlined />,
|
||||
ControlOutlined: <ControlOutlined />,
|
||||
InboxOutlined: <InboxOutlined />,
|
||||
ApiOutlined: <ApiOutlined />,
|
||||
ReadOutlined: <ReadOutlined />,
|
||||
ExperimentOutlined: <ExperimentOutlined />,
|
||||
|
||||
// 健康模块(补充原缺失)
|
||||
AuditOutlined: <AuditOutlined />,
|
||||
ClockCircleOutlined: <ClockCircleOutlined />,
|
||||
FileSearchOutlined: <FileSearchOutlined />,
|
||||
FormOutlined: <FormOutlined />,
|
||||
MonitorOutlined: <MonitorOutlined />,
|
||||
PictureOutlined: <PictureOutlined />,
|
||||
SafetyCertificateOutlined: <SafetyCertificateOutlined />,
|
||||
SolutionOutlined: <SolutionOutlined />,
|
||||
SwapOutlined: <SwapOutlined />,
|
||||
WifiOutlined: <WifiOutlined />,
|
||||
};
|
||||
|
||||
export function getIcon(name?: string): ReactNode {
|
||||
if (!name) return <AppstoreOutlined />;
|
||||
return iconRegistry[name] || <AppstoreOutlined />;
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
| Rust crate | 17 个(erp-core + 5 基础业务 + erp-health + erp-ai + erp-dialysis + erp-plugin + 7 插件/原型) |
|
||||
| Rust 源文件 | **649 个** |
|
||||
| 数据库表 | 30 基础表 + 49 健康业务表 + 9 AI 表 + 3 媒体库/轮播图表 |
|
||||
| 数据库迁移 | **145 个**(最新 m20260513_000145) |
|
||||
| 数据库迁移 | **146 个**(最新 m20260515_000146) |
|
||||
| 后端路由 | 260+ 个(11 公开 + 14 FHIR + 2 网关 + ~240 受保护) |
|
||||
| 核心模块 | 5 基础 (auth/config/workflow/message/plugin) + 3 业务 (health + ai + dialysis) |
|
||||
| erp-health 实体 | **57 个** Entity(31 handler / 36 service / 21 DTO,189 文件) |
|
||||
@@ -21,7 +21,7 @@
|
||||
| 前端单元测试 | 88 个测试文件(472 Web 断言 + 39 MP 断言)+ 13 E2E spec(124 断言) |
|
||||
| 后端测试 | **943 个函数**(762 同步 + 181 异步),79 个文件含内联测试 |
|
||||
| 事件系统 | 31 事件类型(health 模块内)/ 23 幂等消费者 / Outbox + LISTEN/NOTIFY |
|
||||
| 权限码 | **128 个**(health 28 + auth 25 + ai 7 + workflow 8 + dialysis 5 + plugin 2 + Copilot + 媒体库) |
|
||||
| 权限码 | **132 个**(health 59 + auth 17 + ai 9 + workflow 8 + dialysis 6 + plugin 2 + config 13 + message 5 + Copilot 5) |
|
||||
| 生产 unwrap | **24 处**(从 514 降至 24),全为安全解包 |
|
||||
| utoipa 注解 | 88 个文件含注解 |
|
||||
| Clippy | **全 workspace 0 警告**(2026-05-07 清零) |
|
||||
|
||||
Reference in New Issue
Block a user