通过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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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数据库设计…