注意下載的插件的版本"pdfjs-dist": "^2.2.228",
npm i pdfjs-dist@2.2.228
然后封裝一個pdf的遮罩。因為pdf文件有多頁,所以我用了swiper輪播的形式展示。因為用到移動端,手動滑動頁面這樣比點下一頁下一頁的方便多了。
直接貼代碼了
PdfPreview/index.vue
<!--預覽pdf文件的組件--> <template><van-overlay :show="show" @click="close()"><div class="pdf-viewer" ><van-swipe class="my-swipe" indicator-color="red" @click.stop><van-swipe-item v-for="item in pageNum" :key="item"><canvas :id="`pdf-canvas-${item}`" class="pdf-page"/></van-swipe-item><template #indicator="{ active, total }"><div class="custom-indicator">{{ active + 1 }}/{{ total }}</div></template></van-swipe><van-emptyv-if="loadError"image="error"description="PDF加載出錯了..."/></div><van-icon name="close" color="#fff" size="0.3rem"/></van-overlay></template><script setup lang="tsx"> import {ref, nextTick, watch} from 'vue'; import {closeToast, showLoadingToast, showSuccessToast} from "vant";// 引入pdf預覽插件相關的參數,注意這塊開始試了很多網上方法都不好用 import * as pdfjs from 'pdfjs-dist'; import pdfjsWorker from 'pdfjs-dist/build/pdf.worker?url'; // 設置 worker 路徑 pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;const show = ref(true); // html部分涉及的參數 const loadError = ref(false); const detail = ref({}); let pdfDoc = null; // 一定不能使用響應式的數據,會報錯Cannot read from private field---pdf.js const pageNum = ref(0);const props = defineProps({pdfUrl: {type: String,default: ""}, }) const emit= defineEmits(['close'])watch(() => props.pdfUrl, (newVal) => {// console.log("監聽", newVal, props.pdfUrl)showLoadingToast('加載中');nextTick(() => {loadingPdf(props.pdfUrl); })}, {immediate: true,deep:true})// 防抖 debounce 函數的實現正確。 const debounce(func, wait, options = {}) {let timeout;const { leading = false, trailing = true } = options;return function(...args) {const later = () => {timeout = null;if (!leading) func.apply(this, args);};const callNow = leading && !timeout;clearTimeout(timeout);timeout = setTimeout(later, wait);if (callNow) func.apply(this, args);}; }// 使用防抖函數,300ms內只執行一次,避免多次點擊立刻打開又關閉的情況 const close = debounce(() => {show.value = false;emit('close') }, 300, { leading: true, trailing: false });//加載pdf const loadingPdf = (url) => {const afterUrl = {url,httpHeaders: {token: `Bearer-${localStorage.getItem('token')}`,//微信小程序里面打開這個模塊,發現請求401,報錯信息是登陸訪問超時,發現pdfjs加載pdf時沒有攜帶token,于是在加載url時添加token即可},};const loadingTask = pdfjs.getDocument(afterUrl);loadingTask.promise.then((pdf) => {pdfDoc = pdf;pageNum.value = pdf.numPages; nextTick(() => {renderPage();});}).catch(() => {loadError.value = true;}); }// 渲染pdf const renderPage = (num = 1) => {pdfDoc.getPage(num).then((page) => {const canvas = document.getElementById(`pdf-canvas-${num}`); if(!canvas){return}const ctx = canvas.getContext('2d');const scale = 1.5;const viewport = page.getViewport({scale});// 畫布大小,默認值是width:300px,height:150pxcanvas.height = viewport.height;canvas.width = viewport.width;// 畫布的dom大小, 設置移動端,寬度設置鋪滿整個屏幕const {clientWidth} = document.body;// 減去2rem使用因為我的頁面左右加了paddingcanvas.style.width = `calc(${clientWidth}px - 2rem)`;// 根據pdf每頁的寬高比例設置canvas的高度canvas.style.height = `${clientWidth * (viewport.height / viewport.width)}px`;canvas.height = viewport.height;canvas.width = viewport.width;page.render({canvasContext: ctx,viewport,});//隱藏渲染所有的頁面if (num < pageNum.value) {renderPage(num + 1);} else {closeToast();}}); }</script><style scoped> .pdf-viewer{display: flex;justify-content: center;align-items: center;height:100vh;width:100vw;text-align: center; } .custom-indicator {position: absolute;left: 50%;bottom: 15px;transform: translateX(-50%);padding: 2px 5px;font-size: 18px;color: #fff;background: rgba(0, 0, 0, 0.1); } </style>
上傳的頁面先按照這個地址這篇文章寫好。稍微改動一下就可以了vant4+vue3封裝一個上傳公共組件.有上傳和刪除訪問接口的過程。限制上傳的格式和上傳文件大小-CSDN博客
然后給組件添加一個點擊預覽的事件?。并把上面寫好的預覽組件引入
import PdfPreview from "@/components/PdfPreview/index.vue";// 點擊預覽文件 const showPreview=(file)=>{if(file.absoluteUrl.endsWith('.pdf')){pdfUrl.value=file.absoluteUrl;preview.value=true;} }
遇到的問題:
如果報錯Uncaught (in promise) TypeError: Cannot read properties of null (reading 'getContext')
可能是canvas要找的那個id在頁面還沒有渲染出來。所以我用的nextTick,還在獲取canvas后面判斷了一下找到了再繼續 ,注意上面棕色加粗的地方。
如果報錯vue-router.mjs:3518SyntaxError: The requested module '/node_modules/.vite/deps/pdfjs-dist_build_pdf__worker__entry.js?v=8ae4d11f' does not provide an export named 'default'
檢查一下你引入插件的地方。如果是
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';這樣寫的就是錯的
改成
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker?url';
問題:如果你點擊第一次彈窗展示了,但是再點擊就沒有彈出。
原因是預覽的組件渲染是監聽的pdf的url的地址。如果你第一個打開沒有把組件銷毀。那么再次顯示的時候沒有走監聽。就不會顯示。所以要在每次關閉彈窗是組件也銷毀。這就是上面我要在子組件中用@close給組件通知讓他不顯示也就是銷毀子組件的原因。
問題:無意間雙擊了文件導致遮罩馬上顯示又隱藏。頁面效果就是黑色遮罩閃了一下。
可以使用防抖的方式。延遲關閉。參考上面紫色的關閉函數