@worm-vue3-print/core 1.3.0 → 1.3.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/dist/browser/index.cjs +300 -63
- package/dist/browser/index.d.cts +2 -2
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.js +1 -1
- package/dist/{chunk-V4A5623C.js → chunk-B2XJSF4V.js} +1 -1
- package/dist/{chunk-VFEURLAZ.js → chunk-BBSFBAWT.js} +308 -63
- package/dist/client/index.cjs +381 -0
- package/dist/client/index.d.cts +241 -0
- package/dist/client/index.d.ts +241 -0
- package/dist/client/index.js +346 -0
- package/dist/designer/index.cjs +196 -11
- package/dist/designer/index.d.cts +21 -6
- package/dist/designer/index.d.ts +21 -6
- package/dist/designer/index.js +35 -4
- package/dist/{driver-CsWkGK4a.d.cts → driver-Dn_YAzO5.d.cts} +4 -1
- package/dist/{driver-CsWkGK4a.d.ts → driver-Dn_YAzO5.d.ts} +4 -1
- package/dist/index.cjs +316 -63
- package/dist/index.d.cts +29 -7
- package/dist/index.d.ts +29 -7
- package/dist/index.js +18 -2
- package/dist/node/index.d.cts +1 -1
- package/dist/node/index.d.ts +1 -1
- package/dist/{ports-B63x7kCX.d.ts → ports-BvwlA_km.d.ts} +1 -1
- package/dist/{ports-q934kIC7.d.cts → ports-Cf5sjwls.d.cts} +1 -1
- package/package.json +6 -1
|
@@ -340,6 +340,113 @@ var Parser = class {
|
|
|
340
340
|
}
|
|
341
341
|
};
|
|
342
342
|
|
|
343
|
+
// src/numeric.ts
|
|
344
|
+
var MAX_DIGITS = 20;
|
|
345
|
+
var EXTRA_SCALE = 8;
|
|
346
|
+
function toNumber(value, fallback = 0) {
|
|
347
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
|
|
348
|
+
if (typeof value === "string") {
|
|
349
|
+
const s = value.trim();
|
|
350
|
+
if (s === "") return fallback;
|
|
351
|
+
const n = Number(s);
|
|
352
|
+
return Number.isFinite(n) ? n : fallback;
|
|
353
|
+
}
|
|
354
|
+
if (value == null) return fallback;
|
|
355
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
356
|
+
return fallback;
|
|
357
|
+
}
|
|
358
|
+
function isNumericLike(value) {
|
|
359
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
360
|
+
if (typeof value === "string") {
|
|
361
|
+
const s = value.trim();
|
|
362
|
+
return s !== "" && Number.isFinite(Number(s));
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
function normalizeFloat(value) {
|
|
367
|
+
if (!Number.isFinite(value) || Number.isInteger(value)) return value;
|
|
368
|
+
if (Math.abs(value) >= 1e15) return value;
|
|
369
|
+
return Number(value.toPrecision(12));
|
|
370
|
+
}
|
|
371
|
+
function decimalsOf(n) {
|
|
372
|
+
if (!Number.isFinite(n) || Number.isInteger(n)) return 0;
|
|
373
|
+
const s = String(n);
|
|
374
|
+
if (s.includes("e") || s.includes("E")) return 0;
|
|
375
|
+
const i = s.indexOf(".");
|
|
376
|
+
return i === -1 ? 0 : s.length - i - 1;
|
|
377
|
+
}
|
|
378
|
+
function rescale(value, decimals) {
|
|
379
|
+
if (!Number.isFinite(value)) return 0;
|
|
380
|
+
const d = Math.min(Math.max(decimals, 0), 12);
|
|
381
|
+
if (d === 0) return Math.round(value);
|
|
382
|
+
const f = 10 ** d;
|
|
383
|
+
const scaled = value * f;
|
|
384
|
+
if (!Number.isFinite(scaled) || Math.abs(scaled) >= 1e15) return normalizeFloat(value);
|
|
385
|
+
return Math.round(scaled) / f;
|
|
386
|
+
}
|
|
387
|
+
function decimalAdd(left, right) {
|
|
388
|
+
const a = toNumber(left);
|
|
389
|
+
const b = toNumber(right);
|
|
390
|
+
const raw = a + b;
|
|
391
|
+
return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
|
|
392
|
+
}
|
|
393
|
+
function decimalSubtract(left, right) {
|
|
394
|
+
const a = toNumber(left);
|
|
395
|
+
const b = toNumber(right);
|
|
396
|
+
const raw = a - b;
|
|
397
|
+
return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
|
|
398
|
+
}
|
|
399
|
+
function decimalMultiply(left, right) {
|
|
400
|
+
const a = toNumber(left);
|
|
401
|
+
const b = toNumber(right);
|
|
402
|
+
const raw = a * b;
|
|
403
|
+
return rescale(raw, decimalsOf(a) + decimalsOf(b));
|
|
404
|
+
}
|
|
405
|
+
function decimalDivide(left, right) {
|
|
406
|
+
const a = toNumber(left);
|
|
407
|
+
const b = toNumber(right);
|
|
408
|
+
if (b === 0) return 0;
|
|
409
|
+
return normalizeFloat(a / b);
|
|
410
|
+
}
|
|
411
|
+
function decimalModulo(left, right) {
|
|
412
|
+
const a = toNumber(left);
|
|
413
|
+
const b = toNumber(right);
|
|
414
|
+
if (b === 0) return 0;
|
|
415
|
+
return normalizeFloat(a % b);
|
|
416
|
+
}
|
|
417
|
+
function normalizeDigits(digits) {
|
|
418
|
+
if (digits === void 0 || digits === null) return 2;
|
|
419
|
+
const d = Math.trunc(toNumber(digits, 2));
|
|
420
|
+
if (!Number.isFinite(d)) return 2;
|
|
421
|
+
return Math.min(Math.max(d, -MAX_DIGITS), MAX_DIGITS);
|
|
422
|
+
}
|
|
423
|
+
function roundTo(value, digits, mode) {
|
|
424
|
+
const n = toNumber(value, 0);
|
|
425
|
+
const d = normalizeDigits(digits);
|
|
426
|
+
if (n === 0) return 0;
|
|
427
|
+
if (Math.abs(n) >= 1e21) return normalizeFloat(n);
|
|
428
|
+
const scale = Math.max(d, 0) + EXTRA_SCALE;
|
|
429
|
+
const fixed = n.toFixed(scale);
|
|
430
|
+
if (!/^-?\d+(\.\d+)?$/.test(fixed)) return normalizeFloat(n);
|
|
431
|
+
const negative = fixed.startsWith("-");
|
|
432
|
+
const digitsStr = fixed.replace("-", "").replace(".", "");
|
|
433
|
+
let scaled = BigInt(digitsStr || "0");
|
|
434
|
+
if (negative) scaled = -scaled;
|
|
435
|
+
const pow10 = 10n ** BigInt(scale - d);
|
|
436
|
+
const sign = scaled < 0n ? -1n : 1n;
|
|
437
|
+
const abs = scaled < 0n ? -scaled : scaled;
|
|
438
|
+
let q = abs / pow10;
|
|
439
|
+
const r = abs % pow10;
|
|
440
|
+
const doubled = 2n * r;
|
|
441
|
+
if (mode === "up") {
|
|
442
|
+
if (r !== 0n) q += 1n;
|
|
443
|
+
} else if (mode !== "down" && doubled >= pow10) {
|
|
444
|
+
if (doubled > pow10 || mode === "half-up" || q % 2n === 1n) q += 1n;
|
|
445
|
+
}
|
|
446
|
+
const result = sign * q;
|
|
447
|
+
return normalizeFloat(d >= 0 ? Number(result) / 10 ** d : Number(result) * 10 ** -d);
|
|
448
|
+
}
|
|
449
|
+
|
|
343
450
|
// src/evaluator.ts
|
|
344
451
|
var SAFE_GLOBALS = {
|
|
345
452
|
Math,
|
|
@@ -405,16 +512,17 @@ function evalBinary(node, context, functions) {
|
|
|
405
512
|
const left = evaluate(node.left, context, functions);
|
|
406
513
|
const right = evaluate(node.right, context, functions);
|
|
407
514
|
switch (node.op) {
|
|
515
|
+
// 数值运算统一走十进制精确实现:消除 0.1 + 0.2 类浮点噪声、字符串数字按数值处理、除零兜底 0
|
|
408
516
|
case "+":
|
|
409
|
-
return left
|
|
517
|
+
return isNumericOperands(left, right) ? decimalAdd(left, right) : concatOperands(left, right);
|
|
410
518
|
case "-":
|
|
411
|
-
return left
|
|
519
|
+
return decimalSubtract(left, right);
|
|
412
520
|
case "*":
|
|
413
|
-
return left
|
|
521
|
+
return decimalMultiply(left, right);
|
|
414
522
|
case "/":
|
|
415
|
-
return left
|
|
523
|
+
return decimalDivide(left, right);
|
|
416
524
|
case "%":
|
|
417
|
-
return left
|
|
525
|
+
return decimalModulo(left, right);
|
|
418
526
|
case "<":
|
|
419
527
|
return left < right;
|
|
420
528
|
case ">":
|
|
@@ -443,15 +551,29 @@ function evalUnary(node, context, functions) {
|
|
|
443
551
|
const arg = evaluate(node.arg, context, functions);
|
|
444
552
|
switch (node.op) {
|
|
445
553
|
case "-":
|
|
446
|
-
return
|
|
554
|
+
return decimalSubtract(0, arg);
|
|
447
555
|
case "+":
|
|
448
|
-
return
|
|
556
|
+
return toNumber(arg);
|
|
449
557
|
case "!":
|
|
450
558
|
return !arg;
|
|
451
559
|
default:
|
|
452
560
|
throw new Error(`\u672A\u77E5\u4E00\u5143\u8FD0\u7B97\u7B26: ${node.op}`);
|
|
453
561
|
}
|
|
454
562
|
}
|
|
563
|
+
function concatOperands(left, right) {
|
|
564
|
+
return `${left == null ? "" : String(left)}${right == null ? "" : String(right)}`;
|
|
565
|
+
}
|
|
566
|
+
function isNumericOperands(left, right) {
|
|
567
|
+
const l = operandKind(left);
|
|
568
|
+
const r = operandKind(right);
|
|
569
|
+
if (l === "other" || r === "other") return false;
|
|
570
|
+
return l === "num" || r === "num";
|
|
571
|
+
}
|
|
572
|
+
function operandKind(value) {
|
|
573
|
+
if (value == null || value === "") return "neutral";
|
|
574
|
+
if (isNumericLike(value)) return "num";
|
|
575
|
+
return "other";
|
|
576
|
+
}
|
|
455
577
|
function evalMember(node, context, functions) {
|
|
456
578
|
const obj = evaluate(node.object, context, functions);
|
|
457
579
|
if (obj == null) {
|
|
@@ -685,6 +807,34 @@ var systemVars = {
|
|
|
685
807
|
printTime: () => Date.now()
|
|
686
808
|
};
|
|
687
809
|
|
|
810
|
+
// src/functions/math.ts
|
|
811
|
+
function addNumbers(...values) {
|
|
812
|
+
return values.reduce((acc, v) => decimalAdd(acc, v), 0);
|
|
813
|
+
}
|
|
814
|
+
function subtractNumbers(...values) {
|
|
815
|
+
if (values.length === 0) return 0;
|
|
816
|
+
return values.slice(1).reduce((acc, v) => decimalSubtract(acc, v), decimalAdd(values[0], 0));
|
|
817
|
+
}
|
|
818
|
+
function multiplyNumbers(...values) {
|
|
819
|
+
if (values.length === 0) return 0;
|
|
820
|
+
return values.reduce((acc, v) => decimalMultiply(acc, v), 1);
|
|
821
|
+
}
|
|
822
|
+
function divideNumbers(a, b) {
|
|
823
|
+
return decimalDivide(a, b);
|
|
824
|
+
}
|
|
825
|
+
function round(value, digits) {
|
|
826
|
+
return roundTo(value, digits, "half-up");
|
|
827
|
+
}
|
|
828
|
+
function roundUp(value, digits) {
|
|
829
|
+
return roundTo(value, digits, "up");
|
|
830
|
+
}
|
|
831
|
+
function roundDown(value, digits) {
|
|
832
|
+
return roundTo(value, digits, "down");
|
|
833
|
+
}
|
|
834
|
+
function roundHalfEven(value, digits) {
|
|
835
|
+
return roundTo(value, digits, "half-even");
|
|
836
|
+
}
|
|
837
|
+
|
|
688
838
|
// src/render/expression-eval.ts
|
|
689
839
|
var RenderEngine = class {
|
|
690
840
|
constructor() {
|
|
@@ -710,8 +860,19 @@ var FORMAT_FUNCTIONS = {
|
|
|
710
860
|
IF: ifFn,
|
|
711
861
|
CONCAT: (...args) => args.filter((v) => v != null).map(String).join(""),
|
|
712
862
|
IFEMPTY: (v, d) => v != null && v !== "" ? String(v) : d,
|
|
713
|
-
|
|
714
|
-
|
|
863
|
+
LEN: (s) => String(s).length,
|
|
864
|
+
// 四则运算(与 +/-/*// 运算符同一套数值语义,供不方便写运算符的场景使用)
|
|
865
|
+
ADD: addNumbers,
|
|
866
|
+
SUB: subtractNumbers,
|
|
867
|
+
MUL: multiplyNumbers,
|
|
868
|
+
DIV: divideNumbers,
|
|
869
|
+
// 数值修约
|
|
870
|
+
ROUND: round,
|
|
871
|
+
ROUNDUP: roundUp,
|
|
872
|
+
CEIL: roundUp,
|
|
873
|
+
ROUNDDOWN: roundDown,
|
|
874
|
+
FLOOR: roundDown,
|
|
875
|
+
ROUNDBANK: roundHalfEven
|
|
715
876
|
};
|
|
716
877
|
for (const [name, fn] of Object.entries(FORMAT_FUNCTIONS)) {
|
|
717
878
|
engine.registerFunction(name, fn);
|
|
@@ -756,7 +917,7 @@ function evaluateTemplate(text, ctx) {
|
|
|
756
917
|
|
|
757
918
|
// src/render/data-binder.ts
|
|
758
919
|
function bindData(template, printData, baseUrl, fontBaseUrl) {
|
|
759
|
-
const data = printData ?? {};
|
|
920
|
+
const data = { ...resolveSystemVariables(), ...printData ?? {} };
|
|
760
921
|
const bound = JSON.parse(JSON.stringify(template));
|
|
761
922
|
if (bound.header?.elements) {
|
|
762
923
|
bound.header.elements = bound.header.elements.map((el) => bindElement(el, data, baseUrl));
|
|
@@ -785,7 +946,12 @@ function bindData(template, printData, baseUrl, fontBaseUrl) {
|
|
|
785
946
|
function bindElement(el, data, baseUrl) {
|
|
786
947
|
const cloned = { ...el, options: { ...el.options } };
|
|
787
948
|
if (typeof cloned.options.formatter === "string") {
|
|
788
|
-
|
|
949
|
+
const raw = cloned.options.formatter;
|
|
950
|
+
if (referencesPageNumbers(raw)) {
|
|
951
|
+
cloned.options.rawFormatter = raw;
|
|
952
|
+
} else {
|
|
953
|
+
cloned.options.formatter = evaluateTemplate(raw, data);
|
|
954
|
+
}
|
|
789
955
|
}
|
|
790
956
|
const isImage = cloned.type === "image" || cloned.printElementType?.type === "image";
|
|
791
957
|
if (isImage && typeof cloned.options.src === "string") {
|
|
@@ -832,39 +998,25 @@ function bindTableData(el, data) {
|
|
|
832
998
|
if (dataStartIdx < 0) dataStartIdx = renderRows.length;
|
|
833
999
|
const ctx = itemCtx(item);
|
|
834
1000
|
renderRows.push(makeRenderRow(row, (cell) => {
|
|
835
|
-
|
|
836
|
-
if (!formatter) return "";
|
|
837
|
-
return evaluateTemplate(formatter, ctx);
|
|
1001
|
+
return resolveCellText(cell, ctx);
|
|
838
1002
|
}));
|
|
839
1003
|
dataRowCtx.push(ctx);
|
|
840
1004
|
}
|
|
841
1005
|
continue;
|
|
842
1006
|
}
|
|
843
1007
|
if (mode === "dynamic" && row.type === "subtotal") {
|
|
844
|
-
const tpl = makeRenderRow(row, (cell) => {
|
|
845
|
-
const formatter = cell.formatter;
|
|
846
|
-
if (!formatter) return "";
|
|
847
|
-
return evaluateTemplate(formatter, { rows: summaryRows, ...data });
|
|
848
|
-
}, true);
|
|
1008
|
+
const tpl = makeRenderRow(row, (cell) => resolveCellText(cell, { rows: summaryRows, ...data }), true);
|
|
849
1009
|
subtotalTemplates.push(tpl);
|
|
850
1010
|
renderRows.push(tpl);
|
|
851
1011
|
continue;
|
|
852
1012
|
}
|
|
853
1013
|
if (mode === "dynamic" && row.type === "summary") {
|
|
854
|
-
const summaryRow = makeRenderRow(row, (cell) => {
|
|
855
|
-
const formatter = cell.formatter;
|
|
856
|
-
if (!formatter) return "";
|
|
857
|
-
return evaluateTemplate(formatter, { rows: summaryRows, ...data });
|
|
858
|
-
});
|
|
1014
|
+
const summaryRow = makeRenderRow(row, (cell) => resolveCellText(cell, { rows: summaryRows, ...data }));
|
|
859
1015
|
summaryRenderRows.push(summaryRow);
|
|
860
1016
|
renderRows.push(summaryRow);
|
|
861
1017
|
continue;
|
|
862
1018
|
}
|
|
863
|
-
renderRows.push(makeRenderRow(row, (cell) =>
|
|
864
|
-
const formatter = cell.formatter;
|
|
865
|
-
if (!formatter) return "";
|
|
866
|
-
return evaluateTemplate(formatter, data);
|
|
867
|
-
}));
|
|
1019
|
+
renderRows.push(makeRenderRow(row, (cell) => resolveCellText(cell, data)));
|
|
868
1020
|
}
|
|
869
1021
|
opts._renderRows = renderRows;
|
|
870
1022
|
opts._repeatHeaderCount = countRepeatHeader(rows);
|
|
@@ -874,13 +1026,20 @@ function bindTableData(el, data) {
|
|
|
874
1026
|
opts._summaryRows = summaryRenderRows;
|
|
875
1027
|
opts._mainData = data;
|
|
876
1028
|
}
|
|
1029
|
+
function resolveCellText(cell, ctx) {
|
|
1030
|
+
const formatter = cell.formatter;
|
|
1031
|
+
if (!formatter) return "";
|
|
1032
|
+
if (referencesPageNumbers(formatter)) return formatter;
|
|
1033
|
+
return evaluateTemplate(formatter, ctx);
|
|
1034
|
+
}
|
|
877
1035
|
function makeRenderRow(row, resolve, keepRaw = false) {
|
|
878
1036
|
return {
|
|
879
1037
|
type: row.type,
|
|
880
1038
|
height: row.height ?? 8,
|
|
881
1039
|
cells: row.cells.map((cell) => ({
|
|
882
1040
|
content: cell.merged ? "" : resolve(cell),
|
|
883
|
-
|
|
1041
|
+
// 小计行(keepRaw)与引用页码的单元格都保留原始表达式:前者按当页数据行重算,后者按当页页码重算
|
|
1042
|
+
...(keepRaw || referencesPageNumbers(cell.formatter)) && !cell.merged ? { rawFormatter: cell.formatter ?? "" } : {},
|
|
884
1043
|
cellType: cell.cellType,
|
|
885
1044
|
barcodeType: cell.barcodeType,
|
|
886
1045
|
qrCodeLevel: cell.qrCodeLevel,
|
|
@@ -908,16 +1067,35 @@ function makeRenderRow(row, resolve, keepRaw = false) {
|
|
|
908
1067
|
};
|
|
909
1068
|
}
|
|
910
1069
|
function countRepeatHeader(rows) {
|
|
1070
|
+
const headerCount = countLeadingHeaderRows(rows);
|
|
1071
|
+
if (headerCount === 0) return 0;
|
|
1072
|
+
const enabled = rows.slice(0, headerCount).some((row) => row.repeatOnPage === true);
|
|
1073
|
+
if (!enabled) return 0;
|
|
1074
|
+
return alignToRowspanBoundary(rows, headerCount);
|
|
1075
|
+
}
|
|
1076
|
+
function countLeadingHeaderRows(rows) {
|
|
911
1077
|
let n = 0;
|
|
912
1078
|
for (const row of rows) {
|
|
913
|
-
if (row
|
|
914
|
-
|
|
915
|
-
} else {
|
|
916
|
-
break;
|
|
917
|
-
}
|
|
1079
|
+
if (row?.type !== "header") break;
|
|
1080
|
+
n++;
|
|
918
1081
|
}
|
|
919
1082
|
return n;
|
|
920
1083
|
}
|
|
1084
|
+
function isRowspanComplete(rows, n) {
|
|
1085
|
+
for (let r = 0; r < n; r++) {
|
|
1086
|
+
for (const cell of rows[r]?.cells ?? []) {
|
|
1087
|
+
if (cell?.merged) continue;
|
|
1088
|
+
if (r + (cell?.rowspan ?? 1) > n) return false;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return true;
|
|
1092
|
+
}
|
|
1093
|
+
function alignToRowspanBoundary(rows, max2) {
|
|
1094
|
+
for (let n = max2; n >= 1; n--) {
|
|
1095
|
+
if (isRowspanComplete(rows, n)) return n;
|
|
1096
|
+
}
|
|
1097
|
+
return 1;
|
|
1098
|
+
}
|
|
921
1099
|
var pad2 = (n) => String(n).padStart(2, "0");
|
|
922
1100
|
function resolveSystemVariables(now = /* @__PURE__ */ new Date(), page = {}) {
|
|
923
1101
|
return {
|
|
@@ -927,6 +1105,9 @@ function resolveSystemVariables(now = /* @__PURE__ */ new Date(), page = {}) {
|
|
|
927
1105
|
totalPages: page.totalPages ?? 1
|
|
928
1106
|
};
|
|
929
1107
|
}
|
|
1108
|
+
function referencesPageNumbers(expr) {
|
|
1109
|
+
return typeof expr === "string" && /\b(pageIndex|totalPages)\b/.test(expr);
|
|
1110
|
+
}
|
|
930
1111
|
function injectSystemVariables(html, now = /* @__PURE__ */ new Date()) {
|
|
931
1112
|
const { printDate, printTime } = resolveSystemVariables(now);
|
|
932
1113
|
return html.replace(/\{printDate\}/g, printDate).replace(/\{printTime\}/g, printTime);
|
|
@@ -1573,10 +1754,9 @@ function tableDesignBottom(el) {
|
|
|
1573
1754
|
const opts = el.options ?? {};
|
|
1574
1755
|
const top = opts.top ?? 0;
|
|
1575
1756
|
const rows = opts.tableRows ?? [];
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
return top + (opts.height ?? 0);
|
|
1757
|
+
const rowSum = rows.reduce((s, r) => s + (r.height ?? 0), 0);
|
|
1758
|
+
const designHeight = Math.max(opts.height ?? 0, rowSum);
|
|
1759
|
+
return top + designHeight;
|
|
1580
1760
|
}
|
|
1581
1761
|
function buildFollowMap(sorted, excludedIds) {
|
|
1582
1762
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -1717,7 +1897,10 @@ function paginate(template, measuredElements) {
|
|
|
1717
1897
|
let isFirstPage = true;
|
|
1718
1898
|
let pageBroken = false;
|
|
1719
1899
|
function sectionTop(el) {
|
|
1720
|
-
return pageBroken ?
|
|
1900
|
+
return pageBroken ? pageCursorTop() : el.options?.top ?? 0;
|
|
1901
|
+
}
|
|
1902
|
+
function pageCursorTop() {
|
|
1903
|
+
return Math.max(0, fullPageHeight() - remaining);
|
|
1721
1904
|
}
|
|
1722
1905
|
function fullPageHeight() {
|
|
1723
1906
|
return isFirstPage ? contentHeight - overlayH - SAFETY_MARGIN : contentHeight - SAFETY_MARGIN;
|
|
@@ -1784,7 +1967,7 @@ function paginate(template, measuredElements) {
|
|
|
1784
1967
|
function paginateNonTable(el, measured, sortedList, idx) {
|
|
1785
1968
|
const elHeight = measured.get(el.id)?.measuredHeight ?? el.options?.height ?? 0;
|
|
1786
1969
|
if (elHeight <= remaining) {
|
|
1787
|
-
const top2 = pageBroken ?
|
|
1970
|
+
const top2 = pageBroken ? pageCursorTop() : el.options?.top ?? 0;
|
|
1788
1971
|
noteOverflow(top2 + elHeight);
|
|
1789
1972
|
currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
|
|
1790
1973
|
remaining -= elHeight;
|
|
@@ -1803,8 +1986,9 @@ function paginate(template, measuredElements) {
|
|
|
1803
1986
|
}
|
|
1804
1987
|
return idx + 1;
|
|
1805
1988
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1989
|
+
finishPage();
|
|
1990
|
+
const top = pageBroken ? pageCursorTop() : el.options?.top ?? 0;
|
|
1991
|
+
if (top + elHeight > contentHeight) overflowOnCurrent = true;
|
|
1808
1992
|
currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
|
|
1809
1993
|
remaining -= elHeight;
|
|
1810
1994
|
return idx + 1;
|
|
@@ -1820,7 +2004,7 @@ function paginate(template, measuredElements) {
|
|
|
1820
2004
|
}
|
|
1821
2005
|
const unitHeight = Math.max(maxBottom - minTop, 0);
|
|
1822
2006
|
const place = () => {
|
|
1823
|
-
const offset = pageBroken ? -minTop : 0;
|
|
2007
|
+
const offset = pageBroken ? pageCursorTop() - minTop : 0;
|
|
1824
2008
|
noteOverflow(minTop + offset + unitHeight);
|
|
1825
2009
|
for (const m of members) {
|
|
1826
2010
|
currentPage.push({
|
|
@@ -1894,6 +2078,7 @@ function paginate(template, measuredElements) {
|
|
|
1894
2078
|
const groups = buildRowGroups(bodyRows, rowCount);
|
|
1895
2079
|
let sliceStart = 0;
|
|
1896
2080
|
let firstSlice = true;
|
|
2081
|
+
let sliceTop = sectionTop(el);
|
|
1897
2082
|
for (const g of groups) {
|
|
1898
2083
|
const gh = bodyHeights.slice(g.start, g.end).reduce((s, h) => s + h, 0);
|
|
1899
2084
|
if (gh + subtotalH <= remaining) {
|
|
@@ -1908,12 +2093,13 @@ function paginate(template, measuredElements) {
|
|
|
1908
2093
|
endRow: g.start,
|
|
1909
2094
|
subtotal: subtotalH > 0,
|
|
1910
2095
|
...!firstSlice && repeatCount > 0 ? { repeatHeader: true } : {},
|
|
1911
|
-
renderTop:
|
|
2096
|
+
renderTop: sliceTop
|
|
1912
2097
|
});
|
|
1913
2098
|
firstSlice = false;
|
|
1914
2099
|
sliceStart = g.start;
|
|
1915
2100
|
}
|
|
1916
2101
|
finishPage();
|
|
2102
|
+
sliceTop = sectionTop(el);
|
|
1917
2103
|
if (!firstSlice) remaining -= repeatH;
|
|
1918
2104
|
remaining -= gh;
|
|
1919
2105
|
}
|
|
@@ -1925,7 +2111,7 @@ function paginate(template, measuredElements) {
|
|
|
1925
2111
|
endRow: rowCount,
|
|
1926
2112
|
subtotal: subtotalH > 0,
|
|
1927
2113
|
...!firstSlice && repeatCount > 0 ? { repeatHeader: true } : {},
|
|
1928
|
-
renderTop:
|
|
2114
|
+
renderTop: sliceTop
|
|
1929
2115
|
});
|
|
1930
2116
|
}
|
|
1931
2117
|
if (summaryH > 0) {
|
|
@@ -2060,6 +2246,13 @@ function resolveBarcodeSize(input) {
|
|
|
2060
2246
|
}
|
|
2061
2247
|
|
|
2062
2248
|
// src/render/html-generator.ts
|
|
2249
|
+
function pageVarsContext(vars) {
|
|
2250
|
+
return { ...vars.data ?? {}, pageIndex: vars.pageIndex, totalPages: vars.totalPages };
|
|
2251
|
+
}
|
|
2252
|
+
function pageVarsData(printData) {
|
|
2253
|
+
if (Array.isArray(printData)) return printData[0] ?? {};
|
|
2254
|
+
return printData ?? {};
|
|
2255
|
+
}
|
|
2063
2256
|
function generateHtml(template, pageLayouts, printData, options) {
|
|
2064
2257
|
const css = buildFontFaceCss(template.fonts) + buildPageCss(template, options?.pageHeightMm);
|
|
2065
2258
|
const isMeasure = options?.isMeasurementPass === true;
|
|
@@ -2144,22 +2337,25 @@ function renderPage(template, page, pageNum, totalPages, ctx, printData, pageCla
|
|
|
2144
2337
|
height: ctx.pageHeightMm && ctx.pageHeightMm > 0 ? ctx.pageHeightMm : paper.height
|
|
2145
2338
|
};
|
|
2146
2339
|
const contentWidth = paper.width - template.margins.left - template.margins.right;
|
|
2340
|
+
const pageVars = { pageIndex: pageNum, totalPages, data: pageVarsData(printData) };
|
|
2341
|
+
const scoped = withPageNumbers(template, pageVars);
|
|
2342
|
+
const pageCtx = { ...ctx, pageVars };
|
|
2147
2343
|
const headerHtml = renderAreaElements(
|
|
2148
|
-
|
|
2344
|
+
scoped.header?.elements ?? [],
|
|
2149
2345
|
contentWidth,
|
|
2150
2346
|
pageNum,
|
|
2151
2347
|
totalPages,
|
|
2152
|
-
|
|
2348
|
+
pageCtx
|
|
2153
2349
|
);
|
|
2154
2350
|
const footerHtml = renderAreaElements(
|
|
2155
|
-
|
|
2351
|
+
scoped.footer?.elements ?? [],
|
|
2156
2352
|
contentWidth,
|
|
2157
2353
|
pageNum,
|
|
2158
2354
|
totalPages,
|
|
2159
|
-
|
|
2355
|
+
pageCtx
|
|
2160
2356
|
);
|
|
2161
|
-
const overlayHtml = page.pageIndex === 0 ? `<div class="first-page-overlay">${renderAreaElements(
|
|
2162
|
-
let contentHtml = page.sections.map((section) => renderSection(section,
|
|
2357
|
+
const overlayHtml = page.pageIndex === 0 ? `<div class="first-page-overlay">${renderAreaElements(scoped.firstPageOverlay?.elements ?? [], contentWidth, pageNum, totalPages, pageCtx)}</div>` : "";
|
|
2358
|
+
let contentHtml = page.sections.map((section) => renderSection(section, scoped, pageCtx)).join("\n");
|
|
2163
2359
|
contentHtml = contentHtml.replace(/\{pageIndex\}/g, String(pageNum));
|
|
2164
2360
|
contentHtml = contentHtml.replace(/\{totalPages\}/g, String(totalPages));
|
|
2165
2361
|
const pageInner = `
|
|
@@ -2176,6 +2372,39 @@ function renderPage(template, page, pageNum, totalPages, ctx, printData, pageCla
|
|
|
2176
2372
|
${pageBody}
|
|
2177
2373
|
</section>`;
|
|
2178
2374
|
}
|
|
2375
|
+
function withPageNumbers(template, vars) {
|
|
2376
|
+
if (!templateReferencesPageNumbers(template)) return template;
|
|
2377
|
+
const context = pageVarsContext(vars);
|
|
2378
|
+
const mapEl = (el) => {
|
|
2379
|
+
const raw = el.options?.rawFormatter;
|
|
2380
|
+
if (typeof raw !== "string") return el;
|
|
2381
|
+
return { ...el, options: { ...el.options, formatter: evaluateTemplate(raw, context) } };
|
|
2382
|
+
};
|
|
2383
|
+
const mapArea = (area) => area ? { ...area, elements: (area.elements ?? []).map(mapEl) } : area;
|
|
2384
|
+
return {
|
|
2385
|
+
...template,
|
|
2386
|
+
elements: template.elements.map(mapEl),
|
|
2387
|
+
header: mapArea(template.header),
|
|
2388
|
+
footer: mapArea(template.footer),
|
|
2389
|
+
firstPageOverlay: mapArea(template.firstPageOverlay)
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
function templateReferencesPageNumbers(template) {
|
|
2393
|
+
const areas = [
|
|
2394
|
+
template.elements,
|
|
2395
|
+
template.header?.elements,
|
|
2396
|
+
template.footer?.elements,
|
|
2397
|
+
template.firstPageOverlay?.elements
|
|
2398
|
+
];
|
|
2399
|
+
return areas.some((list) => (list ?? []).some((el) => typeof el.options?.rawFormatter === "string"));
|
|
2400
|
+
}
|
|
2401
|
+
function resolveCellContent(cell, ctx) {
|
|
2402
|
+
const raw = cell.rawFormatter;
|
|
2403
|
+
if (typeof raw === "string" && raw !== "" && ctx?.pageVars) {
|
|
2404
|
+
return evaluateTemplate(raw, pageVarsContext(ctx.pageVars));
|
|
2405
|
+
}
|
|
2406
|
+
return cell.content;
|
|
2407
|
+
}
|
|
2179
2408
|
function renderSection(section, template, ctx) {
|
|
2180
2409
|
const el = findElement(template, section.elementId);
|
|
2181
2410
|
if (!el) {
|
|
@@ -2328,7 +2557,7 @@ function matrixCellStyle(cell, opts) {
|
|
|
2328
2557
|
}
|
|
2329
2558
|
return parts.join(";");
|
|
2330
2559
|
}
|
|
2331
|
-
function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOwner = { elementId: "", kind: "b" }) {
|
|
2560
|
+
function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOwner = { elementId: "", kind: "b" }, rowLimit) {
|
|
2332
2561
|
const trs = [];
|
|
2333
2562
|
const defaultPadding = opts.tableDefaultPadding ?? 1;
|
|
2334
2563
|
for (let r = start; r < end; r++) {
|
|
@@ -2336,12 +2565,14 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOw
|
|
|
2336
2565
|
if (!row) continue;
|
|
2337
2566
|
const idxAttr = withRowIndex ? ` data-row-index="${r}"` : "";
|
|
2338
2567
|
const tds = row.cells.map((cell, ci) => ({ cell, ci })).filter(({ cell }) => !cell.merged).map(({ cell, ci }) => {
|
|
2339
|
-
const
|
|
2568
|
+
const rowspan = rowLimit === void 0 ? cell.rowspan ?? 1 : Math.max(1, Math.min(cell.rowspan ?? 1, rowLimit - r));
|
|
2569
|
+
const span = `${rowspan > 1 ? ` rowspan="${rowspan}"` : ""}${cell.colspan > 1 ? ` colspan="${cell.colspan}"` : ""}`;
|
|
2340
2570
|
const colIndex = ci;
|
|
2341
2571
|
let inner;
|
|
2342
2572
|
if (cell.cellType === "barcode" || cell.cellType === "qrcode") {
|
|
2343
|
-
|
|
2344
|
-
|
|
2573
|
+
const codeValue = resolveCellContent(cell, ctx);
|
|
2574
|
+
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;">${codeImgHtml(codeValue, cell.cellType, cell, {
|
|
2575
|
+
fallback: esc(codeValue),
|
|
2345
2576
|
codeRenderer: ctx?.codeRenderer,
|
|
2346
2577
|
fit: cell.fit,
|
|
2347
2578
|
maxWidth: cell.maxWidth,
|
|
@@ -2355,9 +2586,9 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOw
|
|
|
2355
2586
|
const fit = cell.fit || "contain";
|
|
2356
2587
|
const maxWidth = cell.maxWidth ? `max-width:${cell.maxWidth}mm;` : "max-width:100%;";
|
|
2357
2588
|
const maxHeight = cell.maxHeight ? `max-height:${cell.maxHeight}mm;` : "max-height:100%;";
|
|
2358
|
-
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;"><img src="${esc(cell
|
|
2589
|
+
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;"><img src="${esc(resolveCellContent(cell, ctx))}" style="object-fit:${fit};${maxWidth}${maxHeight}display:block;" /></div>`;
|
|
2359
2590
|
} else {
|
|
2360
|
-
inner = renderTextCellInner(renderRows, r, colIndex, cell, opts, fitOwner, defaultPadding);
|
|
2591
|
+
inner = renderTextCellInner(renderRows, r, colIndex, cell, opts, fitOwner, defaultPadding, ctx);
|
|
2361
2592
|
}
|
|
2362
2593
|
return `<td${span} style="${matrixCellStyle(cell, opts)}">${inner}</td>`;
|
|
2363
2594
|
}).join("");
|
|
@@ -2365,9 +2596,9 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOw
|
|
|
2365
2596
|
}
|
|
2366
2597
|
return trs.join("\n");
|
|
2367
2598
|
}
|
|
2368
|
-
function renderTextCellInner(renderRows, rowIndex, colIndex, cell, opts, fitOwner, defaultPadding) {
|
|
2599
|
+
function renderTextCellInner(renderRows, rowIndex, colIndex, cell, opts, fitOwner, defaultPadding, ctx) {
|
|
2369
2600
|
const fit = resolveCellTextFit(cell);
|
|
2370
|
-
const text = esc(cell
|
|
2601
|
+
const text = esc(resolveCellContent(cell, ctx));
|
|
2371
2602
|
if (fit === "autoHeight") return text;
|
|
2372
2603
|
const capMm = cellFitCapMm(renderRows, rowIndex, cell, defaultPadding);
|
|
2373
2604
|
const nowrap = cell.wordWrap === false;
|
|
@@ -2405,7 +2636,7 @@ function renderTableSlice(el, section, ctx) {
|
|
|
2405
2636
|
const startRow = section.startRow ?? 0;
|
|
2406
2637
|
const endRow = section.endRow ?? renderRows.length;
|
|
2407
2638
|
const repeatCount = opts._repeatHeaderCount ?? 0;
|
|
2408
|
-
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }) : "";
|
|
2639
|
+
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }, repeatCount) : "";
|
|
2409
2640
|
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx, { elementId: el.id, kind: "b" });
|
|
2410
2641
|
const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
|
|
2411
2642
|
const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
|
|
@@ -2426,9 +2657,15 @@ function renderSubtotalRows(el, section, opts, ctx) {
|
|
|
2426
2657
|
const dataStart = Math.max(startRow, dataStartIdx);
|
|
2427
2658
|
const dataEnd = Math.max(endRow, dataStart);
|
|
2428
2659
|
const pageCtx = dataEnd > dataStartIdx ? dataRowCtx.slice(Math.max(dataStart - dataStartIdx, 0), dataEnd - dataStartIdx) : [];
|
|
2660
|
+
const pageVars = ctx?.pageVars ? pageVarsContext(ctx.pageVars) : {};
|
|
2429
2661
|
const rows = templates.map((tpl) => ({
|
|
2430
2662
|
...tpl,
|
|
2431
|
-
cells: tpl.cells.map((cell) => cell.rawFormatter ? {
|
|
2663
|
+
cells: tpl.cells.map((cell) => cell.rawFormatter ? {
|
|
2664
|
+
...cell,
|
|
2665
|
+
content: evaluateTemplate(cell.rawFormatter, { rows: pageCtx, ...mainData, ...pageVars }),
|
|
2666
|
+
// 已按本页上下文求值,清标记避免渲染时再按页码重算一次(会丢掉 rows)
|
|
2667
|
+
rawFormatter: ""
|
|
2668
|
+
} : cell)
|
|
2432
2669
|
}));
|
|
2433
2670
|
return renderMatrixRows(rows, 0, rows.length, opts, false, ctx, { elementId: el.id, kind: "st" });
|
|
2434
2671
|
}
|
|
@@ -2451,7 +2688,7 @@ function renderFlowGroup(el, section, template, ctx) {
|
|
|
2451
2688
|
if (endRow > startRow || section.subtotal || section.summary) {
|
|
2452
2689
|
const renderRows = opts._renderRows ?? [];
|
|
2453
2690
|
const repeatCount = opts._repeatHeaderCount ?? 0;
|
|
2454
|
-
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }) : "";
|
|
2691
|
+
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }, repeatCount) : "";
|
|
2455
2692
|
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx, { elementId: el.id, kind: "b" });
|
|
2456
2693
|
const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
|
|
2457
2694
|
const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
|
|
@@ -3470,6 +3707,14 @@ export {
|
|
|
3470
3707
|
setPageIndex,
|
|
3471
3708
|
setTotalPages,
|
|
3472
3709
|
systemVars,
|
|
3710
|
+
addNumbers,
|
|
3711
|
+
subtractNumbers,
|
|
3712
|
+
multiplyNumbers,
|
|
3713
|
+
divideNumbers,
|
|
3714
|
+
round,
|
|
3715
|
+
roundUp,
|
|
3716
|
+
roundDown,
|
|
3717
|
+
roundHalfEven,
|
|
3473
3718
|
safeEval,
|
|
3474
3719
|
evaluateTemplate,
|
|
3475
3720
|
bindData,
|