前端在线预览PDF文件

前言

这里用到了vue-pdf插件,预览PDF相关的操作基本都有实现;

我们需要做的就是各种布局(因为需兼容已有的布局,有的地方可能需要修改),比如翻页按钮,页码展示等等;

vue-pdf的GitHub地址:FranckFreiburger/vue-pdf: vue.js pdf viewer (github.com)

目录

  1. 入门例子
  2. 展示所有页码
  3. 翻页操作
  4. 封装组件
  5. 完整代码

正文

1. 入门例子

安装命令:yarn add vue-pdf

最简单的入门例子,如下所示:

<template>
  <pdf src="/pdf/1.pdf"></pdf>
</template>

<script>
import pdf from 'vue-pdf'

export default {
  components: {
    pdf
  }
}

关于本地文件的路径问题:

这里需要注意一下,要把pdf放在public目录下,然后通过/进行引用;

比如你的pdf路径为:public/pdf/1.pdf,那么src就要写成:/pdf/1.pdf

如果是远程路径,则直接赋值;

2. 展示所有页码

上面的入门例子只是展示了第一页的内容,其他内容没有展示,如果需要展示其他页,则需要添加翻页功能;

但是现在我们先简化,不添加翻页功能,而是用v-for直接展示所有的页码;

<template>
	<div>
		<pdf
			v-for="i in numPages"
			:key="i"
			:src="src"
			:page="i"
			style="display: inline-block; width: 25%"
		></pdf>
	</div>
</template>

<script>

import pdf from 'vue-pdf'

var loadingTask = pdf.createLoadingTask('/pdf/1.pdf');

export default {
	components: {
		pdf
	},
	data() {
		return {
			src: loadingTask,
			numPages: undefined,
		}
	},
	mounted() {

		this.src.promise.then(pdf => {

			this.numPages = pdf.numPages;
		});
	}
}

</script>

展示效果如下所示:

image-20220211170832197

当我们的页码不是很多时,可以采用这种简单粗暴的方式进行展示,很方便;

但是如果页码过多,则不仅看起来很费劲,而且加载也会很慢,这时就需要用到翻页功能;

3. 翻页操作

这里主要增加两个按钮,以及相关属性,下面是部分代码:

<a-list-item>
    <div @click="changePdfPage('pre')"
         :style="currentPage===1?'cursor: not-allowed;':''">
        上一页
    </div>
</a-list-item>
<a-list-item>
    <div @click="changePdfPage('next')"
         :style="currentPage===pageCount?'cursor: not-allowed;':''">
        下一页
    </div>
</a-list-item>
<pdf :src="srcPdf"
     :page="currentPage"
     @num-pages="pageCount=$event"
     style="display: inline-block;width:100%"></pdf>
  • @num-pages 事件:获取pdf的总页数,这里获取到之后传给了pageCount
  • page 属性:就是当前页码,这里通过点击上一页和下一页来修改来更新页码

效果如下所示:

image-20220211172837536

完整代码见下面;

4. 封装组件

为了方便使用,我们可以将上面的预览代码封装成功一个单文件组件,然后在需要的地方进行引入即可;

封装后的组件代码贴到文末了,因为有点长:

我们在展示pdf文件时,可以通过跳转到新标签页的方式进行展示,这样组件内的布局不会有太大的变化;

跳转代码如下所示:

let routeUrl = this.$router.resolve({
              path: '/preview-pdf',
              query:{pdfPath}
          })
window.open(routeUrl.href, '_blank')
  • /preview-pdf:这个路径就是配置在路由里面的,预览pdf的路径

  • pdfPath:这里我们是通过query的方式进行传参,然后在预览组件内通过 this.srcPdf = decodeURIComponent(this.$route.query.pdfPath)进行获取;

    • 因为存在编码问题,所以这里需要加上解码操作;

      如果pdf路径是http远程路径,则不需要解码

5. 完整代码

完整的封装组件如下,这里是参考网上的例子,做了一些修改

<template>
    <div id="container">
        <!-- 上一页、下一页 -->
        <div class="right-btn">
            <a-space>
                <a-list>
                    <a-list-item>
                        <div >
                            <input v-model.number="currentPage"
                                   type="number"
                                   class="inputNumber"
                                   @input="inputEvent()"> / {{pageCount}}
                        </div>
                    </a-list-item>
                    <a-list-item>
                        <div @click="changePdfPage('first')"
                        >
                            首页
                        </div>
                    </a-list-item>
                    <a-list-item>
                        <!-- 在按钮不符合条件时禁用 -->
                        <div @click="changePdfPage('pre')"
                             :style="currentPage===1?'cursor: not-allowed;':''">
                            上一页
                        </div>
                    </a-list-item>
                    <a-list-item>
                        <div @click="changePdfPage('next')"
                             :style="currentPage===pageCount?'cursor: not-allowed;':''">
                            下一页
                        </div>
                    </a-list-item>
                    <a-list-item>
                        <div @click="changePdfPage('last')"
                        >
                            尾页
                        </div>
                    </a-list-item>
                </a-list>
            </a-space>
        </div>

        <div class="pdfArea">
            <pdf :src="srcPdf"
                 ref="pdf"
                 :page="currentPage"
                 @num-pages="pageCount=$event"
                 @page-loaded="currentPage=$event"
                 @loaded="loadPdfHandler"
                 @link-clicked="currentPage = $event"
                 style="display: inline-block;width:100%"></pdf>
        </div>
    </div>
</template>

<script>
import pdf from 'vue-pdf'

export default {
    components: {
        pdf
    },
    computed: {
    },
    created () {
        console.log('query:', this.$route.query)
        this.srcPdf = decodeURIComponent(this.$route.query.pdfPath)
    },
    destroyed () {
    },
    mounted () {

    },
    data () {
        return {
            // ----- vuepdf -----
            // src静态路径: /static/xxx.pdf
            // src服务器路径: 'http://.../xxx.pdf'
            // src: srcPdf,
            // 当前页数
            currentPage: 0,
            // 总页数
            pageCount: 0,
            // 加载进度
            loadedRatio: 0
        }
    },
    methods: {
        // 页面回到顶部
        toTop () {
            document.getElementById('container').scrollTop = 0
        },
        // 输入页码时校验
        inputEvent () {
            if (this.currentPage > this.pageCount) {
                // 1. 大于max
                this.currentPage = this.pageCount
            } else if (this.currentPage < 1) {
                // 2. 小于min
                this.currentPage = 1
            }
        },
        // 切换页数
        changePdfPage (val) {
            if (val === 'pre' && this.currentPage > 1) {
                // 切换后页面回到顶部
                this.currentPage--
                this.toTop()
            } else if (val === 'next' && this.currentPage < this.pageCount) {
                this.currentPage++
                this.toTop()
            } else if (val === 'first') {
                this.currentPage = 1
                this.toTop()
            } else if (val === 'last' && this.currentPage < this.pageCount) {
                this.currentPage = this.pageCount
                this.toTop()
            }
        },

        // pdf加载时
        loadPdfHandler (e) {
            // 加载的时候先加载第一页
            this.currentPage = 1
        },

    }
}
</script>

<style scoped>
#container {
    overflow: auto;
    font-family: PingFang SC;
    width: 100%;
    display: flex;
    position: relative;
}

/* 功能按钮区 */
.right-btn {
    right:4rem;
    position: fixed;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    z-index: 99;
}

.pdfArea {
    width: 80%;
}

/*在谷歌下移除input[number]的上下箭头*/
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    -webkit-appearance: none !important;
    margin: 0;
}
/*在firefox下移除input[number]的上下箭头*/
input[type='number'] {
    -moz-appearance: textfield;
}

.inputNumber {
    border-radius: 8px;
    border: 1px solid #999999;
    font-size: 18px;
    width: 2rem;
    text-align: center;
}
.inputNumber:focus {
    border: 1px solid #00aeff;
    background-color: rgba(18, 163, 230, 0.096);
    outline: none;
    transition: 0.2s;
}

</style>

如何使用?

先注册路由:src/router/index.js

import MyPdf from '../components/MyPdf'

export default new VueRouter({
    routes: [
            {
                path: '/apply-contract-pdf',
                name: 'apply-contract-pdf',
                component: MyPdf
            },
]})

再通过如下方法进行预览:

previewPdf(pdfPath){
    let routeUrl = this.$router.resolve({
        path: '/preview-pdf',
        query:{pdfPath}
    })
    window.open(routeUrl.href, '_blank')
},

总结

本篇介绍了vue-pdf的一些简单使用,包括首页展示、分页展示等;

其实还有一些进度条展示这里没列出来,感兴趣的可以配合a-progress组件和progress 属性进行体验

posted @ 2022-03-18 17:57  汤圆学Java  阅读(94)  评论(0编辑  收藏  举报