1.前言
项目上有一个导出word文档的需求,由于有可视化图表,后端导出会比较麻烦,而且前端也需要把word报告展示出来。
所以在前端做预览和导出,记录下技术选型以及实现方案。
2.Word导出
2.1.docx
docx - Generate .docx documents with JavaScript
- 需要使用指定的api生成和修改.docx文件
- 功能强大,除了标题、段落、图片等基础,还支持封面、页眉、页脚、分页等等
import { Document, Packer, Paragraph, TextRun } from "docx";
import { saveAs } from 'file-saver'
const doc = new Document({
sections: [
{
properties: {},
children: [
new Paragraph({
children: [
new TextRun("Hello World"),
new TextRun({
text: "Foo Bar",
bold: true
}),
new TextRun({
text: "\tGithub is the best",
bold: true
})
]
})
]
}
]
});
Packer.toBlob(doc).then((blob) => {
saveAs(blob, "example.docx");
console.log("Document created successfully");
});
2.2.html-docx-js
html-docx-js - npm (npmjs.com)
- 可以根据html导出word
- 图片、canvas需要转换成Data URL的图片
- 不支持封面、页眉页脚等功能
import { saveAs } from 'file-saver'
import htmlDocx from 'html-docx-js'
// 导出图片的格式
const IMAGE_TYPE = 'image/jpeg'
// 导出图片的质量
const IMAGE_QUALITY = 0.8
/**
* HTML导出Docx
* @param {String} params.element 要导出的元素选择器
* @param {String} params.styleString 样式字符串
* @param {String} params.orientation 页面方向 portrait:竖向、landscape:横向
* @param {String} params.filename 导出文件名称
*/
function exportHtmlToDocx({ element, styleString, margins, orientation = 'portrait', filename = 'htmlDocx' }) {
const html = generateContent(element)
const content = `
<html>
<head>
<style>${styleString ? styleString.replace(/(\s{2,}|\n)/g, '') : ''}</style>
</head>
<body>${html}</body>
</html>
`
const converted = htmlDocx.asBlob(content, { orientation, margins })
saveAs(converted, filename)
}
/**
* 生成导出的html字符串
* @param {String} element 要导出的元素选择器
* @returns {String}
*/
function generateContent(element) {
const sourceElement = document.querySelector(element)
let cloneElement = sourceElement.cloneNode(true)
cloneElement = convertImagesToBase64(cloneElement, sourceElement)
cloneElement = convertCanvasToBase64(cloneElement, sourceElement)
return cloneElement.innerHTML
}
/**
* 转换图片地址为Data URL
* @param {Element} cloneElement 拷贝要导出的元素
* @param {Element} sourceElement 要导出的元素
* @returns {Element}
*/
function convertImagesToBase64(cloneElement, sourceElement) {
const sourceImages = sourceElement.querySelectorAll('img')
const cloneImages = cloneElement.querySelectorAll('img')
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
sourceImages.forEach((imgElement, index) => {
const width = imgElement.width
const height = imgElement.height
// preparing canvas for drawing
ctx.clearRect(0, 0, canvas.width, canvas.height)
canvas.width = width
canvas.height = height
ctx.drawImage(imgElement, 0, 0, width, height)
// by default toDataURL() produces png image, but you can also export to jpeg
// checkout function's documentation for more details
const dataURL = canvas.toDataURL(IMAGE_TYPE, IMAGE_QUALITY)
// 替换拷贝的元素而不是源图片
const replaceElement = cloneImages[index]
replaceElement.setAttribute('src', dataURL)
})
canvas.remove()
return cloneElement
}
/**
* 转换canvas画布为img + Base64 URL
* @param {Element} cloneElement 拷贝要导出的元素
* @param {Element} sourceElement 要导出的元素
* @returns {Element}
*/
function convertCanvasToBase64(cloneElement, sourceElement) {
const sourceCanvas = sourceElement.querySelectorAll('canvas')
const cloneCanvas = cloneCanvas.querySelectorAll('canvas')
sourceCanvas.forEach((canvasElement, index) => {
const dataURL = canvasElement.toDataURL(IMAGE_TYPE, IMAGE_QUALITY)
const imgElement = document.createElement('img')
imgElement.src = dataURL
const replaceElement = cloneCanvas[index]
// 把canvas元素替换成img
replaceElement.parentNode.replaceChild(imgElement, replaceElement)
})
return cloneElement
}
export default exportHtmlToDocx
使用:
import exportHtmlToDocx from 'export-html-to-docx.js'
exportHtmlToDocx({
element: '#htmlDocx',
styleString: 'table td{border: 1px solid #e2e2e2}',
margin: { // 页边距
top: 1440, // eq 2.54cm
right: 1440,
bottom: 1440,
left: 1440
},
orientation: 'portrait',
filename: 'word报告'
})
2.2.1.常见问题
- 样式不生效,不支持多个选择器设置样式:每个标签只使用一个选择器,通过不同的选择器设置样式规则,如正常段落、首行缩进段落、首行缩进且加粗段落
<p class="para-indent">首行缩进</p>
<p class="para-bold">文本加粗</p>
<p class="para-indent-bold">首行缩进并加粗</p>
.para{font-size:12pt;}
.para-indent{font-size:12pt;text-indent:2em;}
.para-bold{font-size:12pt;font-weight:bold;}
.para-indent-bold{font-size:12pt;text-indent:2em;font-weight:bold;}
- 表格内的字体无法设置宋体:给文本嵌套一层P标签
<table>
<tr>
<td>
<p class="para-bold">表格内的段落</p>
</td>
</tr>
</table>
- 表格边框样式设置
不能这样(边框样式会有问题):
table {
border-top: 1px solid #000;
border-left: 1px solid #000;
}
table td,
table th {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
}
要用下面这种:
table {
border: 1px solid #000000;
border-collapse: collapse;
border-spacing: 0;
}
table td,
table th {
border: 1px solid #000000;
}
- 导出图片、Canvas为空:等待图片、DOM加载完成再进行导出
2.3.html-docx
这是基于html-docx-js实现的,封装了一些常用的方法,比如上面的canvas转换、文档封面、页眉页脚等。
- 图片使用网络地址,如果要转成Data URL,需要自己改逻辑
- 文档封面、页眉页脚、分页等是通过修改html-docx-js源码实现的
import HtmlToDocx from 'html-docx'
HtmlToDocx({
exportElement: '#html-content', // 需要转换为word的html标签
exportFileName: 'list.docx', // 转换之后word文档的文件名称
StringStyle: '', // css样式以字符串的形式插入进去
margins:{top: 1440,right: 1440,bottom: 1440,left: 1440,header: 720,footer: 720} // word的边距配置
})
3.HTML预览
这里的预览不是使用Office的服务来预览Word文档,只是用HTML展示出来。
尽量使用原生而不是UI库,因为UI库不好控制组件内的层级、样式等,可能影响最终的导出。
页面尺寸可以参考:https://github.com/cognitom/paper-css
字号换算:(53条消息) Word字体大小对照换算表(字号、磅、英寸、像素)_QAQ_King的博客-CSDN博客
图片、Canvas导出:根据HTML展示的图片尺寸导出
还没有评论,快来抢第一吧