前言:
微信小程序自身提供的文件预览功能
wx.openDocument()
,打开文件时会跳出微信小程序内部,转而在H5
中打开,这样的话,如果转发出去,就只能转发这个文件。而我们领导想要的效果是,文件要在小程序内打开,然后分享出去的时候就可以跟小程序的分享效果一致------------是小程序的卡片形式。
另外,我们还有一个需求,需要获取用户阅读文件的时长,所以只有在小程序内部打开的文件,才能监听到这个行为。
一、需要解决的问题
- 实现在微信小程序 内 预览文件,分享文件时,对方看到的是小程序卡片样式;
- 获取用户阅读文件的时长;
- 需要支持多种文件格式。
备注:
本文提供的方案支持 word(.docx)、pdf、excel(.xlsx, .xls)多种文档的在线预览方案 ,同时还支持 图片预览。
二、实现效果
这是最终需要实现的效果:
而如果调用小程序的预览文件方法wx.openDocument()
的话,就会是下图的效果:
显而易见,最明显的区别就是右上角是否有小程序的退出胶囊,这就代表了------------文件是否是在 小程序内部
打开的。
三、实现思路
一般情况下,在小程序中实现文件预览功能,一般都是用wx.downloadFile()
结合wx.openDocument()
使用来实现,但是这样打开文件就会跳出小程序,无法实现分享卡片功能,也无法获取到用户的阅读时间。
- 尝试过小程序提供的插件,只支持pdf;
- 后来找到
vue-office
插件,放在小程序中会有很多问题; - 最后想到了可以在
H5
中用vue-office
插件,然后在小程序中内嵌一下,完美解决。
四、解决方案------------内嵌 H5
页面实战妙法
- 首先在
H5
项目中,新建一个页面,然后按照 vue-office文档 步骤即可(我这边H5
项目是Vue
的):
下面将全部代码附上,可直接复制粘贴:
html
<template>
<div class="officePreview" ref="reader" @scroll="handleScroll">
<vue-office-pdf
v-if="fileFormat === 'pdf'"
:src="pdf"
@rendered="renderedHandler"
@error="errorHandler"
@loaded="onPdfLoaded"
@page-loaded="currentPage = $event"
style="height: 100vh;"
/>
<vue-office-docx
v-if="fileFormat === 'docx'"
:src="docx"
style="height: 100vh;"
@rendered="rendered"
/>
<vue-office-excel
v-if="fileFormat === 'xlsx' || fileFormat === 'xls'"
:src="excel"
style="height: 100vh;"
@rendered="renderedHandler"
@error="errorHandler"
/>
</div>
</template>
js
<script>
// 引入VueOfficePdf组件
import VueOfficePdf from '@vue-office/pdf';
// 引入VueOfficeDocx组件
import VueOfficeDocx from '@vue-office/docx';
// 引入相关样式
import '@vue-office/docx/lib/index.css';
// 引入VueOfficeExcel组件
import VueOfficeExcel from '@vue-office/excel';
// 引入相关样式
import '@vue-office/excel/lib/index.css';
export default {
components: {
VueOfficePdf,
VueOfficeDocx,
VueOfficeExcel,
},
data() {
return {
fileFormat: '', // 文件类型
fileUrl: '', // 文件地址
pdf: '',
docx: '',
excel: '',
};
},
created() {
// 小程序中,由于传值比较多,传值时需要编码,所以这里接受的时候,需要解码
const fileData = JSON.parse(decodeURIComponent(this.$route.query.file));
this.fileUrl = fileData.fileUrl;
this.fileFormat = fileData.fileFormat;
// 此处其实可优化,可根据自己情况写
if (this.fileFormat === 'pdf') {
this.pdf = this.fileUrl;
} else if (this.fileFormat === 'docx') {
this.docx = this.fileUrl;
} else if (this.fileFormat === 'xlsx' || this.fileFormat === 'xls') {
this.excel = this.fileUrl;
}
},
}
</script>
css
<style lang="less" scoped>
.officePreview {
height: 100vh;
position: relative;
overflow-y: auto;
padding-bottom: px2vw(40);
}
</style>
- 在小程序中,点击文件,跳转到专门放置
web-view
内嵌H5
的新页面,做好传值:
js
openFile() {
this.data.fileData.fileId = this.data.fileId
this.data.fileData.playCourseChapterId = this.data.playCourseChapterId
this.data.fileData.id = this.data.id
this.data.fileData.coursePlanId = this.data.coursePlanId
this.data.fileData.classification = this.data.courseInfo.classification
this.data.fileData.playFileId = this.data.courseInfo.playFileId
const url = `${api.h5Url}officePreview`;
// 由于需要传的值比较多,所有需要编码
wx.navigateTo({
url: `/pages/pdfWebView/pdfWebView?url=${url}&fileData=${encodeURIComponent(JSON.stringify(this.data.fileData))}`,
})
},
- 在小程序中,新建的
pdfWebView
页面中,专门放置需要打开预览的web-view
和图片预览操作,记录用户阅读时长:
记录用户阅读时长思路:
onLoad
时,记录一下页面打开时间(即用户的阅读开始时间startTime
),在onUnload
时记录一下页面关闭时间(即用户的阅读结束时间endTime
),然后两个时间值相减即可得出时长。图片和文件,可根据文件类型进行判断。
wxml
<view class="container">
<image wx:if="{{imgUrl}}" src="{{imgUrl}}" data-url="{{imgUrl}}" bindtap="fnPreviewImage" class="avatarImg" mode="widthFix"></image>
<web-view wx:elif="{{url}}" src="{{url}}"></web-view>
</view>
js
Page({
data: {
url: '',
imgUrl: '',
fileData: {},
startTime: '', // 阅读开始时间(打开文件时触发)
endTime: '', // 阅读结束时间(关闭文件时触发)
},
onLoad(options) {
const fileData = JSON.parse(decodeURIComponent(options.fileData));
const file = {
fileUrl: fileData.fileUrl,
fileFormat: fileData.fileFormat,
}
if (fileData.fileFormat.indexOf('png') > -1 || fileData.fileFormat.indexOf('jpg') > -1 || fileData.fileFormat.indexOf('jpeg') > -1) {
this.setData({ imgUrl: fileData.fileUrl });
}
this.setData({
url: `${options.url}?file=${encodeURIComponent(JSON.stringify(file))}`,
fileData: fileData,
startTime: new Date().getTime(),
})
},
// 预览图片
fnPreviewImage(e) {
let url = e.currentTarget.dataset.url;
wx.previewImage({
current: url, // 当前显示图片的http链接
urls: [url] // 需要预览的图片http链接列表
})
},
onUnload() {
this.setData({
endTime: new Date().getTime(),
});
const playPeriod = (this.data.endTime - this.data.startTime) / 1000
this.getCourseReportPeriod(playPeriod); // 传给接口
},
})
- 最后不要忘记,把
H5
的域名配置在小程序------------微信公众平台的 业务域名 下,否则发布正式后可能打不开哦。 - 这样就实现了闭环,不仅支持打开的文件类型比较多,而且不用担心太多机型兼容问题。
五、总结
有时候某些需求的实现需要"曲线救国",单纯的某一个生态可能无解或者花钱,如果既想要又想不花钱的话,可能就要稍微麻烦一点。
以上,希望对大家有帮助!