npm create vite vuepdfpreview //创建项目npm install vue-pdf-embed
npm install vue3-pdfjs
npm install pdfjs-dist@2.16.105
<!--* |~~~~~~~|* | |* | |* | |* | |* | |* |~.\\\_\~~~~~~~~~~~~~~xx~~~ ~~~~~~~~~~~~~~~~~~~~~/_//;~|* | \ o \_ ,XXXXX), _..-~ o / |* | ~~\ ~-. XXXXX`)))), _.--~~ .-~~~ |* ~~~~~~~`\ ~\~~~XXX' _/ ';)) |~~~~~~..-~ _.-~ ~~~~~~~* `\ ~~--`_\~\, ;;;\)__.---.~~~ _.-~* ~-. `:;;/;; \ _..-~~* ~-._ `'' /-~-~* `\ / /* | , | |* | ' / |* \/; |* ;; |* `; . |* |~~~-----.....|* | \ \* | /\~~--...__ |* (| `\ __-\|* || \_ /~ |* |) \~-' |* | | \ '* | | \ :* \ | | |* | ) ( )* \ /; /\ |* | |/ |* | | |* \ .' ||* | | | |* ( | | |* | \ \ |* || o `.)|* |`\\) |* | |* | |** @Author: geovindu* @Date: 2025-05-08 20:54:24* @LastEditors: geovindu* @LastEditTime: 2025-05-08 22:22:28* @FilePath: \vue\vuepdfpreview\src\App.vue* @Description: geovindu* @lib,packpage:* npm install vue-pdf-embed* npm install vue3-pdfjs* npm install pdfjs-dist@2.16.105* @IDE: vscode* @jslib: node 20 vue.js 3.0* @OS: windows10* @database: mysql 8.0 sql server 2019 postgreSQL 16* Copyright (c) geovindu 2025 by geovindu@163.com, All Rights Reserved.--><template>
<div class="pdf-container"><PDFView :pdfUrl="pdfUrl" v-if="pdfUrl" /><div v-else >加载中...</div><div><a href="https://vite.dev" target="_blank"><img src="/vite.svg" class="logo" alt="Vite logo" /></a><a href="https://vuejs.org/" target="_blank"><img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /></a></div><HelloWorld msg="Vite + Vue" /></div></template><script setup lang="ts">import HelloWorld from './components/HelloWorld.vue'//import PDFView from "./components/pdfPreview.vue" // 可以import PDFView from "./components/pdfjs.vue" //可以//import jsPdf from "http://localhost:5173/pdfs/01.pdf"const pdfUrl = "./pdfs/09.pdf"</script><style scoped>
.logo {height: 6em;padding: 1.5em;will-change: filter;transition: filter 300ms;
}
.logo:hover {filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
<!--* ::* :;J7, :, ::;7:* ,ivYi, , ;LLLFS:* :iv7Yi :7ri;j5PL* ,:ivYLvr ,ivrrirrY2X,* :;r@Wwz.7r: :ivu@kexianli.* :iL7::,:::iiirii:ii;::::,,irvF7rvvLujL7ur* ri::,:,::i:iiiiiii:i:irrv177JX7rYXqZEkvv17* ;i:, , ::::iirrririi:i:::iiir2XXvii;L8OGJr71i* :,, ,,: ,::ir@mingyi.irii:i:::j1jri7ZBOS7ivv,* ,::, ::rv77iiiriii:iii:i::,rvLq@huhao.Li* ,, ,, ,:ir7ir::,:::i;ir:::i:i::rSGGYri712:* ::: ,v7r:: ::rrv77:, ,, ,:i7rrii:::::, ir7ri7Lri* , 2OBBOi,iiir;r:: ,irriiii::,, ,iv7Luur:* ,, i78MBBi,:,:::,:, :7FSL: ,iriii:::i::,,:rLqXv::* : iuMMP: :,:::,:ii;2GY7OBB0viiii:i:iii:i:::iJqL;::* , ::::i ,,,,, ::LuBBu BBBBBErii:i:i:i:i:i:i:r77ii* , : , ,,:::rruBZ1MBBqi, :,,,:::,::::::iiriri:* , ,,,,::::i: @arqiao. ,:,, ,:::ii;i7:* :, rjujLYLi ,,:::::,:::::::::,, ,:i,:,,,,,::i:iii* :: BBBBBBBBB0, ,,::: , ,:::::: , ,,,, ,,:::::::* i, , ,8BMMBBBBBBi ,,:,, ,,, , , , , , :,::ii::i::* : iZMOMOMBBM2::::::::::,,,, ,,,,,,:,,,::::i:irr:i:::,* i ,,:;u0MBMOG1L:::i:::::: ,,,::, ,,, ::::::i:i:iirii:i:i:* : ,iuUuuXUkFu7i:iii:i:::, :,:,: ::::::::i:i:::::iirr7iiri::* : :rk@Yizero.i:::::, ,:ii:::::::i:::::i::,::::iirrriiiri::,* : 5BMBBBBBBSr:,::rv2kuii:::iii::,:i:,, , ,,:,:i@petermu.,* , :r50EZ8MBBBBGOBBBZP7::::i::,:::::,: :,:,::i;rrririiii::* :jujYY7LS0ujJL7r::,::i::,::::::::::::::iirirrrrrrr:ii:* ,: :@kevensun.:,:,,,::::i:i:::::,,::::::iir;ii;7v77;ii;i,* ,,, ,,:,::::::i:iiiii:i::::,, ::::iiiir@xingjief.r;7:i,* , , ,,,:,,::::::::iiiiiiiiii:,:,:::::::::iiir;ri7vL77rrirri::* :,, , ::::::::i:::i:::i:i::,,,,,:,::i:i:::iir;@Secbone.ii:::** @Author: geovindu* @Date: 2025-05-08 21:00:52* @LastEditors: geovindu* @LastEditTime: 2025-05-08 22:47:03* @FilePath: \vue\vuepdfpreview\src\components\pdfjs.vue* @Description: geovindu* @lib,packpage:** @IDE: vscode* @jslib: node 20 vue.js 3.0* @OS: windows10* @database: mysql 8.0 sql server 2019 postgreSQL 16* Copyright (c) geovindu 2025 by geovindu@163.com, All Rights Reserved.-->
<template><div class="pdf-container"><div v-if="loading" class="text-center py-8">加载中...</div><div v-else-if="error" class="text-center py-8 text-red-500">{{ error }}</div><div v-else><div class="flex justify-between items-center mb-4"><div class="flex space-x-2"><button @click="zoomIn" class="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600">放大</button><button @click="zoomOut" class="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600">缩小</button><button @click="downloadPDF" class="px-3 py-1 bg-green-500 text-white rounded hover:bg-green-600">下载文档</button></div><div class="text-center">第 {{ currentPage }} / {{ totalPages }} 页<button @click="prevPage" :disabled="currentPage <= 1" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300">上一页</button><button @click="nextPage" :disabled="currentPage >= totalPages" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300">下一页</button></div></div><div id="pdf-container" class="w-full h-[600px] border border-gray-300 overflow-auto"><canvas ref="pdfCanvas"></canvas></div></div></div></template><script setup>import { ref, onMounted, reactive } from 'vue';import * as pdfjsLib from 'pdfjs-dist';// 设置 worker 路径pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs/pdf.worker.js';const props = defineProps({pdfUrl: { type: String, required: true }});const pdfCanvas = ref(null);const loading = ref(true);const error = ref('');const currentPage = ref(1);const totalPages = ref(0);const scale = ref(1.0);let pdfDoc = null;const renderPage = async (num) => {try {const page = await pdfDoc.getPage(num);const viewport = page.getViewport({ scale: scale.value });// 设置 canvas 尺寸pdfCanvas.value.width = viewport.width;pdfCanvas.value.height = viewport.height;// 渲染页面const renderContext = {canvasContext: pdfCanvas.value.getContext('2d'),viewport: viewport};await page.render(renderContext).promise;currentPage.value = num;} catch (err) {console.error('渲染页面失败:', err);error.value = `渲染失败: ${err.message}`;}};const prevPage = () => {if (currentPage.value > 1) {renderPage(currentPage.value - 1);}};const nextPage = () => {if (currentPage.value < totalPages.value) {renderPage(currentPage.value + 1);}};const zoomIn = () => {scale.value += 0.1;renderPage(currentPage.value);};const zoomOut = () => {if (scale.value > 0.2) {scale.value -= 0.1;renderPage(currentPage.value);}};const downloadPDF = () => {const link = document.createElement('a');link.href = props.pdfUrl;link.download = props.pdfUrl.split('/').pop();link.click();};onMounted(async () => {try {// 加载 PDFconst loadingTask = pdfjsLib.getDocument(props.pdfUrl);pdfDoc = await loadingTask.promise;totalPages.value = pdfDoc.numPages;// 渲染第一页renderPage(1);loading.value = false;} catch (err) {console.error('加载 PDF 失败:', err);error.value = `加载失败: ${err.message}`;loading.value = false;}});</script><style scoped>.pdf-container {max-width: 1000px;margin: 0 auto;}#pdf-container canvas {max-width: 100%;display: block;margin: 0 auto;}</style>
onMounted(async () => {try {// 假设props.pdfUrl现在是Base64编码的PDF字符串// 移除可能存在的Data URI前缀(如"data:application/pdf;base64,")const base64Data = props.pdfUrl.replace(/^data:application\/pdf;base64,/, '');// 将Base64字符串转换为Uint8Arrayconst binaryData = atob(base64Data);const arrayBuffer = new ArrayBuffer(binaryData.length);const uint8Array = new Uint8Array(arrayBuffer);for (let i = 0; i < binaryData.length; i++) {uint8Array[i] = binaryData.charCodeAt(i);}// 使用Uint8Array加载PDFconst loadingTask = pdfjsLib.getDocument({ data: uint8Array });pdfDoc = await loadingTask.promise;totalPages.value = pdfDoc.numPages;// 渲染第一页renderPage(1);loading.value = false;} catch (err) {console.error('加载PDF失败:', err);error.value = `加载失败: ${err.message}`;loading.value = false;}
});



