Skip to content

按模板导出 word

上次更新 2024年7月16日星期二 9:34:0 字数 0 字 时长 0 分钟

所使用的库

shell
npm i --save docxtemplater pizzip jszip-utils file-saver docxtemplater-image-module-free angular-expressions

预制模板规则参考

值得注意

  • 该方案只支持 .docx 格式文件
  • 不要多加空格,否则会解析失败

示例

完整代码
javascript
/**
 * 导出word文档(带图片)
 *
 */
import Docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import JSZipUtils from "jszip-utils";
import * as fileSaver from "file-saver";
import * as ImageModule from "docxtemplater-image-module-free";
import expressionParser from "docxtemplater/expressions.js";

/**
 * 将base64格式的数据转为ArrayBuffer
 * @param {Object} dataURL base64格式的数据
 */
function base64Parser(dataURL) {
  const base64Regex = /^data:image\/(png|jpg|jpeg|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 = Buffer.from(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;
}
export const ExportBriefDataDocx = (
  tempDocxPath,
  data,
  fileName,
  imgSize = {}
) => {
  expressionParser.filters.size = function (input, width, height) {
    return {
      data: input,
      size: [width, height],
    };
  };
  function angularParser(tag) {
    tag = tag
      .replace(/^\.$/, "this")
      .replace(/('|‘)/g, "'")
      .replace(/(“|”)/g, '"');
    const expr = expressions.compile(tag);
    return {
      get: function (scope, context) {
        let obj = {};
        const index = last(context.scopePathItem);
        const scopeList = context.scopeList;
        const num = context.num;
        for (let i = 0, len = num + 1; i < len; i++) {
          obj = assign(obj, scopeList[i]);
        }
        //word模板中使用 $index+1 创建递增序号
        obj = assign(obj, { $index: index });
        return expr(scope, obj);
      },
    };
  }
  JSZipUtils.getBinaryContent(tempDocxPath, (error, content) => {
    console.log("content", content);
    if (error) {
      console.log(error);
    }
    // expressions.filters.size = function (input, width, height) {
    //     return {
    //         data: input,
    //         size: [width, height],
    //     };
    // };
    let imageOptions = {
      //图像是否居中
      centered: true,
      getImage(chartId) {
        //将base64的数据转为ArrayBuffer
        return base64Parser(chartId);
      },
      getSize(img, base64, tagName) {
        // 获取默认图片尺寸
        if (imgSize.hasOwnProperty(tagName)) {
          return imgSize[tagName];
        } else {
          return [200, 200];
        }
      },
    };
    // 创建一个JSZip实例,内容为模板的内容
    const zip = new PizZip(content);
    // 创建并加载 Docxtemplater 实例对象
    let doc = new Docxtemplater();
    doc.attachModule(new ImageModule(imageOptions));
    doc.loadZip(zip);
    doc.setOptions({ parser: expressionParser });
    doc.setData(data);
    try {
      doc.render();
    } catch (error) {
      const e = {
        message: error.message,
        name: error.name,
        stack: error.stack,
        properties: error.properties,
      };
      console.log("err", { error: e });
      throw error;
    }
    // 生成一个代表Docxtemplater对象的zip文件
    const out = doc.getZip().generate({
      type: "blob",
      mimeType:
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    });
    fileSaver.saveAs(out, fileName);
  });
};
/**
 * 将图片的url路径转为base64路径
 * 可以用await等待Promise的异步返回
 * @param {Object} imgUrl 图片路径
 */
export function getBase64Sync(imgUrl) {
  return new Promise(function (resolve, reject) {
    let image = new Image();
    image.src = imgUrl;
    image.setAttribute("crossOrigin", "*");
    image.onload = function () {
      let canvas = document.createElement("canvas");
      canvas.width = image.width;
      canvas.height = image.height;
      let context = canvas.getContext("2d");
      context.drawImage(image, 0, 0, image.width, image.height);
      let ext = image.src
        .substring(image.src.lastIndexOf(".") + 1)
        .toLowerCase();
      let quality = 0.8;
      let dataurl = canvas.toDataURL("image/" + ext, quality);
      resolve(dataurl);
    };
  });
}
/**
 * @description
 * @export
 * @param {imgUrl|base64} base64
 * @return {[width, height]}
 */
// const A4DimensionalStandards = { width: 842, height: 1156, aspectRatio: 842 / 1156} // A4 纸张尺寸 px
const A4DimensionalStandards = {
  width: 421,
  height: 528,
  aspectRatio: 421 / 528,
};
export function getImageSize(base64) {
  //
  return new Promise((resolve, reject) => {
    let img = new Image();
    try {
      img.src = base64;
      img.onload = function () {
        let width;
        let height;
        // 设置A4标准 按比例缩放尺寸
        let aspectRatio = this.width / this.height;
        if (this.width > A4DimensionalStandards.width) {
          if (this.height < A4DimensionalStandards.height) {
            //超宽不超高
            // console.log('超宽不超高');
            width = A4DimensionalStandards.width;
            height = A4DimensionalStandards.width / aspectRatio;
            resolve([width, height]);
          } else {
            //超宽超高
            // 计算谁超的比例多
            if (
              width / A4DimensionalStandards.width >
              height / A4DimensionalStandards.height
            ) {
              //按宽处理
              // console.log('按宽处理');
              width = A4DimensionalStandards.width;
              height = A4DimensionalStandards.width / aspectRatio;
              resolve([width, height]);
            } else {
              //按高处理
              // console.log('按高处理');
              height = A4DimensionalStandards.height;
              width = A4DimensionalStandards.height * aspectRatio;
              resolve([width, height]);
            }
          }
        } else {
          if (this.height < A4DimensionalStandards.height) {
            //不超宽不超高
            // console.log('不超宽不超高');
            width = this.width;
            height = this.height;
            resolve([width, height]);
          } else {
            //不超宽超高  按高处理
            // console.log('不超宽超高  按高处理');
            height = A4DimensionalStandards.height;
            width = A4DimensionalStandards.height * aspectRatio;
            resolve([width, height]);
          }
        }
        // console.log([width, height]);
        // resolve([this.width, this.height])
      };
      img.onerror = function (error) {
        reject(error);
      };
    } catch (error) {
      reject(error);
    }
  });
}

常用官方案例

image-20240716172716043

image-20240716172829374

一维表格 、二维表格 、立式表格、grid表格

image-20240716172154370

image-20240716171620250

一般情况 不使用 常通过修改一份数据来实现

image-20240716172438373