vue3实现pdf文件预览 - vue-pdf-embed

参考地址:https://juejin.cn/post/7105933034771185701
这个参考文章的代码直接可以复制使用,样式也是给到的,但是实现的是一页一页的显示pdf内容,我的需求是要全部展示出来,页码切换时是做一个滚动定位操作。

思路:

  1. 通过vue3-pdfjs插件提供的方法createLoadingTask获取到总页码numPages,通过循环将所有页面渲染
  2. 页码定位通过锚点定位方式实现。

安装插件:

 npm install vue-pdf-embed@1.1.6npm install vue3-pdfjs@0.1.6

注意:vue-pdf-embed使用1.1.6版本,之前未固定版本出现内容不显示问题(大概1版本的可以,2版本的不行,我这边使用的是1.1.6版本)。vue3-pdfjs安装就是0.1.6版本的,记录加上版本好,防止后期有更新出现问题不知道原因。

完整代码:

<template><div class="pdf-preview-container"><!-- 头部导航栏 --><div class="pdf-header"><div class="page-navigation"><div class="page-controls"><!-- 上一页按钮 --><el-button link :disabled="state.pageNum <= 1" @click="prevPage"><el-icon><ArrowUp /></el-icon></el-button><!-- 页码输入框 --><el-input v-model.number="state.pageNum" class="page-input" @keyup.enter="goToPage" @blur="goToPage" /><!-- 下一页按钮 :disabled="currentPage >= totalPages" --><el-button link @click="nextPage"><el-icon><ArrowDown /></el-icon></el-button></div><!-- 总页数 --><span class="total-pages">/&nbsp;&nbsp;{{ state.numPages }}</span></div></div><!-- PDF预览区域 --><div class="pdf-content"><!-- PDF内容显示区域 --><div class="pdf-display-area"><div v-for="item in state.numPages" :key="item" class='pdf-item' :id="`pdf-page-${item}`"><vue-pdf-embed :source="state.source" class="vue-pdf-embed" :page="item" /></div></div></div></div>
</template><script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, computed, reactive } from 'vue';
import { Close, ArrowUp, ArrowDown } from '@element-plus/icons-vue';
import VuePdfEmbed from "vue-pdf-embed";
import { createLoadingTask } from "vue3-pdfjs";const pdfFile = 'https://pic2-cdn.trytalks.com/pic2/202507091052/files_1a1b765ec00125e0FfuRNLZz.pdf?Expires=1754621583&OSSAccessKeyId=LTAI5tQd3SECCekgpXLBnQSo&Signature=nUn9kW375rdiVNHi2wdDLDm0gAM%3D'const state = reactive({source: pdfFile, // 预览pdf文件地址pageNum: 1, // 当前页面scale: 1, // 缩放比例numPages: 0, // 总页数
});const loadingTask: any = ref(null)const pdfInit = () => {loadingTask.value = createLoadingTask(state.source);loadingTask.value.promise.then((pdf: { numPages: number }) => {state.numPages = pdf.numPages;});
}// 滚动到指定页面
let isManualNavigation = false;
const scrollToPage = (page: number) => {// 设置手动导航标志isManualNavigation = true;// 先立即更新页码状态const validPage = Math.max(1, Math.min(page, state.numPages));state.pageNum = validPage;// 执行滚动const element = document.getElementById(`pdf-page-${validPage}`);if (element) {element.scrollIntoView({behavior: 'smooth',block: 'start'});}// 滚动结束后清除标志setTimeout(() => {isManualNavigation = false;}, 1000);
};// 上一页
const prevPage = (): void => {if (state.pageNum > 1) {scrollToPage(state.pageNum - 1);}
};// 下一页
const nextPage = (): void => {const nextPage = state.pageNum + 1;if (nextPage <= state.numPages) {scrollToPage(nextPage);}
};// 跳转到指定页
const goToPage = (): void => {// 确保输入是有效数字let page = parseInt(String(state.pageNum));if (isNaN(page) || page < 1) {page = 1;} else if (page > state.numPages) {page = state.numPages;}// 更新输入框显示正确的页码state.pageNum = page;scrollToPage(page);
};// 处理滚动事件,更新当前页码(带防抖)
let scrollTimeout: number | null = null;
const handleScroll = () => {// 如果是手动导航触发的滚动,则不更新页码if (isManualNavigation) return;if (scrollTimeout) {clearTimeout(scrollTimeout);}scrollTimeout = setTimeout(() => {const container = document.querySelector('.pdf-content');if (!container) return;const scrollPosition = container.scrollTop;const items = document.querySelectorAll('.pdf-item');const containerHeight = container.clientHeight;const threshold = containerHeight * 0.3; // 30%视口高度作为阈值let currentPage = state.pageNum;// 精确检测当前可见页面items.forEach((item) => {const pageNum = parseInt(item.id.replace('pdf-page-', ''));const rect = item.getBoundingClientRect();// 如果页面顶部在视口阈值范围内,则认为是当前页if (rect.top >= -threshold && rect.top <= threshold) {currentPage = pageNum;}});// 只有当页码确实变化时才更新if (currentPage !== state.pageNum) {state.pageNum = currentPage;}}, 150) as unknown as number; // 适当增加防抖时间
};// 组件挂载时
onMounted(() => {// 检查浏览器PDF支持const isPdfSupported = 'application/pdf' in navigator.mimeTypes;console.log('浏览器原生支持PDF:', isPdfSupported);// 添加滚动事件监听const contentEl = document.querySelector('.pdf-content');if (contentEl) {contentEl.addEventListener('scroll', handleScroll);}pdfInit();
});onBeforeUnmount(() => {// 移除滚动事件监听const contentEl = document.querySelector('.pdf-content');if (contentEl) {contentEl.removeEventListener('scroll', handleScroll);}
});
</script><style lang="scss" scoped>
.pdf-preview-container {display: flex;flex-direction: column;height: 100%;background-color: #fff;border-radius: 4px;overflow: hidden;.pdf-header {display: flex;justify-content: space-between;align-items: center;padding: 10px 16px;background-color: #fff;border-bottom: 1px solid #e0e0e0;.back-button {cursor: pointer;display: inline-block;padding: 5px 5px 0px;border-radius: 4px;transition: background-color 0.2s;&:hover {background-color: #f0f0f0;}}.pdf-actions {display: flex;gap: 10px;}.page-navigation {display: flex;align-items: center;.page-controls {display: flex;align-items: center;padding: 2px 8px;.page-input {width: 50px;margin: 0 5px;:deep(.el-input__inner) {text-align: center;padding: 0 5px;}}}.total-pages {margin-left: 5px;color: #606266;}}}.pdf-content {flex: 1;overflow: auto;/* 自定义滚动条样式 */&::-webkit-scrollbar {width: 6px;height: 6px;}&::-webkit-scrollbar-track {background: rgba(0, 0, 0, 0.05);border-radius: 3px;}&::-webkit-scrollbar-thumb {background: rgba(0, 0, 0, 0.15);border-radius: 3px;transition: background 0.2s;&:hover {background: rgba(0, 0, 0, 0.25);}}.pdf-loading,.pdf-error {height: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;}.pdf-display-area {.pdf-item {margin: 16px 0px;}}}
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/88538.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【AI论文】OmniPart:基于语义解耦与结构连贯性的部件感知三维生成

摘要&#xff1a;创建具有显式、可编辑部件结构的三维资产&#xff0c;对于推动交互式应用的发展至关重要。然而&#xff0c;大多数生成方法仅能生成整体式形状&#xff0c;限制了其实际应用价值。我们提出OmniPart——一种新型的部件感知三维物体生成框架&#xff0c;旨在实现…

Pandas-数据查看与质量检查

Pandas-数据查看与质量检查一、数据查看&#xff1a;快速掌握数据概况1. 整体概览&#xff1a;shape与info()2. 数值特征预览&#xff1a;describe()3. 随机抽样&#xff1a;head()与sample()二、数据质量检查&#xff1a;识别与处理问题1. 缺失值检查与处理处理策略&#xff1…

类和对象拓展——日期类

一.前言通过前面对类和对象的学习&#xff0c;现在我们可以开始实践日期类的代码编写。在实际操作过程中&#xff0c;我会补充之前文章中未提及的相关知识点。二.正文 1. 日期类代码实现我们先来看看要实现什么功能吧&#xff0c;把他放在Date.h中#pragma once #include<ios…

大模型KV缓存量化误差补偿机制:提升推理效率的关键技术

大模型KV缓存量化误差补偿机制&#xff1a;提升推理效率的关键技术摘要 随着大型语言模型&#xff08;LLM&#xff09;参数规模突破千亿级别&#xff0c;推理过程中的显存占用与计算延迟成为制约其实际部署的核心瓶颈。KV缓存&#xff08;Key-Value Cache&#xff09;作为Trans…

QT跨平台应用程序开发框架(6)—— 常用显示类控件

目录 一&#xff0c;Label 1.1 主要属性 1.2 文本格式 1.3 设置图片 1.4 其它常用属性 1.5 设置伙伴 二&#xff0c;LCD Number 2.1 主要属性 2.2 实现倒计时 ​2.3 两个问题 三&#xff0c;ProgressBar 3.1 主要属性 3.2 进度条按时间增长 3.3 改变样式 3.4 一个问题 四&#…

LINUX文件系统权限,命令解释器alias,文件查看和查找

1、文件査看:查看/etc/passwd文件的第5行[rootserver ~]# head -5 /etc/passwd | tail -1 #先找到前5行&#xff0c;用管道符过滤&#xff0c;显示倒数第一行2、文件查找(1)在当前目录及子目录中&#xff0c;查找大写字母开头的txt文件[rootserver ~]# find / -name "[…

AI图像修复工具CodeFormer实测:马赛克去除与画质增强效果评测

大家好&#xff01;平时看图片或视频&#xff0c;是不是特别烦人脸被马赛克遮住的地方&#xff1f;比如老照片模糊、视频关键部分被打码&#xff0c;看着很不舒服。今天给大家分享一款超好用的去马赛克神器——CodeFormer&#xff0c;完全免费&#xff0c;新手也能轻松搞定&…

知识宇宙-思考篇:AI大模型如何重塑软件开发流程?

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录AI大模型重塑软件开发&#xff1a;从码农到AI编程伙伴的华丽转身一、AI大模型的编程&…

Rocky Linux上使用NVM安装Node.js 18

问题描述 Rocky Linux 9 默认 yum 安装的 Node.js 版本是16&#xff0c;vite启动报错&#xff1a;TypeError: crypto$2.getRandomValues is not a function &#xff0c;需安装更高版本的 Node.js 使用nvm安装Node.js的好处 多版本管理&#xff0c;NVM 允许你安装多个不同版本的…

JVM 中“对象存活判定方法”全面解析

1. 前言 在 Java 开发过程中&#xff0c;我们常常听到“垃圾回收”&#xff08;Garbage Collection, GC&#xff09;这一术语。JVM 通过垃圾回收机制自动管理内存&#xff0c;极大地简化了程序员的内存控制负担。然而&#xff0c;GC 究竟是如何判断哪些对象该回收、哪些应保留…

苹果公司高ROE分析

公司通过增加负债提升净资产收益率&#xff08;ROE&#xff09;的核心机制在于财务杠杆效应和资本结构优化&#xff0c;以下从原理、操作路径、风险边界及苹果案例四维度展开分析&#xff1a;名称解释&#xff1a; ROIC(投入资本回报率)&#xff1a;ROICNOPATInvested Capital …

【Linux系统】进程概念

1. 进程概念1.1 进程的本质核心定义用户视角&#xff1a;程序的动态执行实例&#xff08;如同时运行多个Chrome窗口即多个进程&#xff09;。内核视角&#xff1a;资源分配的最小实体单位&#xff0c;独享CPU时间片、内存空间和文件资源。现代定义&#xff1a;进程 内核数据结…

从LLM到VLM:视觉语言模型的核心技术与Python实现

本教程的完整代码可以在GitHub上找到&#xff0c;如果你有任何问题或建议&#xff0c;欢迎交流讨论。 引言&#xff1a;为什么需要VLM&#xff1f; 当我们与ChatGPT对话时&#xff0c;它能够理解复杂的文字描述&#xff0c;生成流畅的回答。但如果我们给它一张图片&#xff0c…

老系统改造增加初始化,自动化数据源配置(tomcat+jsp+springmvc)

老系统改造增加初始化&#xff0c;自动化数据源配置一、前言二、改造描述1、环境说明2、实现步骤简要思考三、开始改造1、准备sql初始化文件2、启动时自动读取jdbc文件&#xff0c;创建数据源&#xff0c;如未配置&#xff0c;需要一个默认的临时数据源2.1去掉sping mvc原本配置…

卫星通信终端天线的5种对星模式之二:DVB跟踪

要实现稳定可靠的卫星通信&#xff0c;地面终端天线必须精准地对准远方的卫星。对星的过程是一个不断搜索、不断逼近的过程&#xff0c;其目标是让天线波束中心精确指向卫星&#xff0c;从而获得最大信号接收与发射效率。 卫星通信终端天线的对星技术是保障卫星通信链路稳定的…

重构下一代智能电池“神经中枢”:GCKontrol定义高性能BMS系统级设计标杆

概述BMS&#xff08;电池管理系统&#xff09;作为新能源汽车动力电池与整车的核心纽带&#xff0c;通过实时监控电压、电流、温度及SOC等参数&#xff0c;控制电池充放电过程&#xff0c;保障电池安全性与使用寿命。随着电动汽车智能化发展&#xff0c;对BMS的响应速度、精度和…

面试150 对称二叉树

思路 联想递归三部曲&#xff1a;传入参数、遍历方式、返回什么。本题联想到先序遍历的方式,需要遍历整颗二叉树,最后返回的是一个布尔值。然后我们需要传入的是左子树和左子树的节点,然后分别进行比较。 # Definition for a binary tree node. # class TreeNode: # def __…

多线程的区别和联系

进程和线程的区别和联系1.一个进程可以包含多个线程&#xff0c;不能够没有线程2.进程是系统资源分配的基本单位&#xff0c;线程是系统调度执行的基本单位3.同一个进程里的线程之间&#xff0c;共用同一份系统资源4.线程是当下实现并发编程的主流方式&#xff0c;通过多线程&a…

两个文件夹自动同步

两个文件夹自动同步&#xff0c;非常简单&#xff0c;利用一些工具就可以轻松做到&#xff0c;设置完源和目标文件夹&#xff0c;点击启动就马上可以两个文件夹自动同步&#xff0c;对于一些有文件同步、文件灾备需求的老登&#xff0c;用起来会非常顺手&#xff0c;比如PanguF…

虚拟商品交易维权指南:数字经济时代的消费者权益保护

首席数据官高鹏律师数字经济团队创作AI辅助在元宇宙、NFT、虚拟情绪产品等新兴领域蓬勃发展的今天&#xff0c;虚拟商品交易已成为数字经济的重要组成部分。从游戏皮肤、在线课程到数字藏品&#xff0c;消费者在享受虚拟商品便捷性的同时&#xff0c;也面临着诸多法律风险。作为…