Files

116 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# T1+T2 · 二诊只读病例页 + 跟踪矩阵
> 来源:~/.claude/plans/6-7-fluffy-sunset.mdT1 + T2 节)
> 依赖:T4(已完成 — 提供 PatientInfoCard / PatientCaseCard / blood-thresholds.ts);T3(已完成 — 顺手把 unserved_days 用到 readonly 页头部 badge
> 范围:单 PR,前端 + 后端 + 菜单 SQL,整合进入 fix-diagnosis-20250505 分支
## 用户故事
二中心医助每天进诊单列表,**双击行**或点**「查看」**按钮 → 直达「只读病例页」:
- 患者基本信息 + 完整病例(PatientInfoCard / PatientCaseCard,与接诊台像素级一致)
- 跟踪矩阵:日期为列、6 项指标为行(血糖|血压|西药|胰岛素|饮食|运动),同日血糖三段挤一格,超阈值红字 + ↑
- 顶部 hero:未服务 X 天的红/橙/绿 badge
## 后端
### 接口 `GET /tcm.diagnosis/readonlyDetail?id=:diagnosisId`
**Validate**`DiagnosisValidate::sceneReadonlyDetail``only(['id'])` + `id` 必填且 `checkDiagnosis`
**Logic** `DiagnosisLogic::readonlyDetail($params, $adminId, $adminInfo)`
1. 校验诊单存在
2. 数据权限:医助 (role_id=2) 仅看 `assistant_id == self`DataScope 启用时 `assistant_id IN visibleAdminIds`
3. 拼装 `diagnosis`(复用 `self::detail` + `AppointmentLogic::enrichDiagnosisLabels`
4. 取最近一条挂号(用于 PatientInfoCard 显示挂号信息);没有就给个默认空对象
5. 取最近 30 天的 blood / diet / exercise 三类记录
6. 取医生备注(DoctorNoteLogic
7. 计算 unserved_days= today MAX(blood.record_date) 天)
8. 返回:`{ appointment, diagnosis, blood_records, diet_records, exercise_records, doctor_notes, unserved_days, last_blood_record_at }`
**Controller**`DiagnosisController::readonlyDetail()` thin wrapper
**AppointmentLogic 小改**`enrichDiagnosisLabels``private``public static`,便于 DiagnosisLogic 复用。其它行为不变。
**菜单 SQL**`server/sql/1.9.20260420/add_diagnosis_readonly_menu.sql` —— 注册 `tcm.diagnosis/readonlyDetail` 权限节点。
## 前端
### 路由
`/tcm/diagnosis-readonly?id=:diagnosisId` — 注册在 `admin/src/router/routes.ts`**constantRoutes**(不依赖菜单驱动),用 `LAYOUT` 包裹保留侧边栏;`meta.hidden=true` 不显示在导航;`meta.activeMenu='/tcm/diagnosis'` 高亮诊单父菜单。
> 实践教训:admin 的动态路由是后端菜单表驱动的(`createRouteRecord` 只看 menu API);因此「隐藏路由」**不能仅靠不加菜单 SQL** 解决,否则路径根本没注册到 vue-router。最稳的做法是写 constantRoutes 静态路由 + 用与父菜单不同的路径前缀(这里用连字符 `-readonly` 而非 `/readonly` 子路径,避免和动态菜单同前缀互相影响)。
### 入口(在 diagnosis/index.vue 上)
- 操作列**最前**新增 `<el-button link>查看</el-button>``router.push({ path: '/tcm/diagnosis/readonly', query: { id: row.id } })`
- `<el-table @row-dblclick="goReadonly">` 双击同样跳转
- 操作列原有按钮加 `@click.stop`(避免冒泡触发双击)
### 新建 `admin/src/views/tcm/diagnosis/readonly.vue`
```
┌─ Hero:返回 + 患者姓名 + 「未服务 X 天」 badgecolor 同 T3
├─ <PatientInfoCard :apt :diag />
├─ <PatientCaseCard :apt :diag />
├─ <TrackingMatrix>
└─ <NoteTimeline>(可选;先复用 reception 的样式)
```
### 新建 `admin/src/views/tcm/diagnosis/components/TrackingMatrix.vue`
Props
```ts
{
diagnosisId: number
patientId?: number
age?: number | null // 阈值依赖
bloodRecords: BloodRecord[]
dietRecords: DietRecord[]
exerciseRecords: ExerciseRecord[]
}
```
渲染规则:
| 列 | 来源 | cell |
|---|---|---|
| 日期 (fixed left) | union(blood/diet/exercise.record_date) desc 去重 | `MM-DD(周X` |
| 血糖 | bloodRecords by date | `空 6.5 餐 8.9 其他 7.2`,缺值 `—`;任一段超阈值 → 红字 + ↑ |
| 血压 | bloodRecords by date | `135/90`;超 → 红 + ↑ |
| 西药 | bloodRecords.western_medicine | tooltip 全文,cell 截断 |
| 胰岛素 | bloodRecords.insulin | 同上 |
| 饮食 | dietRecords by date | `已打卡` / `—`;点 cell 弹出当日早/午/晚 |
| 运动 | exerciseRecords by date | `已打卡 · 30min · 中强度` / `—`;点弹详情 |
时间窗:`7 / 30 / 90 / 365 / 自定义`(默认 30);切换时由父组件重新拉数据或客户端过滤。**本期实现简化**:父组件一次拉 30 天,组件本地过滤切换的窗口。
CSS 类:`.matrix-table` `.cell-blood` `.cell-bp` `.cell-empty` `.cell-high` `.cell-up`(红字 + ↑),文字截断/tooltip 走 el-table 的 `show-overflow-tooltip`
阈值判定:`@/utils/blood-thresholds` 的 isHigh*T4 提供)。
### API
`admin/src/api/tcm.ts` 新增:
```ts
export function diagnosisReadonlyDetail(params: { id: number }) {
return request.get({ url: '/tcm.diagnosis/readonlyDetail', params })
}
```
## 不做(边界)
- 不做评论 / 编辑 / 删除按钮 — 整页**严格只读**
- 不引入 `useRouter` 之外的全局状态(页面无状态)
- 不做日期窗口的服务端拉取(窗口切换走前端过滤),后续真有量再做
- 不做表头排序(矩阵本身是日期 desc,无意义)
- 不在菜单里展示路由(不配 system_menu type='C'
- DoctorNote 区块复用 reception 的 NoteTimeline 但不绑 refresh 事件(只读)
- 跟踪矩阵不展示「备注」字段(跟踪场景看血糖/血压数值即可)
## 验收
1. 在诊单列表点「查看」 / 双击行 → 跳到 readonly 页
2. PatientInfoCard / PatientCaseCard 视觉与接诊台一致(T4 已确保)
3. 跟踪矩阵:同一天血糖三段挤一格,红色 + ↑ 与 reception 视觉一致
4. 没有任何打卡时矩阵显示空状态(`el-empty`
5. 编辑/删除按钮全程不存在 — **页面纯只读**
6. 越权访问 → 提示无权限,无 500
7. 时间窗 7/30/90/365 切换有效
8. 操作列其它按钮(编辑/删除/指派)的点击不会触发跳转(`@click.stop`