Files
zyt/.trellis/tasks/archive/2026-05/05-05-tcm-diagnosis-shared-extract/prd.md
T

107 lines
5.1 KiB
Markdown
Raw 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.
# T4 · 阈值工具 + 病例公共组件抽离
> 来源:~/.claude/plans/6-7-fluffy-sunset.mdT4 节)
> 目标:把 reception/index.vue 中的工具函数和两张「患者信息 / 患者病例」卡片抽成共用模块,为 T1 只读病例页面铺路。
> 验收硬指标:reception/index.vue **视觉与功能 0 回归**。
## 背景
- T1 二诊只读病例页要复用 reception/index.vue 的「患者信息」「患者病例」两张卡,且 isHigh* 阈值规则要保持一致。
- T2 跟踪矩阵也用同一套 isHigh* 函数标红。
- 当前两套规则只活在 `reception/index.vue` 内联 → 任何 T1/T2 直接 import 都办不到。
## 拆分
### 1) `admin/src/utils/blood-thresholds.ts`(新建)
`reception/index.vue:540-599` 搬出:
```ts
export function toNumeric(value: unknown): number | null
export function getBloodSugarThresholds(age: unknown): { fasting: number; postprandial: number } | null
export function isHighFastingBloodSugar(value: unknown, age: unknown): boolean
export function isHighPostprandialBloodSugar(value: unknown, age: unknown): boolean
export function isHighOtherBloodSugar(value: unknown, age: unknown): boolean
export function isHighSystolicPressure(value: unknown): boolean
export function isHighDiastolicPressure(value: unknown): boolean
export function isHighBloodPressure(row: { systolic_pressure?: unknown; diastolic_pressure?: unknown }): boolean
export function formatBp(row: { systolic_pressure?: unknown; diastolic_pressure?: unknown }): string
```
### 2) `admin/src/utils/diag-display.ts`(新建)
```ts
export function maskPhone(phone?: string): string
export function formatGender(g?: number): string
export function formatPeriod(period?: string): string
export function joinArray(arr: unknown): string
export function makeTextOf(diagRef: { value: any } | (() => any)): (field: string) => string
```
> `textOf` 在原文件里闭包了 `diag.value`,抽离后做成工厂,由调用方传 ref/getter,避免把 diag 全局化。
### 3) `admin/src/views/tcm/diagnosis/components/PatientInfoCard.vue`(新建)
复用 `reception/index.vue:113-127`
Props
```ts
interface Props {
apt: Record<string, any>
diag: Record<string, any>
}
```
内部组件:函数式 `KVItem` 不需要(这张卡里没用 KVItem)。
### 4) `admin/src/views/tcm/diagnosis/components/PatientCaseCard.vue`(新建)
复用 `reception/index.vue:129-233`
Props
```ts
interface Props {
apt: Record<string, any>
diag: Record<string, any>
}
```
内部组件:把 `KVItem` / `MetricKVItem`reception 376-420 行的函数式组件)一起搬过来。
内部 computed`caseTypeLabel`
工具函数:从 utils 引入。
### 5) `reception/index.vue` 修改
- import 新工具:`@/utils/blood-thresholds``@/utils/diag-display`
- import 新组件:`PatientInfoCard` / `PatientCaseCard`
- 删除:540-599 行的 isHigh* / toNumeric / formatBp / joinArray / getBloodSugarThresholds(共 60 行)
- 删除:376-420 行的内联 KVItem / MetricKVItem(共 ~45 行)
- 删除:maskPhone / formatGender / formatPeriodutils 通用化后 reception 也用 import
- 删除:caseTypeLabel computed(搬到 PatientCaseCard 内)
- 替换:113-233 行两个 card div → `<PatientInfoCard>` `<PatientCaseCard>`
- 保留:`formatGenderAge`(队列里的列表用,跟 card 无关)、`textOf`(仍要在 reception 里被用到 case card 之外的地方)
> 实际只要 reception/index.vue 现存的 isHigh* 用法 / KVItem 用法 / 工具函数引用 全部消失,剩下只用到 utils 和子组件即可。
### 6) 不修改
- `BloodRecordList.vue` / `DietRecordList.vue` 等已有列表组件(它们用各自的内联实现,T4 不动它们 —— 后续可以渐进迁移)。
- 对外 API、模板布局、CSS 类名 全部保持。
## 关键风险与缓解
| 风险 | 缓解 |
|---|---|
| 两张 card 视觉回归 | reception 用与抽离前完全一致的 props 调用;CSS 类名(`.card`/`.case-card`/`.patient-card`/`.kv-grid`/`.grid-block` 等)原样搬到子组件里 `<style scoped>` 中 |
| KVItem / MetricKVItem 函数式组件 type 漂移 | 子组件内部继续用 `h()` 函数式定义,签名不变 |
| textOf 在 reception 顶部还有别的引用? | 抽离前 grep 全文,凡是 reception 模板里非 case-card 区域用到的,由 reception 自己保留 textOf 闭包 |
| caseTypeLabel 移走后影响外部? | reception 模板里只在 case-card 里使用,搬走安全 |
| 类型声明完整性 | 6 个 isHigh* + formatBp / joinArray 都加 `unknown` 入参 + 严格 narrow,避免 any 漏判 |
## 验收
1. **视觉**reception/index.vue 接诊页 patient card / case card 像素级一致(手动 vs git diff 对比)
2. **类型**`npx vue-tsc --noEmit` 无新增错误(T4 文件 0 报错)
3. **功能**:reception 一遍走通:选患者、看 case card、看血糖记录红字标记、点完成接诊
4. **可复用性**T1 只读病例页能直接 `import { PatientInfoCard, PatientCaseCard } from '...'` 使用
5. **回归 grep**`grep -n "isHighFastingBloodSugar\|KVItem\b" reception/index.vue` 应该只剩 `import` 语句