实现微信小程序内预览文件,获取阅读时长

前言:

微信小程序自身提供的文件预览功能wx.openDocument(),打开文件时会跳出微信小程序内部,转而在H5中打开,这样的话,如果转发出去,就只能转发这个文件。

而我们领导想要的效果是,文件要在小程序内打开,然后分享出去的时候就可以跟小程序的分享效果一致------------是小程序的卡片形式。

另外,我们还有一个需求,需要获取用户阅读文件的时长,所以只有在小程序内部打开的文件,才能监听到这个行为。

一、需要解决的问题

  1. 实现在微信小程序 预览文件,分享文件时,对方看到的是小程序卡片样式;
  2. 获取用户阅读文件的时长;
  3. 需要支持多种文件格式。

备注:

本文提供的方案支持 word(.docx)、pdf、excel(.xlsx, .xls)多种文档的在线预览方案 ,同时还支持 图片预览

二、实现效果

这是最终需要实现的效果:

而如果调用小程序的预览文件方法wx.openDocument()的话,就会是下图的效果:

显而易见,最明显的区别就是右上角是否有小程序的退出胶囊,这就代表了------------文件是否是在 小程序内部 打开的。

三、实现思路

一般情况下,在小程序中实现文件预览功能,一般都是用wx.downloadFile()结合wx.openDocument()使用来实现,但是这样打开文件就会跳出小程序,无法实现分享卡片功能,也无法获取到用户的阅读时间。

  1. 尝试过小程序提供的插件,只支持pdf;
  2. 后来找到vue-office插件,放在小程序中会有很多问题;
  3. 最后想到了可以在H5中用vue-office插件,然后在小程序中内嵌一下,完美解决。

四、解决方案------------内嵌 H5 页面实战妙法

  1. 首先在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>
  1. 在小程序中,点击文件,跳转到专门放置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))}`,
    })
  },
  1. 在小程序中,新建的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); // 传给接口
  },
})
  1. 最后不要忘记,把H5的域名配置在小程序------------微信公众平台的 业务域名 下,否则发布正式后可能打不开哦。
  2. 这样就实现了闭环,不仅支持打开的文件类型比较多,而且不用担心太多机型兼容问题。

五、总结

有时候某些需求的实现需要"曲线救国",单纯的某一个生态可能无解或者花钱,如果既想要又想不花钱的话,可能就要稍微麻烦一点。

以上,希望对大家有帮助!

相关推荐
程序员爱技术43 分钟前
Vue 2 + JavaScript + vue-count-to 集成案例
前端·javascript·vue.js
丁总学Java2 小时前
页面、组件、应用、生命周期(微信小程序)
微信小程序·小程序·生命周期
cs_dn_Jie5 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic6 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿6 小时前
webWorker基本用法
前端·javascript·vue.js
customer087 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
getaxiosluo8 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
理想不理想v8 小时前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
栈老师不回家9 小时前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙9 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js