如何把数据库的数据拿到后进行处理,创建符合预期的word文件呢?

189 阅读1分钟

业务背景

最近在开发的过程中,接到了一个需求如下

image.png

也就是说,后端的业务要支持对这个报告的生产,前端需要能支持下载这个文档。

那么我们如何解这个问题呢?

后端(基于express框架)

使用docxtemplater

第一步,我们需要使用docxtemplater和pizzip

npm install docxtemplater pizzip

然后把希望用到的,要生成的word文档写入模版word文档中

image.png

文档内容的渲染规则举例如下:

word文档 image.png

js code

image.png

其中 {startTime} 用于替换单个变量, {#projects} {/projects} 用于渲染数组,也就是说变量 projects是一个对象数组,每一项有date和hour两个属性。

完整代码如下

const PizZip = require("pizzip");
const Docxtemplater = require("docxtemplater");
const fs = require("fs");
const time = require("../until/time");
const path = require("path");

function renderWord(data, type, outputName) {
  // Load the docx file as binary content
  const content = fs.readFileSync(
    path.resolve(`public/template/${type}.docx`),
    "binary"
  );
  const zip = new PizZip(content);
  const doc = new Docxtemplater(zip, {
    paragraphLoop: true,
    linebreaks: true,
  });
  // Render the document (Replace {first_name} by John, {last_name} by Doe, ...)
  if (type === "totalHour") {
    doc.render({
      userId: data.userId,
      startTime: data.startTime,
      endTime: data.endTime,
      totalHour: data.totalHour,
    });
  } else if (type === "projectHour") {
    console.log(data.projects);
    doc.render({
      projectName: data.projectName,
      startTime: data.startTime,
      endTime: data.endTime,
      projects: data.projects,
    });
  } else if (type === "vocation") {
    doc.render({
      vocation: data.vocation,
    });
  } else if (type === "salary") {
    doc.render({
      userId: data.userId,
      salaries: data.salaries,
    });
  }

  const buf = doc.getZip().generate({
    type: "nodebuffer",
    compression: "DEFLATE",
  });

  const random = time.getCurrentTimeInBeijing().replace(" ", "T");
  fs.writeFileSync(
    path.resolve(`public/report/${random}-${outputName}.docx`),
    buf
  );
  console.log(`${random}-${outputName}.docx`);
  return `${random}-${outputName}.docx`;
}

module.exports = {
  renderWord,
};


这里生产的文件名为了防止重复,加上了时间戳前缀,这部分可以自行按需实现random。生成的文件都放到目录 public/report 下生成文档后将对应的文件名返回给前端。

后端启用访问静态资源的中间件

app.use("/static", express.static(path.join(__dirname, "public")));

这样我们在前端组装链接如下即可:

`http://{domain}/static/report/{filename}`