背景
公司的新人文档要根据角色来生成不一样的word文档,但是里面存在大量的重复内容,每次增加删除一个内容需要更新所有的文档,维护起来太麻烦,加上本人喜欢用markdown写文档,所以我就研究了一下如何通过模板生成markdown和word。
功能如下
- 重复内容维护在一个地方
- 输出markdown
- 输出word
- 支持本地图片
假设新人文档如下
如果是管理员的话额外显示条目3,并且有欢迎您,管理员+名称,普通员工只显示普通员工+名称
<!-- template/welcome.md -->
## 注意事项1
+ 条目1
+ 条目2
<% if (admin) { %>+ 条目3<% } %>
<% if (admin) { %>
<h2>欢迎您,管理员<%= userName %></h2>
<% } else { %>
<h3> 欢迎您,普通员工<%= userName %> </h3>
<% } %>

输出到markdown
/**
* @description 替换模板文件中的变量
* @param {*} tempPath 模板路劲
* @param {*} options 需要替换的变量
*/
function renderFile(tempPath, options) {
return new Promise((resolve, reject) => {
ejs.renderFile(tempPath, options, function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
});
});
}
/**
* @param {String} outputPath 输出文件的路径
*/
function renderOutput(outputPath, content) {
return new Promise((resolve, reject) => {
fs.writeFile(outputPath, content, (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
});
}
通过传入不同的options我们可以发现已经可以实现动态内容,以及区分角色
输出到word
markdown 到word需要2步,第一步是用marked将markdown转成html,然后使用html-docx-js将html转为docx
/**
*
* @param {*} markdownPath markdown的路径
* @param {*} outputPath 输出路径
*/
function renderMarkdownToWord(markdownPath, outputPath) {
return new Promise((resolve, reject) => {
fs.readFile(markdownPath, "utf8", function (err, file) {
if (err) {
reject(err);
return;
}
const result = getHtml(marked(file));
renderOutput(outputPath, html2Docx.asBlob(result));
});
});
}
/**
* @description 修改样式
* @param {*} template 渲染内容
*/
function getHtml(template) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 45px;
}
@media (max-width: 767px) {
.markdown-body {
padding: 15px;
}
}
{{{style}}}
</style>
</head>
<body>
<div class="markdown-body">
${template}
</div>
</body>
</html>
`;
}
支持本地图片
因为我们所有的图片都在 `output/img` 下,所以生成的markdown可以直接访问,但是word的路径是错误的,于是我就想生成word的时候直接将图片转为base64
/**
*
* @param {*} imgPath 图片路径
*/
function renderImgToBase64(imgPath) {
let bitmap = fs.readFileSync(imgPath);
return (
"data:image/png;base64," + Buffer.from(bitmap, "binary").toString("base64")
); // base64编码
}
修改renderMarkdownToWord方法

到此本文内容差不多了
技术选型
模板引擎
模板引擎很多其中pug来到现在这家公司才接触的,但是本人看着语法不是很习惯还是习惯用Ejs 或者underscore,本文用 Ejs作为模板引擎
let people = ['geddy', 'neil', 'alex'];
let html = ejs.render('<%= people.join(", "); %>', {people: people}); // echo -> geddy, neil, alex
生成word
生成word采用 html-docx-js
github
本文所有的内容都已上传 github