项目需要导出word,于是乎又是查阅资料。然后自己写。

参考链接

https://github.com/evilc0des/docxtemplater-image-module-free

https://www.jianshu.com/p/b3622d6f8d98

Vue导出Word

配置word模板

新建一个word文档,名字随便取只要对应就好,我创建的是一个template.docx。下面是我的配置,简单解释一下:

1. {#wordList}包裹一个循环对象{/wordList}

2. 对象中有serviceImgUrl,title,pageNumber属性

3. wordList:[{ pageNumber: ‘’, title: ‘’, serviceImgUrl: ‘’ }]

vue3 docxtemplater导出图片 vue导出word模板_List

下载

// 这些插件要了解的自己查阅资料
npm i docxtemplater pizzip  -S
npm i jszip-utils -S
npm i jszip -S
npm i file-saver -S
npm i -S docxtemplater-image-module-free 
npm i pizzip -S

导入

import PizZip from 'pizzip'
import docxtemplater from 'docxtemplater'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'
import { Loading } from 'element-ui';

点击导出按钮

这里需要先了解导出的word数据,上面有提到是一个数组对象wordList:[{ pageNumber: ‘’, title: ‘’, serviceImgUrl: ‘’ }]

// 点击导出文档按钮
handleExportWord() {
      this.wordList = []
      if(!this.chooseCollectWordList.length) return this.$message.warning('请勾选需要导出的文件')
      this.loading = Loading.service({
        lock: true,
        text: '拼命打包中',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      });
      this.chooseCollectWordList.forEach((item, i) => {
       
        this.wordList.push({ pageNumber: item.pageNumber.toString(), title: item.title, serviceImgUrl: '' })
        this.getBase64Image(item.serviceImgUrl, this.wordList, i)
      })
      // 刚开始在这里写
      // this.exportWord()

    },

服务器图片转Base64

这一步需要后端支持,这个问题是我们后端解决的,我也不太清楚,具体可以自行查阅资料。但是要保证图片地址能转成base64。

getBase64Image(src, wordList, i) {
      let _this = this
      var image = new Image();
      // image.src = src + '?v=' + Math.random(); // 处理缓存
      image.src = src
      image.setAttribute("crossOrigin", '*');  // 支持跨域图片
      image.onload = async function() {
        let base64 = ""
        var canvas = document.createElement("canvas");
        canvas.width = image.width;
        canvas.height = image.height;
        var ctx = canvas.getContext("2d");
        ctx.drawImage(image, 0, 0, image.width, image.height);
        base64 = await canvas.toDataURL("image/png");  // 可选其他值 image/jpeg
        wordList[i].serviceImgUrl = base64
        // 这里调用的原因是防止serviceImgUrl还未转换成base64就调用exportWord接口
        if(i == _this.wordList.length - 1) {
         _this.exportWord()
        }
        return base64;
      }
      // 图片加载失败警告
      image.onerror = function() {
        wordList[i].serviceImgUrl = ''
        if(i == _this.wordList.length - 1) {
          _this.exportWord()
          return
        }

      }
    },

base64转二进制

这个什么都不用改,直接复制粘贴

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;
    }

导出文档

exportWord() {
      var ImageModule = require('docxtemplater-image-module-free');
      const expressions = require("angular-expressions");

      let _this = this;

      // 读取并获得模板文件的二进制内容,放在项目中即可
      // template.docx对应上面的word模板
      JSZipUtils.getBinaryContent("template.docx", function(error, content) {
        if(error) {
          _this.loading.close();
          _this.$message.error('出现未知错误')
          console.log(error)
          throw error;
        };

        expressions.filters.size = function(input, width, height) {
          return {
            data: input,
            size: [width, height],
          };
        };
        function angularParser(tag) {
          const expr = expressions.compile(tag.replace(/’/g, "'"));
          return {
            get(scope) {
              return expr(scope);
            },
          };
        }

        // 图片处理
        let opts = {}
        opts = { centered: false };
        opts.getImage = function(chartId) {
          return _this.base64DataURLToArrayBuffer(chartId);
        }
        opts.getSize = function() {
          return [600, 350];
        }

        // 创建一个PizZip实例,内容为模板的内容
        let zip = new PizZip(content);
        // 创建并加载docxtemplater实例对象
        let doc = new docxtemplater();
        doc.attachModule(new ImageModule(opts));
        doc.loadZip(zip).compile();
        // 设置模板变量的值
        // 使用doc.setData开发环境不会出现错误,生产环境就是他喵的要发疯,有几张图片就是导不出来。
        // 这个问题是个毒瘤,害我解决了好久,幸好后面解决了。具体就是用doc.resolveData
        /* doc.setData({
          wordList: _this.wordList
        }); */
        doc.resolveData({
          wordList: _this.wordList
        }).then(() => {
          // 用模板变量的值替换所有模板变量
          doc.render();
          // 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
          let out = doc.getZip().generate({
            type: "blob",
            mimeType:
              "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
          });
          // 将目标文件对象保存为目标类型的文件,并命名
          _this.loading.close();
          let date = new Date()
          
          let fileName = "文件名称"
          saveAs(out, `${fileName}.docx`);
        }).catch((error) => {
          _this.loading.close();
          let e = {
            message: error.message,
            name: error.name,
            stack: error.stack,
            properties: error.properties
          };
          console.log(JSON.stringify({ error: e }));
          throw error;
        })
      });
    },

代码写出来了,如果出现什么问题可以参考上面的第一个链接,第一个链接都没有做出来,那就只能自行查阅了。我的导出是没什么问题的。