通过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>
相关文章:

通过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"…...
RxJS 核心操作符详细用法示例
1. Observable 详细用法 Observable 是 RxJS 的核心概念,代表一个可观察的数据流。 创建和订阅 Observable import { Observable } from "rxjs";// 1. 创建Observable const myObservable new Observable(subscriber > {// 发出三个值subscriber.n…...

视频监控管理平台EasyCVR结合AI分析技术构建高空抛物智能监控系统,筑牢社区安全防护网
高空抛物严重威胁居民生命安全与公共秩序,传统监管手段存在追责难、威慑弱等问题。本方案基于EasyCVR视频监控与AI视频分析技术(智能分析网关),构建高空抛物智能监控系统,实现24小时实时监测、智能识别与精准预警&…...

2.2.1 05年T1复习
引言 从现在进去考研英语基础阶段的进阶,主要任务还是05-09年阅读真题的解题,在本阶段需要注意正确率。阅读最后目标:32-34分,也就是每年真题最多错四个。 做题步骤: 1. 预习:读题干并找关键词 做题&#…...

Python-11(集合)
与字典类似,集合最大的特点就是唯一性。集合中所有的元素都应该是独一无二的,并且也是无序的。 创建集合 使用花括号 set {"python","Java"} print(type(set)) 使用集合推导式 set {s for s in "python"} print(set…...
钉钉开发之AI消息和卡片交互开发文档收集
AI消息和卡片交互开发文档 智能交互接口能力介绍 AI助理发消息(主动直接发送模式 AI 助理发消息 - 主动发送模式 AI 助理发消息 - 回复消息模式 AI 助理发消息 - Webhook 回复消息模式 Stream 模式响应卡片回传请求事件 upload-media-files AI 助理发消息&a…...
JMeter 教程:正则表达式提取器提取 JSON 字段数据
目录 JMeter 教程:正则表达式提取器提取 JSON 字段数据【简单实用】 ✅ 目的说明 📄 示例场景 🛠️ 操作步骤 第一步:发送 HTTP 请求 第二步:添加正则表达式提取器 第三步:使用提取变量 ✅ 正则表达…...

Opixs: Fluxim推出的全新显示仿真模拟软件
Opixs 是 Fluxim 最新研发的显示仿真模拟软件,旨在应对当今显示技术日益复杂的挑战。通过 Opixs,研究人员和工程师可以在制造前,设计并验证 新的像素架构,从而找出更功节能、色彩表现更优的布局方案。 Opixs 适用于学术研究和工业…...
[数据集]无人机视角检测分割数据集合集
数据集名称无人机海洋或河道水上监测检测数据集VOCYOLO格式2903张6类别无人机热红外视角人车检测数据集VOCYOLO格式2866张5类别无人机拍摄红外图像光伏板缺陷检测数据集VOCYOLO格式2723张9类别无人机视角搜索和救援失踪被困人员检测数据集VOCYOLO格式1976张6类别无人机视角垃圾…...

佰力博与您探讨PVDF薄膜极化特性及其影响因素
PVDF(聚偏氟乙烯)薄膜的极化是其压电性能形成的关键步骤,通过极化处理可以显著提高其压电系数和储能能力。极化过程涉及多种方法和条件,以下从不同角度详细说明PVDF薄膜的极化特性及其影响因素。 1、极化方法 热极化:…...
C++ std::find() 函数全解析
std::find()是C标准库中用于线性查找的基础算法,属于<algorithm>头文件,可应用于任何支持迭代器的容器。 一、函数原型与参数 template< class InputIt, class T > InputIt find( InputIt first, InputIt last, const T& value );…...

自动获取ip地址安全吗?如何自动获取ip地址
在数字化网络环境中,IP地址的获取方式直接影响设备连接的便捷性与安全性。自动获取IP地址(通过DHCP协议)虽简化了配置流程,但其安全性常引发用户疑虑。那么,自动获取IP地址安全吗?如何自动获取IP地址&#…...

STM32:深度解析RS-485总线与SP3485芯片
32个设备 知识点1【RS-485的简介】 RS-485是一种物理层差分总线标准,在串口的基础上演变而来; 两者虽然不在同一层次上直接对等,但在实际系统中,往往使用RS-485驱动差分总线,将USART转换为适合长距离、多点通信的物…...

亚马逊搜索代理: 终极指南
文章目录 前言一、为什么需要代理来搜索亚马逊二、如何选择正确的代理三、搜索亚马逊的最佳代理类型四、为亚马逊搜索设置代理五、常见挑战及克服方法六、亚马逊搜索的替代方法总结 前言 在没有代理的情况下搜索亚马逊会导致 IP 禁止、验证码和速度限制,从而使数据…...
QGraphicsView界面的坑(fitInView()函数没反应)
QGraphicsView本身是特别灵活的一种ui,能够自由响应各种动态操作。不过它最大的问题就是在加载好图像以后,将图像自适应贴合到界面大小的时候(fitInView()函数)没有反应。 这是因为fitInView函数在执行的时候,需要计算…...
【Python正则表达式终极指南】从零到工程级实战
目录 🌟 前言🏗️ 技术背景与价值🩹 当前技术痛点🛠️ 解决方案概述👥 目标读者说明 🧠 一、技术原理剖析📊 核心概念图解💡 核心作用讲解🔧 关键技术模块说明⚖️ 技术选…...
leetcode 算法每日一题 #1
#1 ! 题目 3355. 零数组变换 I 中等 相关标签 相关企业 提示 给定一个长度为 n 的整数数组 nums 和一个二维数组 queries,其中 queries[i] [li, ri]。对于每个查询 queries[i]:在 nums 的下标范围 [li, ri] 内选择一个下标 子集。 将选中的…...
用matlab提取abaqus odb文件中的节点信息
在MATLAB中提取Abaqus ODB文件中的节点信息,可以通过以下几种方法实现: 方法1:使用MATLAB的ABAQUS Interface工具箱 https://wenku.csdn.net/answer/77axwtqnys 可以参考这个 MATLAB的ABAQUS Interface工具箱提供了直接读取ODB文件的功能。…...
Spring Bean 注册到容器的方式
Spring Bean 注册到容器的方式主要包括以下几种: 基于 XML 的配置 使用 XML 文件配置 Bean,并定义 Bean 的依赖关系。 基于 Component 注解及其衍生注解 使用注解如 Component、Service、Controller、Repository 等进行配置。 基于 Configuration 和…...
1537. 【中山市第十一届信息学邀请赛决赛】未命名 (noname)
题目描述 这是一个独一无二的世界,所以有 N 张写有互不相同的自然数的卡片,第 i 张卡片写着 Ai ,现在你得到了一个未命名的空白卡片,想在上面写上一个自然数 x 满足以下条件: 1.x 不等于任意一张卡片上的数字。 2.x 可…...
数据库三范式详解与应用建议
数据库三范式(Normalization)是关系型数据库设计的核心原则,旨在减少数据冗余、提高数据一致性,并避免插入、更新和删除异常。以下是三范式的详细说明: 第一范式(1NF) 核心要求:确保…...
信息学奥赛一本通 1539:简单题 | 洛谷 P5057 [CQOI2006] 简单题
【题目链接】 ybt 1539:简单题 洛谷 P5057 [CQOI2006] 简单题 【题目考点】 1. 树状数组 知识点讲解见:洛谷 P3374 【模板】树状数组 【解题思路】 解法1:树状数组 该有01构成数组初值都为0。 某位置的元素被修改奇数次后值为1&#x…...

C++笔记-封装红黑树实现set和map
1.源码及框架分析 上面就是在stl库中set和map的部分源代码。 通过上图对框架的分析,我们可以看到源码中rb_tree⽤了⼀个巧妙的泛型思想实现,rb_tree是实 现key的搜索场景,还是key/value的搜索场景不是直接写死的,⽽是由第⼆个模板…...
deepseek模拟美团高级java开发工程师面试题
美团高级Java开发工程师面试题及参考答案 一、Java基础部分 1. HashMap实现原理 题目: 请详细描述JDK8中HashMap的实现原理为什么JDK8要将链表转为红黑树?阈值为什么是8?HashMap在多线程环境下会出现什么问题?如何解决&#x…...

留给王小川的时间不多了
王小川,这位头顶“天才少年”光环的清华学霸、搜狗输入法创始人、中国互联网初代技术偶像,正迎来人生中最难啃的硬骨头。 他在2023年创立的百川智能,被称为“大模型六小虎”之一。今年4月,王小川在全员信中罕见地反思过去两年工作…...
回溯算法:解锁多种问题的解决之门
经典回溯算法 回溯算法是一种基于深度优先搜索的算法,通过探索所有可能的候选解来找出所有可能的解。当候选解不满足条件时,会回溯到上一步,尝试其他的候选解。下面将介绍回溯算法在组合问题、切割问题、排列问题、子集问题、棋盘问题和图的…...

国产频谱仪性能如何?矢量信号分析仪到底怎么样?
矢量信号分析仪是一种高性能的电子测量设备,具备频谱分析、矢量信号分析、实时频谱分析、脉冲信号分析、噪声系数测量、相位噪声测量等多种功能。它能够对各类复杂信号进行精确的频谱特性分析、调制质量评估、信号完整性检测以及干扰源定位等操作。广泛应用于通信、…...

熔断器(Hystrix,Resilience4j)
熔断器 核心原理 熔断器通过监控服务调用失败率,在达到阈值时自动切断请求,进入熔断状态(类似电路保险丝)。其核心流程为: 关闭状态(Closed):正常处理请求,统计失…...
贪心算法套路模板+详细适用场景+经典题目清单
1. 排序 贪心选择 适用场景: 任务调度问题:需要安排多个任务,尽量完成更多任务或最小冲突。 区间调度问题:选出最多互不重叠的区间。 区间覆盖问题:用最少区间覆盖某个范围。 合并区间问题:合并重叠区…...

C++23 容器从其他兼容范围的可构造性与可赋值性 (P1206R7)
文章目录 背景与动机提案内容与实现细节提案 P1206R7实现细节编译器支持 对开发者的影响提高灵活性简化代码向后兼容性 总结 C23标准引入了对容器构造和赋值的新特性,这些特性使得容器能够更灵活地从其他兼容范围初始化,并支持从范围赋值。这些改进由提案…...