@scvzerng/template-parser
v0.1.0
Published
面向打印/报表场景的轻量级 HTML 模板解析与渲染库。通过在 HTML 表格中书写变量表达式,并提供业务上下文(Context),可完成变量替换、循环行扩展与统计汇总,最终输出可直接用于打印或导出的 HTML。
Readme
template-parser
面向打印/报表场景的轻量级 HTML 模板解析与渲染库。通过在 HTML 表格中书写变量表达式,并提供业务上下文(Context),可完成变量替换、循环行扩展与统计汇总,最终输出可直接用于打印或导出的 HTML。
功能特性
- 所见即所得模板:直接使用标准 HTML(通常是 table)作为模板,易于维护
- 表达式解析:支持
${变量名}与表达式语法,支持多表达式拼接 - 上下文驱动:通过 Context 提供变量值;支持自定义 Context 扩展集合与派生值
- 行模型:将表格解析为行/单元格模型,逐行渲染
- 循环/汇总:按约定识别「循环行」并扩展;支持专用统计语法与表达式汇总
- 轻量可扩展:核心抽象清晰,便于二次封装
安装
npm install template-parser使用方式(ESM):
import { NormalContext, Expression } from 'template-parser'快速开始
1) 表达式解析与求值
import { NormalContext, Expression } from 'template-parser'
const tpl = 'Hello ${name}, your balance is ${amount.toFixed(2)}'
const context = new NormalContext({ name: 'John', amount: 100.5 })
const exps = Expression.parse(tpl) // 提取到 ["${name}", "${amount.toFixed(2)}"]
// 将表达式替换回字符串(示例化简实现)
let i = 0
let result = ''
for (const exp of exps) {
result += tpl.slice(i, exp.getStartIndex())
result += String(exp.eval(context))
i = exp.getEndIndex()
}
result += tpl.slice(i)
// => Hello John, your balance is 100.502) 表格模板渲染(概念示例)
库中提供 HTML 表格到行/单元格模型的解析与渲染基类 HtmlTemplate,适合二次封装成你的业务模板。
HTML 模板(片段):
<table data-type="luckysheet_copy_action_table">
<tbody>
<tr>
<td>客户</td>
<td>${customerName}</td>
</tr>
<!-- 目标行(含集合变量) -->
<tr>
<td>${index + 1}</td>
<td>${item.name}</td>
<td>${item.qty}</td>
</tr>
<!-- 占位空行(用于循环复制) -->
<tr>
<td></td><td></td><td></td>
</tr>
<!-- 合计:专用语法 “字段,小数位” 或表达式 -->
<tr>
<td>合计</td>
<td colspan="2">qty,2</td>
</tr>
</tbody>
</table>简单的模板解析器实现:
import { HtmlTemplate, NormalContext } from 'template-parser'
class MyTableTemplate extends HtmlTemplate<NormalContext> {
parse(ctx: NormalContext): string {
// 逐行渲染(ExpressionCell 会用 ctx 替换表达式)
let row = (this as any).rows[0] // rows 为受保护字段,示例中直接访问;在你的项目中可封装更友好的 API
while (row) {
row.eval(ctx)
row = row.getNext()
}
// 返回模板根节点 HTML。此处 domFragment 为基类中的片段
// 可根据需要改为 outerHTML 或 innerHTML
return (this as any).domFragment.firstElementChild?.outerHTML ?? ''
}
}关于循环与占位:
- 目标行:包含集合相关变量(如
${item.name}),用于被复制 - 占位空行:目标行之后紧邻的“全空”行作为占位,渲染时按集合长度复制目标行并覆盖/追加
注意:集合与统计依赖 Context.getVector 提供集合数据;默认 NormalContext 未实现集合向量(会抛出异常)。你需要实现自定义 Context 以支持集合渲染与统计。
统计单元格(SummeryCell)
- 写法:
字段,小数位(如qty,2),等价于${qty.reduce((t,e)=>t+e,0).toFixed(2)}。依赖Context.getVector(field)提供数组。
核心 API 速查
从 template-parser 导出的主要成员:
- 上下文
Context接口:set/get/merge/has/getVariables/getVectorNormalContext:基础实现(未实现集合 getVector)
- 表达式
Expression:parse(input: string)、eval(context)等- 常量:
EXP_START = '\${',EXP_END = '}'
- 模板与表格
Template<T extends Context>:parse(context: T): stringHtmlTemplate<T extends Context>:HTML 表格模板基类HtmlTableRow/TemplateRow:行级操作(eval/append/remove/override/clone/isEmpty 等)TemplateCell及实现:NormalCell、ExpressionCell、EmptyCell、SummeryCellCellUtils:辅助判断单元格类型(空/普通/表达式/统计)
自定义 Context(支持集合/循环)
import { NormalContext } from 'template-parser'
class ListContext extends NormalContext {
getVector(key: string): (string | number)[] {
const v = this.get(key)
return Array.isArray(v) ? (v as any[]) : []
}
}开发指南
- 本地开发:
npm run dev - 构建:
npm run build(会先跑测试,再打包 Vite 和类型) - 测试:
npm run test(Vitest + jsdom) - Lint:
npm run lint/npm run lint:fix
打包产物(见 package.json exports):
- ESM:
dist/template-parser.es.js - UMD:
dist/template-parser.umd.js - 类型:
dist/types/index.d.ts
兼容性与限制
- 需要 DOM 环境(浏览器或 jsdom)。Node 端纯字符串渲染可仅使用 Expression/Context。
- HtmlTemplate 解析要求根 table ,tbody 必须存在。
- 循环/统计依赖你提供的集合 Context 实现。
