157 lines
4.3 KiB
Vue
157 lines
4.3 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* 签署页。
|
||
*
|
||
* 按业务口径,**患者只签自己的名**:文书里的选项、医师签名都由医生在医护端填好,
|
||
* 因此这一页去掉了原来的「签署人姓名 + 与患者关系」填写 —— 那两项现在由医生填,
|
||
* 这里只做只读展示。
|
||
*
|
||
* 签名仍走 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'
|
||
|
||
const router = useRouter()
|
||
const signatureDataUrl = ref('')
|
||
const errorMessage = ref('')
|
||
const submitting = ref(false)
|
||
|
||
/** 签署人由医生在医护端填好,这里只展示 */
|
||
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(',')
|
||
const mimeType = header?.match(/data:(.*?);base64/)?.[1] ?? 'image/png'
|
||
const binary = atob(encoded ?? '')
|
||
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0))
|
||
|
||
return new Blob([bytes], { type: mimeType })
|
||
}
|
||
|
||
async function submitSigning() {
|
||
if (!signatureDataUrl.value) {
|
||
errorMessage.value = '请完成手写签名。'
|
||
return
|
||
}
|
||
|
||
if (signingState.mode === 'real') {
|
||
submitting.value = true
|
||
errorMessage.value = ''
|
||
|
||
try {
|
||
await submitRealSignature({
|
||
file: dataUrlToBlob(signatureDataUrl.value),
|
||
consentAccepted: signingState.consentAccepted,
|
||
})
|
||
await router.push('/result')
|
||
} catch (error) {
|
||
errorMessage.value = error instanceof Error ? error.message : '签署提交失败,请稍后重试。'
|
||
} finally {
|
||
submitting.value = false
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
signingState.signatureCaptured = true
|
||
signingState.submittedAt = new Date().toLocaleString('zh-CN')
|
||
void router.push('/result')
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<main class="mobile-shell">
|
||
<div class="mobile-stepper">
|
||
<span>1 阅读</span>
|
||
<i />
|
||
<span class="is-active">2 确认</span>
|
||
<i />
|
||
<span>3 完成</span>
|
||
</div>
|
||
|
||
<section class="mobile-heading">
|
||
<p class="eyebrow">签署确认</p>
|
||
<h1>请在文书签名处签名</h1>
|
||
<p>文书内容已由医务人员填写完成,您只需完成患者签名。</p>
|
||
</section>
|
||
|
||
<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>
|
||
|
||
<!-- 签完的字会实时回填到文书里的签名位,患者能立刻看到自己签在哪一格 -->
|
||
<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
|
||
class="mobile-primary-button"
|
||
type="button"
|
||
:disabled="submitting"
|
||
@click="submitSigning"
|
||
>
|
||
{{ submitting ? '提交中…' : '确认提交签署' }}
|
||
</button>
|
||
<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>
|