128 lines
3.8 KiB
Vue
128 lines
3.8 KiB
Vue
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
|
|
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 submitting = ref(false)
|
|
const signatureCanvas = ref<SignatureCanvasExpose | null>(null)
|
|
|
|
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() {
|
|
const trimmedName = signerName.value.trim()
|
|
if (!trimmedName) {
|
|
errorMessage.value = '请输入实际签署人姓名。'
|
|
return
|
|
}
|
|
if (!relation.value) {
|
|
errorMessage.value = '请选择签署人与患者的关系。'
|
|
return
|
|
}
|
|
if (signatureCanvas.value?.isEmpty()) {
|
|
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')
|
|
} catch (error) {
|
|
errorMessage.value = error instanceof Error ? error.message : '签署提交失败,请稍后重试。'
|
|
} 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')
|
|
}
|
|
</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-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>
|
|
|
|
<SignatureCanvas ref="signatureCanvas" @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>
|