/** * 关联处方(演示数据)。 * * 原型 V10.2 里「关联处方」出现在两处,**两处口径本来就不一样,不要合并**: * * ① 新增签署任务弹窗 B 区(原型 `rxOfVisit(v)`):一行摘要,按**就诊号**哈希 * 从 `RX_POOL` 里取药,同一就诊号每次打开必须带出同一条; * ② 签署工作台详情(原型 `taskRxs(t)`):一张 7 列的明细表,按**任务号**派生处方号, * 三条固定药品(麻精类,与麻精知情同意书配套)。 * * 后端没有处方接口(处方由 HIS 带出),因此两份都只在演示模式下生成; * 真实模式一律返回空,**绝不凭空造一条看起来真实的处方**—— * 那会让医务人员以为系统真的从 HIS 拿到了这张处方。 */ import type { PatientVisit, SigningTaskRecord } from './types' import { isSigningMockEnabled } from './signing' /** * 演示处方池。原型 RX_POOL 逐条对齐。 * 只在弹窗摘要里用;工作台明细表用的是下面的 `TASK_PRESCRIPTION_ROWS`。 */ const PRESCRIPTION_POOL = [ { item: '碘佛醇注射液 100ml', usage: '静脉注射 · 检查用' }, { item: '阿莫西林胶囊 0.25g×24', usage: '口服 每日3次' }, { item: '布洛芬缓释胶囊 0.3g×20', usage: '口服 每日2次' }, { item: '奥美拉唑肠溶片 20mg×14', usage: '口服 每日1次 · 晨服' }, { item: '0.9%氯化钠注射液 250ml', usage: '静脉滴注 · 检查前水化' }, ] /** 弹窗摘要形态的关联处方(原型 rxOfVisit 的 `{no, desc}`) */ export interface LinkedPrescription { no: string description: string } /** 工作台明细表形态的处方行(原型 rxCard 表格的 7 列) */ export interface TaskPrescription { /** 处方号 */ no: string /** 处方类别,如「门诊处方」「麻醉处方」 */ category: string /** 药品名称 */ drug: string /** 规格 */ spec: string /** 数量 */ quantity: string /** 用法用量 */ usage: string /** 状态,如「已开立」 */ status: string } /** 就诊号 → 稳定哈希(原型 rxOfVisit 的 seed 算法,改了口径两处会对不上) */ function hashVisitNo(visitNo: string): number { let seed = 0 for (let index = 0; index < visitNo.length; index += 1) { seed = (seed * 31 + visitNo.charCodeAt(index)) % 9973 } return seed } /** * 按就诊号确定性生成演示处方摘要(原型 rxOfVisit)。 * * 用哈希而不是随机:同一就诊号每次打开弹窗必须带出同一条处方, * 否则「关联处方」会随刷新变来变去,演示时没法解释。 */ export function buildLinkedPrescription(visit: PatientVisit | null): LinkedPrescription | null { if (!visit) { return null } const seed = hashVisitNo(visit.visitNo) const first = PRESCRIPTION_POOL[seed % PRESCRIPTION_POOL.length] const second = PRESCRIPTION_POOL[(seed + 2) % PRESCRIPTION_POOL.length] const extra = seed % 3 === 0 ? `;${second.item}(${second.usage})` : '' return { no: `Rx${visit.visitNo.replace(/\D/g, '').slice(-8)}`, description: `${first.item}(${first.usage})${extra}`, } } /** * 演示处方明细(原型 taskRxs 的三条麻精类药品)。 * * 处方号 = `Rx` + 任务号里的数字后 6 位,再拼 `-1/-2/-3`; * 任务号里没有数字时拼出来会是 `Rx-1` 这种残缺编号, * 这时退回用任务号本身做尾缀,保证每条处方的编号可读且唯一。 */ const TASK_PRESCRIPTION_ROWS: ReadonlyArray> = [ { category: '门诊处方', drug: '盐酸羟考酮缓释片(精一)', spec: '5mg×10片/盒', quantity: '1盒', usage: '口服,每12小时一次,整片吞服,遵医嘱', status: '已开立', }, { category: '门诊处方', drug: '地佐辛注射液', spec: '5mg:1ml×5支/盒', quantity: '2支', usage: '肌内注射,必要时,单次不超过10mg', status: '已开立', }, { category: '麻醉处方', drug: '磷酸可待因片(精二)', spec: '30mg×20片/瓶', quantity: '1瓶', usage: '口服,每次1片,每日3次(精神药品不得自开自用)', status: '已开立', }, ] /** * 读取某个签署任务的关联处方。 * * 真实模式返回空数组:处方由 HIS 带出,前端没有接口就拿不到, * 详情页据此整卡不渲染(而不是显示一张空表)。 */ export function readTaskPrescriptions( task: Pick | null, ): TaskPrescription[] { if (!task || !isSigningMockEnabled) { return [] } const digits = task.id.replace(/\D/g, '').slice(-6) const suffix = digits || task.id.replace(/\W/g, '').slice(-6) || '0' return TASK_PRESCRIPTION_ROWS.map((row, index) => ({ ...row, no: `Rx${suffix}-${index + 1}`, })) }