feat(患者签署): 接入签署上下文接口并补齐文书渲染与手写签名链路

This commit is contained in:
yelan
2026-09-28 10:38:22 +08:00
parent d711e03a4e
commit 28c45dc7b2
13 changed files with 1126 additions and 277 deletions
+80 -51
View File
@@ -1,23 +1,33 @@
<script setup lang="ts">
import { ref } from 'vue'
/**
* 签署页。
*
* 按业务口径,**患者只签自己的名**:文书里的选项、医师签名都由医生在医护端填好,
* 因此这一页去掉了原来的「签署人姓名 + 与患者关系」填写 —— 那两项现在由医生填,
* 这里只做只读展示。
*
* 签名仍走 Canvas(`SignatureCanvas`)。将来接真实手写板设备时,换的是采集层
* (设备适配器),这一页的交互不用动。
*/
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import DocumentPaper from '@/components/DocumentPaper.vue'
import SignatureCanvas from '@/components/SignatureCanvas.vue'
import { signingState, submitRealSignature } from '@/composables/useSignFlow'
import { relationOptions, type SignerRelation } from '@/types/signing'
interface SignatureCanvasExpose {
clearSignature: () => void
isEmpty: () => boolean
}
const router = useRouter()
const signerName = ref('')
const relation = ref<SignerRelation | ''>('')
const errorMessage = ref('')
const signatureDataUrl = ref('')
const errorMessage = ref('')
const submitting = ref(false)
const signatureCanvas = ref<SignatureCanvasExpose | null>(null)
/** 签署人由医生在医护端填好,这里只展示 */
const signerLine = computed(() => {
const name = signingState.signerName || signingState.patientName
const relation = signingState.relation || '本人'
return `${name}(${relation})`
})
function dataUrlToBlob(dataUrl: string) {
const [header, encoded] = dataUrl.split(',')
@@ -29,40 +39,18 @@ function dataUrlToBlob(dataUrl: string) {
}
async function submitSigning() {
const trimmedName = signerName.value.trim()
if (!trimmedName) {
errorMessage.value = '请输入实际签署人姓名。'
return
}
if (!relation.value) {
errorMessage.value = '请选择签署人与患者的关系。'
return
}
if (signatureCanvas.value?.isEmpty()) {
if (!signatureDataUrl.value) {
errorMessage.value = '请完成手写签名。'
return
}
const selectedRelation = relation.value
if (!selectedRelation) {
return
}
if (signingState.mode === 'real') {
if (!signatureDataUrl.value) {
errorMessage.value = '未读取到签名图片,请重新签名。'
return
}
submitting.value = true
errorMessage.value = ''
try {
await submitRealSignature({
file: dataUrlToBlob(signatureDataUrl.value),
signerName: trimmedName,
relation: selectedRelation,
consentAccepted: signingState.consentAccepted,
})
await router.push('/result')
@@ -71,11 +59,10 @@ async function submitSigning() {
} finally {
submitting.value = false
}
return
}
signingState.signerName = trimmedName
signingState.relation = selectedRelation
signingState.signatureCaptured = true
signingState.submittedAt = new Date().toLocaleString('zh-CN')
void router.push('/result')
@@ -93,25 +80,38 @@ async function submitSigning() {
</div>
<section class="mobile-heading">
<p class="eyebrow">签署人确认</p>
<h1>请确认实际签署人</h1>
<p>签署人信息将与本次知情同意记录一并保存。</p>
<p class="eyebrow">签署确认</p>
<h1>请在文书签名处签名</h1>
<p>文书内容已由医务人员填写完成,您只需完成患者签名。</p>
</section>
<section class="mobile-card signer-form">
<label class="field-label" for="signer-name">签署人姓名</label>
<input id="signer-name" v-model="signerName" class="mobile-input" placeholder="请输入姓名" />
<label class="field-label" for="signer-relation">与患者关系</label>
<select id="signer-relation" v-model="relation" class="mobile-input">
<option value="" disabled>请选择关系</option>
<option v-for="option in relationOptions" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
<section class="mobile-card signer-summary">
<div>
<span>患者</span>
<strong>{{ signingState.patientName }}</strong>
</div>
<div>
<span>签署人</span>
<strong>{{ signerLine }}</strong>
</div>
<div>
<span>文书</span>
<strong>{{ signingState.documentTitle }} · {{ signingState.templateVersion }}</strong>
</div>
</section>
<SignatureCanvas ref="signatureCanvas" @update:data-url="signatureDataUrl = $event" />
<!-- 签完的字会实时回填到文书里的签名位,患者能立刻看到自己签在哪一格 -->
<DocumentPaper
:content-html="signingState.documentContentHtml"
:answers="signingState.staffAnswers"
:patient-signature="signatureDataUrl || null"
/>
<SignatureCanvas
:height="200"
placeholder="请在此签名"
@update:data-url="signatureDataUrl = $event ?? ''"
/>
<p v-if="errorMessage" class="form-error">{{ errorMessage }}</p>
<button
@@ -125,3 +125,32 @@ async function submitSigning() {
<button class="mobile-text-button" type="button" @click="router.back()">返回修改</button>
</main>
</template>
<style scoped>
.signer-summary {
display: flex;
flex-direction: column;
gap: 10px;
padding: 18px 20px;
margin-bottom: 14px;
}
.signer-summary div {
display: flex;
gap: 10px;
align-items: baseline;
justify-content: space-between;
}
.signer-summary span {
flex-shrink: 0;
color: #8aa0aa;
font-size: 12px;
}
.signer-summary strong {
color: #173b56;
font-size: 14px;
text-align: right;
}
</style>