前言
公司最近有这么个需求,在线预览pdf、excel、word功能,正好有点时间,稍微整理记录一下。
PDF预览
PDF预览用的是pdfjs-dist这个pdf.js构建库
安装
最好记住自己安装的版本,因为后面解析字体的时候可能会用到
css代码解读复制代码pnpm i pdfjs-dist
使用
- 项目中引用 因为我安装的版本是2.6.347,所以我这里用cdn引入需要的工作包版本是2.6.347,最好是引用本地的,因为有些内网没法访问cdn的话会造成异常。
js代码解读复制代码import * as pdfjsLib from ''pdfjs-dist/build/pdf''; pdfjsLib.GlobalWorkerOptions.workerSrc = ''https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.worker.min.js'';
- 上传组件 这时候要简单的写一个上传组件,给组件加上id以及change事件方便测试使用效果
xml代码解读复制代码<div> <!-- 上传组件 --> <input type="file" id="fileInput" @change="uploadChange"> </div>
- 添加一个渲染容器 此时需要一个渲染容器来呈现需要预览的内容,我们添加一个空的div标签,给加上一个id属性,也可以用ref。因为内容需要用到canvas画布,所以我们要v-for循环一个画布,渲染多张,如果需求只是需要渲染一张就不需要v-for循环了,但是此场景下也是兼容一张渲染的。
xml代码解读复制代码<div> <!-- 上传组件 --> <input type="file" id="fileInput" @change="uploadChange"> <!-- 预览容器,内容呈现区 --> <div id="canvasCont"> <canvas v-for="index in canvasTotalPage" :id="`myVancas${index}`" :key="index"></canvas> </div> </div>
- 完善change方法
scss代码解读复制代码/** * 上传pdf文件 */ function uploadFile() { // 获取到上传文件input组件的dom实例,实际上用ref也行,但是vue2和vue3使用ref的写法上有区别,就不麻烦搞ref了,统一用id了 const file = document.getElementById(''fileinput'').files[0]; // FileReader是一个强大的读取文件的api,创建一个FileReader实例来读取文件 const reader = new FileReader(); // 将文件内容转换为base64编码的url,方便pdfjs-dist插件加载pdf文档 reader.readAsDataURL(file); // FileReader读取文件完成后触发,此时拿到base64编码的url了 reader.onload = () => { // 直接用atob代码ts会报错,因为网上说这个已经弃用了,但是在代码里面还是能用,不过ts会报错 const data = window.atob(reader.result.substring(reader.result.indexOf('','') + 1)); // 拿到base64编码的url去加载pdf文档 loadPdfData(data); }; }
- 编写loadPdfData函数
php代码解读复制代码/** * pdf下载以及加载函数(异步) */ const pdfLoadTask = ref(); /** * 引入pdf.js的字体,插件无法解析某些特殊字体,所以需要加载特定字体cmap文件 * 使用cdn的的形式,内网或者没网的时候会失效,建议直接把这个目录下的文件下载到本地,引用本地的资源 */ const cmapUrl = ref<string>(''https://unpkg.com/pdfjs-dist@2.6.347/cmaps/''); /** * 通过pdfjs-dist插件生产pdf * @param data base64编码的url */ function loadPdfData(data: string) { // pdfjsLib.getDocument是获取pdf文档的方法,返回的是premise对象,对象包含一些pdf文档的信息以及操作pdf文档的api pdfLoadTask.value = pdfjsLib.getDocument({ data: data, cMapUrl: cmapUrl.value, cMapPacked: true, }); // 渲染页面,至少一页 renderPage(1); }
- 编写renderPage函数
ini代码解读复制代码/** * 需要绘制pdf的总页数 */ const canvasTotalPage = ref<number>(1); /** * 渲染指定页码的pdf文档 * @param num 指定页码 */ function renderPage(num: number) { // 异步函数结束后返回pdf的基本信息以及一些api,是一个对象 pdfLoadTask.value.promise.then((pdf: any) => { // 记录一下总页数,多页的情况,每页都需要新建一个画布 canvasTotalPage.value = pdf.numPages; // 通过调用pdf对象的getPage方法,将指定的页码传入,可以获取传入页码的引用 pdf.getPage(num).then((page: any) => { // 获取canvas的DOM对象 const canvas: any = document.getElementById(`myVancas${num}`); // 获取canvas的渲染上下文(包含canvas的引用以及绘图功能) const ctx = canvas.getContext(''2d''); // 获取页面的像素比率 const ratio = getRatio(ctx); // 页面的视口宽度,也就是元素的可视宽度 // 不太理解用offsetWidth,可以看下这篇文章https://zhuanlan.zhihu.com/p/603633893 const viewWidth = document.getElementById(''canvasCont'').offsetWidth; // pdf文档的宽度 const pdfWidth = page.view[2]; // 根据视口的宽度/pdf文档宽度得到缩放比 const scale = viewWidth / pdfWidth; // 获取pdf文档的缩放后基本信息 const viewport = page.getViewport({ scale }); // 画布的宽高需要根据实际像素调整,避免出现模糊的情况 canvas.width = viewport.width * ratio; canvas.height = viewport.height * ratio; // 准备page.render()函数需要的参数 const renderContext = { canvasContext: ctx, viewport: viewport, }; // 将数据渲染到画布上 page.render(renderContext) // 多页pdf的情况 if (num < pdf.numPages) { renderPage(num + 1); } }); }); } /** * 获取页面的比率 * @param ctx canvas绘图环境的上下文 */ function getRatio(ctx: any) { // window对象中的属性devicePixelRatio,这个属性决定了浏览器会用多少实际的像素来渲染一个像素(也可以理解为css像素) // 至于为什么要获取这个,不同设备的像素比不同,当这个像素比为2时,会用2个实际像素来渲染一个css像素,就相当于放大了视图一倍,会导致屏幕显示模糊 const dpr = window.devicePixelRatio || 1; // canvas的context上下文中也存在一些关于像素比的属性,逻辑是canvas是用几个像素来存储画布信息 // webkit、moz、ms、o这些代码的是不同浏览器之间的内核识别码 const bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; return dpr / bsr; }
完整代码
ini代码解读复制代码<template> <div> <input id="fileinput" type="file" @change="uploadFile" /> <div id="canvasCont"> <canvas v-for="index in canvasTotalPage" :id="`myVancas${index}`" :key="index"></canvas> </div> </div> </template> <script setup lang="ts"> import { ref } from ''vue''; import * as pdfjsLib from ''pdfjs-dist/build/pdf''; pdfjsLib.GlobalWorkerOptions.workerSrc = ''https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.worker.min.js''; /** * pdf下载以及加载函数(异步) */ const pdfLoadTask = ref(); /** * 需要绘制pdf的总页数 */ const canvasTotalPage = ref<number>(1); /** * 引入pdf.js的字体,插件无法解析某些特殊字体,所以需要加载特定字体cmap文件 * 使用cdn的的形式,内网或者没网的时候会失效,建议直接把这个目录下的文件下载到本地,引用本地的资源 */ const cmapUrl = ref<string>(''https://unpkg.com/pdfjs-dist@2.6.347/cmaps/''); /** * 上传pdf文件 */ function uploadFile() { // 获取到上传文件input组件的dom实例,实际上用ref也行,但是vue2和vue3使用ref的写法上有区别,就不麻烦搞ref了,统一用id了 const file = document.getElementById(''fileinput'').files[0]; // FileReader是一个强大的读取文件的api,创建一个FileReader实例来读取文件 const reader = new FileReader(); // 将文件内容转换为base64编码的url,方便pdfjs-dist插件加载pdf文档 reader.readAsDataURL(file); // FileReader读取文件完成后触发,此时拿到base64编码的url了 reader.onload = () => { // 直接用atob代码ts会报错,因为网上说这个已经弃用了,但是在代码里面还是能用,不过ts会报错 const data = window.atob(reader.result.substring(reader.result.indexOf('','') + 1)); // 拿到base64编码的url去加载pdf文档 loadPdfData(data); }; } /** * 通过pdfjs-dist插件生产pdf * @param data base64编码的url */ function loadPdfData(data: string) { // pdfjsLib.getDocument是获取pdf文档的方法,返回的是premise对象,对象包含一些pdf文档的信息以及操作pdf文档的api pdfLoadTask.value = pdfjsLib.getDocument({ data: data, cMapUrl: cmapUrl.value, cMapPacked: true, }); // 渲染页面,至少一页 renderPage(1); } /** * 渲染指定页码的pdf文档 * @param num 指定页码 */ function renderPage(num: number) { // 异步函数结束后返回pdf的基本信息以及一些api,是一个对象 pdfLoadTask.value.promise.then((pdf: any) => { // 记录一下总页数,多页的情况,每页都需要新建一个画布 canvasTotalPage.value = pdf.numPages; // 通过调用pdf对象的getPage方法,将指定的页码传入,可以获取传入页码的引用 pdf.getPage(num).then((page: any) => { // 获取canvas的DOM对象 const canvas: any = document.getElementById(`myVancas${num}`); // 获取canvas的渲染上下文(包含canvas的引用以及绘图功能) const ctx = canvas.getContext(''2d''); // 获取页面的像素比率 const ratio = getRatio(ctx); // 页面的视口宽度,也就是元素的可视宽度 // 不太理解用offsetWidth,可以看下这篇文章https://zhuanlan.zhihu.com/p/603633893 const viewWidth = document.getElementById(''canvasCont'').offsetWidth; // pdf文档的宽度,不懂为啥用page.view[2]的话,可以打印一下page看看page返回的具体是啥信息 const pdfWidth = page.view[2]; // 根据视口的宽度/pdf文档宽度得到缩放比 const scale = viewWidth / pdfWidth; // 获取pdf文档的缩放后基本信息 const viewport = page.getViewport({ scale }); // 画布的宽高需要根据实际像素调整,避免出现模糊的情况 canvas.width = viewport.width * ratio; canvas.height = viewport.height * ratio; // 准备page.render()函数需要的参数 const renderContext = { canvasContext: ctx, viewport: viewport, }; // 将数据渲染到画布上 page.render(renderContext) // 多页pdf的情况 if (num < pdf.numPages) { renderPage(num + 1); } }); }); } /** * 获取页面的比率 * @param ctx canvas绘图环境的上下文 */ function getRatio(ctx: any) { // window对象中的属性devicePixelRatio,这个属性决定了浏览器会用多少实际的像素来渲染一个像���(也可以理解为css像素) // 至于为什么要获取这个,不同设备的像素比不同,当这个像素比为2时,会用2个实际像素来渲染一个css像素,就相当于放大了视图一倍,会导致屏幕显示模糊 const dpr = window.devicePixelRatio || 1; // canvas的context上下文中也存在一些关于像素比的属性,逻辑是canvas是用几个像素来存储画布信息 // webkit、moz、ms、o这些代码的是不同浏览器之间的内核识别码 const bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; return dpr / bsr; } </script>
效果演示
单页
多页(数据结构一本书)
多页其实滚动条特别长,注意看右边的滚动条
PDF预览加水印
加水印比较简单,在PDF预览代码的page.render函数回调中创建一个画布水印,加到具体页面中。
实现代码
ini代码解读复制代码// 将数据渲染到画布上 page.render(renderContext).promise.then(() => { // 添加水印 addWatermark(num, canvas.width, canvas.height); }); /** * 在画布上添加水印 * @param num 画布索引 */ function addWatermark(num: number, width: number, height: number) { const canvas: any = document.getElementById(`myVancas${num}`); const ctx = canvas.getContext(''2d''); // 方法在指定的方向内重复指定的元素,元素可以是图片、视频,或者其他 <canvas> 元素,被重复的元素可用于绘制/填充矩形、圆形或线条等等。 const pattern = ctx.createPattern(initWatermark(), ''repeat''); // 创建矩形 ctx.rect(0, 0, width, height); // fillStyle 属性设置或返回用于填充绘画的颜色、渐变或模式,也可以设置pattern。 ctx.fillStyle = pattern; // 当前的图像 ctx.fill(); } /** * 初始化水印元素 */ function initWatermark() { const canvas = document.createElement(''canvas''); canvas.width = 200; canvas.height = 200; const ctx: any = canvas.getContext(''2d''); ctx.rotate((45 * Math.PI) / 180); ctx.font = ''15px Verdana''; ctx.fillStyle = ''rgba(0, 0, 0, 0.8)''; ctx.fillText(''我是水印'', 30, 30); return canvas; }
预览加水印完整代码
ini代码解读复制代码<template> <div> <input id="fileinput" type="file" @change="uploadFile" /> <div id="canvasCont"> <canvas v-for="index in canvasTotalPage" :id="`myVancas${index}`" :key="index"></canvas> </div> </div> </template> <script setup lang="ts"> import { ref } from ''vue''; import * as pdfjsLib from ''pdfjs-dist/build/pdf''; pdfjsLib.GlobalWorkerOptions.workerSrc = ''https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.worker.min.js''; /** * pdf下载以及加载函数(异步) */ const pdfLoadTask = ref(); /** * 需要绘制pdf的总页数 */ const canvasTotalPage = ref<number>(1); /** * 引入pdf.js的字体,插件无法解析某些特殊字体,所以需要加载特定字体cmap文件 * 使用cdn的的形式,内网或者没网的时候会失效,建议直接把这个目录下的文件下载到本地,引用本地的资源 */ const cmapUrl = ref<string>(''https://unpkg.com/pdfjs-dist@2.6.347/cmaps/''); /** * 上传pdf文件 */ function uploadFile() { // 获取到上传文件input组件的dom实例,实际上用ref也行,但是vue2和vue3使用ref的写法上有区别,就不麻烦搞ref了,统一用id了 const file = document.getElementById(''fileinput'').files[0]; // FileReader是一个强大的读取文件的api,创建一个FileReader实例来读取文件 const reader = new FileReader(); // 将文件内容转换为base64编码的url,方便pdfjs-dist插件加载pdf文档 reader.readAsDataURL(file); // FileReader读取文件完成后触发,此时拿到base64编码的url了 reader.onload = () => { // 直接用atob代码ts会报错,因为网上说这个已经弃用了,但是在代码里面还是能用,不过ts会报错 const data = window.atob(reader.result.substring(reader.result.indexOf('','') + 1)); // 拿到base64编码的url去加载pdf文档 loadPdfData(data); }; } /** * 通过pdfjs-dist插件生产pdf * @param data base64编码的url */ function loadPdfData(data: string) { // pdfjsLib.getDocument是获取pdf文档的方法,返回的是premise对象,对象包含一些pdf文档的信息以及操作pdf文档的api pdfLoadTask.value = pdfjsLib.getDocument({ data: data, cMapUrl: cmapUrl.value, cMapPacked: true, }); // 渲染页面,至少一页 renderPage(1); } /** * 渲染指定页码的pdf文档 * @param num 指定页码 */ function renderPage(num: number) { // 异步函数结束后返回pdf的基本信息以及一些api,是一个对象 pdfLoadTask.value.promise.then((pdf: any) => { // 记录一下总页数,多页的情况,每页都需要新建一个画布 canvasTotalPage.value = pdf.numPages; // 通过调用pdf对象的getPage方法,将指定的页码传入,可以获取传入页码的引用 pdf.getPage(num).then((page: any) => { // 获取canvas的DOM对象 const canvas: any = document.getElementById(`myVancas${num}`); // 获取canvas的渲染上下文(包含canvas的引用以及绘图功能) const ctx = canvas.getContext(''2d''); // 获取页面的像素比率 const ratio = getRatio(ctx); // 页面的视口宽度,也就是元素的可视宽度 // 不太理解用offsetWidth,可以看下这篇文章https://zhuanlan.zhihu.com/p/603633893 const viewWidth = document.getElementById(''canvasCont'').offsetWidth; // pdf文档的宽度,不懂为啥用page.view[2]的话,可以打印一下page看看page返回的具体是啥信息 const pdfWidth = page.view[2]; // 根据视口的宽度/pdf文档宽度得到缩放比 const scale = viewWidth / pdfWidth; // 获取pdf文档的缩放后基本信息 const viewport = page.getViewport({ scale }); // 画布的宽高需要根据实际像素调整,避免出现模糊的情况 canvas.width = viewport.width * ratio; canvas.height = viewport.height * ratio; // 准备page.render()函数需要的参数 const renderContext = { canvasContext: ctx, viewport: viewport, }; // 将数据渲染到画布上 page.render(renderContext).promise.then(() => { // 添加水印 addWatermark(num, canvas.width, canvas.height); }); // 多页pdf的情况 if (num < pdf.numPages) { renderPage(num + 1); } }); }); } /** * 在画布上添加水印 * @param num 画布索引 */ function addWatermark(num: number, width: number, height: number) { const canvas: any = document.getElementById(`myVancas${num}`); const ctx = canvas.getContext(''2d''); // 方法在指定的方向内重复指定的元素,元素可以是图片、视频,或者其他 <canvas> 元素,被重复的元素可用于绘制/填充矩形、圆形或线条等等。 const pattern = ctx.createPattern(initWatermark(), ''repeat''); // 创建矩形 ctx.rect(0, 0, width, height); // fillStyle 属性设置或返回用于填充绘画的颜色、渐变或模式,也可以设置pattern。 ctx.fillStyle = pattern; // 当前的图像 ctx.fill(); } /** * 初始化水印元素 */ function initWatermark() { const canvas = document.createElement(''canvas''); canvas.width = 200; canvas.height = 200; const ctx: any = canvas.getContext(''2d''); ctx.rotate((45 * Math.PI) / 180); ctx.font = ''15px Verdana''; ctx.fillStyle = ''rgba(0, 0, 0, 0.8)''; ctx.fillText(''我是水印'', 30, 30); return canvas; } /** * 获取页面的比率 * @param ctx canvas绘图环境的上下文 */ function getRatio(ctx: any) { // window对象中的属性devicePixelRatio,这个属性决定了浏览器会用多少实际的像素来渲染一个像素(也可以理解为css像素) // 至于为什么要获取这个,不同设备的像素比不同,当这个像素比为2时,会用2个实际像素来渲染一个css像素,就相当于放大了视图一倍,会导致屏幕显示模糊 const dpr = window.devicePixelRatio || 1; // canvas的context上下文中也存在一些关于像素比的属性,逻辑是canvas是用几个像素来存储画布信息 // webkit、moz、ms、o这些代码的是不同浏览器之间的内核识别码 const bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; return dpr / bsr; } </script>
效果实现
单张水印
多张水印
word预览
word预览介绍两种解决方案
docx-preview
安装
css代码解读复制代码pnpm i docx-preview
使用
- 项目中引用
js代码解读复制代码import { renderAsync } from ''docx-preview'';
- 上传组件 这时候要简单的写一个上传组件,给组件加上id以及change事件方便测试使用效果
xml代码解读复制代码<div> <!-- 上传组件 --> <input type="file" id="fileInput" @change="uploadChange"> </div>
- 添加一个渲染容器 此时需要一个渲染容器来呈现需要预览的内容,我们添加一个空的div标签,给加上一个id属性,也可以用ref。
xml代码解读复制代码<div> <!-- 上传组件 --> <input type="file" id="fileInput" @change="uploadChange"> <!-- 预览容器,内容呈现区 --> <div id="preview"></div> </div>
- 完善change方法
javascript代码解读复制代码/** * 文件上传事件 */ function uploadChange(){ const fileInputDom = document.getElementById(''fileInput'') const previewDom = document.getElementById(''preview'') renderAsync(fileInputDom.files[0], previewDom) }
renderAsync
我们一般都是通过调用renderAsync这个异步函数来触发文件的预览的,他有4个参数
分别是:
- document 类型可以是
Blob | ArrayBuffer | Uint8Array - bodyContainer 用来呈现内容的
html对象 - styleContainer 给内容加样式
- options 一些自定义api,对象形式
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| className | string | docx | 默认的文档样式类的类名/前缀 |
| inWrapper | boolean | true | 启用围绕文档内容的包装器渲染 |
| ignoreWidth | boolean | false | 禁用页面呈现宽度 |
| ignoreHeight | boolean | false | 禁用页面呈现高度 |
| ignoreFonts | boolean | false | 禁用字体渲染 |
| breakPages | boolean | true | 在遇到分页符时启用分页 |
| ignoreLastRenderedPageBreak | boolean | true | 禁用lastRenderedPageBreak标签的分页 |
| experimental | boolean | false | 启用实验功能(制表位计算) |
| trimXmlDeclaration | boolean | true | xml声明 如果true 在文档解析之前移除 |
| useBase64URL | boolean | false | 是否转化为base64编码的url |
| useMathMLPolyfill | boolean | false | 是否包括用于chrome、edge等的MathML多填充 |
| renderChanges | boolean | false | 启用文档更改的实验性渲染(插入/删除) |
| renderHeaders | boolean | true | 启用头部渲染 |
| renderFooters | boolean | true | 启用底部渲染 |
| renderFootnotes | boolean | true | 渲染脚注 |
| renderEndnotes | boolean | true | 渲染尾注 |
| debug | boolean | false | 启用额外的日子记录 |
简单的使用的话styleContainer和options都可以省略
完整代码
vue.js代码解读复制代码<template> <div> <input type="file" id="fileInput" @change="uploadChange"> <div id="preview"></div> </div> </template> <script setup lang="ts"> import { renderAsync } from ''docx-preview''; /** * 文件上传事件 */ function uploadChange(){ const fileInputDom = document.getElementById(''fileInput'') const previewDom = document.getElementById(''preview'') renderAsync(fileInputDom.files[0], previewDom) } </script>
效果演示
目前没发现能自动和word一样分页,如果word不加分页符,自动全部合并成一页
不分页
分页
分页要在word加分隔符并且options的breakPages需要设置为true
mammoth
看下一下mammoth,它的样式调起来好像比较麻烦。
安装
css代码解读复制代码pnpm i mammoth
使用
- 项目中引用
js代码解读复制代码import mammoth from ''mammoth''
- 上传组件 这时候要简单的写一个上传组件,给组件加上id以及change事件方便测试使用效果
xml代码解读复制代码<div> <!-- 上传组件 --> <input type="file" id="fileInput" @change="uploadChange"> </div>
- 添加一个渲染容器 此时需要一个渲染容器来呈现需要预览的内容,我们添加一个空的div标签,给加上一个id属性,也可以用ref。
xml代码解读复制代码<div> <!-- 上传组件 --> <input type="file" id="fileInput" @change="uploadChange"> <!-- 预览容器,内容呈现区 vHtml作为插入内容--> <div id="preview" v-html="vHtml"></div> </div>
- 完善change方法
javascript代码解读复制代码/** * v-html */ const vHtml = ref<string>('''') /** * 文件上传 */ function uploadChange(){ // 读取上传后的文件 const file= document.getElementById(''fileInput'').files[0] // 创建FileReader实例 const reader = new FileReader() // 指定读取的内容是ArrayBuffer类型的数据 reader.readAsArrayBuffer(file) reader.onload = () => { // 读取完成,调用mammoth的convertToHtml函数获取解析到的数据 mammoth.convertToHtml({arrayBuffer: reader.result as ArrayBuffer}).then((resultObject)=>{ vHtml.value = resultObject.value }) } }
convertToHtml
我们一般都是通过调用mammoth的convertToHtml这个异步函数来触发word文件的解析,他有2个参数
分别是:
- input 类型可以是
path | ArrayBuffer | buffer - options 一些自定义api,对象形式
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| styleMap | string/array | 控制Word样式到HTML的映射 | |
| includeEmbeddedStyleMap | boolean | true | 是否包含嵌入的样式映射 |
| includeDefaultStyleMap | boolean | true | styleMap中传递的样式映射是否与默认样式映射相结合 |
| convertImage | boolean | true | 图像是否被转换为<img>元素,源代码包含在src属性中 |
| ignoreEmptyParagraphs | boolean | true | 是否忽略空段落 |
| idPrefix | string | 一个字符串,用于在任何生成的ID(例如书签、脚注和尾注使用的ID)前加前缀。默认为空字符串 | |
| transformDocument | function | 如果设置了此函数,则此函数将应用于转换为HTML之前从docx文件读取的文档 |
完整代码
xml代码解读复制代码<template> <div> <input type="file" id="fileInput" @change="uploadChange"> <div id="preview" v-html="vHtml"></div> </div> </template> <script setup lang="ts"> import mammoth from ''mammoth'' import { ref } from ''vue''; /** * v-html */ const vHtml = ref<string>('''') /** * 文件上传 */ function uploadChange(){ // 读取上传后的文件 const file= document.getElementById(''fileInput'').files[0] // 创建FileReader实例 const reader = new FileReader() // 指定读取的内容是ArrayBuffer类型的数据 reader.readAsArrayBuffer(file) reader.onload = () => { // 读取完成,调用mammoth的convertToHtml函数获取解析到的数据 mammoth.convertToHtml({arrayBuffer: reader.result as ArrayBuffer}).then((resultObject)=>{ vHtml.value = resultObject.value }) } } </script>
效果演示
excel预览
安装
css代码解读复制代码pnpm i xlsx
使用
- 项目中引用
js代码解读复制代码import * as XLSL from ''xlsx''
- 上传组件 这时候要简单的写一个上传组件,给组件加上id以及change事件方便测试使用效果
xml代码解读复制代码<div> <!-- 上传组件 --> <input type="file" id="fileInput" @change="uploadChange"> </div>
- 添加一个渲染容器
xml代码解读复制代码<div> <!-- 上传组件 --> <input type="file" id="fileInput" @change="uploadChange"> <!-- 预览容器,内容呈现区 vHtml作为插入内容--> <div v-html="vHtml"></div> </div>
- 完善change方法
csharp代码解读复制代码/** * v-html */ const vHtml = ref<string>('''') /** * 文件上传 */ function uploadChange(){ // 读取上传后的文件 const file= document.getElementById(''fileInput'').files[0] // 创建FileReader实例 const reader = new FileReader() // 指定读取的内容是ArrayBuffer类型的数据 reader.readAsArrayBuffer(file) reader.onload = () => { // 读取完成,调用XLSL的read函数获取解析上传的xlsx let workbook = XLSL.read(reader.result,{type:''array''}) // 一般都只有一个sheet,取第一个sheet的名称,方便取到sheet的数据 let sheetNames = workbook.SheetNames[0] // 根据sheet的名称获取sheet数据解析成html let html = XLSL.utils.sheet_to_html(workbook.Sheets[sheetNames]) // 将渲染容器替换成解析出来的html标签渲染 vHtml.value = html } }
此时渲染出来的html是没有样式的table,需要自己给table加样式,我们需要给table额外加自定义样式,比如边框、边距啥的
xml代码解读复制代码<style> table { border: 1px solid black; } th { border: 1px solid black; } td { border: 1px solid black; } </style>
效果演示
加border样式
默认样式
完整代码
xml代码解读复制代码<template> <div> <input type="file" id="fileInput" @change="uploadChange"> <div v-html="vHtml"></div> </div> </template> <script setup lang="ts"> import * as XLSL from ''xlsx'' import { ref } from ''vue''; /** * v-html */ const vHtml = ref<string>('''') /** * 文件上传 */ function uploadChange(){ // 读取上传后的文件 const file= document.getElementById(''fileInput'').files[0] // 创建FileReader实例 const reader = new FileReader() // 指定读取的内容是ArrayBuffer类型的数据 reader.readAsArrayBuffer(file) reader.onload = () => { // 读取完成,调用XLSL的read函数获取解析上传的xlsx let workbook = XLSL.read(reader.result,{type:''array''}) // 一般都只有一个sheet,取第一个sheet的名称,方便取到sheet的数据 let sheetNames = workbook.SheetNames[0] // 根据sheet的名称获取sheet数据解析成html let html = XLSL.utils.sheet_to_html(workbook.Sheets[sheetNames]) // 将渲染容器替换成解析出来的html标签渲染 vHtml.value = html } } </script> <!-- <style> table { border: 1px solid black; } th { border: 1px solid black; } td { border: 1px solid black; } </style> -->
建议输出json
建议输出成json格式的,这样的话,可以只拿到数据,然后放到自己想要的组件中。输出的数据是对象数组形式,数组中的对象的key就是excel的第一行。引入一个ant-design-vue的table,将数据解析出来看看
引入ant-design-vue table
- 安装ant-design-vue
css代码解读复制代码pnpm i ant-design-vue
- main.ts引入
javascript代码解读复制代码import Antd from ''ant-design-vue''; createApp(App).use(Antd).mount(''#app'')
效果演示
使用完整代码
xml代码解读复制代码<template> <div> <input type="file" id="fileInput" @change="uploadChange"> <a-table :dataSource="dataSource" :columns="columns" bordered /> </div> </template> <script setup lang="ts"> import * as XLSL from ''xlsx'' import { ref } from ''vue''; interface rowType { [key:string]: string } /** * table columns */ const columns = ref<rowType[]>([]) /** * table dataSource */ const dataSource = ref<rowType[]>([]) /** * 文件上传 */ function uploadChange(){ // 读取上传后的文件 const file= document.getElementById(''fileInput'').files[0] // 创建FileReader实例 const reader = new FileReader() // 指定读取的内容是ArrayBuffer类型的数据 reader.readAsArrayBuffer(file) reader.onload = () => { // 读取完成,调用XLSL的read函数获取解析上传的xlsx let workbook = XLSL.read(reader.result,{type:''array''}) // 一般都只有一个sheet,取第一个sheet的名称,方便取到sheet的数据 let sheetNames = workbook.SheetNames[0] // 根据sheet的名称获取sheet数据解析成json let jsonData = XLSL.utils.sheet_to_json(workbook.Sheets[sheetNames]) as rowType[] // 取excel第一行当表头 Object.keys(jsonData[0]).forEach((item: string)=>{ columns.value.push({ title: item, dataIndex: item }) }) // 其他行都当做数据 jsonData.forEach((item: rowType)=>{ let obj:rowType = {} Object.keys(item).map((key:string)=>{ obj[key] = item[key] }) dataSource.value.push(obj) }) } } </script>