vue3: pdf.js5.2.133 using typescript
npm install pdfjs-dist@5.2.133
<!-- * @creater: geovindu * @since: 2025-05-09 21:56:20 * @LastAuthor: geovindu * @lastTime: 2025-05-09 22:12:17 * @文件相对于项目的路径: \jsstudy\vuepdfpreview\comonents\pdfjs.vue * @message: geovindu * @IDE: vscode * @Development: node.js 20, vuejs3.0 * @package: * @ISO: windows10 * @database: mysql 8.0 sql server 2019 postgresSQL 16 * Copyright (c) 2025 by geovindu email: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"> <i class="fa fa-search-plus mr-1"></i> 放大 </button> <button @click="zoomOut" class="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600"> <i class="fa fa-search-minus mr-1"></i> 缩小 </button> <button @click="downloadPDF" class="px-3 py-1 bg-green-500 text-white rounded hover:bg-green-600"> <i class="fa fa-download mr-1"></i> 下载文档 </button> </div> <div class="text-center"> 缩放比例: {{ Math.round(scale * 100) }}% </div> </div> <!-- PDF 容器 --> <div id="pdf-container" class="w-full h-[600px] border border-gray-300 overflow-auto"> <canvas ref="pdfCanvas"></canvas> </div> <!-- 页码控制 --> <div class="mt-4 text-center"> <button @click="firstPage" :disabled="currentPage <= 1" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300 mx-1"> 第一页 </button> <button @click="prevPage" :disabled="currentPage <= 1" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300 mx-1"> 上一页 </button> <span class="mx-3">第 {{ currentPage }} / {{ totalPages }} 页</span> <button @click="nextPage" :disabled="currentPage >= totalPages" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300 mx-1"> 下一页 </button> <button @click="lastPage" :disabled="currentPage >= totalPages" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300 mx-1"> 最后一页 </button> </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.mjs'; // 动态解析worker路径,确保使用.mjs文件 pdfjsLib.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/build/pdf.worker.mjs', import.meta.url ).toString(); 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 firstPage = () => { if (currentPage.value !== 1) { renderPage(1); } }; const lastPage = () => { if (currentPage.value !== totalPages.value) { renderPage(totalPages.value); } }; const zoomIn = () => { scale.value = Math.min(scale.value + 0.1, 3.0); // 最大缩放 300% renderPage(currentPage.value); }; const zoomOut = () => { scale.value = Math.max(scale.value - 0.1, 0.5); // 最小缩放 50% renderPage(currentPage.value); }; const downloadPDF = () => { try { const link = document.createElement('a'); link.href = props.pdfUrl; link.download = props.pdfUrl.split('/').pop() || 'document.pdf'; link.click(); } catch (e) { console.error('下载失败:', e); error.value = '下载失败,请尝试右键另存为'; window.open(props.pdfUrl, '_blank'); } }; onMounted(async () => { try { // 加载 PDF const 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>
<!-- * @creater: geovindu * @since: 2025-05-09 21:56:20 * @LastAuthor: geovindu * @lastTime: 2025-05-09 22:12:17 * @文件相对于项目的路径: \jsstudy\vuepdfpreview\src\App.vue * @message: geovindu * @IDE: vscode * @Development: node.js 20, vuejs3.0 * @package: * @ISO: windows10 * @database: mysql 8.0 sql server 2019 postgresSQL 16 * Copyright (c) 2025 by geovindu email:geovindu@163.com, All Rights Reserved. --> <template> <div class="pdf-container"> <PDFView :pdfUrl="pdfUrl" v-if="pdfUrl" /> <div v-else >加载中...</div> </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" /> </template> <script setup lang="ts"> import HelloWorld from './components/HelloWorld.vue' //import PDFView from './components/vuepdfjs.vue' // 可以 //import PDFView from './components/pdfPreview.vue' // 可以 import PDFView from "./components/pdfjs.vue" //可以 //import pdfUrl from "./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>
哲学管理(学)人生, 文学艺术生活, 自动(计算机学)物理(学)工作, 生物(学)化学逆境, 历史(学)测绘(学)时间, 经济(学)数学金钱(理财), 心理(学)医学情绪, 诗词美容情感, 美学建筑(学)家园, 解构建构(分析)整合学习, 智商情商(IQ、EQ)运筹(学)生存.---Geovin Du(涂聚文)