业务需求:微信小程序嵌套 H5 以及浙里办嵌套的 H5 需要预览 PDF。
当时拿到需求时候感觉很简单,但是足足试了三种方法才实现预览,讲一下自己的爬坑之路。
第一种方法:a 标签。 侥幸偷懒方法 利用 a 标签点击跳转新页面预览。
<a href="pdf地址" target="_blank" ></a>
ok,就这,然后兴致匆匆测试了下浏览器测试了下是否能预览,嗯,效果不错,提测提测,然后摸了起来,正在遨游的时候钉钉突然闪动起来,心想莫非测试通过了让我发布线上。
测试(吃鱼大师):你这不行啊,H5 可以,嵌套在微信小程序的 web-view 打不来,显示不支持该方式。
我(BUG 缔造者):肯定是你 PDF 的业务域名没有配置,你在微信小程序体验版打开调试工具看看能行不(心虚)。
赶紧放下二郎腿,问了下度娘,发现我这个天真的方案属实不可以。哎哈我还有第二种偷懒方案,iframe
方案二: iframe。 点击 PDF 弹窗,弹窗里面嵌套 iframe。
<iframe src:"PDF地址" width:"100%" height:"100%" ></iframe>
脸滚键盘,霹雳扒拉。在测试还没答复我之前,把第二种方法发布到了测试环境,接着摸。
安静了一会,又安静了一会,哪个不安静的钉又抖动了,心想是不是通知我发布线上环境。
吃鱼大师:你这不行啊,微信小程序 IOS 可以,部分安卓能打开,但是有部分安卓打不来啊。
狂暴战士:谁,谁的安卓打不开?
吃鱼大师:产品
怕了怕了,产品的打不来,只能看看啥原因了,由于开发时间紧张,这里我没有去细致的探索为啥部分安卓 iframe 无法显示。
回到常用的方案吧,利用三方的 PDF 库来打来文件。
这里我是 Vue 的项目所以我选择的是 vue-pdf。
方案三 vue-pdf/pdf.js
在使用过程中最新的 vue-pdf 会报错,所以问完度娘后大家的建议版本是:
"pdfjs-dist": "2.5.207",//安装这个是看了大家的建议避免出错安装的。
"vue-pdf": "4.2.0",
上代码
<VuePdf :src="pdfSrc" ></VuePdf>
上面这种方式是能够预览 PDF 的,但是只能预览一页,所以需要处理下。我这的需求没有那么多的操作要求,这里就展示下预览所有的简单代码。
<template>
<div class="visible" v-if="visible">
<div class="close" @click="handleVisible"></div>
<div v-if="pdfSrc && pageTotalNum && pageNum" class="handle">
<div v-if="pageTotalNum > 1" @click="prePage">上一页</div>
<div>当前页面{{ pageNum }} -- 共{{ pageTotalNum }}页</div>
<div v-if="pageTotalNum > 1" @click="nextPage">下一页</div>
</div>
<div class="iframe" v-if="pdfSrc && !loading">
<VuePdf :src="pdfSrc" ref="pdf" :page="pageNum"></VuePdf>
</div>
<div v-else class="loading">{{ loadingTitle }}</div>
</div>
</template>
<script>
import VuePdf from "vue-pdf";
// 引入这个的原因是发现部分安卓手机无法预览多页的PDF 所以需要兼容下。
import CMapReaderFactory from "vue-pdf/src/CMapReaderFactory.js";
export default {
components: { VuePdf },
props: {
pdfSrc: {
type: String,
default: "",
},
},
data() {
return {
visible: false,
pageNum: 1,
pageTotalNum: 1,
loading: true,
loadingTitle: "文件加载中...",
};
},
mounted() {},
watch: {
pdfSrc: {
handler() {
this.getNumPages();
},
immediate: true,
},
},
methods: {
handleVisible() {
this.visible = !this.visible;
//弹窗时候让底部滚动条消失
// if (this.visible) {
// $("body").css("overflow", "hidden");
// } else $("body").css("overflow", "auto");
},
async getNumPages() {
if (!this.pdfSrc) return;
this.loading = true;
this.loadingTitle = "文件加载中...";
this.pageNum = 1;
this.pageTotalNum = 1;
let loadingTask = VuePdf.createLoadingTask({
url: this.pdfSrc,
CMapReaderFactory,
});
loadingTask.promise
.then((pdf) => {
this.pageTotalNum = pdf?.numPages;
this.loading = false;
})
.catch((err) => {
this.loadingTitle = "文件加载失败!";
console.error("pdf 加载失败", err);
});
},
prePage() {
var page = this.pageNum;
page = page > 1 ? page - 1 : this.pageTotalNum;
this.pageNum = page;
},
nextPage() {
var page = this.pageNum;
page = page < this.pageTotalNum ? page + 1 : 1;
this.pageNum = page;
},
},
};
</script>
<style lang="scss" scoped>
.visible {
box-sizing: border-box;
z-index: 2;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background: #191919;
}
.close {
box-sizing: border-box;
width: 100%;
height: 80px;
color: #c8c9cc;
font-size: 24px;
line-height: 80px;
text-align: right;
padding: 12px 24px;
position: relative;
&::before {
content: "x";
display: inline-block;
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border-radius: 50%;
font-size: 40px;
border: 1px solid #c8c9cc;
background: #f1f8ff;
}
}
.handle {
display: flex;
align-content: center;
justify-content: space-around;
font-size: 24px;
font-weight: bold;
color: #f4f4f4;
height: 80px;
width: 100%;
padding: 24px 0;
box-sizing: border-box;
}
.iframe {
box-sizing: border-box;
width: 100%;
height: calc(100% - 160px);
margin-bottom: 100px;
}
.loading {
font-size: 24px;
color: #f4f4f4;
height: 80px;
line-height: 80px;
text-align: center;
margin-top: 60px;
}
</style>
遇到的问题:部分安卓手机无法预览多页的 PDF,所有在代码里面做了兼容。
import CMapReaderFactory from "vue-pdf/src/CMapReaderFactory.js";
VuePdf.createLoadingTask({
url: this.pdfSrc,
CMapReaderFactory,
});
总结:美好的一天在于瞎折腾。