0


docxtemplater 实现前端word模板修改(普通文本、图片、表格),并且下载

一、问题背景

    项目业务需要,需要实现下载word,并且对word中的内容进行指定输出,包含普通文本、图片、以及表格;

    Documentation | docxtemplater 可以实现对模板word内容的替换; 非常好用,但是存在部分插件使用收费情况,所以选择其它免费的代替,图片使用 docxtemplater-image-module-free,表格使用它自带的循环去实现,缺点就是不能自定义表格,必须要提前在模板中内置好表格使用;

二、相关准备

    我使用的是vite+ts 、使用需要安装指定包;
npm i docxtemplater pizzip docxtemplater-image-module-free file-saver
    准备word模板 - 模板要求

    普通文本 {time}

    图片 {%img}

    表格 {#table}{col1} {col2} {col3}{/table}

    准备好模板后,将模板放入项目的public文件夹内,以便后续使用;

三、过程

    读取文件,传入对应的模板word地址;
import PizZipUtils from "pizzip/utils/index.js";

// 读取并获得模板文件的二进制内容
// url - '/新建DOCX文档.docx'
loadFile(url: string, callback: (error: any, content: any) => void) {
  PizZipUtils.getBinaryContent(url, callback);
}
    在对应的回调里面去生成pizZip对象, 传入  docxtemplater 对象中,进行处理;并且使用免费的图片图例插件,导入docxtemplater中使用;
// docxtemplater 文件模板修改工具
import Docxtemplater from "docxtemplater";
// 文件读取工具
import PizZip from "pizzip";

// 创建一个JSZip实例,内容为模板的内容
  setPizZip(error:Error | null, _content) {
    // 设置图片资源 免费转换操作
    const imageOpts = {
      getImage: function(tagValue, tagName) {
        return new Promise(function (resolve, reject) {
          PizZipUtils.getBinaryContent(tagValue, function (error, content) {
            if (error) {
              return reject(error);
            }
            return resolve(content);
          });
        });
      },
      getSize: (img, tagValue, tagName) => {
        return new Promise(function (resolve, reject) {
          const image = new Image();
          image.src = tagValue;
          image.onload = function () {
            resolve([image.width, image.height]);
          };
          image.onerror = function (e) {
            console.log("img, tagValue, tagName : ", img, tagValue, tagName);
            alert("An error occured while loading " + tagValue);
            reject(e);
          };
        });
      }
    }
    const zip: PizZip = new PizZip(_content);
    // 初始化docxTemplater
    this.docContent = new Docxtemplater(zip, {
      modules: [
        new ImageModule(imageOpts)
      ],
      paragraphLoop: true,
      linebreaks: true
    })

    return this.setTemplateContent(this.data)
  }

最后将模板中需要替换的内容传入

// 设置新模板内容 
// 设置模板变量的值
/**
    _obj = {
        time: '123',
        img:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAkEAAADJCAYAAA' // base64格式 或者图片地址
        table: [{
            col1: 1,
            col2: 1,
            col3: 1
        }]
    }
*/
setTemplateContent(_obj) {
  return this.docContent.renderAsync(_obj)
}
    rederAsync是返回是一个Promise,我们可以在它的then方法里面去执行操作,进行下载操作;
// 下载文件
  downloadFile() {
    // 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
    const out = this.docContent.getZip().generate({
      type: "blob",
      mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    });
    // 将目标文件对象保存为目标类型的文件,并命名
    saveAs(out, this.fileName);
  }

四、实现效果

五、完整代码

    上面逻辑实现,建议封装工具类,以便后续多文件修改调用;
/*
 * @Author: autor
 * @Date: 2024-10-31 09:29:52 
 * @Last Modified by: autor
 * @Last Modified time: 2024-10-31 13:39:40
 */
// docxtemplater 文件模板修改工具
import Docxtemplater from "docxtemplater";
// 文件读取工具
import PizZip from "pizzip";
import PizZipUtils from "pizzip/utils/index.js";
// docxtemplater - 免费转换图片工具
import ImageModule from 'docxtemplater-image-module-free'
// 下载工具
import {
  saveAs
} from "file-saver";

class ExportWord {
  public url: string
  public fileName: string
  public data: any
  public docContent: Docxtemplater

  constructor(option) {
    this.url = option.url
    this.fileName = option.filename
    this.data = option.obj
  }

  // initWord
  initWord(_callback) {
    this.loadFile(this.url, (error:Error | null, _content) => {
      this.setPizZip(error, _content).then(res => {
        _callback(this)
      }).catch(err => {
        ElMessage.error('下载异常,请重试')
      })
    })
  }

  // 读取并获得模板文件的二进制内容
  loadFile(url: string, callback: (error: any, content: any) => void) {
    PizZipUtils.getBinaryContent(url, callback);
  }

  // 创建一个JSZip实例,内容为模板的内容
  setPizZip(error:Error | null, _content) {
    // 设置图片资源 免费转换操作
    const imageOpts = {
      getImage: function(tagValue, tagName) {
        return new Promise(function (resolve, reject) {
          PizZipUtils.getBinaryContent(tagValue, function (error, content) {
            if (error) {
              return reject(error);
            }
            return resolve(content);
          });
        });
      },
      getSize: (img, tagValue, tagName) => {
        return new Promise(function (resolve, reject) {
          const image = new Image();
          image.src = tagValue;
          image.onload = function () {
            resolve([image.width, image.height]);
          };
          image.onerror = function (e) {
            console.log("img, tagValue, tagName : ", img, tagValue, tagName);
            alert("An error occured while loading " + tagValue);
            reject(e);
          };
        });
      }
    }
    const zip: PizZip = new PizZip(_content);
    // 初始化docxTemplater
    this.docContent = new Docxtemplater(zip, {
      modules: [
        new ImageModule(imageOpts)
      ],
      paragraphLoop: true,
      linebreaks: true
    })

    return this.setTemplateContent(this.data)
  }

  // 设置新模板内容 
  // 设置模板变量的值
  setTemplateContent(_obj) {
    try {
      // 用模板变量的值替换所有模板变量
      return this.docContent.renderAsync(_obj);
    } catch (error: any) {
      // The error thrown here contains additional information when logged with JSON.stringify (it contains a properties object containing all suberrors).
      // eslint-disable-next-line no-inner-declarations
      function replaceErrors(key: any, value: any) {
        if (value instanceof Error) {
          return Object.getOwnPropertyNames(value).reduce(function (
            error: any,
            key: string
          ) {
            error[key] = value[key as keyof Error];
            return error;
          }, {});
        }
        return value;
      }
      console.log(JSON.stringify({
        error: error
      }, replaceErrors));

      if (error.properties && error.properties.errors instanceof Array) {
        const errorMessages = error.properties.errors
          .map(function (error: any) {
            return error.properties.explanation;
          })
          .join("\n");
        console.log("errorMessages", errorMessages);
        // errorMessages is a humanly readable message looking like this : 'The tag beginning with "foobar" is unopened'
      }
      throw error;
    }
  }

  // 下载文件
  downloadFile() {
    // 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
    const out = this.docContent.getZip().generate({
      type: "blob",
      mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    });
    // 将目标文件对象保存为目标类型的文件,并命名
    saveAs(out, this.fileName);
  }
}

export default ExportWord;

调用方式:

import ExportWord from '@/utils/exportWord'
import { EChartsOption, init, graphic as echartsGraphic, dispose } from 'echarts';

const _chartImage = Echart.getDataURL({
  backgroundColor: 'rgba(0, 0, 0, 0.7)',
  pixelRatio: 0.8
})
const newFileName = '测试word修改.docx'
const _word = new ExportWord({
  url: '新建DOCX文档.docx',
  filename: newFileName,
  obj: {
     time: '123',
     img: _chartImage,
     tableData:  [{
       col1: 1,
       col2: 1,
       col3: 1
     }],
   }
})
_word.initWord((_this) => {
  _this.downloadFile()
})
标签: 前端 vue typescript

本文转载自: https://blog.csdn.net/m0_51600979/article/details/143393283
版权归原作者 出入编程的小崽子 所有, 如有侵权,请联系我们删除。

“docxtemplater 实现前端word模板修改(普通文本、图片、表格),并且下载”的评论:

还没有评论