实现Vue移动端的PDF预览

文章讲述了在开发中实现PDF预览功能遇到的问题和解决方案,对比了iframe、embed标签以及Vue插件vue-pdf和pdfh5的使用体验。在移动端环境下,iframe样式问题、vue-pdf的兼容性问题以及pdfh5的动态参数配置等细节被逐一探讨,最终通过调整pdfh5的参数解决了问题。

最近做到一个功能,PDF预览;这个功能看着蛮简单的,结果搞了两个下午,真是欲哭无泪。记录一下所查到的预览方法。
我在网上找了蛮多教程的,大致都是以下几个方法实现预览:

  1. 使用 iframe 标签
  2. 使用 embed 标签
  3. 安装 Vue 插件 vue-pdf
  4. 安装 Vue 插件 pdfh5

下面配合代码详细说我实现预览的过程吧!

iframe标签
<iframe :src="url" style="border: none;width: 100%;height: 100%;" frameborder="0" />
<script>
export default {
  data() {
    return {
      show: false, 
      url: '', // pdf地址
    }
  },
  methods: {
}
</script>

这个标签其实蛮方便的,毕竟上一页、下一页,跳转,下载都是齐全的,个人觉得如果是用在pc端就非常的舒适,一个标签解决所有,但是如果在移动端可能就有点小毛病;我这次需要在一个弹窗里预览PDF文件,在展示时样式就很丑(如下图),不过我没多挣扎就放弃了
在这里插入图片描述

embed 标签
<embed :src="url" type="application/pdf" width: 100% height: 100% >
<script>
export default {
  data() {
    return {
      show: false, 
      url: '', // pdf地址
    }
  },
  methods: {
}
</script>

这个方法有局限性,如果浏览器不支持PDF嵌入,那这个标签就什么也不展示。

Vue 插件 vue-pdf
npm install --save vue-pdf // 安装一下
<!--这是一次加载完,还可以分页展示,这里就不详细说了-->
<div class="toast">
          <PDF v-for="i in pageNum" :key="i" ref="pdf" :src="url" :page="i" style="width: 100%" />
</div>
<script>
import PDF from 'vue-pdf' // 引入
import { CellGroup, Field, Popup, Toast } from 'vant'
export default {
  components: {
    PDF, [CellGroup.name]: CellGroup, [Field.name]: Field, [Popup.name]: Popup, [Toast.name]: Toast
  },
  data() {
    return {
      show: false, // 打开弹窗
      url: '', // 预览地址
      currentPage: 0, // 页码
      pageNum: null
    }
  },
  computed: {
  },
  created() {
  },
  methods: {
    // 查看pdf
    lookPdf(item) {
      console.log('打开弹窗')
      this.getNumPages()
      this.show = true
    },
    getNumPages() {
      this.url = 'https://xxx/xxxx/test.pdf'
      const loadingTask = PDF.createLoadingTask(this.url)
      loadingTask.promise
        .then((pdf) => {
          this.pageNum = pdf.numPages
        })
        .catch((err) => {
          console.error('pdf 加载失败', err)
        })
    }
  }
}
</script>

这个方法,我之前做PC端的时候,真的很丝滑;但这次做移动端就开始报错,用浏览器调试时,一到移动端模式就开始报错(如下图),在网上找的解决教程也没起作用,果断放弃。毕竟要多给别的方法机会嘛!
在这里插入图片描述

npm install pdfh5
npm install pdfh5 // 安装一下
<template>
  <div class="m-pdf">
    <div id="pdf-content" />
  </div>
</template>
<script>
import Pdfh5 from 'pdfh5'
import 'pdfh5/css/pdfh5.css'
export default {
  name: 'Pdfh5',
  data() {
    return {
      pdfh5: null,
    }
  },
  mounted() {
  },
  methods: {
    initPdf(pdfUrl) {
      console.log('子组件的地址', pdfUrl)
      // pdfh5实例化时传两个参数:selector选择器,options配置项参数,会返回一个pdfh5实例对象,可以操作pdf,监听相关事件
      this.pdfh5 = new Pdfh5('#pdf-content', {
        pdfurl: pdfUrl
      })
      this.pdfh5.scrollEnable(true) // 允许pdf滚动
      // 监听pdf准备开始渲染,可以拿到pdf总页数
      this.pdfh5.on('ready', function() {
        console.log('总页数:' + this.totalNum)
      })
      // 监听pdf加载完成事件
      this.pdfh5.on('complete', (status, msg, time) => {
        console.log('状态:' + status + ',信息:' + msg + ',耗时:' + time + '毫秒')
      })
    }
  }
}
</script>
// 父组件
<template>
  <button @click="showPdf">
  <div v-if = "show">
      <preview-pdf ref="previewPdfh5" style="height:450px;margin-bottom:12px;" />
   </div>
</template>
<script>
import PreviewPdf from '../pdf.vue'
export default {
components: {
    PreviewPdf
  },
  data() {
    return {
      show: false, 
      url: '', // pdf地址
    }
  },
  methods: {
  showPdf(){
     this.show
     this.url = 'https://xxx/xxxx/test.pdf' // pdf文件的地址
     this.$refs.previewPdfh5.initPdf(this.url) // 父组件调用子组件的方法,动态渲染pdf
  }
}
</script>

这个方法到这里也很丝滑,上述方法的问题都没有碰到;然而在调后端接口时,报错了。
在这里插入图片描述
真是欲哭无泪,我上网找教程,根本没有。接口传过来的PDF里有一些字段是动态变化的,就导致这个问题出现。在我不懈奋斗了几个小时之后,无意间看到了pdfh5的中文文档的一个参数例子(如图),问题就解决啦!
在这里插入图片描述
给代码加个参数

 methods: {
    initPdf(pdfUrl) {
      console.log('子组件的地址', pdfUrl)
      this.pdfh5 = new Pdfh5('#pdf-content', {
        pdfurl: pdfUrl,
        cMapUrl: 'https://unpkg.com/pdfjs-dist@2.0.943/cmaps/' // 改一下这个参数的默认地址
      })
      this.pdfh5.scrollEnable(true) 
      this.pdfh5.on('ready', function() {
        console.log('总页数:' + this.totalNum)
      })
      this.pdfh5.on('complete', (status, msg, time) => {
        console.log('状态:' + status + ',信息:' + msg + ',耗时:' + time + '毫秒')
      })
    }
  }

这是我参考的文档地址:https://github.com/EncodingAESKey/pdfh5/blob/master/README.md

总结一下:磕磕碰碰也终于实现了功能。这几个方法都能实现功能;如果图方便就用iframe标签,图样式简洁且使用方法简单就用 vue-pdf 插件,图样式简洁且功能齐全就用 pdfh5 插件;至于embed 标签,我还没用过。不过我用 vue-pdf 插件遇到的问题,到现在也没解决好,不知道是不是移动端要添加什么配置,还是我代码环境不兼容;后面如果有好的解决方法,我会记得来更。

### Vue3 移动端 PDF 预览实现方法 #### 方法一:使用 `iframe` 或 `embed` 标签 可以利用 HTML 的原生标签来嵌入 PDF 文件。这种方法简单易用,适合快速原型开发。 ```html <template> <div> <!-- 使用 iframe --> <iframe :src="pdfUrl" width="100%" height="800px"></iframe> <!-- 或者使用 embed --> <embed :src="pdfUrl" type="application/pdf" width="100%" height="800px"/> </div> </template> <script setup> import { ref } from 'vue'; const pdfUrl = '/path/to/your/file.pdf'; </script> ``` 此方式适用于不需要复杂交互场景的情况[^1]。 #### 方法二:通过插件 `vue-pdf` 对于更复杂的项目需求,则推荐采用专门处理 PDF 文档的库——`vue-pdf`。该方案提供了更多自定义选项以及更好的性能优化支持。 首先需安装依赖: ```bash npm install vue-pdf ``` 接着可以在组件内引入并配置如下所示: ```html <template> <div class="pdf-viewer"> <pdf v-for="i in numPages" :key="i" :src="pdfSrc" :page="i"></pdf> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import pdf from 'vue-pdf'; let pdfSrc = pdf.createLoadingTask('/path/to/your/file.pdf'); let numPages = ref(0); onMounted(() => { pdfSrc.promise.then(pdf => { numPages.value = pdf.numPages; }); }); </script> ``` 这种方式能够提供更加灵活的操作接口,并且易于维护和扩展。 #### 方法三:基于 `VuePdfEmbed` 组件的方式 为了进一步简化集成过程,在某些情况下还可以考虑直接运用已经封装好的第三方组件如 `VuePdfEmbed` 来完成相同的功能。这不仅减少了自行编写代码的工作量,同时也可能带来额外特性上的优势。 先按照官方文档指引添加必要的 npm 包: ```bash npm i @progress/kendo-vue-pdf ``` 之后参照下面的例子调整页面结构与逻辑: ```html <template> <div ref="preRef" v-loading="loading" class="pdf-preview"> <div v-if="pdfState.source" class="pdf-wrap"> <VuePdfEmbed v-for="item in pdfState.numPages" :key="item" :source="pdfState.source" :style="pdfState.scale" class="vue-embed" :page="item" /> </div> <van-empty v-if="loadError" image="error" description="PDF 加载失败..." /> </div> </template> <script setup> import { reactive, toRefs } from 'vue'; import VuePdfEmbed from '@progress/kendo-vue-pdf/dist/vue-pdf-embed.js'; // 假设这里是从父级传递下来的 props 数据 const props = defineProps({ source: String, }); const state = reactive({ loading: true, loadError: false, ...toRefs(props), scale: {}, numPages: null, }); state.loading = false; if (!props.source) { state.loadError = true; } else { fetch(props.source).then(response => response.blob()).then(blob => { const fileReader = new FileReader(); fileReader.onloadend = () => { state.source = fileReader.result; // 获取总页数等信息... // 这里省略具体实现细节 state.loading = false; }; fileReader.readAsDataURL(blob); }).catch(() => { state.loadError = true; }); } </script> ``` 上述三种途径各有优劣之处,开发者可以根据实际应用场景和个人偏好做出合适的选择[^3]。
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值