Vue3+Typescript合并多个pdf并预览打印
作者:芭拉拉小魔仙
这篇文章主要为大家详细介绍了Vue3如何结合Typescript实现合并多个pdf并预览打印,同时可以兼容低版本浏览器,感兴趣的小伙伴可以跟随小编一起学习一下
一、打印pdf组件
<template> <div> <slot :is-loading="isLoading" ><el-button type="primary">批量打印</el-button> </slot> <el-dialog v-model="pdfVisible" title="PDF预览" width="90%" top="5vh" destroy-on-close > <div v-loading="pdfLoading" class="pdf-container"> <iframe ref="pdfIframe" :src="pdfViewerUrl" class="pdf-viewer"></iframe> </div> <template #footer> <el-button @click="pdfVisible = false">关闭</el-button> <!-- <el-button type="primary" @click="handlePrint">立即打印</el-button> --> </template> </el-dialog> </div> </template> <script setup lang="ts"> defineOptions({ name: "PdfMergePrinter", }); import { ref, onMounted, watch } from "vue"; import { PDFDocument } from "pdf-lib"; import { ElMessage } from "element-plus"; const emit = defineEmits(["printSuccess", "closeLoading"]); const pdfVisible = ref(false); const pdfViewerUrl = ref(""); const pdfLoading = ref(false); const pdfIframe = ref<HTMLIFrameElement>(); const isLoading = ref(false); const abortController = ref<AbortController | null>(null); // PDF合并处理 const mergePDFs = async (urls: string[]) => { const mergedPdf = await PDFDocument.create(); for (const url of urls) { try { const response = await fetch(url, { signal: abortController.value?.signal, }); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); const arrayBuffer = await response.arrayBuffer(); const pdf = await PDFDocument.load(arrayBuffer); const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices()); copiedPages.forEach((page) => mergedPdf.addPage(page)); } catch (error) { throw new Error( `合并PDF失败: ${error instanceof Error ? error.message : String(error)}` ); } } return await mergedPdf.save(); }; const printWithDialog = (mergedPdf: Uint8Array) => { return new Promise<void>((resolve, reject) => { try { // 生成PDF URL const pdfBlob = new Blob([mergedPdf], { type: "application/pdf" }); pdfViewerUrl.value = URL.createObjectURL(pdfBlob); setTimeout(() => { pdfVisible.value = true; }, 500); // 显示弹窗 } catch (error) { reject(error); } }); }; const handlePrint = () => { const iframeWindow = pdfIframe.value?.contentWindow as any; if (iframeWindow?.PDFViewerApplication) { iframeWindow.PDFViewerApplication.triggerPrint(); } else { window.print(); } }; // 在关闭时释放资源 watch(pdfVisible, (val) => { console.log(val); if (!val && pdfViewerUrl.value) { emit("closeLoading"); emit("printSuccess"); URL.revokeObjectURL(pdfViewerUrl.value); } }); // 主打印流程 const startPrint = async (pdfUrls: any) => { try { isLoading.value = true; abortController.value = new AbortController(); // 合并PDF const mergedPdf = await mergePDFs(pdfUrls); await printWithDialog(mergedPdf); } catch (error) { ElMessage.error(error instanceof Error ? error.message : "未知错误"); abortController.value?.abort(); } finally { isLoading.value = false; abortController.value = null; console.log("打印:startPrint finally"); emit("closeLoading"); } }; defineExpose({ startPrint, }); </script>
二、 使用方法
<PdfMergePrinter ref="printPageRef" @printSuccess="printSuccess" @closeLoading="closePringLoading" > <!-- 自定义触发按钮 --> <template #default="{ isLoading }"> <el-button :loading="isLoading" type="warning" @click="printFn()" > 批量打印 </el-button> </template> </PdfMergePrinter> const printFn = useDebounceFn(() => print(), 300); const print = () => { //pdf的url数组 const pdfUrls = [ "https://this.is.pdfurl1.pdf", "https://this.is.pdfurl2.pdf" ] printPageRef.value.startPrint(pdfUrls); }; const printSuccess=()=>{ //预览弹窗关闭时调用 } const closePringLoading=()=>{ //关闭全局loading }
三、兼容浏览器遇到的各种坑
1.360会阻止非用户触发window.print()
最开始使用的方案是pdf-lib + printJs ,直接弹出打印窗口。但是在360浏览器中有时不会弹出打印窗口。经过排查发现pdf已经拼接并渲染完成,问题处在window.print()上。360有时会把非用户自发行为触发的打印行为当作广告屏蔽掉。
2. 360会阻止window.open
window.print()不行便尝试了window.open()。在新窗口中打开拼接pdf,让用户手动触发打印行为。你猜怎么着?window.open()也会被拦截。真的想对360说句谢谢。
最终决定的方案:弹出iframe弹窗,让用户触发打印。
到此这篇关于Vue3+Typescript合并多个pdf并预览打印的文章就介绍到这了,更多相关vue合并多个pdf内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!