/**
|
* 数据绑定:模板文本里的 {{path}}
|
* <p>
|
* 只做取值与替换,不做任何表达式求值;需要计算的场景交给规则引擎。
|
* 取不到的路径渲染为空字符串——报告上不该出现用户看不懂的 {{}}。
|
*/
|
import { formatValue, readPath } from './path';
|
|
/** 绑定表达式:{{ report.reportNo }} */
|
const BINDING_PATTERN = /\{\{\s*([^{}]+?)\s*\}\}/g;
|
|
/** 提取文本里出现的所有绑定路径(去重),用于渲染前的数据依赖检查 */
|
export function listBindingPaths(template: string): string[] {
|
const paths = new Set<string>();
|
for (const match of template.matchAll(BINDING_PATTERN)) {
|
const path = match[1]?.trim();
|
if (path) {
|
paths.add(path);
|
}
|
}
|
return [...paths];
|
}
|
|
/** 解析单个绑定路径,取不到返回 undefined */
|
export function resolveBinding(path: string, context: unknown): unknown {
|
return readPath(context, path.trim());
|
}
|
|
/**
|
* 替换文本里的全部绑定表达式,并回报取不到值的路径。
|
* <p>
|
* 渲染时绑定不上通常意味着数据缺口,静默渲染成空会让报告看起来"正常但缺内容",
|
* 所以把缺口交给调用方,由渲染引擎汇总成可见的问题清单。
|
*/
|
export function resolveTextWithReport(
|
template: string,
|
context: unknown,
|
onMissing: (path: string) => void,
|
): string {
|
return template.replaceAll(BINDING_PATTERN, (_, rawPath: string) => {
|
const path = rawPath.trim();
|
const value = readPath(context, path);
|
if (value === undefined || value === null) {
|
onMissing(path);
|
}
|
return formatValue(value);
|
});
|
}
|
|
/** 替换文本里的全部绑定表达式;未绑定的路径渲染为空 */
|
export function resolveText(template: string, context: unknown): string {
|
return resolveTextWithReport(template, context, () => {});
|
}
|
|
/** 判断一段文本是否是纯绑定表达式({{path}} 单占一段),是则返回路径 */
|
export function asSingleBinding(template: string): string | undefined {
|
const trimmed = template.trim();
|
const match = /^\{\{\s*([^{}]+?)\s*\}\}$/.exec(trimmed);
|
return match?.[1]?.trim();
|
}
|