@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
package/dist/browser/index.cjs
CHANGED
|
@@ -954,6 +954,113 @@ var Parser = class {
|
|
|
954
954
|
}
|
|
955
955
|
};
|
|
956
956
|
|
|
957
|
+
// src/numeric.ts
|
|
958
|
+
var MAX_DIGITS = 20;
|
|
959
|
+
var EXTRA_SCALE = 8;
|
|
960
|
+
function toNumber(value, fallback = 0) {
|
|
961
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
|
|
962
|
+
if (typeof value === "string") {
|
|
963
|
+
const s = value.trim();
|
|
964
|
+
if (s === "") return fallback;
|
|
965
|
+
const n = Number(s);
|
|
966
|
+
return Number.isFinite(n) ? n : fallback;
|
|
967
|
+
}
|
|
968
|
+
if (value == null) return fallback;
|
|
969
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
970
|
+
return fallback;
|
|
971
|
+
}
|
|
972
|
+
function isNumericLike(value) {
|
|
973
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
974
|
+
if (typeof value === "string") {
|
|
975
|
+
const s = value.trim();
|
|
976
|
+
return s !== "" && Number.isFinite(Number(s));
|
|
977
|
+
}
|
|
978
|
+
return false;
|
|
979
|
+
}
|
|
980
|
+
function normalizeFloat(value) {
|
|
981
|
+
if (!Number.isFinite(value) || Number.isInteger(value)) return value;
|
|
982
|
+
if (Math.abs(value) >= 1e15) return value;
|
|
983
|
+
return Number(value.toPrecision(12));
|
|
984
|
+
}
|
|
985
|
+
function decimalsOf(n) {
|
|
986
|
+
if (!Number.isFinite(n) || Number.isInteger(n)) return 0;
|
|
987
|
+
const s = String(n);
|
|
988
|
+
if (s.includes("e") || s.includes("E")) return 0;
|
|
989
|
+
const i = s.indexOf(".");
|
|
990
|
+
return i === -1 ? 0 : s.length - i - 1;
|
|
991
|
+
}
|
|
992
|
+
function rescale(value, decimals) {
|
|
993
|
+
if (!Number.isFinite(value)) return 0;
|
|
994
|
+
const d = Math.min(Math.max(decimals, 0), 12);
|
|
995
|
+
if (d === 0) return Math.round(value);
|
|
996
|
+
const f = 10 ** d;
|
|
997
|
+
const scaled = value * f;
|
|
998
|
+
if (!Number.isFinite(scaled) || Math.abs(scaled) >= 1e15) return normalizeFloat(value);
|
|
999
|
+
return Math.round(scaled) / f;
|
|
1000
|
+
}
|
|
1001
|
+
function decimalAdd(left, right) {
|
|
1002
|
+
const a = toNumber(left);
|
|
1003
|
+
const b = toNumber(right);
|
|
1004
|
+
const raw = a + b;
|
|
1005
|
+
return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
|
|
1006
|
+
}
|
|
1007
|
+
function decimalSubtract(left, right) {
|
|
1008
|
+
const a = toNumber(left);
|
|
1009
|
+
const b = toNumber(right);
|
|
1010
|
+
const raw = a - b;
|
|
1011
|
+
return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
|
|
1012
|
+
}
|
|
1013
|
+
function decimalMultiply(left, right) {
|
|
1014
|
+
const a = toNumber(left);
|
|
1015
|
+
const b = toNumber(right);
|
|
1016
|
+
const raw = a * b;
|
|
1017
|
+
return rescale(raw, decimalsOf(a) + decimalsOf(b));
|
|
1018
|
+
}
|
|
1019
|
+
function decimalDivide(left, right) {
|
|
1020
|
+
const a = toNumber(left);
|
|
1021
|
+
const b = toNumber(right);
|
|
1022
|
+
if (b === 0) return 0;
|
|
1023
|
+
return normalizeFloat(a / b);
|
|
1024
|
+
}
|
|
1025
|
+
function decimalModulo(left, right) {
|
|
1026
|
+
const a = toNumber(left);
|
|
1027
|
+
const b = toNumber(right);
|
|
1028
|
+
if (b === 0) return 0;
|
|
1029
|
+
return normalizeFloat(a % b);
|
|
1030
|
+
}
|
|
1031
|
+
function normalizeDigits(digits) {
|
|
1032
|
+
if (digits === void 0 || digits === null) return 2;
|
|
1033
|
+
const d = Math.trunc(toNumber(digits, 2));
|
|
1034
|
+
if (!Number.isFinite(d)) return 2;
|
|
1035
|
+
return Math.min(Math.max(d, -MAX_DIGITS), MAX_DIGITS);
|
|
1036
|
+
}
|
|
1037
|
+
function roundTo(value, digits, mode) {
|
|
1038
|
+
const n = toNumber(value, 0);
|
|
1039
|
+
const d = normalizeDigits(digits);
|
|
1040
|
+
if (n === 0) return 0;
|
|
1041
|
+
if (Math.abs(n) >= 1e21) return normalizeFloat(n);
|
|
1042
|
+
const scale = Math.max(d, 0) + EXTRA_SCALE;
|
|
1043
|
+
const fixed = n.toFixed(scale);
|
|
1044
|
+
if (!/^-?\d+(\.\d+)?$/.test(fixed)) return normalizeFloat(n);
|
|
1045
|
+
const negative = fixed.startsWith("-");
|
|
1046
|
+
const digitsStr = fixed.replace("-", "").replace(".", "");
|
|
1047
|
+
let scaled = BigInt(digitsStr || "0");
|
|
1048
|
+
if (negative) scaled = -scaled;
|
|
1049
|
+
const pow10 = 10n ** BigInt(scale - d);
|
|
1050
|
+
const sign = scaled < 0n ? -1n : 1n;
|
|
1051
|
+
const abs = scaled < 0n ? -scaled : scaled;
|
|
1052
|
+
let q = abs / pow10;
|
|
1053
|
+
const r = abs % pow10;
|
|
1054
|
+
const doubled = 2n * r;
|
|
1055
|
+
if (mode === "up") {
|
|
1056
|
+
if (r !== 0n) q += 1n;
|
|
1057
|
+
} else if (mode !== "down" && doubled >= pow10) {
|
|
1058
|
+
if (doubled > pow10 || mode === "half-up" || q % 2n === 1n) q += 1n;
|
|
1059
|
+
}
|
|
1060
|
+
const result = sign * q;
|
|
1061
|
+
return normalizeFloat(d >= 0 ? Number(result) / 10 ** d : Number(result) * 10 ** -d);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
957
1064
|
// src/evaluator.ts
|
|
958
1065
|
var SAFE_GLOBALS = {
|
|
959
1066
|
Math,
|
|
@@ -1019,16 +1126,17 @@ function evalBinary(node, context, functions) {
|
|
|
1019
1126
|
const left = evaluate(node.left, context, functions);
|
|
1020
1127
|
const right = evaluate(node.right, context, functions);
|
|
1021
1128
|
switch (node.op) {
|
|
1129
|
+
// 数值运算统一走十进制精确实现:消除 0.1 + 0.2 类浮点噪声、字符串数字按数值处理、除零兜底 0
|
|
1022
1130
|
case "+":
|
|
1023
|
-
return left
|
|
1131
|
+
return isNumericOperands(left, right) ? decimalAdd(left, right) : concatOperands(left, right);
|
|
1024
1132
|
case "-":
|
|
1025
|
-
return left
|
|
1133
|
+
return decimalSubtract(left, right);
|
|
1026
1134
|
case "*":
|
|
1027
|
-
return left
|
|
1135
|
+
return decimalMultiply(left, right);
|
|
1028
1136
|
case "/":
|
|
1029
|
-
return left
|
|
1137
|
+
return decimalDivide(left, right);
|
|
1030
1138
|
case "%":
|
|
1031
|
-
return left
|
|
1139
|
+
return decimalModulo(left, right);
|
|
1032
1140
|
case "<":
|
|
1033
1141
|
return left < right;
|
|
1034
1142
|
case ">":
|
|
@@ -1057,15 +1165,29 @@ function evalUnary(node, context, functions) {
|
|
|
1057
1165
|
const arg = evaluate(node.arg, context, functions);
|
|
1058
1166
|
switch (node.op) {
|
|
1059
1167
|
case "-":
|
|
1060
|
-
return
|
|
1168
|
+
return decimalSubtract(0, arg);
|
|
1061
1169
|
case "+":
|
|
1062
|
-
return
|
|
1170
|
+
return toNumber(arg);
|
|
1063
1171
|
case "!":
|
|
1064
1172
|
return !arg;
|
|
1065
1173
|
default:
|
|
1066
1174
|
throw new Error(`\u672A\u77E5\u4E00\u5143\u8FD0\u7B97\u7B26: ${node.op}`);
|
|
1067
1175
|
}
|
|
1068
1176
|
}
|
|
1177
|
+
function concatOperands(left, right) {
|
|
1178
|
+
return `${left == null ? "" : String(left)}${right == null ? "" : String(right)}`;
|
|
1179
|
+
}
|
|
1180
|
+
function isNumericOperands(left, right) {
|
|
1181
|
+
const l = operandKind(left);
|
|
1182
|
+
const r = operandKind(right);
|
|
1183
|
+
if (l === "other" || r === "other") return false;
|
|
1184
|
+
return l === "num" || r === "num";
|
|
1185
|
+
}
|
|
1186
|
+
function operandKind(value) {
|
|
1187
|
+
if (value == null || value === "") return "neutral";
|
|
1188
|
+
if (isNumericLike(value)) return "num";
|
|
1189
|
+
return "other";
|
|
1190
|
+
}
|
|
1069
1191
|
function evalMember(node, context, functions) {
|
|
1070
1192
|
const obj = evaluate(node.object, context, functions);
|
|
1071
1193
|
if (obj == null) {
|
|
@@ -1277,6 +1399,34 @@ function max(rows, field) {
|
|
|
1277
1399
|
return Math.max(...rows.map((row) => Number(getByPath(row, field)) || 0));
|
|
1278
1400
|
}
|
|
1279
1401
|
|
|
1402
|
+
// src/functions/math.ts
|
|
1403
|
+
function addNumbers(...values) {
|
|
1404
|
+
return values.reduce((acc, v) => decimalAdd(acc, v), 0);
|
|
1405
|
+
}
|
|
1406
|
+
function subtractNumbers(...values) {
|
|
1407
|
+
if (values.length === 0) return 0;
|
|
1408
|
+
return values.slice(1).reduce((acc, v) => decimalSubtract(acc, v), decimalAdd(values[0], 0));
|
|
1409
|
+
}
|
|
1410
|
+
function multiplyNumbers(...values) {
|
|
1411
|
+
if (values.length === 0) return 0;
|
|
1412
|
+
return values.reduce((acc, v) => decimalMultiply(acc, v), 1);
|
|
1413
|
+
}
|
|
1414
|
+
function divideNumbers(a, b) {
|
|
1415
|
+
return decimalDivide(a, b);
|
|
1416
|
+
}
|
|
1417
|
+
function round(value, digits) {
|
|
1418
|
+
return roundTo(value, digits, "half-up");
|
|
1419
|
+
}
|
|
1420
|
+
function roundUp(value, digits) {
|
|
1421
|
+
return roundTo(value, digits, "up");
|
|
1422
|
+
}
|
|
1423
|
+
function roundDown(value, digits) {
|
|
1424
|
+
return roundTo(value, digits, "down");
|
|
1425
|
+
}
|
|
1426
|
+
function roundHalfEven(value, digits) {
|
|
1427
|
+
return roundTo(value, digits, "half-even");
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1280
1430
|
// src/render/expression-eval.ts
|
|
1281
1431
|
var RenderEngine = class {
|
|
1282
1432
|
constructor() {
|
|
@@ -1302,8 +1452,19 @@ var FORMAT_FUNCTIONS = {
|
|
|
1302
1452
|
IF: ifFn,
|
|
1303
1453
|
CONCAT: (...args) => args.filter((v) => v != null).map(String).join(""),
|
|
1304
1454
|
IFEMPTY: (v, d) => v != null && v !== "" ? String(v) : d,
|
|
1305
|
-
|
|
1306
|
-
|
|
1455
|
+
LEN: (s) => String(s).length,
|
|
1456
|
+
// 四则运算(与 +/-/*// 运算符同一套数值语义,供不方便写运算符的场景使用)
|
|
1457
|
+
ADD: addNumbers,
|
|
1458
|
+
SUB: subtractNumbers,
|
|
1459
|
+
MUL: multiplyNumbers,
|
|
1460
|
+
DIV: divideNumbers,
|
|
1461
|
+
// 数值修约
|
|
1462
|
+
ROUND: round,
|
|
1463
|
+
ROUNDUP: roundUp,
|
|
1464
|
+
CEIL: roundUp,
|
|
1465
|
+
ROUNDDOWN: roundDown,
|
|
1466
|
+
FLOOR: roundDown,
|
|
1467
|
+
ROUNDBANK: roundHalfEven
|
|
1307
1468
|
};
|
|
1308
1469
|
for (const [name, fn] of Object.entries(FORMAT_FUNCTIONS)) {
|
|
1309
1470
|
engine.registerFunction(name, fn);
|
|
@@ -1348,7 +1509,7 @@ function evaluateTemplate(text, ctx) {
|
|
|
1348
1509
|
|
|
1349
1510
|
// src/render/data-binder.ts
|
|
1350
1511
|
function bindData(template, printData, baseUrl, fontBaseUrl) {
|
|
1351
|
-
const data = printData ?? {};
|
|
1512
|
+
const data = { ...resolveSystemVariables(), ...printData ?? {} };
|
|
1352
1513
|
const bound = JSON.parse(JSON.stringify(template));
|
|
1353
1514
|
if (bound.header?.elements) {
|
|
1354
1515
|
bound.header.elements = bound.header.elements.map((el) => bindElement(el, data, baseUrl));
|
|
@@ -1377,7 +1538,12 @@ function bindData(template, printData, baseUrl, fontBaseUrl) {
|
|
|
1377
1538
|
function bindElement(el, data, baseUrl) {
|
|
1378
1539
|
const cloned = { ...el, options: { ...el.options } };
|
|
1379
1540
|
if (typeof cloned.options.formatter === "string") {
|
|
1380
|
-
|
|
1541
|
+
const raw = cloned.options.formatter;
|
|
1542
|
+
if (referencesPageNumbers(raw)) {
|
|
1543
|
+
cloned.options.rawFormatter = raw;
|
|
1544
|
+
} else {
|
|
1545
|
+
cloned.options.formatter = evaluateTemplate(raw, data);
|
|
1546
|
+
}
|
|
1381
1547
|
}
|
|
1382
1548
|
const isImage = cloned.type === "image" || cloned.printElementType?.type === "image";
|
|
1383
1549
|
if (isImage && typeof cloned.options.src === "string") {
|
|
@@ -1424,39 +1590,25 @@ function bindTableData(el, data) {
|
|
|
1424
1590
|
if (dataStartIdx < 0) dataStartIdx = renderRows.length;
|
|
1425
1591
|
const ctx = itemCtx(item);
|
|
1426
1592
|
renderRows.push(makeRenderRow(row, (cell) => {
|
|
1427
|
-
|
|
1428
|
-
if (!formatter) return "";
|
|
1429
|
-
return evaluateTemplate(formatter, ctx);
|
|
1593
|
+
return resolveCellText(cell, ctx);
|
|
1430
1594
|
}));
|
|
1431
1595
|
dataRowCtx.push(ctx);
|
|
1432
1596
|
}
|
|
1433
1597
|
continue;
|
|
1434
1598
|
}
|
|
1435
1599
|
if (mode === "dynamic" && row.type === "subtotal") {
|
|
1436
|
-
const tpl = makeRenderRow(row, (cell) => {
|
|
1437
|
-
const formatter = cell.formatter;
|
|
1438
|
-
if (!formatter) return "";
|
|
1439
|
-
return evaluateTemplate(formatter, { rows: summaryRows, ...data });
|
|
1440
|
-
}, true);
|
|
1600
|
+
const tpl = makeRenderRow(row, (cell) => resolveCellText(cell, { rows: summaryRows, ...data }), true);
|
|
1441
1601
|
subtotalTemplates.push(tpl);
|
|
1442
1602
|
renderRows.push(tpl);
|
|
1443
1603
|
continue;
|
|
1444
1604
|
}
|
|
1445
1605
|
if (mode === "dynamic" && row.type === "summary") {
|
|
1446
|
-
const summaryRow = makeRenderRow(row, (cell) => {
|
|
1447
|
-
const formatter = cell.formatter;
|
|
1448
|
-
if (!formatter) return "";
|
|
1449
|
-
return evaluateTemplate(formatter, { rows: summaryRows, ...data });
|
|
1450
|
-
});
|
|
1606
|
+
const summaryRow = makeRenderRow(row, (cell) => resolveCellText(cell, { rows: summaryRows, ...data }));
|
|
1451
1607
|
summaryRenderRows.push(summaryRow);
|
|
1452
1608
|
renderRows.push(summaryRow);
|
|
1453
1609
|
continue;
|
|
1454
1610
|
}
|
|
1455
|
-
renderRows.push(makeRenderRow(row, (cell) =>
|
|
1456
|
-
const formatter = cell.formatter;
|
|
1457
|
-
if (!formatter) return "";
|
|
1458
|
-
return evaluateTemplate(formatter, data);
|
|
1459
|
-
}));
|
|
1611
|
+
renderRows.push(makeRenderRow(row, (cell) => resolveCellText(cell, data)));
|
|
1460
1612
|
}
|
|
1461
1613
|
opts._renderRows = renderRows;
|
|
1462
1614
|
opts._repeatHeaderCount = countRepeatHeader(rows);
|
|
@@ -1466,13 +1618,20 @@ function bindTableData(el, data) {
|
|
|
1466
1618
|
opts._summaryRows = summaryRenderRows;
|
|
1467
1619
|
opts._mainData = data;
|
|
1468
1620
|
}
|
|
1621
|
+
function resolveCellText(cell, ctx) {
|
|
1622
|
+
const formatter = cell.formatter;
|
|
1623
|
+
if (!formatter) return "";
|
|
1624
|
+
if (referencesPageNumbers(formatter)) return formatter;
|
|
1625
|
+
return evaluateTemplate(formatter, ctx);
|
|
1626
|
+
}
|
|
1469
1627
|
function makeRenderRow(row, resolve, keepRaw = false) {
|
|
1470
1628
|
return {
|
|
1471
1629
|
type: row.type,
|
|
1472
1630
|
height: row.height ?? 8,
|
|
1473
1631
|
cells: row.cells.map((cell) => ({
|
|
1474
1632
|
content: cell.merged ? "" : resolve(cell),
|
|
1475
|
-
|
|
1633
|
+
// 小计行(keepRaw)与引用页码的单元格都保留原始表达式:前者按当页数据行重算,后者按当页页码重算
|
|
1634
|
+
...(keepRaw || referencesPageNumbers(cell.formatter)) && !cell.merged ? { rawFormatter: cell.formatter ?? "" } : {},
|
|
1476
1635
|
cellType: cell.cellType,
|
|
1477
1636
|
barcodeType: cell.barcodeType,
|
|
1478
1637
|
qrCodeLevel: cell.qrCodeLevel,
|
|
@@ -1500,16 +1659,35 @@ function makeRenderRow(row, resolve, keepRaw = false) {
|
|
|
1500
1659
|
};
|
|
1501
1660
|
}
|
|
1502
1661
|
function countRepeatHeader(rows) {
|
|
1662
|
+
const headerCount = countLeadingHeaderRows(rows);
|
|
1663
|
+
if (headerCount === 0) return 0;
|
|
1664
|
+
const enabled = rows.slice(0, headerCount).some((row) => row.repeatOnPage === true);
|
|
1665
|
+
if (!enabled) return 0;
|
|
1666
|
+
return alignToRowspanBoundary(rows, headerCount);
|
|
1667
|
+
}
|
|
1668
|
+
function countLeadingHeaderRows(rows) {
|
|
1503
1669
|
let n = 0;
|
|
1504
1670
|
for (const row of rows) {
|
|
1505
|
-
if (row
|
|
1506
|
-
|
|
1507
|
-
} else {
|
|
1508
|
-
break;
|
|
1509
|
-
}
|
|
1671
|
+
if (row?.type !== "header") break;
|
|
1672
|
+
n++;
|
|
1510
1673
|
}
|
|
1511
1674
|
return n;
|
|
1512
1675
|
}
|
|
1676
|
+
function isRowspanComplete(rows, n) {
|
|
1677
|
+
for (let r = 0; r < n; r++) {
|
|
1678
|
+
for (const cell of rows[r]?.cells ?? []) {
|
|
1679
|
+
if (cell?.merged) continue;
|
|
1680
|
+
if (r + (cell?.rowspan ?? 1) > n) return false;
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
return true;
|
|
1684
|
+
}
|
|
1685
|
+
function alignToRowspanBoundary(rows, max2) {
|
|
1686
|
+
for (let n = max2; n >= 1; n--) {
|
|
1687
|
+
if (isRowspanComplete(rows, n)) return n;
|
|
1688
|
+
}
|
|
1689
|
+
return 1;
|
|
1690
|
+
}
|
|
1513
1691
|
var pad2 = (n) => String(n).padStart(2, "0");
|
|
1514
1692
|
function resolveSystemVariables(now = /* @__PURE__ */ new Date(), page = {}) {
|
|
1515
1693
|
return {
|
|
@@ -1519,6 +1697,9 @@ function resolveSystemVariables(now = /* @__PURE__ */ new Date(), page = {}) {
|
|
|
1519
1697
|
totalPages: page.totalPages ?? 1
|
|
1520
1698
|
};
|
|
1521
1699
|
}
|
|
1700
|
+
function referencesPageNumbers(expr) {
|
|
1701
|
+
return typeof expr === "string" && /\b(pageIndex|totalPages)\b/.test(expr);
|
|
1702
|
+
}
|
|
1522
1703
|
function injectSystemVariables(html, now = /* @__PURE__ */ new Date()) {
|
|
1523
1704
|
const { printDate, printTime } = resolveSystemVariables(now);
|
|
1524
1705
|
return html.replace(/\{printDate\}/g, printDate).replace(/\{printTime\}/g, printTime);
|
|
@@ -2087,10 +2268,9 @@ function tableDesignBottom(el) {
|
|
|
2087
2268
|
const opts = el.options ?? {};
|
|
2088
2269
|
const top = opts.top ?? 0;
|
|
2089
2270
|
const rows = opts.tableRows ?? [];
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
return top + (opts.height ?? 0);
|
|
2271
|
+
const rowSum = rows.reduce((s, r) => s + (r.height ?? 0), 0);
|
|
2272
|
+
const designHeight = Math.max(opts.height ?? 0, rowSum);
|
|
2273
|
+
return top + designHeight;
|
|
2094
2274
|
}
|
|
2095
2275
|
function buildFollowMap(sorted, excludedIds) {
|
|
2096
2276
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -2231,7 +2411,10 @@ function paginate(template, measuredElements) {
|
|
|
2231
2411
|
let isFirstPage = true;
|
|
2232
2412
|
let pageBroken = false;
|
|
2233
2413
|
function sectionTop(el) {
|
|
2234
|
-
return pageBroken ?
|
|
2414
|
+
return pageBroken ? pageCursorTop() : el.options?.top ?? 0;
|
|
2415
|
+
}
|
|
2416
|
+
function pageCursorTop() {
|
|
2417
|
+
return Math.max(0, fullPageHeight() - remaining);
|
|
2235
2418
|
}
|
|
2236
2419
|
function fullPageHeight() {
|
|
2237
2420
|
return isFirstPage ? contentHeight - overlayH - SAFETY_MARGIN : contentHeight - SAFETY_MARGIN;
|
|
@@ -2298,7 +2481,7 @@ function paginate(template, measuredElements) {
|
|
|
2298
2481
|
function paginateNonTable(el, measured, sortedList, idx) {
|
|
2299
2482
|
const elHeight = measured.get(el.id)?.measuredHeight ?? el.options?.height ?? 0;
|
|
2300
2483
|
if (elHeight <= remaining) {
|
|
2301
|
-
const top2 = pageBroken ?
|
|
2484
|
+
const top2 = pageBroken ? pageCursorTop() : el.options?.top ?? 0;
|
|
2302
2485
|
noteOverflow(top2 + elHeight);
|
|
2303
2486
|
currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
|
|
2304
2487
|
remaining -= elHeight;
|
|
@@ -2317,8 +2500,9 @@ function paginate(template, measuredElements) {
|
|
|
2317
2500
|
}
|
|
2318
2501
|
return idx + 1;
|
|
2319
2502
|
}
|
|
2320
|
-
|
|
2321
|
-
|
|
2503
|
+
finishPage();
|
|
2504
|
+
const top = pageBroken ? pageCursorTop() : el.options?.top ?? 0;
|
|
2505
|
+
if (top + elHeight > contentHeight) overflowOnCurrent = true;
|
|
2322
2506
|
currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
|
|
2323
2507
|
remaining -= elHeight;
|
|
2324
2508
|
return idx + 1;
|
|
@@ -2334,7 +2518,7 @@ function paginate(template, measuredElements) {
|
|
|
2334
2518
|
}
|
|
2335
2519
|
const unitHeight = Math.max(maxBottom - minTop, 0);
|
|
2336
2520
|
const place = () => {
|
|
2337
|
-
const offset = pageBroken ? -minTop : 0;
|
|
2521
|
+
const offset = pageBroken ? pageCursorTop() - minTop : 0;
|
|
2338
2522
|
noteOverflow(minTop + offset + unitHeight);
|
|
2339
2523
|
for (const m of members) {
|
|
2340
2524
|
currentPage.push({
|
|
@@ -2408,6 +2592,7 @@ function paginate(template, measuredElements) {
|
|
|
2408
2592
|
const groups = buildRowGroups(bodyRows, rowCount);
|
|
2409
2593
|
let sliceStart = 0;
|
|
2410
2594
|
let firstSlice = true;
|
|
2595
|
+
let sliceTop = sectionTop(el);
|
|
2411
2596
|
for (const g of groups) {
|
|
2412
2597
|
const gh = bodyHeights.slice(g.start, g.end).reduce((s, h) => s + h, 0);
|
|
2413
2598
|
if (gh + subtotalH <= remaining) {
|
|
@@ -2422,12 +2607,13 @@ function paginate(template, measuredElements) {
|
|
|
2422
2607
|
endRow: g.start,
|
|
2423
2608
|
subtotal: subtotalH > 0,
|
|
2424
2609
|
...!firstSlice && repeatCount > 0 ? { repeatHeader: true } : {},
|
|
2425
|
-
renderTop:
|
|
2610
|
+
renderTop: sliceTop
|
|
2426
2611
|
});
|
|
2427
2612
|
firstSlice = false;
|
|
2428
2613
|
sliceStart = g.start;
|
|
2429
2614
|
}
|
|
2430
2615
|
finishPage();
|
|
2616
|
+
sliceTop = sectionTop(el);
|
|
2431
2617
|
if (!firstSlice) remaining -= repeatH;
|
|
2432
2618
|
remaining -= gh;
|
|
2433
2619
|
}
|
|
@@ -2439,7 +2625,7 @@ function paginate(template, measuredElements) {
|
|
|
2439
2625
|
endRow: rowCount,
|
|
2440
2626
|
subtotal: subtotalH > 0,
|
|
2441
2627
|
...!firstSlice && repeatCount > 0 ? { repeatHeader: true } : {},
|
|
2442
|
-
renderTop:
|
|
2628
|
+
renderTop: sliceTop
|
|
2443
2629
|
});
|
|
2444
2630
|
}
|
|
2445
2631
|
if (summaryH > 0) {
|
|
@@ -2520,6 +2706,13 @@ function specialRowHeight(renderRows, rowHeights, type) {
|
|
|
2520
2706
|
}
|
|
2521
2707
|
|
|
2522
2708
|
// src/render/html-generator.ts
|
|
2709
|
+
function pageVarsContext(vars) {
|
|
2710
|
+
return { ...vars.data ?? {}, pageIndex: vars.pageIndex, totalPages: vars.totalPages };
|
|
2711
|
+
}
|
|
2712
|
+
function pageVarsData(printData) {
|
|
2713
|
+
if (Array.isArray(printData)) return printData[0] ?? {};
|
|
2714
|
+
return printData ?? {};
|
|
2715
|
+
}
|
|
2523
2716
|
function generateHtml(template, pageLayouts, printData, options) {
|
|
2524
2717
|
const css = buildFontFaceCss(template.fonts) + buildPageCss(template, options?.pageHeightMm);
|
|
2525
2718
|
const isMeasure = options?.isMeasurementPass === true;
|
|
@@ -2604,22 +2797,25 @@ function renderPage(template, page, pageNum, totalPages, ctx, printData, pageCla
|
|
|
2604
2797
|
height: ctx.pageHeightMm && ctx.pageHeightMm > 0 ? ctx.pageHeightMm : paper.height
|
|
2605
2798
|
};
|
|
2606
2799
|
const contentWidth = paper.width - template.margins.left - template.margins.right;
|
|
2800
|
+
const pageVars = { pageIndex: pageNum, totalPages, data: pageVarsData(printData) };
|
|
2801
|
+
const scoped = withPageNumbers(template, pageVars);
|
|
2802
|
+
const pageCtx = { ...ctx, pageVars };
|
|
2607
2803
|
const headerHtml = renderAreaElements(
|
|
2608
|
-
|
|
2804
|
+
scoped.header?.elements ?? [],
|
|
2609
2805
|
contentWidth,
|
|
2610
2806
|
pageNum,
|
|
2611
2807
|
totalPages,
|
|
2612
|
-
|
|
2808
|
+
pageCtx
|
|
2613
2809
|
);
|
|
2614
2810
|
const footerHtml = renderAreaElements(
|
|
2615
|
-
|
|
2811
|
+
scoped.footer?.elements ?? [],
|
|
2616
2812
|
contentWidth,
|
|
2617
2813
|
pageNum,
|
|
2618
2814
|
totalPages,
|
|
2619
|
-
|
|
2815
|
+
pageCtx
|
|
2620
2816
|
);
|
|
2621
|
-
const overlayHtml = page.pageIndex === 0 ? `<div class="first-page-overlay">${renderAreaElements(
|
|
2622
|
-
let contentHtml = page.sections.map((section) => renderSection(section,
|
|
2817
|
+
const overlayHtml = page.pageIndex === 0 ? `<div class="first-page-overlay">${renderAreaElements(scoped.firstPageOverlay?.elements ?? [], contentWidth, pageNum, totalPages, pageCtx)}</div>` : "";
|
|
2818
|
+
let contentHtml = page.sections.map((section) => renderSection(section, scoped, pageCtx)).join("\n");
|
|
2623
2819
|
contentHtml = contentHtml.replace(/\{pageIndex\}/g, String(pageNum));
|
|
2624
2820
|
contentHtml = contentHtml.replace(/\{totalPages\}/g, String(totalPages));
|
|
2625
2821
|
const pageInner = `
|
|
@@ -2636,6 +2832,39 @@ function renderPage(template, page, pageNum, totalPages, ctx, printData, pageCla
|
|
|
2636
2832
|
${pageBody}
|
|
2637
2833
|
</section>`;
|
|
2638
2834
|
}
|
|
2835
|
+
function withPageNumbers(template, vars) {
|
|
2836
|
+
if (!templateReferencesPageNumbers(template)) return template;
|
|
2837
|
+
const context = pageVarsContext(vars);
|
|
2838
|
+
const mapEl = (el) => {
|
|
2839
|
+
const raw = el.options?.rawFormatter;
|
|
2840
|
+
if (typeof raw !== "string") return el;
|
|
2841
|
+
return { ...el, options: { ...el.options, formatter: evaluateTemplate(raw, context) } };
|
|
2842
|
+
};
|
|
2843
|
+
const mapArea = (area) => area ? { ...area, elements: (area.elements ?? []).map(mapEl) } : area;
|
|
2844
|
+
return {
|
|
2845
|
+
...template,
|
|
2846
|
+
elements: template.elements.map(mapEl),
|
|
2847
|
+
header: mapArea(template.header),
|
|
2848
|
+
footer: mapArea(template.footer),
|
|
2849
|
+
firstPageOverlay: mapArea(template.firstPageOverlay)
|
|
2850
|
+
};
|
|
2851
|
+
}
|
|
2852
|
+
function templateReferencesPageNumbers(template) {
|
|
2853
|
+
const areas = [
|
|
2854
|
+
template.elements,
|
|
2855
|
+
template.header?.elements,
|
|
2856
|
+
template.footer?.elements,
|
|
2857
|
+
template.firstPageOverlay?.elements
|
|
2858
|
+
];
|
|
2859
|
+
return areas.some((list) => (list ?? []).some((el) => typeof el.options?.rawFormatter === "string"));
|
|
2860
|
+
}
|
|
2861
|
+
function resolveCellContent(cell, ctx) {
|
|
2862
|
+
const raw = cell.rawFormatter;
|
|
2863
|
+
if (typeof raw === "string" && raw !== "" && ctx?.pageVars) {
|
|
2864
|
+
return evaluateTemplate(raw, pageVarsContext(ctx.pageVars));
|
|
2865
|
+
}
|
|
2866
|
+
return cell.content;
|
|
2867
|
+
}
|
|
2639
2868
|
function renderSection(section, template, ctx) {
|
|
2640
2869
|
const el = findElement(template, section.elementId);
|
|
2641
2870
|
if (!el) {
|
|
@@ -2788,7 +3017,7 @@ function matrixCellStyle(cell, opts) {
|
|
|
2788
3017
|
}
|
|
2789
3018
|
return parts.join(";");
|
|
2790
3019
|
}
|
|
2791
|
-
function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOwner = { elementId: "", kind: "b" }) {
|
|
3020
|
+
function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOwner = { elementId: "", kind: "b" }, rowLimit) {
|
|
2792
3021
|
const trs = [];
|
|
2793
3022
|
const defaultPadding = opts.tableDefaultPadding ?? 1;
|
|
2794
3023
|
for (let r = start; r < end; r++) {
|
|
@@ -2796,12 +3025,14 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOw
|
|
|
2796
3025
|
if (!row) continue;
|
|
2797
3026
|
const idxAttr = withRowIndex ? ` data-row-index="${r}"` : "";
|
|
2798
3027
|
const tds = row.cells.map((cell, ci) => ({ cell, ci })).filter(({ cell }) => !cell.merged).map(({ cell, ci }) => {
|
|
2799
|
-
const
|
|
3028
|
+
const rowspan = rowLimit === void 0 ? cell.rowspan ?? 1 : Math.max(1, Math.min(cell.rowspan ?? 1, rowLimit - r));
|
|
3029
|
+
const span = `${rowspan > 1 ? ` rowspan="${rowspan}"` : ""}${cell.colspan > 1 ? ` colspan="${cell.colspan}"` : ""}`;
|
|
2800
3030
|
const colIndex = ci;
|
|
2801
3031
|
let inner;
|
|
2802
3032
|
if (cell.cellType === "barcode" || cell.cellType === "qrcode") {
|
|
2803
|
-
|
|
2804
|
-
|
|
3033
|
+
const codeValue = resolveCellContent(cell, ctx);
|
|
3034
|
+
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;">${codeImgHtml(codeValue, cell.cellType, cell, {
|
|
3035
|
+
fallback: esc(codeValue),
|
|
2805
3036
|
codeRenderer: ctx?.codeRenderer,
|
|
2806
3037
|
fit: cell.fit,
|
|
2807
3038
|
maxWidth: cell.maxWidth,
|
|
@@ -2815,9 +3046,9 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOw
|
|
|
2815
3046
|
const fit = cell.fit || "contain";
|
|
2816
3047
|
const maxWidth = cell.maxWidth ? `max-width:${cell.maxWidth}mm;` : "max-width:100%;";
|
|
2817
3048
|
const maxHeight = cell.maxHeight ? `max-height:${cell.maxHeight}mm;` : "max-height:100%;";
|
|
2818
|
-
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;"><img src="${esc(cell
|
|
3049
|
+
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>`;
|
|
2819
3050
|
} else {
|
|
2820
|
-
inner = renderTextCellInner(renderRows, r, colIndex, cell, opts, fitOwner, defaultPadding);
|
|
3051
|
+
inner = renderTextCellInner(renderRows, r, colIndex, cell, opts, fitOwner, defaultPadding, ctx);
|
|
2821
3052
|
}
|
|
2822
3053
|
return `<td${span} style="${matrixCellStyle(cell, opts)}">${inner}</td>`;
|
|
2823
3054
|
}).join("");
|
|
@@ -2825,9 +3056,9 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOw
|
|
|
2825
3056
|
}
|
|
2826
3057
|
return trs.join("\n");
|
|
2827
3058
|
}
|
|
2828
|
-
function renderTextCellInner(renderRows, rowIndex, colIndex, cell, opts, fitOwner, defaultPadding) {
|
|
3059
|
+
function renderTextCellInner(renderRows, rowIndex, colIndex, cell, opts, fitOwner, defaultPadding, ctx) {
|
|
2829
3060
|
const fit = resolveCellTextFit(cell);
|
|
2830
|
-
const text = esc(cell
|
|
3061
|
+
const text = esc(resolveCellContent(cell, ctx));
|
|
2831
3062
|
if (fit === "autoHeight") return text;
|
|
2832
3063
|
const capMm = cellFitCapMm(renderRows, rowIndex, cell, defaultPadding);
|
|
2833
3064
|
const nowrap = cell.wordWrap === false;
|
|
@@ -2865,7 +3096,7 @@ function renderTableSlice(el, section, ctx) {
|
|
|
2865
3096
|
const startRow = section.startRow ?? 0;
|
|
2866
3097
|
const endRow = section.endRow ?? renderRows.length;
|
|
2867
3098
|
const repeatCount = opts._repeatHeaderCount ?? 0;
|
|
2868
|
-
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }) : "";
|
|
3099
|
+
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }, repeatCount) : "";
|
|
2869
3100
|
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx, { elementId: el.id, kind: "b" });
|
|
2870
3101
|
const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
|
|
2871
3102
|
const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
|
|
@@ -2886,9 +3117,15 @@ function renderSubtotalRows(el, section, opts, ctx) {
|
|
|
2886
3117
|
const dataStart = Math.max(startRow, dataStartIdx);
|
|
2887
3118
|
const dataEnd = Math.max(endRow, dataStart);
|
|
2888
3119
|
const pageCtx = dataEnd > dataStartIdx ? dataRowCtx.slice(Math.max(dataStart - dataStartIdx, 0), dataEnd - dataStartIdx) : [];
|
|
3120
|
+
const pageVars = ctx?.pageVars ? pageVarsContext(ctx.pageVars) : {};
|
|
2889
3121
|
const rows = templates.map((tpl) => ({
|
|
2890
3122
|
...tpl,
|
|
2891
|
-
cells: tpl.cells.map((cell) => cell.rawFormatter ? {
|
|
3123
|
+
cells: tpl.cells.map((cell) => cell.rawFormatter ? {
|
|
3124
|
+
...cell,
|
|
3125
|
+
content: evaluateTemplate(cell.rawFormatter, { rows: pageCtx, ...mainData, ...pageVars }),
|
|
3126
|
+
// 已按本页上下文求值,清标记避免渲染时再按页码重算一次(会丢掉 rows)
|
|
3127
|
+
rawFormatter: ""
|
|
3128
|
+
} : cell)
|
|
2892
3129
|
}));
|
|
2893
3130
|
return renderMatrixRows(rows, 0, rows.length, opts, false, ctx, { elementId: el.id, kind: "st" });
|
|
2894
3131
|
}
|
|
@@ -2911,7 +3148,7 @@ function renderFlowGroup(el, section, template, ctx) {
|
|
|
2911
3148
|
if (endRow > startRow || section.subtotal || section.summary) {
|
|
2912
3149
|
const renderRows = opts._renderRows ?? [];
|
|
2913
3150
|
const repeatCount = opts._repeatHeaderCount ?? 0;
|
|
2914
|
-
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }) : "";
|
|
3151
|
+
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }, repeatCount) : "";
|
|
2915
3152
|
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx, { elementId: el.id, kind: "b" });
|
|
2916
3153
|
const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
|
|
2917
3154
|
const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
|
package/dist/browser/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PageLayout, T as TemplateData, M as MultiPageTemplateData, C as CodeRenderer, D as DriverFactory, R as RawMeasurement, a as CodeSpec } from '../driver-
|
|
2
|
-
import { P as PrintRuntime, F as FitFontSize } from '../ports-
|
|
1
|
+
import { P as PageLayout, T as TemplateData, M as MultiPageTemplateData, C as CodeRenderer, D as DriverFactory, R as RawMeasurement, a as CodeSpec } from '../driver-Dn_YAzO5.cjs';
|
|
2
|
+
import { P as PrintRuntime, F as FitFontSize } from '../ports-Cf5sjwls.cjs';
|
|
3
3
|
|
|
4
4
|
interface BrowserRenderResult {
|
|
5
5
|
/** 最终多页 HTML 字符串 */
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PageLayout, T as TemplateData, M as MultiPageTemplateData, C as CodeRenderer, D as DriverFactory, R as RawMeasurement, a as CodeSpec } from '../driver-
|
|
2
|
-
import { P as PrintRuntime, F as FitFontSize } from '../ports-
|
|
1
|
+
import { P as PageLayout, T as TemplateData, M as MultiPageTemplateData, C as CodeRenderer, D as DriverFactory, R as RawMeasurement, a as CodeSpec } from '../driver-Dn_YAzO5.js';
|
|
2
|
+
import { P as PrintRuntime, F as FitFontSize } from '../ports-BvwlA_km.js';
|
|
3
3
|
|
|
4
4
|
interface BrowserRenderResult {
|
|
5
5
|
/** 最终多页 HTML 字符串 */
|
package/dist/browser/index.js
CHANGED