前端將html導出為word文件的功能實現(xiàn)
介紹
需求:在開發(fā)過程中,遇到了產(chǎn)品要求將展示的可視化報告直接導出到word格式的需求,導出的html包括Echarts圖、文字、表格。經(jīng)過一番查找,找到一個插件html-docx-js。
功能實現(xiàn)
exportWord函數(shù):主函數(shù)接受一個dom元素,通過a標簽導出文件
import htmlDocx from 'html-docx-js/dist/html-docx'
export async function exportWord(ele) {
let htmlContent = await wordStyleProcess(ele)
const docxBlob = htmlDocx.asBlob(htmlContent)
const URL = window.URL || window.webkitURL
let a = document.createElement('a')
a.download = 'name'
a.rel = 'noopener'
a.target = '_blank'
a.href = URL.createObjectURL(docxBlob)
a.click()
URL.revokeObjectURL(a.href) // 20
}
wordStyleProcess函數(shù):對元素進行樣式處理。首先,復制元素,防止修改樣式影響頁面樣式。使用shortId標記元素,通過id找到頁面渲染的原始元素,對樣式進行調(diào)整。
export async function wordStyleProcess(ele) {
if (!ele) return console.warn('未傳入元素')
// 設置標記, 方便復制樣式,通過data-export-file-id標記,用于后續(xù)找到頁面的真實渲染樣式的元素
Array.from(ele.querySelectorAll('*')).forEach((item) => {
item.setAttribute('data-export-file-id', 'str' + stortId.generate())
})
//不影響頁面樣式,復制dom
let exportFileEle = ele.cloneNode(true)
// 遍歷iframe中的元素,將類樣式設置行內(nèi)樣式
Array.from(exportFileEle.querySelectorAll('*')).forEach((item) => {
let dataExportFileId = item.getAttribute('data-export-file-id')
let originDom = ele.querySelector('[data-export-file-id="' + dataExportFileId + '"]') //原始dom才能獲取真實樣式
if (originDom) {
let originSty = getComputedStyle(originDom) //利用getComputedStyle獲取真實頁面dom的樣式
if (originSty.display === 'none' || originSty.opacity === '0') return item.remove()
setStyle(item, originSty)
}
//處理table類型的元素
if (item.getAttribute('data-transform-table')) {
const wordTable = convertElTableToHtmlTableWord(item, ele)
const table = document.createElement('div')
const parent = item.parentNode
parent.style.height = 'auto'
table.innerHTML = wordTable
if (item.nextSibling) {
parent.insertBefore(table, item.nextSibling)
} else {
parent.appendChild(table)
}
item.remove()
}
})
// 處理echarts類型元素:遍歷真實頁面中的元素,帶有自定義屬性data-transform-image的元素為需要轉(zhuǎn)換的元素,真實頁面的canvas轉(zhuǎn)換為圖片,替換到iframe中對應位置
await convertHTMLToImage(Array.from(ele.querySelectorAll('[data-transform-image="true"]')), exportFileEle)
//
const htmlContent = exportFileEle.innerHTML
if (exportFileEle) {
exportFileEle.remove()
}
return htmlContent
}
setStyle函數(shù):獲取到的元素沒有類樣式,通過getComputedStyle獲取的頁面真實樣式賦值到導出的元素中。
/**
* 把元素類樣式設置行內(nèi)樣式
*
* @param ele DOM元素
* @param sty 包含樣式屬性的對象
*/
export function setStyle(ele, sty) {
if (ele.nodeName.toLowerCase() !== 'img') {
ele.setAttribute(
'style',
(ele.getAttribute('style') || '') +
`;font-size: ${sty.fontSize};color: ${sty.color};font-style: ${sty.fontStyle};line-height: ${sty.lineHeight};font-weight: ${sty.fontWeight};
font-family: ${sty.fontFamily};text-align: ${sty.textAlign};text-indent: ${sty.textIndent}; margin: ${sty.margin}; padding: ${sty.padding};width: ${sty.width}; height: ${sty.height};
white-space:${sty.whiteSpace};word-break:${sty.wordBreak};display:${sty.display}; flex-direction:${sty.flexDirection};align-items: ${sty.alignItems}; `
)
}
}
convertHTMLToImage:對echarts圖表元素導出的問題,一開始我使用的html2canvas,網(wǎng)上的資料的很多也是用的這個插件,但是這個插件實在的太太太太慢了?。。?!,幾個echarts圖,居然十幾秒。 后面我翻了一下資料,echarts實例是支持導出base64的,然后嘗試了一下,直接秒導出,快太多了。 html2canvas版本:
export async function convertHTMLToImage(elements, exportFileEle, canvasWidth) {
// 使用html2canvas捕捉DOM元素
for (let element of elements) {
const option = {
// 使用html2Canvas進行截圖
logging: false,
allowTaint: true, // 允許跨域圖片渲染
useCORS: true, // 使用跨域資源
imageTimeout: 0, // 圖片加載延遲,默認延遲為0,單位毫秒
scale: 1.2, // 設置縮放比例
willReadFrequently: true,
}
const canvas = await html2canvas(element, option)
canvas.setAttribute('data-export-file-id', element.getAttribute('data-export-file-id'))
const url = canvas.toDataURL('image/jpg', 1.0)
let img = new Image()
img.src = url
canvasWidth ? (img.width = canvasWidth) : (img.style.width = '100%')
//移除原來的圖表元素,替換為圖片
let canvasEle = exportFileEle.querySelector(`[data-export-file-id=${element.getAttribute('data-export-file-id')}]`)
if (canvasEle) {
const parent = canvasEle.parentNode
if (canvasEle.nextSibling) {
parent.insertBefore(img, canvasEle.nextSibling)
} else {
parent.appendChild(img)
}
canvasEle.remove()
}
}
}
使用echart的版本:從這里看好像也差不多,但是element有個data-url的自定義屬性,這個需要在外部配合,將獲取到的base64賦值給元素的自定義屬性。
export async function convertHTMLToImage(elements, exportFileEle, canvasWidth) {
// 使用html2canvas捕捉DOM元素
for (let element of elements) {
const option = {
// 使用html2Canvas進行截圖
logging: false,
allowTaint: true, // 允許跨域圖片渲染
useCORS: true, // 使用跨域資源
imageTimeout: 0, // 圖片加載延遲,默認延遲為0,單位毫秒
scale: 1.2, // 設置縮放比例
willReadFrequently: true,
}
const url = element.getAttribute('data-url')
let img = new Image()
img.src = url
canvasWidth ? (img.width = canvasWidth) : (img.style.width = '100%')
//移除原來的圖表元素,替換為圖片
let canvasEle = exportFileEle.querySelector(`[data-export-file-id=${element.getAttribute('data-export-file-id')}]`)
if (canvasEle) {
const parent = canvasEle.parentNode
if (canvasEle.nextSibling) {
parent.insertBefore(img, canvasEle.nextSibling)
} else {
parent.appendChild(img)
}
canvasEle.remove()
}
}
}
echart組件核心代碼:這里只簡單寫了一下,主要核心意思就是,在echarts渲染完成之后,生成base64,然后賦值到元素的自定義屬性上。
<div ref="refEchartsContainer" class="echarts-container" :style="cardStyle" data-transform-image="true" :data-url="url"/>
this.chartInstance.on('finished', () => {
this.url = this.chartInstance.getDataURL({ type: 'png' })
})
convertElTableToHtmlTableWord:處理el-table表格
export function convertElTableToHtmlTableWord(elTable, renderEle) {
if (!elTable) return ''
const tableEmptyBlockStyHeight = '100%'
// 獲取 el-table 的表頭數(shù)據(jù),包括多級表頭
const tableOptions = {
tableStyle: 'border-collapse: collapse; width:100%',
headerStyle: 'border: 1px solid black; width: 60px;height: 50px;',
rowStyle: 'mso-yfti-irow:0; mso-yfti-firstrow:yes; mso-yfti-lastrow:yes; page-break-inside:avoid;height: 20px;',
cellStyle: 'border: 1px solid black; min-width: 50px;max-width: 100px;height: 20px;',
}
const theadRows = elTable.querySelectorAll('thead tr') || []
// 獲取 el-table 的數(shù)據(jù)行
const tbodyRows = elTable.querySelectorAll('tbody tr') || []
// 開始構(gòu)建 HTML 表格的字符串,設置表格整體樣式和邊框樣式
let htmlTable = `<table style="${tableOptions.tableStyle}"><thead>`
let columnsNum = 0
// 處理表頭
theadRows.forEach((row) => {
htmlTable += `<tr style="${tableOptions.rowStyle}">`
const columns = row.querySelectorAll('th') || []
// 獲取表頭列數(shù),用于后面暫無數(shù)據(jù)的colspan屬性
columnsNum = columns.length || 0
columns.forEach((column) => {
if (column.style.display !== 'none') {
const colspan = column.getAttribute('colspan') || '1'
const rowspan = column.getAttribute('rowspan') || '1'
htmlTable += `<th colspan="${colspan}" rowspan="${rowspan}" style="${tableOptions.headerStyle}">${column.innerText}</th>`
}
})
htmlTable += '</tr>'
})
htmlTable += '</thead><tbody>'
// 構(gòu)建數(shù)據(jù)
if (tbodyRows && tbodyRows.length > 0) {
tbodyRows.forEach((row) => {
htmlTable += `<tr style="${tableOptions.rowStyle}">`
const cells = row.querySelectorAll('td') || []
cells.forEach((cell) => {
if (cell.querySelector('div')) {
htmlTable += `<td style="${tableOptions.cellStyle}">${cell.querySelector('div').innerHTML}</td>`
} else {
htmlTable += `<td style="${tableOptions.cellStyle}">${cell.innerText}</td>`
}
})
htmlTable += '</tr>'
})
} else {
htmlTable += `<tr style="${tableOptions.rowStyle}"><td colspan="${columnsNum}" style="width: 100%; border: 1px solid black; height: ${tableEmptyBlockStyHeight};"><div style="display: inline-block;width: 100%; text-align: center;">暫無數(shù)據(jù)</div></td></tr>`
}
htmlTable += '</tbody></table>'
return htmlTable
}
以上就是前端將html導出為word文件的功能實現(xiàn)的詳細內(nèi)容,更多關于前端html導出為word文件的資料請關注腳本之家其它相關文章!
相關文章
構(gòu)造函數(shù)+原型模式構(gòu)造js自定義對象(最通用)
這種方式是javascript中最通用的創(chuàng)建對象的方式,下面用示例為大家介紹下2014-05-05
微信小程序?qū)崿F(xiàn)獲取準確的騰訊定位地址功能示例
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)獲取準確的騰訊定位地址功能,結(jié)合實例形式詳細分析了微信小程序使用騰訊地理位置接口的相關注冊、操作步驟及接口使用技巧,需要的朋友可以參考下2019-03-03
JavaScript反轉(zhuǎn)數(shù)組常用的4種方法
這篇文章主要給大家介紹了關于JavaScript反轉(zhuǎn)數(shù)組常用的4種方法,反轉(zhuǎn)數(shù)組可以將數(shù)組中的元素順序顛倒過來,從而達到一些特定的需求,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-07-07
javascript中mouseover、mouseout使用詳解
這篇文章主要介紹了javascript中mouseover、mouseout使用詳解的相關資料,需要的朋友可以參考下2015-07-07
BootStrap 智能表單實戰(zhàn)系列(十)自動完成組件的支持
這篇文章主要介紹了BootStrap 智能表單實戰(zhàn)系列(十)自動完成組件的支持 的相關資料,需要的朋友可以參考下2016-06-06

