Skip to content

前端生成 word

docxtemplater 介绍

docxtemplater 使用 JSON 数据格式作为输入,可以处理docx 和 ppt模板。不像一些其它的工具,比如 docx.js 等,需要自己编写代码来生成文件,docxtemplater 只需要用户通过标签的形式编写模板,就可以生成文件。

项目依赖

项目所需依赖:docxtemplater,jszip,jszip-utils,pizzip,file-saver。

  • docxtemplater:这个插件可以通过预先写好的word,excel等文件模板生成对应带数据的文件
  • pizzip:这个插件用来创建,读取或编辑.zip的文件(同步的,还有一个插件是jszip,异步的)
  • jszip-utils:与jszip/pizzip一起使用,jszip-utils 提供一个getBinaryContent(path, data)接口,path即是文件的路径,支持AJAX get请求,data为读取的文件内容。
  • file-saver:适合在客户端生成文件的工具,它提供的接口saveAs(blob, "1.docx")将会使用到,方便我们保存文件。
sh
npm install  docxtemplater pizzip --save  // 处理docx模板
npm install  jszip-utils --save
npm install  jszip --save   
npm install  file-saver --save  // 处理输出文件

docxtemplater 入门

渲染字符串

语法:

sh
{变量名}

在 word 文档上把需要替换的值换成 {变量名} 的格式

循环遍历

语法:

sh
{#变量名}{/变量名}

遍历表格
word

当然如果你的数组只是存了基本数据类型情况下,要这样写法才能渲染出来

sh
{#变量名}{$index}{.}{/变量名}

当然有时候你希望控制数组为空的情况下渲染其它的,那么 docxtemplater 提供了 ^ 来判断

sh
{#变量名}{.}{/变量名}
{^变量名} 数据为空 {/变量名}

插入 docxXml 格式

有时候会有一些比较复杂的需求,例如下面的性别要用不同的颜色显示
word
这时候我们可以使用 docxXml 语法来渲染。

语法:

sh
{@变量名}

相比于渲染字符串,渲染 docxXml 要在前面加多一个 @ 符号,并且变量的值要为 docxXml 语法,如下面所示:

xml
<w:p>
    <w:pPr>
        <w:pStyle w:val="11"/>
        <w:rPr>
            <w:rFonts w:hint="eastAsia" w:eastAsia="微软雅黑"/>
            <w:color w:val="${color}"/>
            <w:szCs w:val="20"/>
            <w:lang w:eastAsia="zh-CN"/>
            <w:vAlign w:val="center"/>
        </w:rPr>
    </w:pPr>
    <w:r>
        <w:rPr>
            <w:rFonts w:hint="eastAsia"/>
            <!-- 设置字体颜色 -->
            <w:color w:val="${color}"/> 
            <w:szCs w:val="20"/>
            <w:lang w:val="en-US" w:eastAsia="zh-CN"/>
        </w:rPr>
        <w:t>男</w:t>
    </w:r>
</w:p>

if 条件语法

语法:{#变量名}{/}
docxtemplater 默认只支持 boolean 值的判断

如果想支持更高级的判断方式的话,必须得添加额外得包

sh
npm install --save angular-expressions

docxtemplater 配置

js
const expressionParser = require("docxtemplater/expressions.js")
new Docxtemplater(zip, { parser: expressionParser })

这样你就能支持更高级的判断方式了

sh
{#users.length>1}{/}
{#users[0].name == "John"}{/}

当然,这个包不仅扩展了高级的判断方式,更有其它额外的功能,详情情况官网介绍expressions

指定 DOM

在某些情况下, docxtemplater 并不能很好识别我们想要操作的 DOM,例如: 在单列的表格的单元格下,我们希望能控制整行的显示和隐藏
word
想象中的结果是这样
word
但事实却是这样
word
这是因为 docxtemplater 在单列情况下,默认是操作里面内容的 DOM,而不是整行的 DOM。

所在在这样的情况下,就需要我们指定告诉 docxtemplater ,我想你操作的是行 Dom :

sh
{-w:tr table1s.length == 0}未发现{/}

如果是循环也可以这样用

sh
{-w:tr table1s.length == 0}{.}{/table1s}

导出 word 完整代码实现

将demoUrl传入给JSZipUtils.getBinaryContent方法读取模板文件的二进制内容,之后创建一个PizZip实例,内容为模板的内容,再创建并加载docxtemplater实例对象。

使用doc.setData方法设置模板变量的值,对象的键需要和模板上的变量名一致,值就是你要放在模板上的值。

js
import JSZipUtils from "jszip-utils";
import docxtemplater from "docxtemplater";
import { saveAs } from "file-saver";
import PizZip from "pizzip";

/**
 * @param {*} demoUrl 模板路径
 * @param {*} docxData 渲染的数据
 * @param {*} fileName 生成的文件名
 * @returns
 */
export const exportWordDocx = (demoUrl, docxData, fileName) => {
    // 读取并获得模板文件的二进制内容
    JSZipUtils.getBinaryContent(
        demoUrl,
        function (error, content) {
            // 抛出异常
            if (error) {
                throw error;
            }

            // 创建一个PizZip实例,内容为模板的内容
            let zip = new PizZip(content);
            // 创建并加载docxtemplater实例对象
            let doc = new docxtemplater().loadZip(zip);
            // 去除未定义值所显示的undefined
            doc.setOptions({
                nullGetter: function () {
                    return "";
                }
            }); // 设置角度解析器
            // 设置模板变量的值,对象的键需要和模板上的变量名一致,值就是你要放在模板上的值

            doc.setData({
                ...docxData,
            });

            try {
                // 用模板变量的值替换所有模板变量
                doc.render();
            } catch (error) {
                // 抛出异常
                let e = {
                    message: error.message,
                    name: error.name,
                    stack: error.stack,
                    properties: error.properties,
                };
                console.log(JSON.stringify({ error: e }));
                throw error;
            }

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