Commit 97fb782f by luoqi

feat(web): 牙位时间轴补'桥体覆盖'note(固定桥几何)

牙位事实视图缺固定桥几何:修复类同象限基牙间的缺失牙=桥体,补'桥体 · 已由 X-Y 固定桥覆盖'行(teal,对齐既有非修复 note)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent d7c3c3e2
...@@ -34,6 +34,37 @@ export function ToothTimeline({ facts }: { facts: AdaptedFact[] }) { ...@@ -34,6 +34,37 @@ export function ToothTimeline({ facts }: { facts: AdaptedFact[] }) {
return <div className="text-center py-12 text-sm text-slate-400">无牙位事实</div>; return <div className="text-center py-12 text-sm text-slate-400">无牙位事实</div>;
} }
// 固定桥"桥体覆盖":修复(prosthodontic)治疗记在【基牙】上(如 25;27),桥体牙(26)自己泳道
// 看不到这座桥 → 像没修。这里按几何补:同象限基牙之间的缺失位 = 桥体,在那颗牙补一行
// "已由 X-Y 固定桥覆盖"(同后端 btx 桥区间口径:基牙间缺失位由桥覆盖)。
const bridgeNote = new Map<string, string>(); // 桥体牙位 → 跨度"25-27"
for (const f of facts) {
if (f.type !== 'treatment_record') continue;
if (String((f.content ?? {}).category ?? '') !== 'prosthodontic') continue;
const nums = factTeeth(f)
.map((t) => parseInt(t, 10))
.filter((n) => !Number.isNaN(n));
if (nums.length < 2) continue;
// 按象限(十位)分组;组内 min..max 间的缺失位即桥体
const byQuad = new Map<number, number[]>();
for (const n of nums) {
const q = Math.floor(n / 10);
(byQuad.get(q) ?? byQuad.set(q, []).get(q)!).push(n);
}
for (const ts of byQuad.values()) {
if (ts.length < 2) continue;
const sorted = [...ts].sort((a, b) => a - b);
const lo = sorted[0]!;
const hi = sorted[sorted.length - 1]!;
const present = new Set(sorted);
for (let n = lo + 1; n < hi; n++) {
if (!present.has(n) && !bridgeNote.has(String(n))) {
bridgeNote.set(String(n), `${lo}-${hi}`);
}
}
}
}
// 检查所见"缺牙间隙关闭/无修复间隙"→ 每颗牙的无修复注记(同后端 gap a''' 口径)。 // 检查所见"缺牙间隙关闭/无修复间隙"→ 每颗牙的无修复注记(同后端 gap a''' 口径)。
// 信息在 emr_record.exam_findings 文本里(本视图诊断/治疗都看不到)→ 补一行让客服懂为何不跟进。 // 信息在 emr_record.exam_findings 文本里(本视图诊断/治疗都看不到)→ 补一行让客服懂为何不跟进。
const noRestorNote = new Map<string, string>(); const noRestorNote = new Map<string, string>();
...@@ -118,6 +149,17 @@ export function ToothTimeline({ facts }: { facts: AdaptedFact[] }) { ...@@ -118,6 +149,17 @@ export function ToothTimeline({ facts }: { facts: AdaptedFact[] }) {
{rows.map((f) => ( {rows.map((f) => (
<ToothFactRow key={`${k}-${f.id}`} fact={f} /> <ToothFactRow key={`${k}-${f.id}`} fact={f} />
))} ))}
{!isWhole && bridgeNote.has(k) && (
<div className="flex items-baseline gap-2 px-2.5 py-1.5 text-[12px] bg-teal-50/40">
<span className="w-[74px] flex-none" />
<span className="flex-none px-1.5 py-px rounded text-[10px] font-medium bg-teal-50 text-teal-700">
桥体
</span>
<span className="flex-1 min-w-0 text-slate-600 leading-snug">
已由 {bridgeNote.get(k)} 固定桥覆盖(桥体)
</span>
</div>
)}
{!isWhole && noRestorNote.has(k) && ( {!isWhole && noRestorNote.has(k) && (
<div className="flex items-baseline gap-2 px-2.5 py-1.5 text-[12px] bg-slate-50/60"> <div className="flex items-baseline gap-2 px-2.5 py-1.5 text-[12px] bg-slate-50/60">
<span className="w-[74px] flex-none" /> <span className="w-[74px] flex-none" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment