feat(signing): 前端生成签署后 PDF 原型
This commit is contained in:
@@ -164,6 +164,13 @@ onMounted(() => {
|
||||
applyStoredValues()
|
||||
syncFromDom()
|
||||
})
|
||||
|
||||
/** 供「导出 PDF」使用:拿到已渲染的文书容器(含固定页眉与签署态取值) */
|
||||
function getDocumentElement() {
|
||||
return containerRef.value
|
||||
}
|
||||
|
||||
defineExpose({ getDocumentElement })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
import type {
|
||||
SigningAnswers,
|
||||
SigningArtifact,
|
||||
@@ -33,6 +35,15 @@ const emit = defineEmits<{
|
||||
selectionChange: [items: string[]]
|
||||
fieldChange: [answers: SigningAnswers, progress: SigningFieldProgress]
|
||||
}>()
|
||||
|
||||
const documentRef = ref<InstanceType<typeof SigningDocumentInteractive> | null>(null)
|
||||
|
||||
/** 供父级「导出 PDF」使用:转发到实际渲染文书的那一层 */
|
||||
function getDocumentElement() {
|
||||
return documentRef.value?.getDocumentElement() ?? null
|
||||
}
|
||||
|
||||
defineExpose({ getDocumentElement })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -150,6 +161,7 @@ const emit = defineEmits<{
|
||||
<!-- 任务创建时固化了文书 HTML 的走交互化渲染;老任务回退到静态纸样预览 -->
|
||||
<SigningDocumentInteractive
|
||||
v-if="task.documentContentHtml"
|
||||
ref="documentRef"
|
||||
:task="task"
|
||||
:interactive="allowLocalSigning && task.status !== 'signed' && task.status !== 'void'"
|
||||
@selection-change="emit('selectionChange', $event)"
|
||||
|
||||
@@ -34,6 +34,7 @@ import type {
|
||||
import NewSigningTaskDialog from '@/components/signing/NewSigningTaskDialog.vue'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import type { SigningFieldProgress } from '@/utils/signing-document'
|
||||
import { downloadSigningPdf } from '@/utils/signing-pdf'
|
||||
|
||||
import OnlineSigningDialog from './components/OnlineSigningDialog.vue'
|
||||
import SignaturePadDialog from './components/SignaturePadDialog.vue'
|
||||
@@ -106,6 +107,8 @@ const newDialogVisible = ref(false)
|
||||
const signatureDialogVisible = ref(false)
|
||||
const onlineDialogVisible = ref(false)
|
||||
const voidDialogVisible = ref(false)
|
||||
const taskDetailRef = ref<InstanceType<typeof SigningTaskDetail> | null>(null)
|
||||
const pdfExporting = ref(false)
|
||||
|
||||
let listRequestId = 0
|
||||
let detailRequestId = 0
|
||||
@@ -467,7 +470,7 @@ async function handleTaskAction(action: SigningTaskAction) {
|
||||
if (artifact) {
|
||||
await downloadArtifact(artifact)
|
||||
} else if (isSigningMockEnabled) {
|
||||
ElMessage.info('原型环境不生成 PDF,可在下方「签署文件」中下载签名原图')
|
||||
await exportSignedPdf()
|
||||
} else {
|
||||
ElMessage.info('当前任务暂无签署后 PDF')
|
||||
}
|
||||
@@ -530,6 +533,36 @@ async function handleTaskAction(action: SigningTaskAction) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 原型阶段:后端没有签署后 PDF 时,前端按当前渲染结果现场生成一份。
|
||||
* 真实实现里 PDF 由服务端生成(正式文书以服务端产物为准),
|
||||
* 这里只是让前端能先看到成品效果,因此仅在演示模式下兜底。
|
||||
*/
|
||||
async function exportSignedPdf() {
|
||||
const task = selectedTask.value
|
||||
const element = taskDetailRef.value?.getDocumentElement()
|
||||
|
||||
if (!task || !element) {
|
||||
ElMessage.info('该任务没有固化的文书正文,无法导出 PDF')
|
||||
return
|
||||
}
|
||||
|
||||
if (pdfExporting.value) {
|
||||
return
|
||||
}
|
||||
|
||||
pdfExporting.value = true
|
||||
|
||||
try {
|
||||
await downloadSigningPdf({ element, fileName: `${task.documentName}-${task.id}` })
|
||||
ElMessage.success('签署后 PDF 已生成')
|
||||
} catch {
|
||||
ElMessage.error('PDF 生成失败,请稍后重试')
|
||||
} finally {
|
||||
pdfExporting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadArtifact(artifact: SigningArtifact) {
|
||||
try {
|
||||
const blob = await downloadSigningArtifact(artifact.id)
|
||||
@@ -691,6 +724,7 @@ onMounted(() => {
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
<SigningTaskDetail
|
||||
ref="taskDetailRef"
|
||||
:task="selectedTask"
|
||||
:loading="detailLoading"
|
||||
:audit-events="taskEvents"
|
||||
|
||||
Reference in New Issue
Block a user