VUE3导出WORD图片和文字

250 阅读2分钟

<script setup>
import { onMounted, ref } from 'vue';
import *  as echarts from 'echarts';
import PizZip from 'pizzip'
import Docxtemplater from 'docxtemplater'
import JszipUtils from 'jszip-utils'
import FileSave from 'file-saver'
import ImageModule from 'docxtemplater-image-module-free'


const chartRef = ref(null);
let myChart = null
const initChart = () => {
  const option = {
    xAxis: {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
      type: 'value'
    },
    series: [
      {
        data: [150, 230, 224, 218, 135, 147, 260],
        type: 'line'
      }
    ]
  };
  myChart = echarts.init(chartRef.value);
  myChart.setOption(option);
}

const exportDocx = async () => {
  if (!myChart) return
  const content = await JszipUtils.getBinaryContent('template.docx')

  let opts = {
    centered: false,
    fileType: 'docx'
  }
  opts.getImage = function (tagValue) {
    if (tagValue.size && tagValue.data) {
      return base64DataURLToArrayBuffer(tagValue.data)
    }
    return base64DataURLToArrayBuffer(tagValue)
  }
  opts.getSize = function (_, tagValue) {
    if (tagValue.size && tagValue.data) {
      return tagValue.size
    }
    return [500, 300] // word里面图片的尺寸
  }

  // 将文件转为zip文件
  let pizZip = new PizZip(content)
  let doc = new Docxtemplater()
  doc.loadZip(pizZip)
  doc.setOptions({
    nullGetter: function () {
      return ''
    }
  })

  doc.attachModule(new ImageModule(opts))

  const docData = {
    image: myChart.getDataURL({
      // pixelRatio: 5, // 导出的图片分辨率比率,默认是1
      // backgroundColor: '#afdfe4', // 图表背景色
      // excludeComponents: ['toolbox'], // 忽略组件的列表
      // type: 'png' // 图片类型支持png和jpeg
    }),
    title: '测试',
    content: '测试文章内容,哈哈',
    list: [
      {
        url: 'https://www.baidu.com',
      },
      {
        url: 'https://www.baidu.com',
      },
      {
        url: 'https://www.baidu.com',
      },
    ],
    checked: true,
  }
  doc.setData(docData)

  try {
    doc.render()
  } catch (error) {
    // 抛出异常
    let e = {
      message: error.message,
      name: error.name,
      stack: error.stack,
      properties: error.properties
    }

    throw error
  }



  const out = doc.getZip().generate({
    type: 'blob',
    mimeType:
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  })

  FileSave.saveAs(out, 'test.docx')
}


/**
 * base64转为ArrayBuffer
 * 这个我也不知道为什么,我看最后下载打印的时候连接是base64格式的
 */
function base64DataURLToArrayBuffer(dataURL) {
  const base64Regex = /^data:image\/(png|jpg|svg|svg\+xml);base64,/
  if (!base64Regex.test(dataURL)) {
    return false
  }
  const stringBase64 = dataURL.replace(base64Regex, '')
  let binaryString
  if (typeof window !== 'undefined') {
    binaryString = window.atob(stringBase64)
  } else {
    binaryString = new Buffer(stringBase64, 'base64').toString('binary')
  }
  const len = binaryString.length
  const bytes = new Uint8Array(len)
  for (let i = 0; i < len; i++) {
    const ascii = binaryString.charCodeAt(i)
    bytes[i] = ascii
  }
  return bytes.buffer
}

/**
 * 下载图片什么需要将格式转化为base64
 * 图片转base64方法
 */
function urlToBase64(url) {
  return new Promise((resolve) => {
    const image = new Image()
    image.setAttribute('crossOrigin', 'Anonymous')
    image.onload = function () {
      const canvas = document.createElement('canvas')
      canvas.width = image.width
      canvas.height = image.height
      canvas.getContext('2d').drawImage(image, 0, 0)
      const result = canvas.toDataURL('image/png')
      resolve(result)
    }
    image.src = url
  })
}


onMounted(() => {
  initChart();

})
</script>

<template>
  <div class="container">
    <button @click="exportDocx">导出</button>
    <div class="chart" ref="chartRef">
    </div>
  </div>
</template>

<style scoped>
.container {
  width: 100%;
  height: 100%;
}

.chart {
  width: 500px;
  height: 300px;
}

button {
  margin-top: 20px;
}
</style>

DOCX模板:

20250429-110522.jpg

导出DOX:

20250429-110535.jpg