feat(签署工作台): 补代签标签、关联处方卡、线上等待态与只读副本
按原型 V10.2 详情页补齐四块: - 任务列表「代签」标签(原型 .vtag.chk 橙底):SigningTaskRecord 增加 signerType / signerRelation,仅签署人非「本人」时显示。 有意偏离:关系为空时不输出空括号(原型会渲染成「家属代签()」)。 - 「关联处方」可折叠卡(原型 rxCard):新建 api/workbench/prescriptions.ts 统一处方来源,把弹窗里那份搬过来消除重复,并新增详情用的 7 列明细。 原型两处处方口径本就不同(弹窗按就诊号哈希、详情按任务号派生),分开维护不合并。 真实模式返回空 → 整卡不渲染,不显示空表(会被读成「系统查过了,确实没处方」)。 顺带按原型调了详情卡片顺序:文书 → 签署文件 → 关联处方 → 操作留痕。 - 短信 / 公众号待签署的等待态(原型 .tg-wait):号码取后端 smsMobileMasked, 没有值时不拼号码,不拿患者档案里的号码冒充发送目标。 - 已签署的「在线预览(PDF)」与「只读副本」: 预览弹窗复用 SigningDocumentInteractive(只读态),保证预览 / 工作台 / 导出 PDF 三处版式一致;只读副本走 signing-pdf 新增的 watermark 选项, 在 html2canvas 克隆体上平铺斜排水印,审计编号直接用任务号。 水印行数按克隆体 scrollHeight 算,写死行数会让长文书下半部分没水印。
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* 关联处方(演示数据)。
|
||||
*
|
||||
* 原型 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<Omit<TaskPrescription, 'no'>> = [
|
||||
{
|
||||
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<SigningTaskRecord, 'id' | 'visitNo'> | 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}`,
|
||||
}))
|
||||
}
|
||||
Reference in New Issue
Block a user