美文网首页
移动端预览pdf组件封装(封装优化)

移动端预览pdf组件封装(封装优化)

作者: Amanda妍 | 来源:发表于2024-09-24 17:32 被阅读0次

    项目中使用的是vue框架

    1、在入口文件中添加 <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    2、代码如下

    <template>
      <div class="components-preview-pdf-page">
        <div v-if="src" class="pdf_list">
          <div class="show-pdf-info">
            <span>{{currentPage}} / {{totalPages}}</span>
            <van-button @click="changePage('last')" :disabled="currentPage === 1">上一页</van-button>
            <van-button @click="changePage('next')" v-if="totalPages > 1" :disabled="currentPage === totalPages">下一页</van-button>
          </div>
    
          <canvas id="the-canvas"></canvas>
        </div>
        <div v-else>
          <default-page showText="暂无数据"></default-page>
        </div>
      </div>
    </template>
    
    <script>
    import defaultPage from '@/components/app/common/defaultPage.vue'
    import {mapActions, mapMutations} from "vuex";
    
    export default {
      components: { defaultPage},
      data() {
        return {
          currentPage: 1,//当前页
          totalPages: 0,//总页数
        }
      },
      watch: {},
      props: {
        src: String
      },
      computed: {},
      mounted() {
        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js'
        if (this.src) {
          this.loadPdfPage()
        }
      },
      methods: {
        ...mapMutations(['setLoading']),
        ...mapActions([
        ]),
        //因为插件每次只展示一页,所以要手动控制页数
        changePage(val) {
          if (val === 'last' && this.currentPage > 1) {
            this.currentPage--
          }
          else if (val === 'next' && this.currentPage < this.totalPages) {
            this.currentPage++
          }
          this.loadPdfPage()
        },
        loadPdfPage(){
          this.setLoading(true)
          pdfjsLib.getDocument(this.src).promise.then( (pdf) => {
            this.totalPages = pdf.numPages
            pdf.getPage(this.currentPage).then( (page) => {
              var scale = 1.5
    
              var viewport = page.getViewport({ scale: scale });
    
              var canvas = document.getElementById('the-canvas');
              var context = canvas.getContext('2d');
              canvas.height = viewport.height;
              canvas.width = viewport.width;
    
              var renderContext = {
                canvasContext: context,
                viewport: viewport
              }
              this.setLoading(false)
              page.render(renderContext);
            })
    
          })
        },
    
      }
    
    }
    </script>
    <style scoped lang="less">
    .components-preview-pdf-page {
      height: 100vh;
    
      .pdf_list {
        //height: 100%;
        //overflow: scroll;
        font-size: 0.28rem;
    
        .show-pdf-info{
          display: flex;
          align-items: center;
          justify-content: flex-end;
          padding: 0.1rem 0.32rem;
    
          .van-button{
            margin-left: 0.12rem;
            height: 0.56rem;
            padding: 0.08rem 0.12rem;
            font-size: 0.28rem;
          }
        }
    
        #the-canvas{
          width: 100%;
          //height: 100%;
          overflow-y: scroll;
        }
      }
    }
    </style>
    

    3、组件引用

    <template>
      <div class="app-yk-user-examination-report-detail-page">
        <header-top title="体检报告" showReturn="black"></header-top>
        <div class="date-time" >体检时间:{{examinationDate}}</div>
        <div class="pdf_list">
                <preview-pdf v-if="fileStream" :src="fileStream"></preview-pdf>
        </div>
      </div>
    </template>
    
    <script>
    import headerTop from '@/components/app/common/header/top.vue'
    import previewPdf from '@/components/previewPdf.vue'
    import {mapActions, mapMutations} from 'vuex'
    
    export default {
      name: 'appYingkangLifeTimeUserExaminationReport',
      components: {headerTop, previewPdf},
      data() {
        return {
          fileStream: '',
          id: this.$route.query.id || '',
          examinationDate: this.$route.query.examinationDate || ''
        }
      },
      watch: {},
      props: {},
      computed: {},
      mounted() {
        this.loadPdf()
      },
      methods: {
        ...mapMutations(['setLoading']),
        ...mapActions([
        ]),
        loadPdf(){
          // 移动端预览需要将文件流转化为arrayBuffer
          this.setLoading(true)
          this.$http.get('接口名字?tjbh=' + this.id, {
            responseType: 'arraybuffer',
          }).then(res => {
            this.fileStream = res.request.response
            this.setLoading(false)
          }).catch(error => {
            this.setLoading(false)
          }).finally( msg => {
            this.setLoading(false)
          })
        }
        }
    
    
    }
    </script>
    <style scoped lang="less">
    .app-yk-user-examination-report-detail-page {
      height: 100vh;
    
      .date-time{
        text-align: right;
        padding: 0.2rem 0.32rem;
        font-size: 0.28rem;
      }
    
      .pdf_list {
        height: calc(100% - 1.68rem);
        //overflow: scroll;
        font-size: 0.28rem;
    
        .components-preview-pdf-page{
          height: 100%!important;
        }
    
        .show-pdf-info {
          display: flex;
          align-items: center;
          justify-content: flex-end;
          padding: 0.1rem 0.32rem;
    
          .van-button {
            margin-left: 0.12rem;
            height: 0.56rem;
            padding: 0.08rem 0.12rem;
            font-size: 0.28rem;
          }
        }
    
        #the-canvas {
          width: 100%;
          //height: 100%;
          overflow-y: scroll;
        }
      }
    }
    </style>
    

    总结:移动端预览需要将文件流转化为arrayBuffer,否则使用插件会报错。通过 Axios 的 get 方法请求一个资源,并将 responseType 设置为 arraybuffer,这样 Axios 会将响应体作为 arrayBuffer 返回。然后在 .then 回调中可以对这个 arrayBuffer 进行进一步的处理。

    组件升级优化:预览pdf在组件内部处理文件流还是预览在线pdf

    <template>
      <div class="components-preview-pdf-page">
        <div v-if="src" class="pdf_list" ref="pdfBox">
        </div>
        <div v-else>
          <default-page showText="暂无数据"></default-page>
        </div>
      </div>
    </template>
    
    <script>
    import defaultPage from '@/components/app/common/defaultPage.vue'
    import {mapActions, mapMutations} from "vuex";
    
    export default {
      components: { defaultPage},
      data() {
        return {
        }
      },
      watch: {},
      props: {
        src: String,//在线pdf路径,或者如果跟后端确认必须走接口获取文件流格式预览的话,type是stream,这个src就是接口名字
        type: {
          type: String,
          default: 'online' //online是线上的,stream是文件流
        }
      },
      computed: {},
      mounted() {
        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js'
        if (this.src) {
          this.loadPdf(this.src)
        }
      },
      methods: {
        ...mapMutations(['setLoading']),
        ...mapActions([
        ]),
        // 获取文件流 arraybuffer格式的
        getFileStream(url) {
          return new Promise((resolve, reject) => {
            this.$http.get(url, {
              responseType: 'arraybuffer',
            }).then(res => {
              resolve(res.request.response)
            }).catch(error => {
              resolve(error)
            }).finally( msg => {
              resolve(msg)
            })
          })
        },
        //加载pdf
        async loadPdf(url) {
          this.setLoading(true)
          try {
            let loadingTask = ''
            if (this.type === 'online') loadingTask = pdfjsLib.getDocument(url);
            else {
              // 如果是文件流格式的话需要获取一下arraybuffer
              let data = await this.getFileStream(url)
              loadingTask = pdfjsLib.getDocument({ data });
            }
            const pdfDocument = await loadingTask.promise;
    
            for (let pageNumber = 1; pageNumber <= pdfDocument.numPages; pageNumber++) {
              const page = await pdfDocument.getPage(pageNumber);
              var scale = 1.5
              var viewport = page.getViewport({ scale: scale });
              const canvas = document.createElement('canvas');
              const context = canvas.getContext('2d');
              canvas.width = viewport.width;
              canvas.height = viewport.height;
              canvas.style = 'width: 100%'
              this.$refs.pdfBox.appendChild(canvas);
              const renderTask = page.render({
                canvasContext: context,
                viewport: viewport
              });
              await renderTask.promise;
              this.setLoading(false)
            }
          } catch (error) {
            console.error('Error loading PDF:', error);
          }
    
    
      },
      }
    
    }
    </script>
    <style scoped lang="less">
    .components-preview-pdf-page {
      height: 100vh;
    
      .pdf_list {
        //height: 100%;
        //overflow: scroll;
        font-size: 0.28rem;
    
        >.sdd{
          width: 100%!important;
          height: auto;
        }
    
        .show-pdf-info{
          display: flex;
          align-items: center;
          justify-content: flex-end;
          padding: 0.1rem 0.32rem;
    
          .van-button{
            margin-left: 0.12rem;
            height: 0.56rem;
            padding: 0.08rem 0.12rem;
            font-size: 0.28rem;
          }
        }
    
        #the-canvas{
          width: 100%;
          //height: 100%;
          overflow-y: scroll;
        }
      }
    }
    </style>
    

    组件使用的时候可以表明是在线预览pdf还是走接口形式

    image.png

    相关文章

      网友评论

          本文标题:移动端预览pdf组件封装(封装优化)

          本文链接:https://www.haomeiwen.com/subject/qqpqrjtx.html