vue3: pdf.js5.2.133 using typescript

bash 复制代码
npm install pdfjs-dist@5.2.133

项目结构:

javascript 复制代码
<!--
 * @creater: geovindu
 * @since: 2025-05-09 21:56:20
 * @LastAuthor: geovindu
 * @lastTime: 2025-05-09 22:12:17
 * @文件相对于项目的路径: \jsstudy\vuepdfpreview\comonents\pdfjs.vue
 * @message: geovindu
 * @IDE: vscode
 * @Development: node.js 20, vuejs3.0
 * @package:
 * @ISO: windows10
 * @database: mysql 8.0 sql server 2019 postgresSQL 16
 * Copyright (c) 2025 by geovindu email:geovindu@163.com, All Rights Reserved.
-->
<template>
    <div class="pdf-container">
      <div v-if="loading" class="text-center py-8">加载中...</div>
      <div v-else-if="error" class="text-center py-8 text-red-500">{{ error }}</div>
      <div v-else>
        <!-- 控制工具栏 -->
        <div class="flex justify-between items-center mb-4">
          <div class="flex space-x-2">
            <button @click="zoomIn" class="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600">
              <i class="fa fa-search-plus mr-1"></i> 放大
            </button>
            <button @click="zoomOut" class="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600">
              <i class="fa fa-search-minus mr-1"></i> 缩小
            </button>
            <button @click="downloadPDF" class="px-3 py-1 bg-green-500 text-white rounded hover:bg-green-600">
              <i class="fa fa-download mr-1"></i> 下载文档
            </button>
          </div>
          <div class="text-center">
            缩放比例: {{ Math.round(scale * 100) }}%
          </div>
        </div>
          
        <!-- PDF 容器 -->
        <div id="pdf-container" class="w-full h-[600px] border border-gray-300 overflow-auto">
          <canvas ref="pdfCanvas"></canvas>
        </div>
          
        <!-- 页码控制 -->
        <div class="mt-4 text-center">
          <button @click="firstPage" :disabled="currentPage <= 1" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300 mx-1">
            第一页
          </button>
          <button @click="prevPage" :disabled="currentPage <= 1" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300 mx-1">
            上一页
          </button>
          <span class="mx-3">第 {{ currentPage }} / {{ totalPages }} 页</span>
          <button @click="nextPage" :disabled="currentPage >= totalPages" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300 mx-1">
            下一页
          </button>
          <button @click="lastPage" :disabled="currentPage >= totalPages" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300 mx-1">
            最后一页
          </button>
        </div>
      </div>
    </div>
  </template>
    
  <script setup>
  import { ref, onMounted, reactive } from 'vue';
  import * as pdfjsLib from 'pdfjs-dist';
    
  // 设置 worker 路径
  //pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs/pdf.worker.mjs';
  // 动态解析worker路径,确保使用.mjs文件
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
  'pdfjs-dist/build/pdf.worker.mjs',
  import.meta.url
).toString();
 
    
  const props = defineProps({
    pdfUrl: { type: String, required: true }
  });
    
  const pdfCanvas = ref(null);
  const loading = ref(true);
  const error = ref('');
  const currentPage = ref(1);
  const totalPages = ref(0);
  const scale = ref(1.0);
  let pdfDoc = null;
    
  const renderPage = async (num) => {
    try {
      const page = await pdfDoc.getPage(num);
      const viewport = page.getViewport({ scale: scale.value });
    
      // 设置 canvas 尺寸
      pdfCanvas.value.width = viewport.width;
      pdfCanvas.value.height = viewport.height;
    
      // 渲染页面
      const renderContext = {
        canvasContext: pdfCanvas.value.getContext('2d'),
        viewport: viewport
      };
    
      await page.render(renderContext).promise;
      currentPage.value = num;
    } catch (err) {
      console.error('渲染页面失败:', err);
      error.value = `渲染失败: ${err.message}`;
    }
  };
    
  const prevPage = () => {
    if (currentPage.value > 1) {
      renderPage(currentPage.value - 1);
    }
  };
    
  const nextPage = () => {
    if (currentPage.value < totalPages.value) {
      renderPage(currentPage.value + 1);
    }
  };
    
  const firstPage = () => {
    if (currentPage.value !== 1) {
      renderPage(1);
    }
  };
    
  const lastPage = () => {
    if (currentPage.value !== totalPages.value) {
      renderPage(totalPages.value);
    }
  };
    
  const zoomIn = () => {
    scale.value = Math.min(scale.value + 0.1, 3.0); // 最大缩放 300%
    renderPage(currentPage.value);
  };
    
  const zoomOut = () => {
    scale.value = Math.max(scale.value - 0.1, 0.5); // 最小缩放 50%
    renderPage(currentPage.value);
  };
    
  const downloadPDF = () => {
    try {
      const link = document.createElement('a');
      link.href = props.pdfUrl;
      link.download = props.pdfUrl.split('/').pop() || 'document.pdf';
      link.click();
    } catch (e) {
      console.error('下载失败:', e);
      error.value = '下载失败,请尝试右键另存为';
      window.open(props.pdfUrl, '_blank');
    }
  };
    
  onMounted(async () => {
    try {
      // 加载 PDF
      const loadingTask = pdfjsLib.getDocument(props.pdfUrl);
      pdfDoc = await loadingTask.promise;
      totalPages.value = pdfDoc.numPages;
    
      // 渲染第一页
      renderPage(1);
      loading.value = false;
    } catch (err) {
      console.error('加载 PDF 失败:', err);
      error.value = `加载失败: ${err.message}`;
      loading.value = false;
    }
  });
  </script>
    
  <style scoped>
  .pdf-container {
    max-width: 1000px;
    margin: 0 auto;
  }
    
  #pdf-container canvas {
    max-width: 100%;
    display: block;
    margin: 0 auto;
  }
  </style>
javascript 复制代码
<!--
 * @creater: geovindu
 * @since: 2025-05-09 21:56:20
 * @LastAuthor: geovindu
 * @lastTime: 2025-05-09 22:12:17
 * @文件相对于项目的路径: \jsstudy\vuepdfpreview\src\App.vue
 * @message: geovindu
 * @IDE: vscode
 * @Development: node.js 20, vuejs3.0
 * @package:
 * @ISO: windows10
 * @database: mysql 8.0 sql server 2019 postgresSQL 16
 * Copyright (c) 2025 by geovindu email:geovindu@163.com, All Rights Reserved.
-->
 <template>
<div class="pdf-container">
    <PDFView :pdfUrl="pdfUrl" v-if="pdfUrl" />
    <div v-else >加载中...</div>
</div>
  <div>
    <a href="https://vite.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
  </div>
  <HelloWorld msg="Vite + Vue" />
</template>
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
//import PDFView from './components/vuepdfjs.vue' // 可以
 //import PDFView from './components/pdfPreview.vue' // 可以
 import PDFView from "./components/pdfjs.vue"   //可以
 //import pdfUrl from "./pdfs/01.pdf"
 const pdfUrl = "./pdfs/09.pdf"
 
 
</script>
<style scoped>
.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: filter 300ms;
}
.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
  filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

输出:

相关推荐
轻语呢喃12 分钟前
React智能前端:从零开始的识图学单词项目(一)
javascript·react.js·aigc
断竿散人12 分钟前
JavaScript 异常捕获完全指南(下):前端框架与生产监控实战
前端·javascript·前端框架
LRH16 分钟前
JS基础 - 基于 Generator + Promise 实现 async/await 原理
前端·javascript
自由逐风17 分钟前
前端小数点精度问题解析
javascript
断竿散人17 分钟前
JavaScript 异常捕获完全指南(上):从同步异步到 Promise 错误处理
前端·javascript·promise
laperter18 分钟前
js中继承关系杂乱?这篇带你理清
javascript
婉婉耶19 分钟前
VUE带你乘风破浪~
前端·vue.js
小赖同学啊32 分钟前
光伏园区3d系统管理
前端·javascript·3d
前端进阶者40 分钟前
js通知提醒
前端·javascript
快起来别睡了1 小时前
看完你就知道JavaScript 中的对象创建与继承方式原来这么简单?!
javascript