feat(签署工作台): 补齐短信/公众号发送弹窗与 Pad 定位签名位流程

This commit is contained in:
yelan
2026-09-27 16:50:13 +08:00
parent 39d27ae5e7
commit ea5a248a0c
9 changed files with 981 additions and 20 deletions
@@ -8,7 +8,12 @@ import type {
SigningTaskEvent,
SigningTaskRecord,
} from '@/api/workbench/types'
import type { SigningFieldProgress, SigningSignatureRequest } from '@/utils/signing-document'
import {
SIGN_FIELD_LOCATE_CLASS,
SIGN_FIELD_LOCATE_MS,
type SigningFieldProgress,
type SigningSignatureRequest,
} from '@/utils/signing-document'
import { formatSigningMethodTitle, readSigningMethod } from '@/utils/signing-method'
import SigningDocumentInteractive from '@/components/signing/SigningDocumentInteractive.vue'
@@ -81,6 +86,30 @@ function alternateMethods(current: SigningMethod): SigningMethod[] {
return ALTERNATE_METHODS.filter((item) => item !== current)
}
/**
* 「其余三种方式」按钮各自打开哪个流程(原型 wbActs 里四个按钮的 onclick)。
*
* 原型这四个按钮**互为入口**:点「短信签署」是弹「发送签署短信」、
* 点「公众号签署」是弹「消息推送」、点「Pad 签署」是切换方式并把文书滚到签名位 ——
* 都不是「只把方式改一下」。
*/
function entryActionFor(target: SigningMethod): SigningTaskAction {
if (target === 'pad') {
return 'pad-sign'
}
if (target === 'ipad') {
return 'switch-to-ipad'
}
return target === 'wx' ? 'send-wx-push' : 'send-sms'
}
/** 「查看患者签署页」的按钮文案:原型两处写法不同(短信写 H5、公众号写患者签署页),照抄 */
function viewPatientPageLabel(method: SigningMethod): string {
return method === 'wx' ? '查看患者签署页(公众号)' : '查看线上签署(H5)'
}
/**
* 现场设备签署(手写板 / Pad)走本地采集入口;线上(短信 / 公众号)走患者端页面。
* 这只是**入口形态**的分组,不是取值归并 —— 按钮文案、状态标签仍按具体取值区分。
@@ -113,7 +142,34 @@ function getDocumentElement() {
return documentRef.value?.getDocumentElement() ?? null
}
defineExpose({ getDocumentElement })
/**
* 「📲 Pad 签署」:把文书滚到第一个签名位并高亮(原型 `openIpad`)。
*
* 原型这一步**不弹签名板** —— 它只把签名位滚到眼前、闪一下、提示「点击签名框弹出签名板」,
* 由操作人自己在文书上点那个签名位。这样「Pad 签署」与「手写板签署」的差别才看得出来:
* 前者是在文书上定位后现场签,后者是直接开签名板。
*
* 返回是否找到了签名位(没找到由父级提示,原型也是 toast 报错)。
*/
function locateSignatureSlot(): boolean {
const root = documentRef.value?.getDocumentElement()
const slot = root?.querySelector<HTMLElement>('[data-sign-kind="signature"]')
if (!slot) {
return false
}
slot.scrollIntoView({ behavior: 'smooth', block: 'center' })
// 重放动画:先摘类、强制 reflow、再加回去(原型用同一手法)
slot.classList.remove(SIGN_FIELD_LOCATE_CLASS)
void slot.offsetWidth
slot.classList.add(SIGN_FIELD_LOCATE_CLASS)
window.setTimeout(() => slot.classList.remove(SIGN_FIELD_LOCATE_CLASS), SIGN_FIELD_LOCATE_MS)
return true
}
defineExpose({ getDocumentElement, locateSignatureSlot })
</script>
<template>
@@ -128,7 +184,9 @@ defineExpose({ getDocumentElement })
<span class="task-action-meta">任务 {{ task.id }} · {{ task.patientName }}</span>
<template v-if="task.status === 'pending'">
<!-- 主按钮 = 当前方式的签署入口(原型:手写板/Pad 走现场设备,短信/公众号走线上页面) -->
<!-- 主按钮 = 当前方式的签署入口(原型 wbActs 的四个分支):
手写板 → 直接开签名板;Pad → 把文书滚到签名位等高亮;短信 → 开「发送签署短信」;
公众号 → 开「消息推送」 -->
<template v-if="isOnsiteMethod(task.method)">
<template v-if="allowLocalSigning">
<button type="button" class="action-button" @click="emit('action', 'pad-sign')">
@@ -152,8 +210,12 @@ defineExpose({ getDocumentElement })
<span v-else class="action-hint">创建会话后仍需接入厂商设备适配器完成签名采集</span>
</template>
</template>
<!-- 线上方式:消息已经发出去了,此刻等的是患者(原型 `.tg-wait`)。
所以主按钮不是「再发一次」,而是「查看患者那一页」;
再加一个演示用的「模拟患者完成签署」——
演示环境里没人真的去点短信链接,不给这个入口整条链路就走不完 -->
<template v-else>
<!-- 线上方式先给等待态:这条任务此刻等的是患者,不是操作人 -->
<span class="action-wait">{{ waitingHint(task) }}</span>
<button
v-if="allowLocalSigning"
@@ -161,31 +223,40 @@ defineExpose({ getDocumentElement })
class="action-button"
@click="emit('action', 'online-sign')"
>
查看{{ readSigningMethod(task.method).short }}签署
{{ viewPatientPageLabel(task.method) }}
</button>
<span v-else class="action-hint">线上签署页面接口待接入</span>
<button
v-if="allowLocalSigning"
type="button"
class="action-button action-button--ghost"
@click="emit('action', 'simulate-patient-sign')"
>
✓ 模拟患者完成签署(演示)
</button>
<button
v-if="canDeliveryWrite"
type="button"
class="action-button action-button--ghost"
@click="emit('action', task.method === 'wx' ? 'resend-wx-push' : 'resend-sms')"
>
{{ task.method === 'wx' ? '重发推送' : '重发短信' }}
</button>
</template>
<!-- 其余三种方式:一键转换(原型四种方式互为切换入口) -->
<!-- 其余三种方式:**直接打开各自的流程**(原型四个按钮互为入口,不是「只切方式」)。
短信按钮先弹「发送签署短信」、公众号按钮先弹「消息推送」,
发送/推送成功后才把任务切成那个方式 —— 与原型 openSmsFlow / openWxPush 一致;
Pad 按钮 = 切换方式 + 把文书滚到签名位(原型 switchToIpad) -->
<button
v-for="target in alternateMethods(task.method)"
:key="target"
type="button"
class="action-button action-button--ghost"
@click="emit('action', 'switch-method', target)"
@click="emit('action', entryActionFor(target))"
>
{{ readSigningMethod(target).short }}签署
{{ readSigningMethod(target).buttonLabel }}
</button>
<button
v-if="canDeliveryWrite && task.method === 'sms'"
type="button"
class="action-button action-button--ghost"
@click="emit('action', 'resend-sms')"
>
重发短信
</button>
<span v-else-if="task.method === 'wx'" class="action-hint">公众号推送接口待接入</span>
</template>
<!-- 待科室确认:患者已签完,此刻要动的是科室那一道章(原型 confirmDept 的动作组)。