通過vue-pdf和print-js實現PDF和圖片在線預覽

npm install vue-pdf
npm install print-js

<template><div><!-- PDF 預覽模態框 --><a-modal:visible="showDialog":footer="null"@cancel="handleCancel":width="800":maskClosable="true":keyboard="true"><div style="overflow-y: auto; overflow-x: hidden; height: 600px"><!-- 使用 sticky 定位打印按鈕 --><div style="position: sticky; top: 0; background: white; padding: 0px 0; z-index: 1;"><a-buttonshape="round"icon="file-pdf"@click="handlePrint"size="small"style="margin-bottom: 10px">打印</a-button></div><div id="printFrom"><pdfv-if="isPdf"ref="pdf"v-for="item in pageTotal":src="previewFileSrc":key="item":page="item"></pdf><imgv-else-if="isImage":src="previewImage"style="max-width: 100%; max-height: 500px; display: block; margin: 0 auto"/><div v-else style="text-align: center"><p>不支持預覽此文件類型</p><a :href="previewFileSrc" download>下載文件</a></div></div></div></a-modal></div>
</template><script>
import Vue from "vue";
import { ACCESS_TOKEN } from "@/store/mutation-types";
import { getFileAccessHttpUrl } from "@/api/manage";
import pdf from "vue-pdf";
import printJS from "print-js";const FILE_TYPE_ALL = "all";
const FILE_TYPE_IMG = "image";
const FILE_TYPE_TXT = "file";export default {name: "AutoFilePreview",components: { pdf },props: {// 是否顯示預覽showDialog: {type: Boolean,default: false,},},data() {return {fileUrl: null,printData: {printable: "printFrom",header: "",ignore: ["no-print"],},previewImage: "",previewFileSrc: "",pageTotal: null,isImage: false,isPdf: false,};},watch: {fileUrl: {immediate: true,handler(newVal) {if (newVal) {this.previewFileSrc = newVal;this.checkFileType();}},},showDialog: {immediate: true,handler(newVal) {if (newVal && this.fileUrl) {this.previewFileSrc = this.fileUrl;this.checkFileType();}},},},methods: {handlePrint() {printJS({printable: "printFrom",type: "html",header: "",targetStyles: ["*"],style: "@page {margin:0 10mm};",ignoreElements: ["no-print"],});this.test();},async checkFileType() {// 重置狀態this.isImage = false;this.isPdf = false;// 獲取文件類型const fileType = this.matchFileType(this.fileUrl);if (fileType === "image") {this.previewImage = this.fileUrl;this.isImage = true;this.pageTotal = 1;} else if (fileType === "pdf") {this.isPdf = true;await this.getTotal();} else {// 其他文件類型直接下載this.isImage = false;this.isPdf = false;}},async getTotal() {try {// 多頁pdf的src中不能直接使用后端獲取的pdf地址// 需要使用下述方法的返回值作為urlconst loadingTask = pdf.createLoadingTask(this.previewFileSrc);this.previewFileSrc = loadingTask;// 獲取頁碼const pdfDoc = await loadingTask.promise;this.pageTotal = pdfDoc.numPages;} catch (error) {console.error("PDF加載錯誤:", error);this.$message.error("PDF文件加載失敗");}},matchFileType(fileName) {// 后綴獲取let suffix = "";// 獲取類型結果let result = "";// 從URL中提取文件名const urlParts = fileName.split("/");const fullFileName = urlParts[urlParts.length - 1];try {// 截取文件后綴suffix = fullFileName.substr(fullFileName.lastIndexOf(".") + 1, fullFileName.length);// 文件后綴轉小寫,方便匹配suffix = suffix.toLowerCase();} catch (err) {suffix = "";}// fileName無后綴返回 falseif (!suffix) {result = false;return result;}const fileTypeList = [// 圖片類型{ typeName: "image", types: ["png", "jpg", "jpeg", "bmp", "gif"] },// 文本類型{ typeName: "txt", types: ["txt"] },// excel類型{ typeName: "excel", types: ["xls", "xlsx"] },{ typeName: "word", types: ["doc", "docx"] },{ typeName: "pdf", types: ["pdf"] },{ typeName: "ppt", types: ["ppt"] },// 視頻類型{ typeName: "video", types: ["mp4", "m2v", "mkv"] },// 音頻{ typeName: "radio", types: ["mp3", "wav", "wmv"] },];for (let i = 0; i < fileTypeList.length; i++) {const fileTypeItem = fileTypeList[i];const typeName = fileTypeItem.typeName;const types = fileTypeItem.types;result = types.some(function(item) {return item === suffix;});if (result) {return typeName;}}return "other";},handleCancel() {this.$emit("update:showDialog", false);},loadFileUrl(url) {this.fileUrl = url}},
};
</script><style lang="less" scoped>
/* 可以根據需要添加樣式 */
</style>

父組件調用

 <a-button:ghost="true"type="primary"icon="eye"size="small"@click="showFilePreview(text)">預覽</a-button>
<auto-file-preview ref="autoFilePreview" :show-dialog.sync="showPreview"></auto-file-preview>
showFilePreview(url){this.showPreview = truethis.$refs.autoFilePreview.loadFileUrl(this.getImgView(url))
}

show-dialog是否展示true,false

效果:
在這里插入圖片描述
在這里插入圖片描述

進階版,支持word,excel,音頻,視頻預覽,組件使用方式相同

先安裝所需依賴:
npm install xlsx mammoth

<template><div><a-modal:visible="showDialog":footer="null"@cancel="handleCancel":width="800":maskClosable="true":keyboard="true"><div style="overflow-y: auto; overflow-x: hidden; height: 600px"><div style="position: sticky; top: 0; background: white; padding: 10px 0; z-index: 1;"><a-buttonshape="round"icon="file-pdf"@click="handlePrint"size="small"style="margin-bottom: 10px">打印</a-button></div><div id="printFrom"><pdfv-if="isPdf"ref="pdf"v-for="item in pageTotal":src="previewFileSrc":key="item":page="item"></pdf><imgv-else-if="isImage":src="previewImage"style="max-width: 100%; max-height: 500px; display: block; margin: 0 auto"/><pre v-else-if="isText" style="white-space: pre-wrap; word-wrap: break-word;">{{ textContent }}</pre><table v-else-if="isExcel"><tr v-for="(row, index) in excelData" :key="index"><td v-for="(value, key) in row" :key="key">{{ value }}</td></tr></table><pre v-else-if="isWord" style="white-space: pre-wrap; word-wrap: break-word;">{{ wordContent }}</pre><videov-else-if="isVideo"controlsstyle="max-width: 100%; display: block; margin: 0 auto"><source :src="previewFileSrc" :type="getVideoType(previewFileSrc)" />您的瀏覽器不支持視頻標簽。</video><audiov-else-if="isAudio"controlsstyle="display: block; margin: 0 auto"><source :src="previewFileSrc" :type="getAudioType(previewFileSrc)" />您的瀏覽器不支持音頻標簽。</audio><div v-else-if="isUnsupported" style="text-align: center"><p>不支持預覽此文件類型</p><a :href="previewFileSrc" download>下載文件</a></div></div></div></a-modal></div>
</template><script>
import pdf from "vue-pdf";
import printJS from "print-js";
import * as XLSX from "xlsx";
import mammoth from "mammoth";export default {name: "AutoFilePreview",components: { pdf },props: {showDialog: {type: Boolean,default: false,},},data() {return {fileUrl: null,previewImage: "",previewFileSrc: "",pageTotal: null,isImage: false,isPdf: false,isText: false,isExcel: false,isWord: false,isVideo: false,isAudio: false,isUnsupported: false,textContent: "",excelData: [],wordContent: "",};},watch: {fileUrl: {immediate: true,handler(newVal) {if (newVal) {this.previewFileSrc = newVal;this.checkFileType();}},},showDialog: {immediate: true,handler(newVal) {if (newVal && this.fileUrl) {this.previewFileSrc = this.fileUrl;this.checkFileType();}},},},methods: {handlePrint() {printJS({printable: "printFrom",type: "html",header: "",targetStyles: ["*"],style: "@page {margin:0 10mm};",ignoreElements: ["no-print"],});},async checkFileType() {this.isImage = false;this.isPdf = false;this.isText = false;this.isExcel = false;this.isWord = false;this.isVideo = false;this.isAudio = false;this.isUnsupported = false;const fileType = this.matchFileType(this.fileUrl);if (fileType === "image") {this.previewImage = this.fileUrl;this.isImage = true;this.pageTotal = 1;} else if (fileType === "pdf") {this.isPdf = true;await this.getTotal();} else if (fileType === "txt") {await this.fetchTextFile();this.isText = true;} else if (fileType === "excel") {await this.fetchExcelFile();this.isExcel = true;} else if (fileType === "word") {await this.fetchWordFile();this.isWord = true;} else if (fileType === "video") {this.isVideo = true;} else if (fileType === "audio") {this.isAudio = true;} else {this.isUnsupported = true;}},async getTotal() {try {const loadingTask = pdf.createLoadingTask(this.previewFileSrc);const pdfDoc = await loadingTask.promise;this.pageTotal = pdfDoc.numPages;} catch (error) {console.error("PDF加載錯誤:", error);this.$message.error("PDF文件加載失敗");}},matchFileType(fileName) {let suffix = "";try {suffix = fileName.split(".").pop().toLowerCase();} catch (e) {suffix = "";}const fileTypeList = [{ typeName: "image", types: ["png", "jpg", "jpeg", "bmp", "gif"] },{ typeName: "pdf", types: ["pdf"] },{ typeName: "txt", types: ["txt"] },{ typeName: "excel", types: ["xls", "xlsx"] },{ typeName: "word", types: ["doc", "docx"] },{ typeName: "video", types: ["mp4", "webm", "ogg", "mov", "avi"] },{ typeName: "audio", types: ["mp3", "wav", "ogg", "aac"] },];for (const fileTypeItem of fileTypeList) {if (fileTypeItem.types.includes(suffix)) {return fileTypeItem.typeName;}}return "other";},async fetchTextFile() {try {const response = await fetch(this.fileUrl);this.textContent = await response.text();} catch (error) {console.error("加載文本文件失敗:", error);this.$message.error("文件加載失敗");}},async fetchExcelFile() {try {const response = await fetch(this.fileUrl);const arrayBuffer = await response.arrayBuffer();const workbook = XLSX.read(arrayBuffer, { type: "array" });const firstSheetName = workbook.SheetNames[0];const worksheet = workbook.Sheets[firstSheetName];this.excelData = XLSX.utils.sheet_to_json(worksheet);} catch (error) {console.error("加載 Excel 文件失敗:", error);this.$message.error("文件加載失敗");}},async fetchWordFile() {try {const response = await fetch(this.fileUrl);const arrayBuffer = await response.arrayBuffer();const result = await mammoth.extractRawText({ arrayBuffer });this.wordContent = result.value;} catch (error) {console.error("加載 Word 文件失敗:", error);this.$message.error("文件加載失敗");}},getVideoType(url) {let suffix = "";try {suffix = url.split(".").pop().toLowerCase();} catch (e) {suffix = "";}const videoTypes = {mp4: "video/mp4",webm: "video/webm",ogg: "video/ogg",mov: "video/quicktime",avi: "video/x-msvideo",};return videoTypes[suffix] || "video/mp4";},getAudioType(url) {let suffix = "";try {suffix = url.split(".").pop().toLowerCase();} catch (e) {suffix = "";}const audioTypes = {mp3: "audio/mpeg",wav: "audio/wav",ogg: "audio/ogg",aac: "audio/aac",};return audioTypes[suffix] || "audio/mpeg";},handleCancel() {this.$emit("update:showDialog", false);},loadFileUrl(url) {this.fileUrl = url;},},
};
</script><style lang="less" scoped>
/* 可以根據需要添加樣式 */
</style>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/906690.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/906690.shtml
英文地址,請注明出處:http://en.pswp.cn/news/906690.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SQL解析工具JSQLParser

目錄 一、引言二、JSQLParser常見類2.1 Class Diagram2.2 Statement2.3 Expression2.4 Select2.5 Update2.6 Delete2.7 Insert2.8 PlainSelect2.9 SetOperationList2.10 ParenthesedSelect2.11 FromItem2.12 Table2.13 ParenthesedFromItem2.14 SelectItem2.15 BinaryExpressio…

安裝完dockers后就無法聯網了,執行sudo nmcli con up Company-WiFi,一直在加載中

Docker服務狀態檢查 執行 systemctl status docker 確認服務是否正常 若未運行&#xff0c;使用 sudo systemctl start docker && sudo systemctl enable docker 網絡配置沖突 Docker會創建docker0虛擬網橋&#xff0c;可能與宿主機網絡沖突 檢查路由表 ip route sho…

Docker 運維管理

Docker 運維管理 一、Swarm集群管理1.1 Swarm的核心概念1.1.1 集群1.1.2 節點1.1.3 服務和任務1.1.4 負載均衡 1.2 Swarm安裝準備工作創建集群添加工作節點到集群發布服務到集群擴展一個或多個服務從集群中刪除服務ssh免密登錄 二、Docker Compose與 Swarm 一起使用 Compose 三…

軟媒魔方——一款集合多種系統輔助組件的軟件

停更4年&#xff0c;但依舊吊炸天&#xff01; 親們&#xff0c;是不是覺得電腦用久了就像老牛拉車&#xff0c;慢得讓人著急&#xff1f;別急&#xff0c;我今天要給大家安利一個超好用的電腦優化神器——軟媒魔方&#xff01; 軟件介紹 首先&#xff0c;這貨真心是免費的&a…

upload-labs通關筆記-第19關文件上傳之條件競爭

目錄 一、條件競爭 二、源碼分析 1、源碼分析 2、攻擊原理 3、滲透思路 三、實戰滲透 1、構造腳本 2、制作圖片馬 3、獲取上傳腳本URL 4、構造訪問母狼腳本的Python代碼 5、bp不斷并發上傳母狼圖片馬 &#xff08;1&#xff09;開啟專業版bp &#xff08;2&#xf…

分布式消息隊列kafka詳解

分布式消息隊列kafka詳解 引言 Apache Kafka是一個開源的分布式事件流平臺&#xff0c;最初由LinkedIn開發&#xff0c;現已成為處理高吞吐量、實時數據流的行業標準。Kafka不僅僅是一個消息隊列&#xff0c;更是一個完整的分布式流處理平臺&#xff0c;能夠發布、訂閱、存儲…

uni-app(3):互相引用

1 絕對路徑和相對路徑 在日常開發中&#xff0c;經常會遇到使用絕對路徑還是相對路徑的問題&#xff0c;下面我們介紹下這兩種路徑。 1.1 絕對路徑 絕對路徑&#xff1a;是指從項目根目錄開始的完整路徑。它用于指定文件或目錄的確切位置。絕對路徑通常以斜杠&#xff08;/&am…

python與flask框架

一、理論 Flask是一個輕量級的web框架&#xff0c;靈活易用。提供構建web應用所需的核心工具。 Flask依賴python的兩個庫 Werkzeug&#xff1a;flask的底層庫&#xff0c;提供了WSGI接口、HTTP請求和響應處理、路由等核心功能。 Jinja2&#xff1a;模板引擎&#xff0…

esp32-idf框架學習筆記/教程

esp32型號: 環境搭建 安裝:就按這個來,別的試了好多次都不行,這個一次成功!!!! vscode下ESP32開發環境配置&#xff08;100%成功&#xff09;_嗶哩嗶哩_bilibili esp芯片的兩種模式: ESP32 固件燒錄教程_嗶哩嗶哩_bilibili 1.運行模式 2.下載模式 esp32s3程序下載 1.數據…

VKontakte(VK)注冊教程

VKontakte&#xff08;簡稱VK&#xff09;是俄羅斯最大的社交網絡平臺&#xff0c;類似于Facebook&#xff0c;用戶可以通過它進行社交、分享圖片、視頻、音樂等內容&#xff0c;并參與各類社群討論&#xff0c;是與俄羅斯及其他東歐地區的朋友建立聯系的便捷平臺。對于做俄羅斯…

STM32+ESP8266+ONENET+微信小程序上傳數據下發指令避坑指南

之前只做過類似的但是以為這種爛大街的功能應該不難結果還是踩了不少坑&#xff0c;記錄幾個需要注意的點 首先貼一個非常有用的視頻&#xff0c;里面講的很詳細&#xff0c;給的資料也很全【【新版OneNet云平臺】STM32ESP8266上傳數據&#xff0c;簡單易上手&#xff01;】 h…

【知識點】關于vue3中markRow、shallowRef、shallowReactive的了解

首先我們先了解一下這三個函數的定義以及區別 markRow 定義&#xff1a; 一個用于標記對象為非響應式的工具函數 shallowRef 定義&#xff1a; 一個用于創建淺層響應式引用的函數&#xff0c;只對 .value 本身進行響應式處理&#xff0c;不會遞歸地將 .value 指向的對象或…

后端開發實習生-抖音生活服務

職位描述 ByteIntern&#xff1a;面向2026屆畢業生&#xff08;2025年9月-2026年8月期間畢業&#xff09;&#xff0c;為符合崗位要求的同學提供轉正機會。 團隊介紹&#xff1a;生活服務業務依托于抖音、抖音極速版等平臺&#xff0c;致力于促進用戶與本地服務的連接。過去一…

OceanBase 共享存儲:云原生數據庫的存儲

目錄 探會——第三屆 OceanBase 開發者大會 重磅發布&#xff1a;OceanBase 4.3 開發者生態全面升級 實戰演講&#xff1a;用戶案例與行業落地 OceanBase 共享存儲架構解析 什么是共享存儲架構&#xff1f; 云原生數據庫的架構 性能、彈性與多云的統一 為何OceanBase能…

C++ 結構體封裝模式與 Promise 鏈式調用:設計思想的異曲同工

C 結構體封裝模式與 Promise 鏈式調用&#xff1a;設計思想的異曲同工 在軟件開發中&#xff0c;我們常常追求代碼的可維護性、可擴展性和可讀性。不同的編程語言和場景下&#xff0c;雖然實現方式各異&#xff0c;但背后的設計思想往往存在著奇妙的相似性。本文將探討 C 中結…

【Go】1、Go語言基礎

前言 本系列文章參考自稀土掘金上的 【字節內部課】公開課&#xff0c;做自我學習總結整理。 Go語言的特點 Go語言由Google團隊設計&#xff0c;以簡潔、高效、并發友好為核心目標。 具有以下優點&#xff1a; 語法簡單、學習曲線平緩&#xff1a;語法關鍵字很少&#xff0c;且…

AI時代的新營銷范式:生成式引擎優化(GEO)的崛起——品牌如何被大模型收錄

在數字化浪潮席卷全球的今天&#xff0c;我們正站在一個前所未有的歷史拐點。如果說過去二十年&#xff0c;搜索引擎優化&#xff08;SEO&#xff09;重塑了企業與消費者的連接方式&#xff0c;那么未來二十年&#xff0c;生成式引擎優化&#xff08;GEO&#xff09;將徹底顛覆…

實用藍牙耳機哪款好?先做好使用場景分析!

市面上的藍牙耳機款式繁多&#xff0c;618到來之際&#xff0c;消費者如何選擇適合自己的藍牙耳機&#xff1f;實用藍牙耳機哪款好&#xff1f;關鍵在于做好使用場景分析&#xff01;今天&#xff0c;就帶大家結合不同的使用場景&#xff0c;分享三款倍思音頻的精品藍牙耳機。 …

PTA刷題筆記3(微難,有詳解)

7-15 計算圓周率 代碼如下&#xff1a; #include <stdio.h>int main() {double threshold;scanf("%lf", &threshold);double pi_over_2 1.0; // π/2的初始值&#xff08;第一項1&#xff09;double term 1.0; // 當前項的值int n 1; …

基于SpringBoot+Vue的社區醫院信息平臺設計與實現

項目背景與概述 隨著醫療健康信息化的發展&#xff0c;社區醫院的管理逐漸由傳統的手工模式轉向信息化管理。為了提高醫院的管理效率、減少人工操作、提升服務質量&#xff0c;開發一個高效且實用的社區醫院信息平臺顯得尤為重要。本系統基于Spring Boot框架與MySQL數據庫設計…