Backend: .NET 10 + PostgreSQL + EF Core + JWT + SignalR Frontend patient: React 19 + TypeScript + Vite (mobile H5) Frontend doctor: React 19 + TypeScript + Vite (desktop web)
28 KiB
健康管家(健康管家)— 技术审核报告
项目名称:health-manager-demo
中文名称:健康管家 · 心脏健康管理
定位:PCI 术后患者心脏健康管理移动端 Web 应用
审核日期:2026-05-19
审核范围:全部源代码(87 个文件)
目录
1. 项目概述
1.1 这是什么项目?
这是一个心脏健康管理 App 的前端原型,目标用户是做过 PCI(冠状动脉支架植入)手术的患者。它用网页技术(React)模拟了一个手机 App,包含以下功能模块:
| 模块 | 功能 |
|---|---|
| 登录/注册 | 手机号 + 短信验证码登录 |
| 首页看板 | 健康概览(血压、心率)、快捷入口、健康小贴士 |
| 健康数据 | 手动录入血压/心率/血糖/血氧/体重/步数,查看趋势图,健康日历 |
| 设备绑定 | 模拟蓝牙设备绑定(血压计、手表、体脂秤等) |
| 用药管理 | 药品列表、服药记录、用药依从性统计 |
| 在线问诊 | 医生列表、科室筛选、图文聊天 |
| 报告解读 | 上传/查看医疗报告、AI 解读 |
| 复查管理 | 术后复查预约记录 |
| 运动饮食 | 运动/饮食推荐、运动/饮食打卡 |
| 通知中心 | 系统通知列表 |
| 个人中心 | 个人资料、设置、隐私政策、关于 |
1.2 重要前提
这是一个纯前端 Demo,没有后端服务。 所有数据(用户、健康记录、医生列表、药品等)都是写在前端代码里的假数据(Mock Data)。点击交互看似"正常",但数据不会真正保存到任何服务器,刷新页面后所有修改都会丢失。
2. 技术栈
| 类别 | 技术 | 版本 | 用途 |
|---|---|---|---|
| 语言 | TypeScript | ~6.0.2 | 类型安全 |
| 框架 | React | ^19.2.6 | UI 构建 |
| 构建工具 | Vite | ^8.0.12 | 开发/打包 |
| 路由 | react-router-dom | ^7.15.1 | 页面跳转 |
| 状态管理 | zustand | ^5.0.13 | 全局状态(登录状态、通知) |
| 图表 | echarts + echarts-for-react | ^6.0.0 | 折线图、柱状图、饼图 |
| 动画 | framer-motion | ^12.39.0 | 页面切换动画 |
| 日期 | dayjs | ^1.11.20 | 日期格式化 |
| 样式 | CSS Modules + CSS 变量 | Vite 内置 | 组件级样式隔离 |
| 代码检查 | ESLint | ^10.3.0 | 代码规范 |
2.1 技术要点说明
- React 19:最新版 React,支持 Suspense、Server Components 等新特性(本项目未使用)
- zustand:比 Redux 更轻量的状态管理库,本项目用它管理登录状态和通知,登录状态会持久化到 localStorage
- echarts:Apache 出品的图表库,本项目用它画趋势图、饼图、柱状图
- CSS Modules:每个组件有自己的
.module.css文件,样式不会互相污染 - framer-motion:给页面切换加过渡动画
- dayjs:比 moment.js 轻量 97% 的日期处理库
3. 目录结构与文件说明
haruite-medical-demo/
│
├── index.html # 应用入口 HTML(设置 viewport、标题、PWA meta)
├── package.json # 项目配置(名称、依赖、npm 脚本)
├── vite.config.ts # Vite 构建配置(路径别名、端口、插件)
├── tsconfig.json # TypeScript 根配置(引用子配置)
├── tsconfig.app.json # 应用代码 TS 配置(JSX、路径别名)
├── tsconfig.node.json # Node 端 TS 配置(vite.config.ts 用)
├── eslint.config.js # ESLint 代码检查规则(扁平配置)
├── README.md # 项目说明
│
├── public/
│ ├── favicon.svg # 浏览器标签页图标
│ └── icons.svg # 社交媒体图标(Bluesky、Discord、GitHub、X)
│
└── src/
├── main.tsx # 应用启动入口,渲染 <App /> 到 <div id="root">
├── App.tsx # 根组件,初始化路由
│
├── assets/styles/ # 全局样式
│ ├── variables.css # CSS 变量(颜色、间距、圆角、阴影、字体、层级)
│ ├── reset.css # CSS 重置(统一各浏览器默认样式)
│ └── global.css # 全局样式(页面布局、滚动容器、过渡动画)
│
├── types/ # TypeScript 类型定义
│ ├── index.ts # 统一导出所有类型
│ ├── user.ts # User、LoginRequest、RegisterRequest
│ ├── health.ts # HealthRecord、HealthStats、MeasurementType
│ ├── device.ts # Device、DeviceType、DeviceStatus
│ ├── medication.ts # Medication、MedicationRecord、MedicationAdherence
│ ├── consultation.ts # Doctor、Consultation、ConsultationMessage
│ ├── report.ts # Report、ReportFinding、ReportResult
│ ├── followup.ts # FollowUp、FollowUpStatus
│ ├── exercise-diet.ts # FoodItem、ExerciseRecord、DietRecord
│ ├── notification.ts # Notification、NotificationGroup
│ └── calendar.ts # CalendarMarker、CalendarDay
│
├── utils/ # 工具函数
│ ├── constants.ts # 常量(测量类型、导航项、药品列表、健康提示)
│ ├── format.ts # 格式化(日期、数字、血压风险等级)
│ ├── storage.ts # localStorage 封装(带错误处理)
│ └── validator.ts # 表单验证(手机号、验证码、必填、数字范围)
│
├── hooks/ # 自定义 React Hooks
│ ├── useAuth.ts # 登录状态 Hook(useAuthStore 的封装)
│ └── useCountdown.ts # 倒计时 Hook(短信验证码重发倒计时)
│
├── stores/ # 全局状态管理(zustand)
│ ├── auth.store.ts # 登录状态(用户信息、token、登录/注册/登出)
│ └── notification.store.ts # 通知状态(通知列表、未读数)
│
├── services/ # API 服务层
│ ├── api.ts # API 请求封装(目前直接返回 Mock 数据)
│ ├── auth.service.ts # 登录/注册/短信/个人资料
│ ├── health.service.ts # 健康记录 CRUD + 统计分析
│ ├── device.service.ts # 设备扫描/绑定/解绑
│ ├── medication.service.ts # 药品 CRUD + 用药记录 + 依从性
│ ├── consultation.service.ts # 问诊(医生列表、咨询、消息)
│ ├── report.service.ts # 报告上传/查看/解读
│ ├── followup.service.ts # 复查预约 CRUD
│ ├── exercise-diet.service.ts # 运动/饮食记录 + 推荐
│ └── notification.service.ts # 通知列表/已读/未读数
│
├── mock/ # Mock 假数据
│ ├── index.ts # Mock 基础设施(延迟模拟、响应格式、ID 生成)
│ ├── users.ts # 假用户数据(1 个用户)
│ ├── health-records.ts # 假健康记录(91 天 × 6 种测量类型)
│ ├── devices.ts # 假设备数据(4 已绑定 + 2 可扫描)
│ ├── medications.ts # 假药品 + 30 天服药记录
│ ├── consultations.ts # 假医生(8 人)+ 问诊记录
│ ├── reports.ts # 假医疗报告(5 份)
│ ├── followups.ts # 假复查记录(3 条)
│ ├── exercise-diet.ts # 假运动/饮食记录(14 天)
│ └── notifications.ts # 假通知(8 条)
│
├── router/ # 路由配置
│ ├── index.tsx # 全部路由定义
│ └── AuthGuard.tsx # 登录守卫(未登录自动跳转登录页)
│
├── components/ # 可复用组件
│ ├── common/ # 通用 UI 组件
│ │ ├── Badge.tsx/css # 角标/红点
│ │ ├── Button.tsx/css # 按钮(主要/次要/线框/文字 + 加载态)
│ │ ├── Card.tsx/css # 卡片容器
│ │ ├── Empty.tsx/css # 空状态占位
│ │ ├── Input.tsx/css # 输入框(带标签和错误提示)
│ │ └── Toast.tsx/css # 轻提示(全局 toast 通知)
│ ├── layout/ # 布局组件
│ │ ├── AppLayout.tsx/css # 主布局(顶部 + 内容区 + 底部 TabBar)
│ │ ├── StackLayout.tsx # 堆栈页面布局(无底栏,仅 <Outlet />)
│ │ ├── PageHeader.tsx/css # 页面顶栏(返回按钮 + 标题 + 右侧操作)
│ │ └── TabBar.tsx/css # 底部导航栏(首页/健康/服务/我的)
│ └── charts/ # 图表组件
│ ├── BarChart.tsx # 柱状图(echarts)
│ ├── LineChart.tsx # 折线图(支持双线 + 警戒线)
│ └── PieChart.tsx # 饼图/环形图(echarts)
│
└── pages/ # 页面组件
├── auth/ # 登录/注册
├── home/ # 首页 + 设备绑定
├── health/ # 健康数据(Hub、记录列表、录入、趋势图、日历)
├── medication/ # 用药管理(列表、编辑、详情)
├── services/ # 服务(Hub、医生列表、聊天、报告、复查)
├── exercise-diet/ # 运动饮食
├── notifications/ # 通知列表
└── profile/ # 个人中心、设置、静态页面
4. 架构分析
4.1 整体架构
┌─────────────────────────────────────────────┐
│ Pages(页面) │
│ 每个页面是一个 React 组件,负责 UI 渲染 │
└──────────────┬──────────────────────────────┘
│ 调用
┌──────────────▼──────────────────────────────┐
│ Services(服务层) │
│ 封装业务逻辑,每个域一个文件 │
└──────────────┬──────────────────────────────┘
│ 调用
┌──────────────▼──────────────────────────────┐
│ Mock(模拟数据层) │
│ 返回假数据,模拟后端 API 响应 │
└─────────────────────────────────────────────┘
4.2 状态管理架构
┌──────────────────────┐ ┌──────────────────────┐
│ auth.store.ts │ │ notification.store.ts │
│ (zustand + persist) │ │ (zustand) │
│ │ │ │
│ - user │ │ - notifications[] │
│ - token │ │ - unreadCount │
│ - isAuthenticated │ │ - loading │
│ - login() │ │ - fetchNotifications│
│ - register() │ │ - markRead() │
│ - logout() │ │ - markAllRead() │
│ - updateProfile() │ │ │
└──────────────────────┘ └──────────────────────┘
4.3 路由架构
/ (root)
├── /login → LoginPage(登录页)
├── /register → RegisterPage(注册页)
│
├── [AuthGuard] ← 登录验证
│ ├── [AppLayout] ← 带底部 TabBar 的布局
│ │ ├── /home → HomePage(首页)
│ │ ├── /health → HealthHubPage(健康 Hub)
│ │ ├── /services → ServicesHubPage(服务 Hub)
│ │ └── /profile → ProfilePage(我的)
│ │
│ └── [StackLayout] ← 无底部栏的布局
│ ├── /home/device-binding → 设备绑定
│ ├── /health/records → 健康记录列表
│ ├── /health/records/add → 手动录入
│ ├── /health/trends/:type → 趋势图
│ ├── /health/calendar → 健康日历
│ ├── /health/medications → 用药列表
│ ├── /services/consultation → 医生列表
│ ├── /services/consultation/chat/:doctorId → 聊天
│ ├── /services/reports → 报告列表
│ ├── /services/follow-ups → 复查列表
│ ├── /profile/settings → 设置
│ └── /notifications → 通知列表
5. Bug 清单
5.1 严重 Bug(会导致崩溃或白屏)
🔴 BUG-001:ProfilePage 在未登录时崩溃
文件:src/pages/profile/ProfilePage.tsx:45
问题:当 user 为 null 时,user?.medicalHistory.join(',') 会抛出 TypeError。
原因:可选链 ?. 只在 user 为 null/undefined 时短路,但 medicalHistory 本身也可能是 undefined。.join() 在 undefined 上调用直接报错。
// ❌ 错误
{user?.medicalHistory.join(',')}
// ✅ 正确
{user?.medicalHistory?.join(',') || '无'}
🔴 BUG-002:ChatPage 缺少 doctorId 时崩溃
文件:src/pages/services/ChatPage.tsx:42
问题:doctorId! 使用了 TypeScript 的非空断言。如果用户直接访问 /services/consultation/chat/(没有 doctorId),页面直接崩溃。
// ❌ 错误
await consultationService.sendMessage(doctorId!, consultation.id, text);
// ✅ 正确
if (!doctorId) { toast('医生信息缺失'); return; }
🔴 BUG-003:FollowUpEditPage 服务调用失败时按钮永久 Loading
文件:src/pages/services/FollowUpEditPage.tsx:19-24
问题:handleSubmit 没有 try/catch。如果 addFollowUp() 抛出异常,setLoading(false) 永远不会执行,按钮永远显示"提交中..."
// ❌ 错误
const handleSubmit = async () => {
setLoading(true);
await followupService.addFollowUp({...}); // 抛异常就卡死
setLoading(false);
};
// ✅ 正确
const handleSubmit = async () => {
setLoading(true);
try {
await followupService.addFollowUp({...});
} catch {
toast('提交失败');
} finally {
setLoading(false);
}
};
🔴 BUG-004:路由修复前 — 所有子页面白屏(已修复)
文件:src/router/index.tsx:61
问题(已修复):第二个路由组渲染 <AuthGuard>{null}</AuthGuard>,没有 <Outlet />,导致 20+ 个堆栈页面全部白屏。
修复:将其改为 <AuthGuard><StackLayout /></AuthGuard>,StackLayout 内部渲染 <Outlet />。
5.2 中等问题(功能不正常,但不会崩溃)
🟡 BUG-005:健康趋势分析始终显示"稳定"
文件:src/services/health.service.ts:51-54
问题:getLatestStats() 中对 weekRecords(对象数组)做了 .reduce((a,b) => a+b, 0),而不是对数值数组。这导致 olderAvg 和 newerAvg 始终是 NaN,趋势永远返回 'stable'。
// ❌ 错误 — weekRecords 是 HealthRecord 对象数组,不能相加
const olderAvg = olderValues.reduce((a, b) => a + b, 0) / olderValues.length;
// ✅ 正确 — 应该用 values(数值数组)
const olderAvg = olderValues.reduce((a, b) => a + b, 0) / olderValues.length;
🟡 BUG-006:健康日历使用写死的假数据
文件:src/pages/health/HealthCalendarPage.tsx:7-9
问题:直接从 mock/ 目录 import 假数据,不经过 service 层。无论用户录入了多少健康数据、吃了什么药,日历永远显示同样的假数据。
// ❌ 绕过 service 层,直接引用 mock
import { mockHealthRecords } from '@/mock/health-records';
import { mockMedicationRecords } from '@/mock/medications';
// ✅ 应该通过 service 获取
healthService.getRecords({...})
medicationService.getMedicationRecords(...)
🟡 BUG-007:复查列表卡片点击跳转到错误页面
文件:src/pages/services/FollowUpListPage.tsx:39
问题:点击复查卡片跳转到 /health/medications(用药列表),而不是复查详情页。明显是复制粘贴错误。
🟡 BUG-008:用药详情页获取全部数据而非按 ID 查询
文件:src/pages/medication/MedicationDetailPage.tsx:17-19
问题:请求所有药品和所有服药记录,然后在客户端 .find() 筛选。如果药品数量很大,这会造成不必要的性能开销。应该按 ID 精确查询。
🟡 BUG-009:手动录入页 Loading 状态永远不会激活
文件:src/pages/health/ManualEntryPage.tsx:24
问题:loading 状态声明了但从未设成 true,用户快速点击提交按钮可以重复提交。
🟡 BUG-010:报告上传失败时仍显示"上传成功"
文件:src/pages/services/ReportUploadPage.tsx:21
问题:handleSubmit 没有 try/catch,不管 uploadReport() 是否成功,都会显示"上传成功"并返回上一页。
🟡 BUG-011:短信验证码从未真正发送
文件:src/pages/auth/LoginPage.tsx 和 RegisterPage.tsx
问题:点击"发送验证码"直接 toast"验证码已发送",只启动了倒计时,实际上没有调用任何 API。smsCode 参数在 auth.service.ts 中标记为 _smsCode(未使用)。
🟡 BUG-012:AuthGuard 在页面刷新时会闪到登录页
文件:src/router/AuthGuard.tsx
问题:zustand 的 persist 中间件是异步从 localStorage 恢复数据的。在恢复完成前,isAuthenticated 为 false,导致 AuthGuard 短暂显示登录页,等数据恢复后又跳回来。用户体验很差。
🟡 BUG-013:useCountdown 内存泄漏
文件:src/hooks/useCountdown.ts
问题:
- 组件卸载时
setInterval不会被清除,持续在后台运行 - 多次调用
start()会创建多个并发的倒计时,倒计时速度翻倍
5.3 低严重度(小问题)
- mock/devices.ts:小米体脂秤的设备类型写成了
smartwatch,应该是scale(但DeviceType联合类型里没有scale) - mock/notifications.ts:80:存在乱码字符
"已完<E5B7B2><E5AE8C><EFBFBD>状态",应为"已完成状态" - utils/format.ts:
formatRelative对未来的日期显示"刚刚",逻辑不对 - stores/auth.store.ts:
register()接收code参数但不传给 service - types/health.ts vs utils/constants.ts:
MeasurementType类型在两个文件中分别定义,可能不同步
6. 代码质量问题
6.1 系统性问题
| 问题 | 影响范围 | 说明 |
|---|---|---|
所有页面缺少 .catch() 错误处理 |
几乎所有页面 | Promise 被静默吞掉,出错时用户看不到任何反馈 |
| 所有页面缺少 Loading 状态 | 几乎所有页面 | 数据加载时显示空状态,然后突然出现内容,体验差 |
| 所有页面缺少 Error 状态 | 几乎所有页面 | API 调用失败时没有错误提示和重试按钮 |
| 大量 inline 箭头函数 | 所有页面 | onClick={() => ...} 每次渲染创建新函数,影响 memo 优化 |
| 没有 Error Boundary | 全局 | 任何一个组件崩溃都会导致整个页面白屏 |
| URL 参数无验证 | 多个页面 | as MeasurementType 等方式把 URL 参数直接断言为类型,无运行时校验 |
缺少 <form> 元素 |
所有表单页面 | 用 <div> + onClick 代替 <form> + onSubmit,失去原生表单行为 |
确认框用 window.confirm() |
ProfilePage | 阻塞主线程,移动端体验差,样式不可定制 |
6.2 Mock 数据问题
| 问题 | 说明 |
|---|---|
用户 ID 硬编码 'u001' |
所有 service 文件都写死 userId: 'u001',无法支持多用户 |
| Mock 数据被可变修改 | 所有"写"操作直接修改模块级数组,测试之间状态会互相污染 |
| 无法模拟错误 | mockApiResponse 永远返回 code: 200,无法测试错误处理流程 |
| 健康记录时间不真实 | 所有记录时间戳都相同(08:00:00),步数是 20:00:00 |
| 饮食记录只有早餐 | i % 3 === 0 过滤导致只生成大约 5 条早餐记录,无午餐/晚餐 |
| ChatPage 里有硬编码的假回复 | 生产代码里写了 getDoctorReply(),用随机选择的模板回复 |
6.3 类型安全问题
PieChart.tsx中 tooltip 用as Record<string, unknown>[]强制转换,丢失类型检查- 多处 URL 参数用
as断言跳过类型校验 ChatPage用了非空断言doctorId!main.tsx用了非空断言document.getElementById('root')!
7. 安全性问题
| 等级 | 问题 | 说明 |
|---|---|---|
| 🔴 高 | Token 存 localStorage | 登录 token 通过 zustand persist 存 localStorage,任何同源 JS 都能读取(XSS 攻击面) |
| 🟡 中 | 短信验证码无服务端校验 | sendSmsCode 接受 _phone 但从未真正验证,客户端倒计时可绕过 |
| 🟡 中 | 无 CSRF 保护 | Mock 层没有实现任何防护,后续接入真实后端需补充 |
| 🟡 中 | LineChart tooltip XSS 隐患 | tooltip HTML 用模板字符串拼接数据值,若数据含 < 等字符可能被注入 |
| 🟢 低 | 用户隐私数据明文存储 | User 类型包含手机号、生日、病史,存 localStorage 无加密 |
8. 性能问题
| 等级 | 问题 | 文件 | 说明 |
|---|---|---|---|
| 🔴 高 | 用药详情获取全部数据 | MedicationDetailPage | 请求所有药品和记录后在客户端 filter,应服务端按 ID 查 |
| 🔴 高 | 健康统计每次重新计算 | health.service.ts | getLatestStats 遍历全部记录做统计,无缓存 |
| 🟡 中 | 多次重复 diff 调用 |
format.ts | formatRelative 中 3 次调用 dayjs.diff(),可一次完成 |
| 🟡 中 | Inline 函数导致无效渲染 | 全项目 | 所有 onClick={() => ...} 在每次 render 创建新函数引用 |
| 🟡 中 | 数组浅拷贝开销 | 各 service | 每次读取数据都 [...mockArray] 创建新数组 |
| 🟢 低 | 咨询消息全量加载 | 类型定义 | Consultation 的 messages 是完整数组,没有分页 |
9. 无障碍(Accessibility)问题
9.1 重大问题
| 问题 | 文件 | 说明 |
|---|---|---|
| 通知设置页开关不可交互 | staticPages.tsx:19-21 |
开关是纯装饰性 <div>,无 onClick、无 role="switch"、无 aria-checked |
| 报告上传区域不可交互 | ReportUploadPage.tsx:39-43 |
上传区域是 <div>,没有 <input type="file">,没有 role="button",没有键盘事件 |
| Input 组件 label 不关联 input | Input.tsx:11 |
<label> 没有 htmlFor 属性,点击标签不会聚焦输入框 |
| Input 组件错误不关联 input | Input.tsx:13 |
错误信息没有 aria-describedby,屏幕阅读器不会读错误 |
9.2 普适性问题
| 问题 | 影响范围 |
|---|---|
Emoji 图标无 aria-label |
所有使用 💓💊👨⚕️📋📅🏃🔔💙 等 emoji 做图标的地方 |
Tab 切换无 role="tab" / aria-selected |
所有 Tab 切换页面 |
导航按钮无 aria-label |
所有快捷操作按钮 |
| 健康日历无键盘导航 | HealthCalendarPage — 没有 role="grid"、没有 tabindex |
聊天消息无 aria-live |
ChatPage — 新消息不会被屏幕阅读器自动播报 |
<button> 缺少 type="button" |
多处按钮默认 type="submit",若被 <form> 包裹可能触发意外提交 |
10. 改进建议
10.1 立即修复(影响核心功能)
- 修复 ProfilePage 崩溃 →
user?.medicalHistory?.join(',') - 修复健康趋势永远显示"稳定" →
health.service.ts改用数值数组计算 - 修复健康日历写死假数据 → 通过 service 层获取数据
- 修复复查列表跳转错误 → 改为正确路径
- 修复 ChatPage 非空断言 → 加 guard 判断
- 修复 FollowUpEditPage try/catch → 加异常处理
10.2 短期优化(提升体验)
- 所有数据获取加
.catch()错误处理 - 所有异步页面加 Loading 骨架屏
- 加 Error Boundary 防止全局崩溃
- 修复
useCountdown的内存泄漏 - 修复 AuthGuard 的 hydration 闪烁问题
- 用
<form>+onSubmit替代<div>+onClick - URL 参数加运行时校验
10.3 中期改进(架构升级)
- 统一
MeasurementType定义(从 constants 派生,类型文件引用) - 重构 Toast 组件为 Context/Provider 模式
- Service 层抽离接口,方便后续替换为真实 HTTP 调用
- Mock 层加入错误模拟能力
- 图表组件的无障碍替代文本
- 移除生产代码中的假数据(日历、问诊回复等)
10.4 长期规划(生产就绪)
- 接入真实后端 API(HTTP Client 替换 Mock 层)
- Token 改用 httpOnly Cookie 存储
- 添加短信验证码服务端校验 + 发送频率限制
- TypeScript 严格模式(
strict: true) - 单元测试 + E2E 测试
- PWA 离线支持(Service Worker)
- i18n 国际化(目前中文硬编码)
- CI/CD 自动构建部署
附录 A:修复前后路由对比
// ❌ 修复前 — 第二个路由组渲染 null,所有子页面白屏
{
path: '/',
element: <AuthGuard>{null}</AuthGuard>,
children: [ /* 20+ 页面全部无法渲染 */ ],
}
// ✅ 修复后 — 新增 StackLayout 组件渲染 <Outlet />
{
path: '/',
element: (
<AuthGuard>
<StackLayout /> // 只渲染 <Outlet />,无底部栏
</AuthGuard>
),
children: [ /* 正常渲染 */ ],
}
附录 B:关键文件依赖关系图
main.tsx
└── App.tsx
└── router/index.tsx
├── AuthGuard.tsx → hooks/useAuth.ts → stores/auth.store.ts
├── AppLayout.tsx → TabBar.tsx
├── StackLayout.tsx (新增)
└── pages/** (25 个页面组件)
└── services/*.service.ts
└── mock/index.ts → mock/*.ts
审核结论:项目架构基本合理,组件拆分清晰,类型定义完整。已修复一个导致 20+ 页面白屏的路由 Bug。剩余主要问题是缺少错误处理、Loading 状态、以及部分页面存在假数据硬编码。建议优先处理 10.1 节中的 6 个严重 Bug。