移动端预览pdf组件封装(封装优化)
2024-09-24 本文已影响0人
Amanda妍
项目中使用的是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>