# T4 · 阈值工具 + 病例公共组件抽离 > 来源:~/.claude/plans/6-7-fluffy-sunset.md(T4 节) > 目标:把 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 diag: Record } ``` 内部组件:函数式 `KVItem` 不需要(这张卡里没用 KVItem)。 ### 4) `admin/src/views/tcm/diagnosis/components/PatientCaseCard.vue`(新建) 复用 `reception/index.vue:129-233`。 Props: ```ts interface Props { apt: Record diag: Record } ``` 内部组件:把 `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 / formatPeriod(utils 通用化后 reception 也用 import) - 删除:caseTypeLabel computed(搬到 PatientCaseCard 内) - 替换:113-233 行两个 card div → `` `` - 保留:`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` 等)原样搬到子组件里 `