@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.
@@ -106,6 +106,7 @@ __export(designer_exports, {
106
106
  roundFontSize: () => roundFontSize,
107
107
  safeEval: () => safeEval2,
108
108
  searchProperties: () => searchProperties,
109
+ setHeaderRepeat: () => setHeaderRepeat,
109
110
  setRowType: () => setRowType,
110
111
  splitCells: () => splitCells,
111
112
  syncTableElementSize: () => syncTableElementSize,
@@ -537,6 +538,8 @@ function insertRow(rows, index, position) {
537
538
  id: `row-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
538
539
  type,
539
540
  height: ref.height,
541
+ // 新表头行沿用相邻表头行的重复设置:漏勾会让续片只重复部分表头,多级表头结构断裂
542
+ ...type === "header" ? { repeatOnPage: [rows[insertAt - 1], rows[insertAt]].find((r) => r?.type === "header")?.repeatOnPage === true } : {},
540
543
  cells: Array.from({ length: colCount }, () => createCell())
541
544
  };
542
545
  for (let c = 0; c < colCount; c++) {
@@ -660,11 +663,20 @@ function setRowType(rows, index, type) {
660
663
  }
661
664
  }
662
665
  rows[index].type = type;
663
- if (type !== "header") {
666
+ if (type === "header") {
667
+ const neighbour = [rows[index - 1], rows[index + 1]].find((r) => r?.type === "header");
668
+ rows[index].repeatOnPage = rows[index].repeatOnPage !== void 0 ? rows[index].repeatOnPage === true : neighbour?.repeatOnPage === true;
669
+ } else {
664
670
  delete rows[index].repeatOnPage;
665
671
  }
666
672
  return null;
667
673
  }
674
+ function setHeaderRepeat(rows, enabled) {
675
+ for (const row of rows) {
676
+ if (row.type !== "header") break;
677
+ row.repeatOnPage = enabled;
678
+ }
679
+ }
668
680
  function applyBorderPreset(rows, rect, preset, border) {
669
681
  for (let r = rect.r1; r <= rect.r2; r++) {
670
682
  for (let c = rect.c1; c <= rect.c2; c++) {
@@ -1574,6 +1586,113 @@ var Parser = class {
1574
1586
  }
1575
1587
  };
1576
1588
 
1589
+ // src/numeric.ts
1590
+ var MAX_DIGITS = 20;
1591
+ var EXTRA_SCALE = 8;
1592
+ function toNumber(value, fallback = 0) {
1593
+ if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
1594
+ if (typeof value === "string") {
1595
+ const s = value.trim();
1596
+ if (s === "") return fallback;
1597
+ const n = Number(s);
1598
+ return Number.isFinite(n) ? n : fallback;
1599
+ }
1600
+ if (value == null) return fallback;
1601
+ if (typeof value === "boolean") return value ? 1 : 0;
1602
+ return fallback;
1603
+ }
1604
+ function isNumericLike(value) {
1605
+ if (typeof value === "number") return Number.isFinite(value);
1606
+ if (typeof value === "string") {
1607
+ const s = value.trim();
1608
+ return s !== "" && Number.isFinite(Number(s));
1609
+ }
1610
+ return false;
1611
+ }
1612
+ function normalizeFloat(value) {
1613
+ if (!Number.isFinite(value) || Number.isInteger(value)) return value;
1614
+ if (Math.abs(value) >= 1e15) return value;
1615
+ return Number(value.toPrecision(12));
1616
+ }
1617
+ function decimalsOf(n) {
1618
+ if (!Number.isFinite(n) || Number.isInteger(n)) return 0;
1619
+ const s = String(n);
1620
+ if (s.includes("e") || s.includes("E")) return 0;
1621
+ const i = s.indexOf(".");
1622
+ return i === -1 ? 0 : s.length - i - 1;
1623
+ }
1624
+ function rescale(value, decimals) {
1625
+ if (!Number.isFinite(value)) return 0;
1626
+ const d = Math.min(Math.max(decimals, 0), 12);
1627
+ if (d === 0) return Math.round(value);
1628
+ const f = 10 ** d;
1629
+ const scaled = value * f;
1630
+ if (!Number.isFinite(scaled) || Math.abs(scaled) >= 1e15) return normalizeFloat(value);
1631
+ return Math.round(scaled) / f;
1632
+ }
1633
+ function decimalAdd(left, right) {
1634
+ const a = toNumber(left);
1635
+ const b = toNumber(right);
1636
+ const raw = a + b;
1637
+ return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
1638
+ }
1639
+ function decimalSubtract(left, right) {
1640
+ const a = toNumber(left);
1641
+ const b = toNumber(right);
1642
+ const raw = a - b;
1643
+ return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
1644
+ }
1645
+ function decimalMultiply(left, right) {
1646
+ const a = toNumber(left);
1647
+ const b = toNumber(right);
1648
+ const raw = a * b;
1649
+ return rescale(raw, decimalsOf(a) + decimalsOf(b));
1650
+ }
1651
+ function decimalDivide(left, right) {
1652
+ const a = toNumber(left);
1653
+ const b = toNumber(right);
1654
+ if (b === 0) return 0;
1655
+ return normalizeFloat(a / b);
1656
+ }
1657
+ function decimalModulo(left, right) {
1658
+ const a = toNumber(left);
1659
+ const b = toNumber(right);
1660
+ if (b === 0) return 0;
1661
+ return normalizeFloat(a % b);
1662
+ }
1663
+ function normalizeDigits(digits) {
1664
+ if (digits === void 0 || digits === null) return 2;
1665
+ const d = Math.trunc(toNumber(digits, 2));
1666
+ if (!Number.isFinite(d)) return 2;
1667
+ return Math.min(Math.max(d, -MAX_DIGITS), MAX_DIGITS);
1668
+ }
1669
+ function roundTo(value, digits, mode) {
1670
+ const n = toNumber(value, 0);
1671
+ const d = normalizeDigits(digits);
1672
+ if (n === 0) return 0;
1673
+ if (Math.abs(n) >= 1e21) return normalizeFloat(n);
1674
+ const scale = Math.max(d, 0) + EXTRA_SCALE;
1675
+ const fixed = n.toFixed(scale);
1676
+ if (!/^-?\d+(\.\d+)?$/.test(fixed)) return normalizeFloat(n);
1677
+ const negative = fixed.startsWith("-");
1678
+ const digitsStr = fixed.replace("-", "").replace(".", "");
1679
+ let scaled = BigInt(digitsStr || "0");
1680
+ if (negative) scaled = -scaled;
1681
+ const pow10 = 10n ** BigInt(scale - d);
1682
+ const sign = scaled < 0n ? -1n : 1n;
1683
+ const abs = scaled < 0n ? -scaled : scaled;
1684
+ let q = abs / pow10;
1685
+ const r = abs % pow10;
1686
+ const doubled = 2n * r;
1687
+ if (mode === "up") {
1688
+ if (r !== 0n) q += 1n;
1689
+ } else if (mode !== "down" && doubled >= pow10) {
1690
+ if (doubled > pow10 || mode === "half-up" || q % 2n === 1n) q += 1n;
1691
+ }
1692
+ const result = sign * q;
1693
+ return normalizeFloat(d >= 0 ? Number(result) / 10 ** d : Number(result) * 10 ** -d);
1694
+ }
1695
+
1577
1696
  // src/evaluator.ts
1578
1697
  var SAFE_GLOBALS = {
1579
1698
  Math,
@@ -1639,16 +1758,17 @@ function evalBinary(node, context, functions) {
1639
1758
  const left = evaluate(node.left, context, functions);
1640
1759
  const right = evaluate(node.right, context, functions);
1641
1760
  switch (node.op) {
1761
+ // 数值运算统一走十进制精确实现:消除 0.1 + 0.2 类浮点噪声、字符串数字按数值处理、除零兜底 0
1642
1762
  case "+":
1643
- return left + right;
1763
+ return isNumericOperands(left, right) ? decimalAdd(left, right) : concatOperands(left, right);
1644
1764
  case "-":
1645
- return left - right;
1765
+ return decimalSubtract(left, right);
1646
1766
  case "*":
1647
- return left * right;
1767
+ return decimalMultiply(left, right);
1648
1768
  case "/":
1649
- return left / right;
1769
+ return decimalDivide(left, right);
1650
1770
  case "%":
1651
- return left % right;
1771
+ return decimalModulo(left, right);
1652
1772
  case "<":
1653
1773
  return left < right;
1654
1774
  case ">":
@@ -1677,15 +1797,29 @@ function evalUnary(node, context, functions) {
1677
1797
  const arg = evaluate(node.arg, context, functions);
1678
1798
  switch (node.op) {
1679
1799
  case "-":
1680
- return -arg;
1800
+ return decimalSubtract(0, arg);
1681
1801
  case "+":
1682
- return +arg;
1802
+ return toNumber(arg);
1683
1803
  case "!":
1684
1804
  return !arg;
1685
1805
  default:
1686
1806
  throw new Error(`\u672A\u77E5\u4E00\u5143\u8FD0\u7B97\u7B26: ${node.op}`);
1687
1807
  }
1688
1808
  }
1809
+ function concatOperands(left, right) {
1810
+ return `${left == null ? "" : String(left)}${right == null ? "" : String(right)}`;
1811
+ }
1812
+ function isNumericOperands(left, right) {
1813
+ const l = operandKind(left);
1814
+ const r = operandKind(right);
1815
+ if (l === "other" || r === "other") return false;
1816
+ return l === "num" || r === "num";
1817
+ }
1818
+ function operandKind(value) {
1819
+ if (value == null || value === "") return "neutral";
1820
+ if (isNumericLike(value)) return "num";
1821
+ return "other";
1822
+ }
1689
1823
  function evalMember(node, context, functions) {
1690
1824
  const obj = evaluate(node.object, context, functions);
1691
1825
  if (obj == null) {
@@ -1897,6 +2031,34 @@ function max(rows, field) {
1897
2031
  return Math.max(...rows.map((row) => Number(getByPath(row, field)) || 0));
1898
2032
  }
1899
2033
 
2034
+ // src/functions/math.ts
2035
+ function addNumbers(...values) {
2036
+ return values.reduce((acc, v) => decimalAdd(acc, v), 0);
2037
+ }
2038
+ function subtractNumbers(...values) {
2039
+ if (values.length === 0) return 0;
2040
+ return values.slice(1).reduce((acc, v) => decimalSubtract(acc, v), decimalAdd(values[0], 0));
2041
+ }
2042
+ function multiplyNumbers(...values) {
2043
+ if (values.length === 0) return 0;
2044
+ return values.reduce((acc, v) => decimalMultiply(acc, v), 1);
2045
+ }
2046
+ function divideNumbers(a, b) {
2047
+ return decimalDivide(a, b);
2048
+ }
2049
+ function round(value, digits) {
2050
+ return roundTo(value, digits, "half-up");
2051
+ }
2052
+ function roundUp(value, digits) {
2053
+ return roundTo(value, digits, "up");
2054
+ }
2055
+ function roundDown(value, digits) {
2056
+ return roundTo(value, digits, "down");
2057
+ }
2058
+ function roundHalfEven(value, digits) {
2059
+ return roundTo(value, digits, "half-even");
2060
+ }
2061
+
1900
2062
  // src/render/expression-eval.ts
1901
2063
  var RenderEngine = class {
1902
2064
  constructor() {
@@ -1922,8 +2084,19 @@ var FORMAT_FUNCTIONS = {
1922
2084
  IF: ifFn,
1923
2085
  CONCAT: (...args) => args.filter((v) => v != null).map(String).join(""),
1924
2086
  IFEMPTY: (v, d) => v != null && v !== "" ? String(v) : d,
1925
- ROUND: (n, d) => Number(Number(n).toFixed(d)),
1926
- LEN: (s) => String(s).length
2087
+ LEN: (s) => String(s).length,
2088
+ // 四则运算(与 +/-/*// 运算符同一套数值语义,供不方便写运算符的场景使用)
2089
+ ADD: addNumbers,
2090
+ SUB: subtractNumbers,
2091
+ MUL: multiplyNumbers,
2092
+ DIV: divideNumbers,
2093
+ // 数值修约
2094
+ ROUND: round,
2095
+ ROUNDUP: roundUp,
2096
+ CEIL: roundUp,
2097
+ ROUNDDOWN: roundDown,
2098
+ FLOOR: roundDown,
2099
+ ROUNDBANK: roundHalfEven
1927
2100
  };
1928
2101
  for (const [name, fn] of Object.entries(FORMAT_FUNCTIONS)) {
1929
2102
  engine.registerFunction(name, fn);
@@ -1968,7 +2141,18 @@ var EXTENDED_FUNCTIONS = {
1968
2141
  },
1969
2142
  SUBSTR: (str, start, len) => String(str).slice(start, len ? start + len : void 0),
1970
2143
  LEN: (str) => String(str).length,
1971
- ROUND: (num, decimals) => Number(Number(num).toFixed(decimals)),
2144
+ // 四则运算(与 +/-/*// 运算符同一套数值语义,供不方便写运算符的场景使用)
2145
+ ADD: addNumbers,
2146
+ SUB: subtractNumbers,
2147
+ MUL: multiplyNumbers,
2148
+ DIV: divideNumbers,
2149
+ // 数值修约
2150
+ ROUND: round,
2151
+ ROUNDUP: roundUp,
2152
+ CEIL: roundUp,
2153
+ ROUNDDOWN: roundDown,
2154
+ FLOOR: roundDown,
2155
+ ROUNDBANK: roundHalfEven,
1972
2156
  NOW: () => (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
1973
2157
  IFEMPTY: (value, defaultVal) => value != null && value !== "" ? String(value) : defaultVal,
1974
2158
  PAD: (value, len, char) => String(value).padStart(len, char),
@@ -2629,6 +2813,7 @@ function useBindingDisplay() {
2629
2813
  roundFontSize,
2630
2814
  safeEval,
2631
2815
  searchProperties,
2816
+ setHeaderRepeat,
2632
2817
  setRowType,
2633
2818
  splitCells,
2634
2819
  syncTableElementSize,
@@ -1,7 +1,7 @@
1
- import { b as TemplateData, c as TableRow, d as TableCellBorder, e as TableCell, f as TableRowType, E as ElementOptions, g as ElementType, h as RuntimeElement, i as ElementZone, j as PrintBusinessField, B as BindingDescriptor, k as ElementRect, A as AdsorbResult, l as ResizePoint } from '../driver-CsWkGK4a.cjs';
2
- export { m as AlignLine, n as DesignBackground, o as ElementFieldBinding, p as MultiPageTemplateData, q as PaginationConfig, r as PaperSize, s as PrintElementData, t as PrintElementTypeMeta, u as RequestScreenshotFn, S as ScreenshotRequest, v as TableCellBorders, w as TableCellType, x as TablePaginationConfig, y as TableSelection, z as TemplateElement, F as TextAlign, G as TextFit, U as UploadDesignBackgroundFn, H as UploadImageFn, V as VerticalAlign, W as WatermarkOptions } from '../driver-CsWkGK4a.cjs';
3
- export { C as CellFitRowKind, D as DEFAULT_SHRINK_MIN_FONT_SIZE_PT, F as FitFontSize, M as MIN_SHRINK_FONT_SIZE_PT, c as cellFitCapMm, a as cellFitKey, b as cellFitWidthMm, f as floorFontSize, p as parseCellFitKey, r as resolveCellTextFit, d as resolveElementTextFit, e as resolveShrinkMinFontSize, g as roundFontSize } from '../ports-q934kIC7.cjs';
4
- import { TemplateEngine, formatMoney, ifFn, formatDate, toUpperCaseAmount } from '../index.cjs';
1
+ import { b as TemplateData, c as TableRow, d as TableCellBorder, e as TableCell, f as TableRowType, E as ElementOptions, g as ElementType, h as RuntimeElement, i as ElementZone, j as PrintBusinessField, B as BindingDescriptor, k as ElementRect, A as AdsorbResult, l as ResizePoint } from '../driver-Dn_YAzO5.cjs';
2
+ export { m as AlignLine, n as DesignBackground, o as ElementFieldBinding, p as MultiPageTemplateData, q as PaginationConfig, r as PaperSize, s as PrintElementData, t as PrintElementTypeMeta, u as RequestScreenshotFn, S as ScreenshotRequest, v as TableCellBorders, w as TableCellType, x as TablePaginationConfig, y as TableSelection, z as TemplateElement, F as TextAlign, G as TextFit, U as UploadDesignBackgroundFn, H as UploadImageFn, V as VerticalAlign, W as WatermarkOptions } from '../driver-Dn_YAzO5.cjs';
3
+ export { C as CellFitRowKind, D as DEFAULT_SHRINK_MIN_FONT_SIZE_PT, F as FitFontSize, M as MIN_SHRINK_FONT_SIZE_PT, c as cellFitCapMm, a as cellFitKey, b as cellFitWidthMm, f as floorFontSize, p as parseCellFitKey, r as resolveCellTextFit, d as resolveElementTextFit, e as resolveShrinkMinFontSize, g as roundFontSize } from '../ports-Cf5sjwls.cjs';
4
+ import { TemplateEngine, formatMoney, ifFn, addNumbers, subtractNumbers, multiplyNumbers, divideNumbers, round, roundUp, roundDown, roundHalfEven, formatDate, toUpperCaseAmount } from '../index.cjs';
5
5
 
6
6
  /** 1 英寸 = 25.4mm */
7
7
  declare const MM_PER_INCH = 25.4;
@@ -166,6 +166,12 @@ declare function insertCol(rows: TableRow[], colWidths: number[], index: number,
166
166
  declare function deleteCol(rows: TableRow[], colWidths: number[], index: number): string | null;
167
167
  /** 行类型变更校验 + 写入。约束:header 顶部连续;data 唯一;subtotal/summary 在 data 之后;subtotal 在 summary 之前 */
168
168
  declare function setRowType(rows: TableRow[], index: number, type: TableRowType): string | null;
169
+ /**
170
+ * 统一设置表头区的「每页顶部重复」。
171
+ * 多级表头的各行由 rowspan/colspan 连成结构整体,只勾选其中部分行会让续片表头被切在半截上,
172
+ * 因此该开关作用于整个表头区(第 0 行起的连续 header 行)而非单行。
173
+ */
174
+ declare function setHeaderRepeat(rows: TableRow[], enabled: boolean): void;
169
175
  type BorderPreset = 'all' | 'outer' | 'inner' | 'none';
170
176
  /** 按预设批量写边框;none 时清空选区内所有格的 borders */
171
177
  declare function applyBorderPreset(rows: TableRow[], rect: CellRect, preset: BorderPreset, border: TableCellBorder): void;
@@ -316,7 +322,16 @@ declare const EXTENDED_FUNCTIONS: {
316
322
  FORMAT: (value: any) => string;
317
323
  SUBSTR: (str: string, start: number, len?: number) => string;
318
324
  LEN: (str: string) => number;
319
- ROUND: (num: number, decimals: number) => number;
325
+ ADD: typeof addNumbers;
326
+ SUB: typeof subtractNumbers;
327
+ MUL: typeof multiplyNumbers;
328
+ DIV: typeof divideNumbers;
329
+ ROUND: typeof round;
330
+ ROUNDUP: typeof roundUp;
331
+ CEIL: typeof roundUp;
332
+ ROUNDDOWN: typeof roundDown;
333
+ FLOOR: typeof roundDown;
334
+ ROUNDBANK: typeof roundHalfEven;
320
335
  NOW: () => string;
321
336
  IFEMPTY: (value: any, defaultVal: string) => string;
322
337
  PAD: (value: any, len: number, char: string) => string;
@@ -445,4 +460,4 @@ declare function useBindingDisplay(): {
445
460
  getBindingDisplayState: (element: any) => BindingDisplayState;
446
461
  };
447
462
 
448
- export { type AdsorbConfig, AdsorbResult, type AlignMode, BARCODE_BAR_HEIGHT_MODULES, BARCODE_MARGIN_BOTTOM_MODULES, BARCODE_MODULE_WIDTH_MM, BARCODE_QUIET_ZONE_MODULES, BARCODE_TEXT_FONT_SIZE_MODULES, type BarcodeSize, type BarcodeSizeInput, BindingDescriptor, type BorderPreset, type CellRect, DEFAULT_DEMO_DATA, ELEMENT_BINDING_REGISTRY, EXTENDED_FUNCTIONS, ElementOptions, ElementRect, ElementType, ElementZone, FIT_SCALE_MIN_PERCENT, type FieldGroup, GHOST_BORDER_CSS, type KeyboardHandlers, LABEL_PAPER_GROUP, MIN_COL_WIDTH_MM, MIN_SCALE_PERCENT, MM_PER_INCH, type ManualGuides, PAPER_PRESETS, PRESET_COLORS, PROPERTY_REGISTRY, type PaperPreset, PrintBusinessField, type PropertyGroupDef, type PropertyItem, RESIZE_POINTS, RULER_THICKNESS, type ResizeOptions, ResizePoint, type ResizeRect, type RulerTick, RuntimeElement, TableCell, TableCellBorder, TableRow, TableRowType, TemplateData, ZONE_ALLOWED_TYPES, type ZoneRectPt, applyBorderPreset, barcodeAvailableBoxMm, barcodePreferredModuleWidthMm, barcodeUnitsPerModule, buildRulerTicks, calcResizeRect, canMergeReason, chooseMajorStepMM, clampResizedColumnWidth, clampScalePercent, clampToZone, computeAdsorb, computeFitScale, createCell, createDefaultOptions, createDefaultTable, createFieldElement, createRuntimeElement, deleteCol, deleteRow, engine, evaluateTemplate, filterGroups, finalizeElementZone, findMainCell, generateGroupId, generateId, getDemoData, getElementBindings, getPaperDimensions, getPaperSizeMM, getSpanRect, getTableCellBindings, getZoneRects, groupFields, insertCol, insertRow, isContinuousPaperSize, isLabelPaperSize, labelPaperDefaults, matchKeywords, mergeCells, minorStepMM, mmToPx, nextWheelScale, normalizeSelection, normalizeTemplateUnits, ptToMm, pxToMm, resolveBarcodeDesignValue, resolveBarcodeSize, resolveCellBorderCss, resolveTextBinding, safeEval, searchProperties, setRowType, splitCells, syncTableElementSize, useAlign, useBindingDisplay, useGroup, useKeyboard, useResize, zoneFromPaperPoint };
463
+ export { type AdsorbConfig, AdsorbResult, type AlignMode, BARCODE_BAR_HEIGHT_MODULES, BARCODE_MARGIN_BOTTOM_MODULES, BARCODE_MODULE_WIDTH_MM, BARCODE_QUIET_ZONE_MODULES, BARCODE_TEXT_FONT_SIZE_MODULES, type BarcodeSize, type BarcodeSizeInput, BindingDescriptor, type BorderPreset, type CellRect, DEFAULT_DEMO_DATA, ELEMENT_BINDING_REGISTRY, EXTENDED_FUNCTIONS, ElementOptions, ElementRect, ElementType, ElementZone, FIT_SCALE_MIN_PERCENT, type FieldGroup, GHOST_BORDER_CSS, type KeyboardHandlers, LABEL_PAPER_GROUP, MIN_COL_WIDTH_MM, MIN_SCALE_PERCENT, MM_PER_INCH, type ManualGuides, PAPER_PRESETS, PRESET_COLORS, PROPERTY_REGISTRY, type PaperPreset, PrintBusinessField, type PropertyGroupDef, type PropertyItem, RESIZE_POINTS, RULER_THICKNESS, type ResizeOptions, ResizePoint, type ResizeRect, type RulerTick, RuntimeElement, TableCell, TableCellBorder, TableRow, TableRowType, TemplateData, ZONE_ALLOWED_TYPES, type ZoneRectPt, applyBorderPreset, barcodeAvailableBoxMm, barcodePreferredModuleWidthMm, barcodeUnitsPerModule, buildRulerTicks, calcResizeRect, canMergeReason, chooseMajorStepMM, clampResizedColumnWidth, clampScalePercent, clampToZone, computeAdsorb, computeFitScale, createCell, createDefaultOptions, createDefaultTable, createFieldElement, createRuntimeElement, deleteCol, deleteRow, engine, evaluateTemplate, filterGroups, finalizeElementZone, findMainCell, generateGroupId, generateId, getDemoData, getElementBindings, getPaperDimensions, getPaperSizeMM, getSpanRect, getTableCellBindings, getZoneRects, groupFields, insertCol, insertRow, isContinuousPaperSize, isLabelPaperSize, labelPaperDefaults, matchKeywords, mergeCells, minorStepMM, mmToPx, nextWheelScale, normalizeSelection, normalizeTemplateUnits, ptToMm, pxToMm, resolveBarcodeDesignValue, resolveBarcodeSize, resolveCellBorderCss, resolveTextBinding, safeEval, searchProperties, setHeaderRepeat, setRowType, splitCells, syncTableElementSize, useAlign, useBindingDisplay, useGroup, useKeyboard, useResize, zoneFromPaperPoint };
@@ -1,7 +1,7 @@
1
- import { b as TemplateData, c as TableRow, d as TableCellBorder, e as TableCell, f as TableRowType, E as ElementOptions, g as ElementType, h as RuntimeElement, i as ElementZone, j as PrintBusinessField, B as BindingDescriptor, k as ElementRect, A as AdsorbResult, l as ResizePoint } from '../driver-CsWkGK4a.js';
2
- export { m as AlignLine, n as DesignBackground, o as ElementFieldBinding, p as MultiPageTemplateData, q as PaginationConfig, r as PaperSize, s as PrintElementData, t as PrintElementTypeMeta, u as RequestScreenshotFn, S as ScreenshotRequest, v as TableCellBorders, w as TableCellType, x as TablePaginationConfig, y as TableSelection, z as TemplateElement, F as TextAlign, G as TextFit, U as UploadDesignBackgroundFn, H as UploadImageFn, V as VerticalAlign, W as WatermarkOptions } from '../driver-CsWkGK4a.js';
3
- export { C as CellFitRowKind, D as DEFAULT_SHRINK_MIN_FONT_SIZE_PT, F as FitFontSize, M as MIN_SHRINK_FONT_SIZE_PT, c as cellFitCapMm, a as cellFitKey, b as cellFitWidthMm, f as floorFontSize, p as parseCellFitKey, r as resolveCellTextFit, d as resolveElementTextFit, e as resolveShrinkMinFontSize, g as roundFontSize } from '../ports-B63x7kCX.js';
4
- import { TemplateEngine, formatMoney, ifFn, formatDate, toUpperCaseAmount } from '../index.js';
1
+ import { b as TemplateData, c as TableRow, d as TableCellBorder, e as TableCell, f as TableRowType, E as ElementOptions, g as ElementType, h as RuntimeElement, i as ElementZone, j as PrintBusinessField, B as BindingDescriptor, k as ElementRect, A as AdsorbResult, l as ResizePoint } from '../driver-Dn_YAzO5.js';
2
+ export { m as AlignLine, n as DesignBackground, o as ElementFieldBinding, p as MultiPageTemplateData, q as PaginationConfig, r as PaperSize, s as PrintElementData, t as PrintElementTypeMeta, u as RequestScreenshotFn, S as ScreenshotRequest, v as TableCellBorders, w as TableCellType, x as TablePaginationConfig, y as TableSelection, z as TemplateElement, F as TextAlign, G as TextFit, U as UploadDesignBackgroundFn, H as UploadImageFn, V as VerticalAlign, W as WatermarkOptions } from '../driver-Dn_YAzO5.js';
3
+ export { C as CellFitRowKind, D as DEFAULT_SHRINK_MIN_FONT_SIZE_PT, F as FitFontSize, M as MIN_SHRINK_FONT_SIZE_PT, c as cellFitCapMm, a as cellFitKey, b as cellFitWidthMm, f as floorFontSize, p as parseCellFitKey, r as resolveCellTextFit, d as resolveElementTextFit, e as resolveShrinkMinFontSize, g as roundFontSize } from '../ports-BvwlA_km.js';
4
+ import { TemplateEngine, formatMoney, ifFn, addNumbers, subtractNumbers, multiplyNumbers, divideNumbers, round, roundUp, roundDown, roundHalfEven, formatDate, toUpperCaseAmount } from '../index.js';
5
5
 
6
6
  /** 1 英寸 = 25.4mm */
7
7
  declare const MM_PER_INCH = 25.4;
@@ -166,6 +166,12 @@ declare function insertCol(rows: TableRow[], colWidths: number[], index: number,
166
166
  declare function deleteCol(rows: TableRow[], colWidths: number[], index: number): string | null;
167
167
  /** 行类型变更校验 + 写入。约束:header 顶部连续;data 唯一;subtotal/summary 在 data 之后;subtotal 在 summary 之前 */
168
168
  declare function setRowType(rows: TableRow[], index: number, type: TableRowType): string | null;
169
+ /**
170
+ * 统一设置表头区的「每页顶部重复」。
171
+ * 多级表头的各行由 rowspan/colspan 连成结构整体,只勾选其中部分行会让续片表头被切在半截上,
172
+ * 因此该开关作用于整个表头区(第 0 行起的连续 header 行)而非单行。
173
+ */
174
+ declare function setHeaderRepeat(rows: TableRow[], enabled: boolean): void;
169
175
  type BorderPreset = 'all' | 'outer' | 'inner' | 'none';
170
176
  /** 按预设批量写边框;none 时清空选区内所有格的 borders */
171
177
  declare function applyBorderPreset(rows: TableRow[], rect: CellRect, preset: BorderPreset, border: TableCellBorder): void;
@@ -316,7 +322,16 @@ declare const EXTENDED_FUNCTIONS: {
316
322
  FORMAT: (value: any) => string;
317
323
  SUBSTR: (str: string, start: number, len?: number) => string;
318
324
  LEN: (str: string) => number;
319
- ROUND: (num: number, decimals: number) => number;
325
+ ADD: typeof addNumbers;
326
+ SUB: typeof subtractNumbers;
327
+ MUL: typeof multiplyNumbers;
328
+ DIV: typeof divideNumbers;
329
+ ROUND: typeof round;
330
+ ROUNDUP: typeof roundUp;
331
+ CEIL: typeof roundUp;
332
+ ROUNDDOWN: typeof roundDown;
333
+ FLOOR: typeof roundDown;
334
+ ROUNDBANK: typeof roundHalfEven;
320
335
  NOW: () => string;
321
336
  IFEMPTY: (value: any, defaultVal: string) => string;
322
337
  PAD: (value: any, len: number, char: string) => string;
@@ -445,4 +460,4 @@ declare function useBindingDisplay(): {
445
460
  getBindingDisplayState: (element: any) => BindingDisplayState;
446
461
  };
447
462
 
448
- export { type AdsorbConfig, AdsorbResult, type AlignMode, BARCODE_BAR_HEIGHT_MODULES, BARCODE_MARGIN_BOTTOM_MODULES, BARCODE_MODULE_WIDTH_MM, BARCODE_QUIET_ZONE_MODULES, BARCODE_TEXT_FONT_SIZE_MODULES, type BarcodeSize, type BarcodeSizeInput, BindingDescriptor, type BorderPreset, type CellRect, DEFAULT_DEMO_DATA, ELEMENT_BINDING_REGISTRY, EXTENDED_FUNCTIONS, ElementOptions, ElementRect, ElementType, ElementZone, FIT_SCALE_MIN_PERCENT, type FieldGroup, GHOST_BORDER_CSS, type KeyboardHandlers, LABEL_PAPER_GROUP, MIN_COL_WIDTH_MM, MIN_SCALE_PERCENT, MM_PER_INCH, type ManualGuides, PAPER_PRESETS, PRESET_COLORS, PROPERTY_REGISTRY, type PaperPreset, PrintBusinessField, type PropertyGroupDef, type PropertyItem, RESIZE_POINTS, RULER_THICKNESS, type ResizeOptions, ResizePoint, type ResizeRect, type RulerTick, RuntimeElement, TableCell, TableCellBorder, TableRow, TableRowType, TemplateData, ZONE_ALLOWED_TYPES, type ZoneRectPt, applyBorderPreset, barcodeAvailableBoxMm, barcodePreferredModuleWidthMm, barcodeUnitsPerModule, buildRulerTicks, calcResizeRect, canMergeReason, chooseMajorStepMM, clampResizedColumnWidth, clampScalePercent, clampToZone, computeAdsorb, computeFitScale, createCell, createDefaultOptions, createDefaultTable, createFieldElement, createRuntimeElement, deleteCol, deleteRow, engine, evaluateTemplate, filterGroups, finalizeElementZone, findMainCell, generateGroupId, generateId, getDemoData, getElementBindings, getPaperDimensions, getPaperSizeMM, getSpanRect, getTableCellBindings, getZoneRects, groupFields, insertCol, insertRow, isContinuousPaperSize, isLabelPaperSize, labelPaperDefaults, matchKeywords, mergeCells, minorStepMM, mmToPx, nextWheelScale, normalizeSelection, normalizeTemplateUnits, ptToMm, pxToMm, resolveBarcodeDesignValue, resolveBarcodeSize, resolveCellBorderCss, resolveTextBinding, safeEval, searchProperties, setRowType, splitCells, syncTableElementSize, useAlign, useBindingDisplay, useGroup, useKeyboard, useResize, zoneFromPaperPoint };
463
+ export { type AdsorbConfig, AdsorbResult, type AlignMode, BARCODE_BAR_HEIGHT_MODULES, BARCODE_MARGIN_BOTTOM_MODULES, BARCODE_MODULE_WIDTH_MM, BARCODE_QUIET_ZONE_MODULES, BARCODE_TEXT_FONT_SIZE_MODULES, type BarcodeSize, type BarcodeSizeInput, BindingDescriptor, type BorderPreset, type CellRect, DEFAULT_DEMO_DATA, ELEMENT_BINDING_REGISTRY, EXTENDED_FUNCTIONS, ElementOptions, ElementRect, ElementType, ElementZone, FIT_SCALE_MIN_PERCENT, type FieldGroup, GHOST_BORDER_CSS, type KeyboardHandlers, LABEL_PAPER_GROUP, MIN_COL_WIDTH_MM, MIN_SCALE_PERCENT, MM_PER_INCH, type ManualGuides, PAPER_PRESETS, PRESET_COLORS, PROPERTY_REGISTRY, type PaperPreset, PrintBusinessField, type PropertyGroupDef, type PropertyItem, RESIZE_POINTS, RULER_THICKNESS, type ResizeOptions, ResizePoint, type ResizeRect, type RulerTick, RuntimeElement, TableCell, TableCellBorder, TableRow, TableRowType, TemplateData, ZONE_ALLOWED_TYPES, type ZoneRectPt, applyBorderPreset, barcodeAvailableBoxMm, barcodePreferredModuleWidthMm, barcodeUnitsPerModule, buildRulerTicks, calcResizeRect, canMergeReason, chooseMajorStepMM, clampResizedColumnWidth, clampScalePercent, clampToZone, computeAdsorb, computeFitScale, createCell, createDefaultOptions, createDefaultTable, createFieldElement, createRuntimeElement, deleteCol, deleteRow, engine, evaluateTemplate, filterGroups, finalizeElementZone, findMainCell, generateGroupId, generateId, getDemoData, getElementBindings, getPaperDimensions, getPaperSizeMM, getSpanRect, getTableCellBindings, getZoneRects, groupFields, insertCol, insertRow, isContinuousPaperSize, isLabelPaperSize, labelPaperDefaults, matchKeywords, mergeCells, minorStepMM, mmToPx, nextWheelScale, normalizeSelection, normalizeTemplateUnits, ptToMm, pxToMm, resolveBarcodeDesignValue, resolveBarcodeSize, resolveCellBorderCss, resolveTextBinding, safeEval, searchProperties, setHeaderRepeat, setRowType, splitCells, syncTableElementSize, useAlign, useBindingDisplay, useGroup, useKeyboard, useResize, zoneFromPaperPoint };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TemplateEngine
3
- } from "../chunk-V4A5623C.js";
3
+ } from "../chunk-AKWV6SID.js";
4
4
  import {
5
5
  BARCODE_BAR_HEIGHT_MODULES,
6
6
  BARCODE_MARGIN_BOTTOM_MODULES,
@@ -10,6 +10,7 @@ import {
10
10
  DEFAULT_SHRINK_MIN_FONT_SIZE_PT,
11
11
  MIN_SHRINK_FONT_SIZE_PT,
12
12
  MM_PER_INCH,
13
+ addNumbers,
13
14
  avg,
14
15
  barcodeAvailableBoxMm,
15
16
  barcodePreferredModuleWidthMm,
@@ -18,6 +19,7 @@ import {
18
19
  cellFitKey,
19
20
  cellFitWidthMm,
20
21
  count,
22
+ divideNumbers,
21
23
  floorFontSize,
22
24
  formatDate,
23
25
  formatMoney,
@@ -25,6 +27,7 @@ import {
25
27
  max,
26
28
  min,
27
29
  mmToPx,
30
+ multiplyNumbers,
28
31
  parseCellFitKey,
29
32
  ptToMm,
30
33
  pxToMm,
@@ -32,10 +35,15 @@ import {
32
35
  resolveCellTextFit,
33
36
  resolveElementTextFit,
34
37
  resolveShrinkMinFontSize,
38
+ round,
39
+ roundDown,
35
40
  roundFontSize,
41
+ roundHalfEven,
42
+ roundUp,
43
+ subtractNumbers,
36
44
  sum,
37
45
  toUpperCaseAmount
38
- } from "../chunk-VFEURLAZ.js";
46
+ } from "../chunk-KFRPTLFV.js";
39
47
 
40
48
  // src/designer/utils/scale.ts
41
49
  var MIN_SCALE_PERCENT = 25;
@@ -329,6 +337,8 @@ function insertRow(rows, index, position) {
329
337
  id: `row-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
330
338
  type,
331
339
  height: ref.height,
340
+ // 新表头行沿用相邻表头行的重复设置:漏勾会让续片只重复部分表头,多级表头结构断裂
341
+ ...type === "header" ? { repeatOnPage: [rows[insertAt - 1], rows[insertAt]].find((r) => r?.type === "header")?.repeatOnPage === true } : {},
332
342
  cells: Array.from({ length: colCount }, () => createCell())
333
343
  };
334
344
  for (let c = 0; c < colCount; c++) {
@@ -452,11 +462,20 @@ function setRowType(rows, index, type) {
452
462
  }
453
463
  }
454
464
  rows[index].type = type;
455
- if (type !== "header") {
465
+ if (type === "header") {
466
+ const neighbour = [rows[index - 1], rows[index + 1]].find((r) => r?.type === "header");
467
+ rows[index].repeatOnPage = rows[index].repeatOnPage !== void 0 ? rows[index].repeatOnPage === true : neighbour?.repeatOnPage === true;
468
+ } else {
456
469
  delete rows[index].repeatOnPage;
457
470
  }
458
471
  return null;
459
472
  }
473
+ function setHeaderRepeat(rows, enabled) {
474
+ for (const row of rows) {
475
+ if (row.type !== "header") break;
476
+ row.repeatOnPage = enabled;
477
+ }
478
+ }
460
479
  function applyBorderPreset(rows, rect, preset, border) {
461
480
  for (let r = rect.r1; r <= rect.r2; r++) {
462
481
  for (let c = rect.c1; c <= rect.c2; c++) {
@@ -1038,7 +1057,18 @@ var EXTENDED_FUNCTIONS = {
1038
1057
  },
1039
1058
  SUBSTR: (str, start, len) => String(str).slice(start, len ? start + len : void 0),
1040
1059
  LEN: (str) => String(str).length,
1041
- ROUND: (num, decimals) => Number(Number(num).toFixed(decimals)),
1060
+ // 四则运算(与 +/-/*// 运算符同一套数值语义,供不方便写运算符的场景使用)
1061
+ ADD: addNumbers,
1062
+ SUB: subtractNumbers,
1063
+ MUL: multiplyNumbers,
1064
+ DIV: divideNumbers,
1065
+ // 数值修约
1066
+ ROUND: round,
1067
+ ROUNDUP: roundUp,
1068
+ CEIL: roundUp,
1069
+ ROUNDDOWN: roundDown,
1070
+ FLOOR: roundDown,
1071
+ ROUNDBANK: roundHalfEven,
1042
1072
  NOW: () => (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
1043
1073
  IFEMPTY: (value, defaultVal) => value != null && value !== "" ? String(value) : defaultVal,
1044
1074
  PAD: (value, len, char) => String(value).padStart(len, char),
@@ -1698,6 +1728,7 @@ export {
1698
1728
  roundFontSize,
1699
1729
  safeEval,
1700
1730
  searchProperties,
1731
+ setHeaderRepeat,
1701
1732
  setRowType,
1702
1733
  splitCells,
1703
1734
  syncTableElementSize,
@@ -644,7 +644,10 @@ interface RenderCellBorders {
644
644
  /** 绑定后的单元格:content 已是最终文本(小计行 content 为占位值,真实值按每页数据渲染时求值) */
645
645
  interface RenderCell {
646
646
  content: string;
647
- /** 小计行专用:单元格原始 formatter 表达式,渲染阶段以当前页数据行上下文求值 */
647
+ /**
648
+ * 单元格原始 formatter 表达式,渲染阶段重新求值:
649
+ * 小计行以当前页数据行上下文求值;引用了页码(pageIndex / totalPages)的单元格按该页页码求值。
650
+ */
648
651
  rawFormatter?: string;
649
652
  /** 内容类型:text(默认)/ barcode / qrcode / image,非 text 时 content 为码值或图片 URL */
650
653
  cellType?: string;
@@ -644,7 +644,10 @@ interface RenderCellBorders {
644
644
  /** 绑定后的单元格:content 已是最终文本(小计行 content 为占位值,真实值按每页数据渲染时求值) */
645
645
  interface RenderCell {
646
646
  content: string;
647
- /** 小计行专用:单元格原始 formatter 表达式,渲染阶段以当前页数据行上下文求值 */
647
+ /**
648
+ * 单元格原始 formatter 表达式,渲染阶段重新求值:
649
+ * 小计行以当前页数据行上下文求值;引用了页码(pageIndex / totalPages)的单元格按该页页码求值。
650
+ */
648
651
  rawFormatter?: string;
649
652
  /** 内容类型:text(默认)/ barcode / qrcode / image,非 text 时 content 为码值或图片 URL */
650
653
  cellType?: string;