@worm-vue3-print/core 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -0
- package/dist/browser/index.cjs +1920 -0
- package/dist/browser/index.d.cts +23 -0
- package/dist/browser/index.d.ts +23 -0
- package/dist/browser/index.js +170 -0
- package/dist/chunk-HLCZHPUF.js +31 -0
- package/dist/chunk-JZIVNXZ7.js +1777 -0
- package/dist/designer/index.cjs +2419 -0
- package/dist/designer/index.d.cts +652 -0
- package/dist/designer/index.d.ts +652 -0
- package/dist/designer/index.js +1620 -0
- package/dist/index.d.cts +4 -184
- package/dist/index.d.ts +4 -184
- package/dist/index.js +36 -1764
- package/dist/types-BGBAbQD5.d.cts +184 -0
- package/dist/types-BGBAbQD5.d.ts +184 -0
- package/package.json +21 -4
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { P as PageLayout, T as TemplateData, C as CodeRenderer } from '../types-BGBAbQD5.cjs';
|
|
2
|
+
|
|
3
|
+
interface BrowserRenderResult {
|
|
4
|
+
/** 最终多页 HTML 字符串 */
|
|
5
|
+
html: string;
|
|
6
|
+
/** 总页数 */
|
|
7
|
+
pageCount: number;
|
|
8
|
+
/** 分页布局(调试/高级用途) */
|
|
9
|
+
pageLayouts: PageLayout[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 浏览器内完成「数据绑定 → 测量 → 分页 → 最终 HTML」全流程。
|
|
13
|
+
* @param template 模板 JSON(设计器 TemplateData 结构兼容)
|
|
14
|
+
* @param printData 业务数据
|
|
15
|
+
* @param baseUrl 图片相对路径拼接前缀
|
|
16
|
+
* @param codeRenderer 条码渲染器(browserCodeRenderer)
|
|
17
|
+
*/
|
|
18
|
+
declare function renderHtmlPages(template: TemplateData, printData?: Record<string, any> | Record<string, any>[], baseUrl?: string, codeRenderer?: CodeRenderer): Promise<BrowserRenderResult>;
|
|
19
|
+
|
|
20
|
+
/** 浏览器条码/二维码渲染器;码值非法或为空时抛错,由渲染管线降级文本占位 */
|
|
21
|
+
declare const browserCodeRenderer: CodeRenderer;
|
|
22
|
+
|
|
23
|
+
export { type BrowserRenderResult, browserCodeRenderer, renderHtmlPages };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { P as PageLayout, T as TemplateData, C as CodeRenderer } from '../types-BGBAbQD5.js';
|
|
2
|
+
|
|
3
|
+
interface BrowserRenderResult {
|
|
4
|
+
/** 最终多页 HTML 字符串 */
|
|
5
|
+
html: string;
|
|
6
|
+
/** 总页数 */
|
|
7
|
+
pageCount: number;
|
|
8
|
+
/** 分页布局(调试/高级用途) */
|
|
9
|
+
pageLayouts: PageLayout[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 浏览器内完成「数据绑定 → 测量 → 分页 → 最终 HTML」全流程。
|
|
13
|
+
* @param template 模板 JSON(设计器 TemplateData 结构兼容)
|
|
14
|
+
* @param printData 业务数据
|
|
15
|
+
* @param baseUrl 图片相对路径拼接前缀
|
|
16
|
+
* @param codeRenderer 条码渲染器(browserCodeRenderer)
|
|
17
|
+
*/
|
|
18
|
+
declare function renderHtmlPages(template: TemplateData, printData?: Record<string, any> | Record<string, any>[], baseUrl?: string, codeRenderer?: CodeRenderer): Promise<BrowserRenderResult>;
|
|
19
|
+
|
|
20
|
+
/** 浏览器条码/二维码渲染器;码值非法或为空时抛错,由渲染管线降级文本占位 */
|
|
21
|
+
declare const browserCodeRenderer: CodeRenderer;
|
|
22
|
+
|
|
23
|
+
export { type BrowserRenderResult, browserCodeRenderer, renderHtmlPages };
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bindData,
|
|
3
|
+
generateHtml,
|
|
4
|
+
getPaperDimensions,
|
|
5
|
+
paginate
|
|
6
|
+
} from "../chunk-JZIVNXZ7.js";
|
|
7
|
+
|
|
8
|
+
// src/browser/browser-pagination.ts
|
|
9
|
+
var PX_PER_MM = 3.7795275591;
|
|
10
|
+
var READY_TIMEOUT_MS = 3e3;
|
|
11
|
+
async function renderHtmlPages(template, printData, baseUrl, codeRenderer) {
|
|
12
|
+
const boundTemplate = bindData(template, printData, baseUrl);
|
|
13
|
+
const measuredElements = await measureElements(boundTemplate, codeRenderer);
|
|
14
|
+
const pageLayouts = paginate(boundTemplate, measuredElements);
|
|
15
|
+
const html = generateHtml(boundTemplate, pageLayouts, printData, {
|
|
16
|
+
codeRenderer
|
|
17
|
+
});
|
|
18
|
+
return { html, pageCount: pageLayouts.length, pageLayouts };
|
|
19
|
+
}
|
|
20
|
+
async function measureElements(template, codeRenderer) {
|
|
21
|
+
const paper = getPaperDimensions(template);
|
|
22
|
+
const measureHtml = generateHtml(template, [], void 0, {
|
|
23
|
+
isMeasurementPass: true,
|
|
24
|
+
codeRenderer
|
|
25
|
+
});
|
|
26
|
+
const iframe = document.createElement("iframe");
|
|
27
|
+
iframe.setAttribute("aria-hidden", "true");
|
|
28
|
+
iframe.style.cssText = `position:fixed;left:-10000px;top:0;width:${paper.width}mm;height:${paper.height}mm;border:0;visibility:hidden;pointer-events:none;`;
|
|
29
|
+
document.body.appendChild(iframe);
|
|
30
|
+
try {
|
|
31
|
+
const win = iframe.contentWindow;
|
|
32
|
+
const doc = iframe.contentDocument;
|
|
33
|
+
if (!win || !doc) throw new Error("\u65E0\u6CD5\u521B\u5EFA\u6D4B\u91CF iframe");
|
|
34
|
+
doc.open();
|
|
35
|
+
doc.write(measureHtml);
|
|
36
|
+
doc.close();
|
|
37
|
+
await waitForRenderReady(win);
|
|
38
|
+
const measurements = readMeasurements(doc);
|
|
39
|
+
const measuredMap = /* @__PURE__ */ new Map();
|
|
40
|
+
const elementIndex = new Map(template.elements.map((el) => [el.id, el]));
|
|
41
|
+
for (const m of measurements) {
|
|
42
|
+
const el = elementIndex.get(m.id);
|
|
43
|
+
const repeatCount = el?.options?._repeatHeaderCount ?? 0;
|
|
44
|
+
measuredMap.set(m.id, {
|
|
45
|
+
id: m.id,
|
|
46
|
+
measuredHeight: m.height,
|
|
47
|
+
measuredRowHeights: m.rowHeights,
|
|
48
|
+
repeatHeaderHeight: m.rowHeights && repeatCount > 0 ? m.rowHeights.slice(0, repeatCount).reduce((s, h) => s + h, 0) : 0
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return measuredMap;
|
|
52
|
+
} finally {
|
|
53
|
+
iframe.remove();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function waitForRenderReady(win) {
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
let settled = false;
|
|
59
|
+
const finish = () => {
|
|
60
|
+
if (!settled) {
|
|
61
|
+
settled = true;
|
|
62
|
+
resolve();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const timer = setTimeout(finish, READY_TIMEOUT_MS);
|
|
66
|
+
const run = async () => {
|
|
67
|
+
try {
|
|
68
|
+
const fonts = win.document.fonts;
|
|
69
|
+
if (fonts?.ready) {
|
|
70
|
+
await Promise.race([fonts.ready, timeout(READY_TIMEOUT_MS)]);
|
|
71
|
+
}
|
|
72
|
+
await Promise.race([waitForImages(win.document), timeout(READY_TIMEOUT_MS)]);
|
|
73
|
+
} catch {
|
|
74
|
+
} finally {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
finish();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
if (win.document.readyState === "complete") {
|
|
80
|
+
void run();
|
|
81
|
+
} else {
|
|
82
|
+
win.addEventListener("load", () => void run(), { once: true });
|
|
83
|
+
setTimeout(finish, READY_TIMEOUT_MS);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function timeout(ms) {
|
|
88
|
+
return new Promise((_resolve, reject) => setTimeout(() => reject(new Error("timeout")), ms));
|
|
89
|
+
}
|
|
90
|
+
function waitForImages(doc) {
|
|
91
|
+
const imgs = Array.from(doc.images ?? []);
|
|
92
|
+
return Promise.all(
|
|
93
|
+
imgs.map(
|
|
94
|
+
(img) => img.complete ? Promise.resolve() : new Promise((resolve) => {
|
|
95
|
+
img.addEventListener("load", () => resolve(), { once: true });
|
|
96
|
+
img.addEventListener("error", () => resolve(), { once: true });
|
|
97
|
+
})
|
|
98
|
+
)
|
|
99
|
+
).then(() => void 0);
|
|
100
|
+
}
|
|
101
|
+
function readMeasurements(doc) {
|
|
102
|
+
const result = [];
|
|
103
|
+
const elements = doc.querySelectorAll("[data-measure-id]");
|
|
104
|
+
for (const el of elements) {
|
|
105
|
+
const htmlEl = el;
|
|
106
|
+
const id = htmlEl.getAttribute("data-measure-id");
|
|
107
|
+
if (!id) continue;
|
|
108
|
+
const heightPx = htmlEl.offsetHeight;
|
|
109
|
+
const table = htmlEl.querySelector("table.print-table");
|
|
110
|
+
if (table) {
|
|
111
|
+
const rowHeights = [];
|
|
112
|
+
const rows = table.querySelectorAll("tbody > tr[data-row-index]");
|
|
113
|
+
for (const row of rows) {
|
|
114
|
+
rowHeights.push(row.offsetHeight / PX_PER_MM);
|
|
115
|
+
}
|
|
116
|
+
result.push({ id, height: heightPx / PX_PER_MM, rowHeights });
|
|
117
|
+
} else {
|
|
118
|
+
result.push({ id, height: heightPx / PX_PER_MM });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/browser/browser-code-renderer.ts
|
|
125
|
+
import JsBarcode from "jsbarcode";
|
|
126
|
+
import QRCode from "qrcode";
|
|
127
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
128
|
+
function renderBarcodeSvg(value, opts) {
|
|
129
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
130
|
+
JsBarcode(svg, value, {
|
|
131
|
+
format: opts.barcodeType || "CODE128",
|
|
132
|
+
width: Math.max(1, (opts.barWidth ?? 2) / 2),
|
|
133
|
+
height: 30,
|
|
134
|
+
displayValue: opts.showText !== false,
|
|
135
|
+
fontSize: opts.fontSize ?? 10,
|
|
136
|
+
margin: 0,
|
|
137
|
+
marginBottom: 2
|
|
138
|
+
});
|
|
139
|
+
return svg.outerHTML;
|
|
140
|
+
}
|
|
141
|
+
function renderQrSvg(value, opts) {
|
|
142
|
+
const level = (opts.qrCodeLevel ?? "M").toUpperCase();
|
|
143
|
+
const qr = QRCode.create(value, {
|
|
144
|
+
errorCorrectionLevel: ["L", "M", "Q", "H"].includes(level) ? level : "M"
|
|
145
|
+
});
|
|
146
|
+
const modules = qr.modules;
|
|
147
|
+
const size = modules.size;
|
|
148
|
+
const dot = 4;
|
|
149
|
+
const quiet = 4 * dot;
|
|
150
|
+
const dim = size * dot + quiet * 2;
|
|
151
|
+
let rects = "";
|
|
152
|
+
for (let y = 0; y < size; y++) {
|
|
153
|
+
for (let x = 0; x < size; x++) {
|
|
154
|
+
if (modules.get(x, y)) {
|
|
155
|
+
rects += `<rect x="${quiet + x * dot}" y="${quiet + y * dot}" width="${dot}" height="${dot}" fill="#000"/>`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return `<svg xmlns="${SVG_NS}" viewBox="0 0 ${dim} ${dim}" width="${dim}" height="${dim}" shape-rendering="crispEdges">${rects}</svg>`;
|
|
160
|
+
}
|
|
161
|
+
var browserCodeRenderer = {
|
|
162
|
+
render(value, cellType, opts = {}) {
|
|
163
|
+
if (!value) throw new Error("empty barcode value");
|
|
164
|
+
return cellType === "qrcode" ? renderQrSvg(value, opts) : renderBarcodeSvg(value, opts);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
export {
|
|
168
|
+
browserCodeRenderer,
|
|
169
|
+
renderHtmlPages
|
|
170
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compileTemplate,
|
|
3
|
+
evaluate,
|
|
4
|
+
parse,
|
|
5
|
+
tokenize
|
|
6
|
+
} from "./chunk-JZIVNXZ7.js";
|
|
7
|
+
|
|
8
|
+
// src/index.ts
|
|
9
|
+
var TemplateEngine = class {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.functions = {};
|
|
12
|
+
if (options?.functions) {
|
|
13
|
+
this.functions = { ...options.functions };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
registerFunction(name, fn) {
|
|
17
|
+
this.functions[name] = fn;
|
|
18
|
+
}
|
|
19
|
+
evaluate(expression, context) {
|
|
20
|
+
const tokens = tokenize(expression);
|
|
21
|
+
const ast = parse(tokens);
|
|
22
|
+
return evaluate(ast, context, this.functions);
|
|
23
|
+
}
|
|
24
|
+
render(template, context) {
|
|
25
|
+
return compileTemplate(template, this.functions)(context);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
TemplateEngine
|
|
31
|
+
};
|