前端导出word文件—包含canvas(echarts图表)

一、使用的插件

html-docx-js

二、整体思路

因为canvas是运行在内存中的,所以不能简单的通过dom获取canvas图片,需要手动的先将canvas转为image。

三、实现
  1. 先克隆要下载的DOM的副本。
  2. 因为canvas是运行在内存中的,所以也不能通过cloneNode方法克隆下来(克隆下来是空的)。我们这里将原DOM中的canvas转成图片,然后插入到副本的对应位置,这样操作不会影响原DOM。
  3. 将DOM副本传入插件,生成文件对象,并下载下来。
import htmlDocx from 'html-docx-js/dist/html-docx'
 /*
 步骤1 :因为canvas是运行在内存中的,所以也不能通过cloneNode方法克隆下来(克隆下来是空的),
 所以先克隆再在克隆的dom上进行操作是不可取的。所以需要在原DOM上生成img,
 设置display: none从而使图片不影响页面展示,并插入到对应canvas元素之前(为了保证顺序不变)。
 */
  const app = document.getElementById('app')
  const cloneApp = app.cloneNode(true)
  const canvases = app.getElementsByTagName('canvas')
  const cloneCanvases = cloneApp.getElementsByTagName('canvas')
  const promises = Array.from(canvases).map((ca, index) => {
    return new Promise((res) => {
      const url = ca.toDataURL('image/png', 1)
      const img = new Image()
      img.onload = () => {
        URL.revokeObjectURL(url)
        res()
      }
      img.src = url
      // 插入clone的dom的canvas之前
      cloneCanvases[index].parentNode.insertBefore(img, cloneCanvases[index])
    })
  })
  
  /* 
  步骤2 :删除掉canvas元素 
  */
  // 删除clone的dom中的所有的canvas
  const cloneCanvas = cloneApp.getElementsByTagName('canvas')
  Array.from(cloneCanvas).forEach((ca) => ca.parentNode.removeChild(ca))

  /* 步骤3 :将dom副本传入插件,生成文件对象,并下载下来 */
  Promise.all(promises).then(() => {
    const converted = htmlDocx.asBlob(`
    <!DOCTYPE html>
    <html lang="en">
    ${document.head.outerHTML}
    <body>
    ${cloneApp.outerHTML}
    </body>
    </html>`)
    saveAs(converted, 'test.docx')
  })
  
  // 下载文件
  function saveAs (blob, fileName) {
    const a = document.createElement('a')
    const url = URL.createObjectURL(blob)
    
    a.href = url
    a.download = fileName
    a.display = 'none'
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)
  }