@worm-vue3-print/core 1.3.0 → 1.3.1
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 +284 -52
- 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-AKWV6SID.js} +1 -1
- package/dist/{chunk-VFEURLAZ.js → chunk-KFRPTLFV.js} +292 -52
- 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 +300 -52
- package/dist/index.d.cts +24 -5
- package/dist/index.d.ts +24 -5
- 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);
|
|
@@ -2060,6 +2241,13 @@ function resolveBarcodeSize(input) {
|
|
|
2060
2241
|
}
|
|
2061
2242
|
|
|
2062
2243
|
// src/render/html-generator.ts
|
|
2244
|
+
function pageVarsContext(vars) {
|
|
2245
|
+
return { ...vars.data ?? {}, pageIndex: vars.pageIndex, totalPages: vars.totalPages };
|
|
2246
|
+
}
|
|
2247
|
+
function pageVarsData(printData) {
|
|
2248
|
+
if (Array.isArray(printData)) return printData[0] ?? {};
|
|
2249
|
+
return printData ?? {};
|
|
2250
|
+
}
|
|
2063
2251
|
function generateHtml(template, pageLayouts, printData, options) {
|
|
2064
2252
|
const css = buildFontFaceCss(template.fonts) + buildPageCss(template, options?.pageHeightMm);
|
|
2065
2253
|
const isMeasure = options?.isMeasurementPass === true;
|
|
@@ -2144,22 +2332,25 @@ function renderPage(template, page, pageNum, totalPages, ctx, printData, pageCla
|
|
|
2144
2332
|
height: ctx.pageHeightMm && ctx.pageHeightMm > 0 ? ctx.pageHeightMm : paper.height
|
|
2145
2333
|
};
|
|
2146
2334
|
const contentWidth = paper.width - template.margins.left - template.margins.right;
|
|
2335
|
+
const pageVars = { pageIndex: pageNum, totalPages, data: pageVarsData(printData) };
|
|
2336
|
+
const scoped = withPageNumbers(template, pageVars);
|
|
2337
|
+
const pageCtx = { ...ctx, pageVars };
|
|
2147
2338
|
const headerHtml = renderAreaElements(
|
|
2148
|
-
|
|
2339
|
+
scoped.header?.elements ?? [],
|
|
2149
2340
|
contentWidth,
|
|
2150
2341
|
pageNum,
|
|
2151
2342
|
totalPages,
|
|
2152
|
-
|
|
2343
|
+
pageCtx
|
|
2153
2344
|
);
|
|
2154
2345
|
const footerHtml = renderAreaElements(
|
|
2155
|
-
|
|
2346
|
+
scoped.footer?.elements ?? [],
|
|
2156
2347
|
contentWidth,
|
|
2157
2348
|
pageNum,
|
|
2158
2349
|
totalPages,
|
|
2159
|
-
|
|
2350
|
+
pageCtx
|
|
2160
2351
|
);
|
|
2161
|
-
const overlayHtml = page.pageIndex === 0 ? `<div class="first-page-overlay">${renderAreaElements(
|
|
2162
|
-
let contentHtml = page.sections.map((section) => renderSection(section,
|
|
2352
|
+
const overlayHtml = page.pageIndex === 0 ? `<div class="first-page-overlay">${renderAreaElements(scoped.firstPageOverlay?.elements ?? [], contentWidth, pageNum, totalPages, pageCtx)}</div>` : "";
|
|
2353
|
+
let contentHtml = page.sections.map((section) => renderSection(section, scoped, pageCtx)).join("\n");
|
|
2163
2354
|
contentHtml = contentHtml.replace(/\{pageIndex\}/g, String(pageNum));
|
|
2164
2355
|
contentHtml = contentHtml.replace(/\{totalPages\}/g, String(totalPages));
|
|
2165
2356
|
const pageInner = `
|
|
@@ -2176,6 +2367,39 @@ function renderPage(template, page, pageNum, totalPages, ctx, printData, pageCla
|
|
|
2176
2367
|
${pageBody}
|
|
2177
2368
|
</section>`;
|
|
2178
2369
|
}
|
|
2370
|
+
function withPageNumbers(template, vars) {
|
|
2371
|
+
if (!templateReferencesPageNumbers(template)) return template;
|
|
2372
|
+
const context = pageVarsContext(vars);
|
|
2373
|
+
const mapEl = (el) => {
|
|
2374
|
+
const raw = el.options?.rawFormatter;
|
|
2375
|
+
if (typeof raw !== "string") return el;
|
|
2376
|
+
return { ...el, options: { ...el.options, formatter: evaluateTemplate(raw, context) } };
|
|
2377
|
+
};
|
|
2378
|
+
const mapArea = (area) => area ? { ...area, elements: (area.elements ?? []).map(mapEl) } : area;
|
|
2379
|
+
return {
|
|
2380
|
+
...template,
|
|
2381
|
+
elements: template.elements.map(mapEl),
|
|
2382
|
+
header: mapArea(template.header),
|
|
2383
|
+
footer: mapArea(template.footer),
|
|
2384
|
+
firstPageOverlay: mapArea(template.firstPageOverlay)
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
function templateReferencesPageNumbers(template) {
|
|
2388
|
+
const areas = [
|
|
2389
|
+
template.elements,
|
|
2390
|
+
template.header?.elements,
|
|
2391
|
+
template.footer?.elements,
|
|
2392
|
+
template.firstPageOverlay?.elements
|
|
2393
|
+
];
|
|
2394
|
+
return areas.some((list) => (list ?? []).some((el) => typeof el.options?.rawFormatter === "string"));
|
|
2395
|
+
}
|
|
2396
|
+
function resolveCellContent(cell, ctx) {
|
|
2397
|
+
const raw = cell.rawFormatter;
|
|
2398
|
+
if (typeof raw === "string" && raw !== "" && ctx?.pageVars) {
|
|
2399
|
+
return evaluateTemplate(raw, pageVarsContext(ctx.pageVars));
|
|
2400
|
+
}
|
|
2401
|
+
return cell.content;
|
|
2402
|
+
}
|
|
2179
2403
|
function renderSection(section, template, ctx) {
|
|
2180
2404
|
const el = findElement(template, section.elementId);
|
|
2181
2405
|
if (!el) {
|
|
@@ -2328,7 +2552,7 @@ function matrixCellStyle(cell, opts) {
|
|
|
2328
2552
|
}
|
|
2329
2553
|
return parts.join(";");
|
|
2330
2554
|
}
|
|
2331
|
-
function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOwner = { elementId: "", kind: "b" }) {
|
|
2555
|
+
function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOwner = { elementId: "", kind: "b" }, rowLimit) {
|
|
2332
2556
|
const trs = [];
|
|
2333
2557
|
const defaultPadding = opts.tableDefaultPadding ?? 1;
|
|
2334
2558
|
for (let r = start; r < end; r++) {
|
|
@@ -2336,12 +2560,14 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOw
|
|
|
2336
2560
|
if (!row) continue;
|
|
2337
2561
|
const idxAttr = withRowIndex ? ` data-row-index="${r}"` : "";
|
|
2338
2562
|
const tds = row.cells.map((cell, ci) => ({ cell, ci })).filter(({ cell }) => !cell.merged).map(({ cell, ci }) => {
|
|
2339
|
-
const
|
|
2563
|
+
const rowspan = rowLimit === void 0 ? cell.rowspan ?? 1 : Math.max(1, Math.min(cell.rowspan ?? 1, rowLimit - r));
|
|
2564
|
+
const span = `${rowspan > 1 ? ` rowspan="${rowspan}"` : ""}${cell.colspan > 1 ? ` colspan="${cell.colspan}"` : ""}`;
|
|
2340
2565
|
const colIndex = ci;
|
|
2341
2566
|
let inner;
|
|
2342
2567
|
if (cell.cellType === "barcode" || cell.cellType === "qrcode") {
|
|
2343
|
-
|
|
2344
|
-
|
|
2568
|
+
const codeValue = resolveCellContent(cell, ctx);
|
|
2569
|
+
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;">${codeImgHtml(codeValue, cell.cellType, cell, {
|
|
2570
|
+
fallback: esc(codeValue),
|
|
2345
2571
|
codeRenderer: ctx?.codeRenderer,
|
|
2346
2572
|
fit: cell.fit,
|
|
2347
2573
|
maxWidth: cell.maxWidth,
|
|
@@ -2355,9 +2581,9 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOw
|
|
|
2355
2581
|
const fit = cell.fit || "contain";
|
|
2356
2582
|
const maxWidth = cell.maxWidth ? `max-width:${cell.maxWidth}mm;` : "max-width:100%;";
|
|
2357
2583
|
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
|
|
2584
|
+
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
2585
|
} else {
|
|
2360
|
-
inner = renderTextCellInner(renderRows, r, colIndex, cell, opts, fitOwner, defaultPadding);
|
|
2586
|
+
inner = renderTextCellInner(renderRows, r, colIndex, cell, opts, fitOwner, defaultPadding, ctx);
|
|
2361
2587
|
}
|
|
2362
2588
|
return `<td${span} style="${matrixCellStyle(cell, opts)}">${inner}</td>`;
|
|
2363
2589
|
}).join("");
|
|
@@ -2365,9 +2591,9 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOw
|
|
|
2365
2591
|
}
|
|
2366
2592
|
return trs.join("\n");
|
|
2367
2593
|
}
|
|
2368
|
-
function renderTextCellInner(renderRows, rowIndex, colIndex, cell, opts, fitOwner, defaultPadding) {
|
|
2594
|
+
function renderTextCellInner(renderRows, rowIndex, colIndex, cell, opts, fitOwner, defaultPadding, ctx) {
|
|
2369
2595
|
const fit = resolveCellTextFit(cell);
|
|
2370
|
-
const text = esc(cell
|
|
2596
|
+
const text = esc(resolveCellContent(cell, ctx));
|
|
2371
2597
|
if (fit === "autoHeight") return text;
|
|
2372
2598
|
const capMm = cellFitCapMm(renderRows, rowIndex, cell, defaultPadding);
|
|
2373
2599
|
const nowrap = cell.wordWrap === false;
|
|
@@ -2405,7 +2631,7 @@ function renderTableSlice(el, section, ctx) {
|
|
|
2405
2631
|
const startRow = section.startRow ?? 0;
|
|
2406
2632
|
const endRow = section.endRow ?? renderRows.length;
|
|
2407
2633
|
const repeatCount = opts._repeatHeaderCount ?? 0;
|
|
2408
|
-
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }) : "";
|
|
2634
|
+
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }, repeatCount) : "";
|
|
2409
2635
|
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx, { elementId: el.id, kind: "b" });
|
|
2410
2636
|
const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
|
|
2411
2637
|
const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
|
|
@@ -2426,9 +2652,15 @@ function renderSubtotalRows(el, section, opts, ctx) {
|
|
|
2426
2652
|
const dataStart = Math.max(startRow, dataStartIdx);
|
|
2427
2653
|
const dataEnd = Math.max(endRow, dataStart);
|
|
2428
2654
|
const pageCtx = dataEnd > dataStartIdx ? dataRowCtx.slice(Math.max(dataStart - dataStartIdx, 0), dataEnd - dataStartIdx) : [];
|
|
2655
|
+
const pageVars = ctx?.pageVars ? pageVarsContext(ctx.pageVars) : {};
|
|
2429
2656
|
const rows = templates.map((tpl) => ({
|
|
2430
2657
|
...tpl,
|
|
2431
|
-
cells: tpl.cells.map((cell) => cell.rawFormatter ? {
|
|
2658
|
+
cells: tpl.cells.map((cell) => cell.rawFormatter ? {
|
|
2659
|
+
...cell,
|
|
2660
|
+
content: evaluateTemplate(cell.rawFormatter, { rows: pageCtx, ...mainData, ...pageVars }),
|
|
2661
|
+
// 已按本页上下文求值,清标记避免渲染时再按页码重算一次(会丢掉 rows)
|
|
2662
|
+
rawFormatter: ""
|
|
2663
|
+
} : cell)
|
|
2432
2664
|
}));
|
|
2433
2665
|
return renderMatrixRows(rows, 0, rows.length, opts, false, ctx, { elementId: el.id, kind: "st" });
|
|
2434
2666
|
}
|
|
@@ -2451,7 +2683,7 @@ function renderFlowGroup(el, section, template, ctx) {
|
|
|
2451
2683
|
if (endRow > startRow || section.subtotal || section.summary) {
|
|
2452
2684
|
const renderRows = opts._renderRows ?? [];
|
|
2453
2685
|
const repeatCount = opts._repeatHeaderCount ?? 0;
|
|
2454
|
-
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }) : "";
|
|
2686
|
+
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }, repeatCount) : "";
|
|
2455
2687
|
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx, { elementId: el.id, kind: "b" });
|
|
2456
2688
|
const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
|
|
2457
2689
|
const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
|
|
@@ -3470,6 +3702,14 @@ export {
|
|
|
3470
3702
|
setPageIndex,
|
|
3471
3703
|
setTotalPages,
|
|
3472
3704
|
systemVars,
|
|
3705
|
+
addNumbers,
|
|
3706
|
+
subtractNumbers,
|
|
3707
|
+
multiplyNumbers,
|
|
3708
|
+
divideNumbers,
|
|
3709
|
+
round,
|
|
3710
|
+
roundUp,
|
|
3711
|
+
roundDown,
|
|
3712
|
+
roundHalfEven,
|
|
3473
3713
|
safeEval,
|
|
3474
3714
|
evaluateTemplate,
|
|
3475
3715
|
bindData,
|