@worm-vue3-print/core 1.2.1 → 1.3.0

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.
@@ -0,0 +1,1710 @@
1
+ import {
2
+ TemplateEngine
3
+ } from "../chunk-V4A5623C.js";
4
+ import {
5
+ BARCODE_BAR_HEIGHT_MODULES,
6
+ BARCODE_MARGIN_BOTTOM_MODULES,
7
+ BARCODE_MODULE_WIDTH_MM,
8
+ BARCODE_QUIET_ZONE_MODULES,
9
+ BARCODE_TEXT_FONT_SIZE_MODULES,
10
+ DEFAULT_SHRINK_MIN_FONT_SIZE_PT,
11
+ MIN_SHRINK_FONT_SIZE_PT,
12
+ MM_PER_INCH,
13
+ avg,
14
+ barcodeAvailableBoxMm,
15
+ barcodePreferredModuleWidthMm,
16
+ barcodeUnitsPerModule,
17
+ cellFitCapMm,
18
+ cellFitKey,
19
+ cellFitWidthMm,
20
+ count,
21
+ floorFontSize,
22
+ formatDate,
23
+ formatMoney,
24
+ ifFn,
25
+ max,
26
+ min,
27
+ mmToPx,
28
+ parseCellFitKey,
29
+ ptToMm,
30
+ pxToMm,
31
+ resolveBarcodeSize,
32
+ resolveCellTextFit,
33
+ resolveElementTextFit,
34
+ resolveShrinkMinFontSize,
35
+ roundFontSize,
36
+ sum,
37
+ toUpperCaseAmount
38
+ } from "../chunk-VFEURLAZ.js";
39
+
40
+ // src/designer/utils/scale.ts
41
+ var MIN_SCALE_PERCENT = 25;
42
+ var FIT_SCALE_MIN_PERCENT = 5;
43
+ function clampScalePercent(percent) {
44
+ return Math.max(MIN_SCALE_PERCENT, percent);
45
+ }
46
+ function nextWheelScale(percent, direction, factor = 1.1) {
47
+ const next = direction > 0 ? percent * factor : percent / factor;
48
+ return clampScalePercent(Math.round(next));
49
+ }
50
+ function computeFitScale(containerW, containerH, paperW, paperH, ratio = 0.9) {
51
+ if (paperW <= 0 || paperH <= 0) return 100;
52
+ const fit = Math.min(containerW / paperW, containerH / paperH) * ratio;
53
+ const pct = Math.round(fit * 100);
54
+ return Math.max(FIT_SCALE_MIN_PERCENT, pct);
55
+ }
56
+
57
+ // src/designer/utils/migrate.ts
58
+ var PT_KEYS = ["left", "top", "width", "height"];
59
+ function convertElement(el) {
60
+ const options = { ...el.options };
61
+ for (const key of PT_KEYS) {
62
+ const v = options[key];
63
+ if (typeof v === "number" && Number.isFinite(v)) {
64
+ ;
65
+ options[key] = round2(ptToMm(v));
66
+ }
67
+ }
68
+ return { ...el, options };
69
+ }
70
+ function round2(n) {
71
+ return Math.round(n * 100) / 100;
72
+ }
73
+ function normalizeTemplateUnits(data) {
74
+ if (data.unit === "mm") return data;
75
+ const convert = (els) => els?.map(convertElement) ?? [];
76
+ return {
77
+ ...data,
78
+ unit: "mm",
79
+ elements: convert(data.elements),
80
+ header: data.header ? { ...data.header, elements: convert(data.header.elements) } : data.header,
81
+ footer: data.footer ? { ...data.footer, elements: convert(data.footer.elements) } : data.footer,
82
+ firstPageOverlay: data.firstPageOverlay ? { ...data.firstPageOverlay, elements: convert(data.firstPageOverlay.elements) } : data.firstPageOverlay
83
+ };
84
+ }
85
+
86
+ // src/designer/utils/default-config.ts
87
+ var LABEL_PAPER_GROUP = "\u6807\u7B7E\u7EB8";
88
+ var PAPER_PRESETS = {
89
+ A4: { width: 210, height: 297, label: "A4 210\xD7297mm", group: "\u5E38\u7528\u7EB8\u5F20" },
90
+ A3: { width: 297, height: 420, label: "A3 297\xD7420mm", group: "\u5E38\u7528\u7EB8\u5F20" },
91
+ A5: { width: 148, height: 210, label: "A5 148\xD7210mm", group: "\u5E38\u7528\u7EB8\u5F20" },
92
+ Letter: { width: 216, height: 279, label: "Letter 216\xD7279mm", group: "\u5E38\u7528\u7EB8\u5F20" },
93
+ Legal: { width: 216, height: 356, label: "Legal 216\xD7356mm", group: "\u5E38\u7528\u7EB8\u5F20" },
94
+ // 针式打印纸(241 系列等分):11 英寸整张 279.4mm 按等分取整
95
+ DOT_FULL: { width: 241, height: 279.4, label: "\u5168\u7B49\u5206 241\xD7279.4mm", group: "\u9488\u5F0F\u6253\u5370\u7EB8" },
96
+ DOT_HALF: { width: 241, height: 139.7, label: "\u4E8C\u7B49\u5206 241\xD7139.7mm", group: "\u9488\u5F0F\u6253\u5370\u7EB8" },
97
+ DOT_THIRD: { width: 241, height: 93.1, label: "\u4E09\u7B49\u5206 241\xD793.1mm", group: "\u9488\u5F0F\u6253\u5370\u7EB8" },
98
+ // 标签纸
99
+ LABEL_80X60: { width: 80, height: 60, label: "80\xD760mm", group: LABEL_PAPER_GROUP },
100
+ LABEL_60X40: { width: 60, height: 40, label: "60\xD740mm", group: LABEL_PAPER_GROUP },
101
+ LABEL_40X30: { width: 40, height: 30, label: "40\xD730mm", group: LABEL_PAPER_GROUP },
102
+ // 小票纸(热敏卷纸):高度仅为设计画布高度,出纸按内容推导
103
+ THERMAL_57: { width: 57, height: 297, continuous: true, label: "57mm", group: "\u5C0F\u7968\u7EB8" },
104
+ THERMAL_80: { width: 80, height: 297, continuous: true, label: "80mm", group: "\u5C0F\u7968\u7EB8" },
105
+ THERMAL_110: { width: 110, height: 297, continuous: true, label: "110mm", group: "\u5C0F\u7968\u7EB8" },
106
+ // 连续纸:默认 80mm 热敏;高度仅为设计画布高度,出纸按内容推导
107
+ CONTINUOUS: { width: 80, height: 297, continuous: true, label: "\u8FDE\u7EED\u7EB8\uFF08\u81EA\u5B9A\u4E49\u5BBD\u5EA6\uFF09", group: "\u8FDE\u7EED\u7EB8" }
108
+ };
109
+ function isContinuousPaperSize(paperSize) {
110
+ return !!paperSize && PAPER_PRESETS[paperSize]?.continuous === true;
111
+ }
112
+ function isLabelPaperSize(paperSize) {
113
+ return !!paperSize && PAPER_PRESETS[paperSize]?.group === LABEL_PAPER_GROUP;
114
+ }
115
+ function labelPaperDefaults(t) {
116
+ return {
117
+ margins: { top: 0, right: 0, bottom: 0, left: 0 },
118
+ header: { ...t.header, height: 0 },
119
+ footer: { ...t.footer, height: 0 }
120
+ };
121
+ }
122
+ function getPaperDimensions(t) {
123
+ const continuous = isContinuousPaperSize(t.paperSize);
124
+ let base;
125
+ if (t.paperSize === "CUSTOM" || continuous) {
126
+ const preset = PAPER_PRESETS[t.paperSize];
127
+ base = {
128
+ width: t.customWidth ?? preset?.width ?? 210,
129
+ height: t.customHeight ?? preset?.height ?? 297
130
+ };
131
+ } else {
132
+ const preset = PAPER_PRESETS[t.paperSize] || PAPER_PRESETS["A4"];
133
+ base = { width: preset.width, height: preset.height };
134
+ }
135
+ return t.orientation === "landscape" && !continuous ? { width: base.height, height: base.width } : base;
136
+ }
137
+
138
+ // src/designer/utils/table-matrix.ts
139
+ function createCellIdGenerator() {
140
+ let seq = 0;
141
+ return function genCellId2() {
142
+ seq += 1;
143
+ return `cell-${Date.now().toString(36)}-${seq}`;
144
+ };
145
+ }
146
+ function createRowIdGenerator() {
147
+ let seq = 0;
148
+ return function genRowId2() {
149
+ seq += 1;
150
+ return `row-${Date.now().toString(36)}-${seq}`;
151
+ };
152
+ }
153
+ var genCellId = createCellIdGenerator();
154
+ var genRowId = createRowIdGenerator();
155
+ var DEFAULT_BORDER = { width: 0.75, style: "solid", color: "#333333" };
156
+ function deepClone(v) {
157
+ return JSON.parse(JSON.stringify(v));
158
+ }
159
+ function createCell(partial) {
160
+ return {
161
+ id: genCellId(),
162
+ borders: {
163
+ top: { ...DEFAULT_BORDER },
164
+ right: { ...DEFAULT_BORDER },
165
+ bottom: { ...DEFAULT_BORDER },
166
+ left: { ...DEFAULT_BORDER }
167
+ },
168
+ ...partial
169
+ };
170
+ }
171
+ function createDefaultTable() {
172
+ const colWidths = [50, 50, 50];
173
+ const mk = (type, repeat) => ({
174
+ id: genRowId(),
175
+ type,
176
+ height: 8,
177
+ ...repeat !== void 0 ? { repeatOnPage: repeat } : {},
178
+ cells: colWidths.map(() => createCell())
179
+ });
180
+ const header = mk("header", true);
181
+ header.cells.forEach((c, i) => {
182
+ c.formatter = `\u5217${i + 1}`;
183
+ c.align = "center";
184
+ c.fontWeight = "bold";
185
+ });
186
+ return { rows: [header, mk("data"), mk("subtotal"), mk("summary")], colWidths };
187
+ }
188
+ function findMainCell(rows, r, c) {
189
+ if (!rows[r].cells[c].merged) {
190
+ return { r, c };
191
+ }
192
+ for (let ri = r; ri >= 0; ri--) {
193
+ for (let ci = c; ci >= 0; ci--) {
194
+ const cell = rows[ri].cells[ci];
195
+ if (cell.merged) {
196
+ continue;
197
+ }
198
+ const rs = cell.rowspan ?? 1;
199
+ const cs = cell.colspan ?? 1;
200
+ if (ri + rs - 1 >= r && ci + cs - 1 >= c) {
201
+ return { r: ri, c: ci };
202
+ }
203
+ }
204
+ }
205
+ return { r, c };
206
+ }
207
+ function getSpanRect(rows, r, c) {
208
+ const main = findMainCell(rows, r, c);
209
+ const cell = rows[main.r].cells[main.c];
210
+ return {
211
+ r1: main.r,
212
+ c1: main.c,
213
+ r2: main.r + (cell.rowspan ?? 1) - 1,
214
+ c2: main.c + (cell.colspan ?? 1) - 1
215
+ };
216
+ }
217
+ function normalizeSelection(rows, a, b) {
218
+ let rect = {
219
+ r1: Math.min(a.r, b.r),
220
+ c1: Math.min(a.c, b.c),
221
+ r2: Math.max(a.r, b.r),
222
+ c2: Math.max(a.c, b.c)
223
+ };
224
+ for (; ; ) {
225
+ let changed = false;
226
+ for (let r = rect.r1; r <= rect.r2; r++) {
227
+ for (let c = rect.c1; c <= rect.c2; c++) {
228
+ const span = getSpanRect(rows, r, c);
229
+ const merged = {
230
+ r1: Math.min(rect.r1, span.r1),
231
+ c1: Math.min(rect.c1, span.c1),
232
+ r2: Math.max(rect.r2, span.r2),
233
+ c2: Math.max(rect.c2, span.c2)
234
+ };
235
+ if (merged.r1 !== rect.r1 || merged.c1 !== rect.c1 || merged.r2 !== rect.r2 || merged.c2 !== rect.c2) {
236
+ rect = merged;
237
+ changed = true;
238
+ }
239
+ }
240
+ }
241
+ if (!changed) {
242
+ return rect;
243
+ }
244
+ }
245
+ }
246
+ function canMergeReason(rows, rect) {
247
+ const cellCount = (rect.r2 - rect.r1 + 1) * (rect.c2 - rect.c1 + 1);
248
+ if (cellCount < 2) {
249
+ return "\u81F3\u5C11\u9009\u62E9 2 \u4E2A\u5355\u5143\u683C";
250
+ }
251
+ const types = /* @__PURE__ */ new Set();
252
+ for (let r = rect.r1; r <= rect.r2; r++) {
253
+ types.add(rows[r].type);
254
+ }
255
+ if (types.size > 1) {
256
+ return "\u5408\u5E76\u533A\u57DF\u4E0D\u80FD\u8DE8\u8D8A\u4E0D\u540C\u7C7B\u578B\u7684\u884C";
257
+ }
258
+ const norm = normalizeSelection(rows, { r: rect.r1, c: rect.c1 }, { r: rect.r2, c: rect.c2 });
259
+ if (norm.r1 !== rect.r1 || norm.c1 !== rect.c1 || norm.r2 !== rect.r2 || norm.c2 !== rect.c2) {
260
+ return "\u9009\u533A\u4E0E\u5DF2\u6709\u5408\u5E76\u533A\u57DF\u90E8\u5206\u91CD\u53E0";
261
+ }
262
+ return null;
263
+ }
264
+ function mergeCells(rows, rect) {
265
+ const main = rows[rect.r1].cells[rect.c1];
266
+ const contents = [];
267
+ for (let r = rect.r1; r <= rect.r2; r++) {
268
+ for (let c = rect.c1; c <= rect.c2; c++) {
269
+ const cell = rows[r].cells[c];
270
+ if (cell.formatter) {
271
+ contents.push(cell.formatter);
272
+ }
273
+ if (r === rect.r1 && c === rect.c1) {
274
+ continue;
275
+ }
276
+ cell.merged = true;
277
+ cell.rowspan = 1;
278
+ cell.colspan = 1;
279
+ cell.formatter = "";
280
+ }
281
+ }
282
+ main.rowspan = rect.r2 - rect.r1 + 1;
283
+ main.colspan = rect.c2 - rect.c1 + 1;
284
+ main.formatter = contents.join(" ");
285
+ }
286
+ function splitCells(rows, rect) {
287
+ for (let r = rect.r1; r <= rect.r2; r++) {
288
+ for (let c = rect.c1; c <= rect.c2; c++) {
289
+ const cell = rows[r].cells[c];
290
+ if (!cell.merged && ((cell.rowspan ?? 1) > 1 || (cell.colspan ?? 1) > 1)) {
291
+ const span = getSpanRect(rows, r, c);
292
+ const styleSrc = cell;
293
+ for (let sr = span.r1; sr <= span.r2; sr++) {
294
+ for (let sc = span.c1; sc <= span.c2; sc++) {
295
+ const t = rows[sr].cells[sc];
296
+ t.merged = false;
297
+ t.rowspan = 1;
298
+ t.colspan = 1;
299
+ copyCellStyle(styleSrc, t);
300
+ }
301
+ }
302
+ cell.rowspan = 1;
303
+ cell.colspan = 1;
304
+ }
305
+ }
306
+ }
307
+ }
308
+ function copyCellStyle(src, dst) {
309
+ if (src === dst) {
310
+ return;
311
+ }
312
+ dst.align = src.align;
313
+ dst.valign = src.valign;
314
+ dst.fontSize = src.fontSize;
315
+ dst.fontFamily = src.fontFamily;
316
+ dst.fontWeight = src.fontWeight;
317
+ dst.color = src.color;
318
+ dst.backgroundColor = src.backgroundColor;
319
+ dst.borders = src.borders ? deepClone(src.borders) : void 0;
320
+ dst.padding = src.padding;
321
+ dst.wordWrap = src.wordWrap;
322
+ }
323
+ function insertRow(rows, index, position) {
324
+ const insertAt = position === "above" ? index : index + 1;
325
+ const ref = rows[index];
326
+ const colCount = ref.cells.length;
327
+ const type = ref.type === "data" ? "header" : ref.type;
328
+ const newRow = {
329
+ id: `row-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
330
+ type,
331
+ height: ref.height,
332
+ cells: Array.from({ length: colCount }, () => createCell())
333
+ };
334
+ for (let c = 0; c < colCount; c++) {
335
+ if (insertAt > 0 && insertAt < rows.length) {
336
+ const span = getSpanRect(rows, insertAt - 1, c);
337
+ if (span.r1 < insertAt && span.r2 >= insertAt) {
338
+ const main = rows[span.r1].cells[span.c1];
339
+ main.rowspan = (main.rowspan ?? 1) + 1;
340
+ newRow.cells[c].merged = true;
341
+ }
342
+ }
343
+ }
344
+ rows.splice(insertAt, 0, newRow);
345
+ }
346
+ function deleteRow(rows, index) {
347
+ if (rows.length <= 1) {
348
+ return "\u4E0D\u80FD\u5220\u9664\u6700\u540E\u4E00\u884C";
349
+ }
350
+ const row = rows[index];
351
+ const colCount = row.cells.length;
352
+ for (let c = 0; c < colCount; c++) {
353
+ const cell = row.cells[c];
354
+ if (!cell.merged && (cell.rowspan ?? 1) > 1) {
355
+ const next = rows[index + 1].cells[c];
356
+ Object.assign(next, deepClone(cell), { id: next.id });
357
+ next.rowspan = (cell.rowspan ?? 1) - 1;
358
+ next.merged = false;
359
+ } else if (cell.merged) {
360
+ const span = getSpanRect(rows, index, c);
361
+ if (span.r1 < index && span.c1 === c) {
362
+ const main = rows[span.r1].cells[span.c1];
363
+ main.rowspan = (main.rowspan ?? 1) - 1;
364
+ }
365
+ }
366
+ }
367
+ rows.splice(index, 1);
368
+ return null;
369
+ }
370
+ function insertCol(rows, colWidths, index, position) {
371
+ const insertAt = position === "left" ? index : index + 1;
372
+ colWidths.splice(insertAt, 0, colWidths[index]);
373
+ for (const row of rows) {
374
+ const newCell = createCell();
375
+ if (insertAt > 0 && insertAt < row.cells.length) {
376
+ const r = rows.indexOf(row);
377
+ const span = getSpanRect(rows, r, insertAt - 1);
378
+ if (span.c1 < insertAt && span.c2 >= insertAt) {
379
+ const main = rows[span.r1].cells[span.c1];
380
+ if (r === span.r1) {
381
+ main.colspan = (main.colspan ?? 1) + 1;
382
+ }
383
+ newCell.merged = true;
384
+ }
385
+ }
386
+ row.cells.splice(insertAt, 0, newCell);
387
+ }
388
+ }
389
+ function deleteCol(rows, colWidths, index) {
390
+ if (colWidths.length <= 1) {
391
+ return "\u4E0D\u80FD\u5220\u9664\u6700\u540E\u4E00\u5217";
392
+ }
393
+ for (let r = 0; r < rows.length; r++) {
394
+ const cell = rows[r].cells[index];
395
+ if (!cell.merged && (cell.colspan ?? 1) > 1) {
396
+ const next = rows[r].cells[index + 1];
397
+ Object.assign(next, deepClone(cell), { id: next.id });
398
+ next.colspan = (cell.colspan ?? 1) - 1;
399
+ next.merged = false;
400
+ } else if (cell.merged) {
401
+ const span = getSpanRect(rows, r, index);
402
+ if (span.c1 < index && span.r1 === r) {
403
+ const main = rows[span.r1].cells[span.c1];
404
+ main.colspan = (main.colspan ?? 1) - 1;
405
+ }
406
+ }
407
+ }
408
+ for (const row of rows) {
409
+ row.cells.splice(index, 1);
410
+ }
411
+ colWidths.splice(index, 1);
412
+ return null;
413
+ }
414
+ function setRowType(rows, index, type) {
415
+ if (type === "data") {
416
+ const existing = rows.findIndex((r, i) => r.type === "data" && i !== index);
417
+ if (existing >= 0) {
418
+ return `\u6570\u636E\u884C\u5DF2\u5B58\u5728\uFF08\u7B2C ${existing + 1} \u884C\uFF09\uFF0C\u5168\u8868\u6700\u591A\u4E00\u884C`;
419
+ }
420
+ }
421
+ if (type === "header") {
422
+ for (let i = 0; i < index; i++) {
423
+ if (rows[i].type !== "header") {
424
+ return "\u6807\u9898\u884C\u5FC5\u987B\u4ECE\u8868\u683C\u9876\u90E8\u8FDE\u7EED\u8BBE\u7F6E";
425
+ }
426
+ }
427
+ }
428
+ if (type === "subtotal" || type === "summary") {
429
+ const dataIdx = rows.findIndex((r) => r.type === "data");
430
+ if (dataIdx < 0) {
431
+ return "\u8BF7\u5148\u8BBE\u7F6E\u6570\u636E\u884C\uFF0C\u5C0F\u8BA1/\u6C47\u603B\u884C\u5FC5\u987B\u4F4D\u4E8E\u6570\u636E\u884C\u4E4B\u540E";
432
+ }
433
+ if (index < dataIdx) {
434
+ return "\u5C0F\u8BA1/\u6C47\u603B\u884C\u5FC5\u987B\u4F4D\u4E8E\u6570\u636E\u884C\u4E4B\u540E";
435
+ }
436
+ }
437
+ if (type === "subtotal") {
438
+ if (rows.slice(0, index).some((r) => r.type === "summary")) {
439
+ return "\u5C0F\u8BA1\u884C\u5FC5\u987B\u4F4D\u4E8E\u6C47\u603B\u884C\u4E4B\u524D";
440
+ }
441
+ }
442
+ if (type === "summary") {
443
+ if (rows.slice(index + 1).some((r) => r.type === "subtotal")) {
444
+ return "\u6C47\u603B\u884C\u5FC5\u987B\u4F4D\u4E8E\u5C0F\u8BA1\u884C\u4E4B\u540E";
445
+ }
446
+ }
447
+ if (rows[index].type === "header" && type !== "header") {
448
+ for (let i = index + 1; i < rows.length; i++) {
449
+ if (rows[i].type === "header") {
450
+ return "\u53D6\u6D88\u8BE5\u6807\u9898\u884C\u4F1A\u5BFC\u81F4\u6807\u9898\u884C\u4E0D\u8FDE\u7EED\uFF0C\u8BF7\u5148\u8C03\u6574\u540E\u7EED\u6807\u9898\u884C";
451
+ }
452
+ }
453
+ }
454
+ rows[index].type = type;
455
+ if (type !== "header") {
456
+ delete rows[index].repeatOnPage;
457
+ }
458
+ return null;
459
+ }
460
+ function applyBorderPreset(rows, rect, preset, border) {
461
+ for (let r = rect.r1; r <= rect.r2; r++) {
462
+ for (let c = rect.c1; c <= rect.c2; c++) {
463
+ const cell = rows[r].cells[c];
464
+ if (cell.merged) {
465
+ continue;
466
+ }
467
+ if (preset === "none") {
468
+ delete cell.borders;
469
+ continue;
470
+ }
471
+ const span = getSpanRect(rows, r, c);
472
+ const atTop = span.r1 === rect.r1;
473
+ const atBottom = span.r2 === rect.r2;
474
+ const atLeft = span.c1 === rect.c1;
475
+ const atRight = span.c2 === rect.c2;
476
+ const b = { ...cell.borders ?? {} };
477
+ if (preset === "all") {
478
+ b.top = { ...border };
479
+ b.bottom = { ...border };
480
+ b.left = { ...border };
481
+ b.right = { ...border };
482
+ } else if (preset === "outer") {
483
+ if (atTop) {
484
+ b.top = { ...border };
485
+ }
486
+ if (atBottom) {
487
+ b.bottom = { ...border };
488
+ }
489
+ if (atLeft) {
490
+ b.left = { ...border };
491
+ }
492
+ if (atRight) {
493
+ b.right = { ...border };
494
+ }
495
+ } else if (preset === "inner") {
496
+ if (!atTop) {
497
+ b.top = { ...border };
498
+ }
499
+ if (!atBottom) {
500
+ b.bottom = { ...border };
501
+ }
502
+ if (!atLeft) {
503
+ b.left = { ...border };
504
+ }
505
+ if (!atRight) {
506
+ b.right = { ...border };
507
+ }
508
+ }
509
+ cell.borders = b;
510
+ }
511
+ }
512
+ }
513
+ var GHOST_BORDER_CSS = "1px dashed rgba(0,0,0,0.18)";
514
+ function resolveCellBorderCss(b, opts = {}) {
515
+ if (b && b.style !== "none") return `${b.width}pt ${b.style} ${b.color}`;
516
+ if (opts.designMode && opts.showGhostBorder !== false) return GHOST_BORDER_CSS;
517
+ return "none";
518
+ }
519
+ function syncTableElementSize(options) {
520
+ const wMm = (options.tableColWidths ?? []).reduce((s, w) => s + w, 0);
521
+ const hMm = (options.tableRows ?? []).reduce((s, r) => s + r.height, 0);
522
+ options.width = Math.round(wMm * 100) / 100;
523
+ options.height = Math.round(hMm * 100) / 100;
524
+ }
525
+ var MIN_COL_WIDTH_MM = 5;
526
+ function clampResizedColumnWidth(colWidths, index, targetMm, maxTableWidth) {
527
+ const others = colWidths.reduce((s, w, i) => i === index ? s : s + w, 0);
528
+ const cap = maxTableWidth === Infinity ? Infinity : Math.max(MIN_COL_WIDTH_MM, maxTableWidth - others);
529
+ const rounded = Math.round(targetMm * 10) / 10;
530
+ return Math.min(Math.max(rounded, MIN_COL_WIDTH_MM), cap);
531
+ }
532
+
533
+ // src/designer/utils/element-factory.ts
534
+ function generateId() {
535
+ return `el-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
536
+ }
537
+ function createDefaultOptions(type) {
538
+ const base = { left: 7, top: 17.6, width: 42.3, height: 7.1 };
539
+ switch (type) {
540
+ case "text":
541
+ return { ...base, formatter: "\u6587\u672C", fontSize: 12, textAlign: "left", height: 5.3 };
542
+ case "image":
543
+ return { ...base, width: 28.2, height: 28.2, fit: "contain" };
544
+ case "longText":
545
+ return { ...base, width: 141.1, height: 28.2, fontSize: 12, lineHeight: 18 };
546
+ case "table": {
547
+ const { rows, colWidths } = createDefaultTable();
548
+ const totalW = colWidths.reduce((s, w) => s + w, 0);
549
+ const totalH = rows.reduce((s, r) => s + r.height, 0);
550
+ return {
551
+ ...base,
552
+ width: totalW,
553
+ height: totalH,
554
+ // 表格尺寸由列宽/行高(mm)派生,与渲染端一致
555
+ tableMode: "dynamic",
556
+ tableRows: rows,
557
+ tableColWidths: colWidths,
558
+ tableDefaultFontSize: 10,
559
+ tableDefaultColor: "#333333",
560
+ tableDefaultPadding: 1,
561
+ fields: [],
562
+ tablePagination: { enabled: true }
563
+ };
564
+ }
565
+ case "hline":
566
+ return { ...base, width: 70.6, height: 1.8, borderWidth: 0.75 };
567
+ case "vline":
568
+ return { ...base, width: 1.8, height: 35.3, borderWidth: 0.75 };
569
+ case "rect":
570
+ return { ...base, width: 28.2, height: 28.2, borderWidth: 1, borderColor: "#333" };
571
+ case "oval":
572
+ return { ...base, width: 28.2, height: 28.2, borderWidth: 1, borderColor: "#333" };
573
+ case "barcode":
574
+ return { ...base, width: 56.4, height: 14.1, title: "\u6761\u5F62\u7801", barcodeType: "code128", testData: "12345678" };
575
+ case "qrcode":
576
+ return { ...base, width: 28.2, height: 28.2, title: "\u4E8C\u7EF4\u7801", testData: "qrcode" };
577
+ case "html":
578
+ return { ...base, width: 28.2, height: 28.2 };
579
+ default:
580
+ return base;
581
+ }
582
+ }
583
+ function createRuntimeElement(type, options) {
584
+ const typeMetaMap = {
585
+ text: { title: "\u6587\u672C" },
586
+ image: { title: "\u56FE\u7247" },
587
+ longText: { title: "\u957F\u6587" },
588
+ table: { title: "\u8868\u683C" },
589
+ hline: { title: "\u6A2A\u7EBF" },
590
+ vline: { title: "\u7AD6\u7EBF" },
591
+ rect: { title: "\u77E9\u5F62" },
592
+ oval: { title: "\u692D\u5706" },
593
+ barcode: { title: "\u6761\u5F62\u7801" },
594
+ qrcode: { title: "\u4E8C\u7EF4\u7801" },
595
+ html: { title: "HTML" },
596
+ pageNumber: { title: "\u9875\u7801" }
597
+ };
598
+ return {
599
+ id: generateId(),
600
+ options: { ...createDefaultOptions(type), ...options },
601
+ printElementType: { type, title: typeMetaMap[type]?.title || type }
602
+ };
603
+ }
604
+
605
+ // src/designer/utils/zone-layout.ts
606
+ var ZONE_ALLOWED_TYPES = ["text", "image", "hline", "vline", "rect", "oval", "barcode", "qrcode"];
607
+ function getPaperSizeMM(t) {
608
+ return getPaperDimensions(t);
609
+ }
610
+ function getZoneRects(t) {
611
+ const paper = getPaperSizeMM(t);
612
+ const m = t.margins;
613
+ const headerH = t.header.height;
614
+ const footerH = t.footer.height;
615
+ const contentW = paper.width - m.left - m.right;
616
+ const contentH = paper.height - m.top - m.bottom - headerH - footerH;
617
+ const rect = (l, tp, w, h) => ({
618
+ left: l,
619
+ top: tp,
620
+ width: w,
621
+ height: h
622
+ });
623
+ return {
624
+ header: rect(m.left, m.top, contentW, headerH),
625
+ content: rect(m.left, m.top + headerH, contentW, contentH),
626
+ footer: rect(m.left, paper.height - m.bottom - footerH, contentW, footerH)
627
+ };
628
+ }
629
+ function zoneFromPaperPoint(t, _x, y) {
630
+ const r = getZoneRects(t);
631
+ if (r.footer.height > 0 && y >= r.footer.top) return "footer";
632
+ if (r.header.height > 0 && y < r.header.top + r.header.height) return "header";
633
+ return "content";
634
+ }
635
+ function clampToZone(el, zone) {
636
+ const o = el.options;
637
+ o.width = Math.min(o.width, zone.width);
638
+ o.height = Math.min(o.height, zone.height);
639
+ o.left = Math.min(Math.max(0, o.left), zone.width - o.width);
640
+ o.top = Math.min(Math.max(0, o.top), zone.height - o.height);
641
+ }
642
+ function finalizeElementZone(el, t) {
643
+ const rects = getZoneRects(t);
644
+ const fromZone = el.zone || "content";
645
+ const from = rects[fromZone];
646
+ const cx = from.left + el.options.left + el.options.width / 2;
647
+ const cy = from.top + el.options.top + el.options.height / 2;
648
+ let target = zoneFromPaperPoint(t, cx, cy);
649
+ let rejected = false;
650
+ if (target !== "content" && !ZONE_ALLOWED_TYPES.includes(el.printElementType.type)) {
651
+ target = "content";
652
+ rejected = true;
653
+ }
654
+ if (target !== fromZone) {
655
+ const to = rects[target];
656
+ el.options.left = from.left + el.options.left - to.left;
657
+ el.options.top = from.top + el.options.top - to.top;
658
+ el.zone = target;
659
+ }
660
+ if (target !== "content") {
661
+ clampToZone(el, rects[target]);
662
+ } else if (rejected) {
663
+ clampToZone(el, rects.content);
664
+ }
665
+ return { rejected };
666
+ }
667
+
668
+ // src/designer/utils/ruler.ts
669
+ var RULER_THICKNESS = 22;
670
+ var STEP_SEQUENCE = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1e3];
671
+ function chooseMajorStepMM(scale, targetPx = 64) {
672
+ const pxPerMM = scale * (96 / 25.4);
673
+ for (const step of STEP_SEQUENCE) {
674
+ if (step * pxPerMM >= targetPx) return step;
675
+ }
676
+ return STEP_SEQUENCE[STEP_SEQUENCE.length - 1];
677
+ }
678
+ function minorStepMM(majorStepMM) {
679
+ const exp = Math.floor(Math.log10(majorStepMM) + 1e-9);
680
+ const base = majorStepMM / 10 ** exp;
681
+ if (base === 5 || base === 1 && majorStepMM >= 10) return majorStepMM / 5;
682
+ if (base === 2) return majorStepMM / 2;
683
+ return null;
684
+ }
685
+ function roundMM(mm) {
686
+ return Math.round(mm * 1e3) / 1e3;
687
+ }
688
+ function buildRulerTicks(startMM, endMM, majorStepMM) {
689
+ if (endMM < startMM || majorStepMM <= 0) return [];
690
+ const minor = minorStepMM(majorStepMM);
691
+ const fine = minor ?? majorStepMM;
692
+ const first = Math.ceil(startMM / fine - 1e-9) * fine;
693
+ const lastIndex = Math.floor((endMM - first) / fine + 1e-9);
694
+ const ticks = [];
695
+ for (let i = 0; i <= lastIndex; i++) {
696
+ const mm = roundMM(first + i * fine);
697
+ const isMajor = Math.abs(mm / majorStepMM - Math.round(mm / majorStepMM)) < 1e-6;
698
+ if (isMajor) {
699
+ ticks.push({ mm, major: true, label: String(mm) });
700
+ } else {
701
+ ticks.push({ mm, major: false });
702
+ }
703
+ }
704
+ return ticks;
705
+ }
706
+
707
+ // src/designer/utils/preset-colors.ts
708
+ var PRESET_COLORS = [
709
+ "#000000",
710
+ "#333333",
711
+ "#666666",
712
+ "#999999",
713
+ "#cccccc",
714
+ "#ffffff",
715
+ "#e53935",
716
+ "#fb8c00",
717
+ "#1e88e5",
718
+ "#43a047",
719
+ "#8d6e63",
720
+ "#00acc1"
721
+ ];
722
+
723
+ // src/designer/utils/property-search.ts
724
+ var PROPERTY_REGISTRY = [
725
+ {
726
+ group: "position-size",
727
+ groupLabel: "\u4F4D\u7F6E\u4E0E\u5C3A\u5BF8",
728
+ items: [
729
+ { key: "ps-left", keywords: ["X", "\u6A2A\u5750\u6807", "left", "\u4F4D\u7F6E"] },
730
+ { key: "ps-top", keywords: ["Y", "\u7EB5\u5750\u6807", "top", "\u4F4D\u7F6E"] },
731
+ { key: "ps-width", keywords: ["W", "\u5BBD\u5EA6", "width", "\u5C3A\u5BF8"] },
732
+ { key: "ps-height", keywords: ["H", "\u9AD8\u5EA6", "height", "\u5C3A\u5BF8"] }
733
+ ]
734
+ },
735
+ {
736
+ group: "appearance",
737
+ groupLabel: "\u5916\u89C2",
738
+ items: [
739
+ { key: "ap-font-family", keywords: ["\u5B57\u4F53", "\u5B57\u4F53\u540D", "\u5B57\u4F53\u65CF", "fontfamily"] },
740
+ { key: "ap-font-size", keywords: ["\u5B57\u4F53\u5927\u5C0F", "fontsize", "\u5B57\u53F7"] },
741
+ { key: "ap-font-weight", keywords: ["\u5B57\u4F53\u7C97\u7EC6", "\u7C97\u4F53", "bold"] },
742
+ { key: "ap-align", keywords: ["\u5BF9\u9F50", "align"] },
743
+ { key: "ap-vertical-align", keywords: ["\u5782\u76F4\u5BF9\u9F50", "verticalalign", "\u9876\u5BF9\u9F50", "\u5E95\u5BF9\u9F50"] },
744
+ { key: "ap-color", keywords: ["\u989C\u8272", "color"] },
745
+ { key: "ap-bg-color", keywords: ["\u80CC\u666F\u8272", "background"] },
746
+ { key: "ap-line-height", keywords: ["\u884C\u9AD8", "lineheight"] },
747
+ { key: "ap-letter-spacing", keywords: ["\u5B57\u95F4\u8DDD", "letterspacing"] },
748
+ { key: "ap-text-fit", keywords: ["\u6EA2\u51FA", "\u6EA2\u51FA\u663E\u793A", "\u622A\u65AD", "\u81EA\u52A8\u7F29\u5C0F", "\u81EA\u9002\u5E94\u884C\u9AD8", "textfit", "overflow"] },
749
+ { key: "ap-shrink-min", keywords: ["\u6700\u5C0F\u5B57\u53F7", "\u7F29\u653E\u4E0B\u9650", "shrinkminfontsize"] },
750
+ { key: "ap-word-wrap", keywords: ["\u81EA\u52A8\u6362\u884C", "\u4E0D\u6362\u884C", "wordwrap"] }
751
+ ]
752
+ },
753
+ {
754
+ group: "code",
755
+ groupLabel: "\u6761\u7801\u8BBE\u7F6E",
756
+ items: [
757
+ { key: "cd-type", keywords: ["\u7801\u5236", "\u6761\u7801\u7C7B\u578B", "\u6761\u5F62\u7801\u7C7B\u578B", "barcodetype", "code128", "ean13", "code39", "itf14"] },
758
+ { key: "cd-dpi", keywords: ["\u6253\u5370\u673A\u5206\u8FA8\u7387", "dpi", "\u6253\u5370\u70B9", "\u70B9\u9635\u5BF9\u9F50", "203", "300"] },
759
+ { key: "cd-bar-width", keywords: ["\u6761\u5BBD", "\u6761\u7C97\u7EC6", "barwidth", "\u6A21\u5757\u5BBD"] },
760
+ { key: "cd-show-text", keywords: ["\u663E\u793A\u6587\u672C", "\u6761\u7801\u6587\u672C", "\u663E\u793A\u6570\u5B57"] },
761
+ { key: "cd-font-size", keywords: ["\u6761\u7801\u5B57\u53F7", "\u6587\u672C\u5B57\u53F7"] },
762
+ { key: "cd-ec-level", keywords: ["\u7EA0\u9519\u7EA7\u522B", "\u5BB9\u9519", "qrcodelevel"] },
763
+ { key: "cd-fit", keywords: ["\u7F29\u653E\u6A21\u5F0F", "\u62C9\u4F38", "\u7B49\u6BD4", "fit", "contain", "cover"] },
764
+ { key: "cd-max-width", keywords: ["\u6700\u5927\u5BBD\u5EA6", "\u6700\u5927\u5BBD", "maxwidth"] },
765
+ { key: "cd-max-height", keywords: ["\u6700\u5927\u9AD8\u5EA6", "\u6700\u5927\u9AD8", "maxheight"] }
766
+ ]
767
+ },
768
+ {
769
+ group: "content",
770
+ groupLabel: "\u5185\u5BB9",
771
+ items: [
772
+ { key: "ct-title", keywords: ["\u5185\u5BB9", "\u6587\u672C", "title"] },
773
+ { key: "ct-formatter", keywords: ["\u683C\u5F0F\u5316", "\u8868\u8FBE\u5F0F", "formatter"] },
774
+ { key: "ct-table-mode", keywords: ["\u6570\u636E\u6A21\u5F0F", "\u9759\u6001\u5E03\u5C40", "\u52A8\u6001\u6570\u636E"] },
775
+ { key: "ct-table-source", keywords: ["\u6570\u636E\u6E90", "\u5217\u8868\u5B57\u6BB5"] },
776
+ { key: "ct-table-font", keywords: ["\u9ED8\u8BA4\u5B57\u53F7", "\u9ED8\u8BA4\u989C\u8272", "\u9ED8\u8BA4\u5185\u8FB9\u8DDD"] },
777
+ { key: "ct-table-cols", keywords: ["\u5217\u5BBD", "\u8868\u683C\u5217"] }
778
+ ]
779
+ },
780
+ {
781
+ group: "border-bg",
782
+ groupLabel: "\u8FB9\u6846\u4E0E\u80CC\u666F",
783
+ items: [
784
+ { key: "bb-border-width", keywords: ["\u8FB9\u6846\u5BBD\u5EA6", "border"] },
785
+ { key: "bb-border-color", keywords: ["\u8FB9\u6846\u989C\u8272"] },
786
+ { key: "bb-bg-color", keywords: ["\u80CC\u666F\u8272", "background"] }
787
+ ]
788
+ },
789
+ {
790
+ group: "binding",
791
+ groupLabel: "\u5B57\u6BB5\u7ED1\u5B9A",
792
+ items: [
793
+ { key: "bd-formatter", keywords: ["\u683C\u5F0F\u5316", "\u8868\u8FBE\u5F0F", "formatter", "fx", "\u5B57\u6BB5", "\u7ED1\u5B9A", "field"] },
794
+ { key: "bd-content", keywords: ["\u5185\u5BB9", "\u8868\u8FBE\u5F0F", "\u6587\u672C", "\u53D8\u91CF"] }
795
+ ]
796
+ },
797
+ {
798
+ group: "pagination",
799
+ groupLabel: "\u5206\u9875",
800
+ items: [
801
+ { key: "pg-enabled", keywords: ["\u5206\u9875", "pagination", "\u542F\u7528\u5206\u9875"] },
802
+ { key: "pg-pageable", keywords: ["\u53C2\u4E0E\u5206\u9875", "pageable"] },
803
+ { key: "pg-keep-with-next", keywords: ["\u540C\u9875", "keepWithNext"] }
804
+ ]
805
+ },
806
+ {
807
+ group: "advanced",
808
+ groupLabel: "\u9AD8\u7EA7",
809
+ items: [
810
+ { key: "ad-locked", keywords: ["\u9501\u5B9A", "lock"] },
811
+ { key: "ad-fixed", keywords: ["\u6BCF\u9875\u91CD\u590D", "repeat"] },
812
+ { key: "ad-z-index", keywords: ["\u5C42\u7EA7", "zindex"] },
813
+ { key: "ad-delete", keywords: ["\u5220\u9664"] }
814
+ ]
815
+ }
816
+ ];
817
+ function matchKeywords(search, keywords) {
818
+ const s = search.trim().toLowerCase();
819
+ if (!s) {
820
+ return true;
821
+ }
822
+ return keywords.some((k) => k.toLowerCase().includes(s));
823
+ }
824
+ function searchProperties(search) {
825
+ const groups = [];
826
+ const itemKeys = /* @__PURE__ */ new Set();
827
+ for (const g of PROPERTY_REGISTRY) {
828
+ const matched = g.items.filter((item) => matchKeywords(search, [...item.keywords, g.groupLabel]));
829
+ if (matched.length > 0) {
830
+ groups.push(g.group);
831
+ for (const item of matched) {
832
+ itemKeys.add(item.key);
833
+ }
834
+ }
835
+ }
836
+ return { groups, itemKeys };
837
+ }
838
+
839
+ // src/designer/utils/field-groups.ts
840
+ function groupFields(fields) {
841
+ const sorted = [...fields].sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
842
+ const groupMap = /* @__PURE__ */ new Map();
843
+ const groupOrder = [];
844
+ for (const f of sorted) {
845
+ const dot = f.fieldKey.indexOf(".");
846
+ if (dot === -1) continue;
847
+ const key = f.fieldKey.slice(0, dot);
848
+ let g = groupMap.get(key);
849
+ if (!g) {
850
+ g = { key, label: key, isList: false, fields: [] };
851
+ groupMap.set(key, g);
852
+ groupOrder.push(key);
853
+ }
854
+ g.fields.push(f);
855
+ }
856
+ const groups = [];
857
+ const standalone = [];
858
+ for (const f of sorted) {
859
+ if (f.fieldKey.includes(".")) continue;
860
+ const g = groupMap.get(f.fieldKey);
861
+ if (g) {
862
+ g.label = f.fieldLabel;
863
+ g.isList = f.fieldType === "list";
864
+ } else {
865
+ standalone.push(f);
866
+ }
867
+ }
868
+ if (standalone.length) groups.push({ key: "", label: "", isList: false, fields: standalone });
869
+ for (const key of groupOrder) groups.push(groupMap.get(key));
870
+ return groups;
871
+ }
872
+ function filterGroups(groups, keyword) {
873
+ const kw = keyword.trim().toLowerCase();
874
+ if (!kw) return groups;
875
+ return groups.map((g) => ({
876
+ ...g,
877
+ fields: g.fields.filter(
878
+ (f) => f.fieldLabel.toLowerCase().includes(kw) || f.fieldKey.toLowerCase().includes(kw)
879
+ )
880
+ })).filter((g) => g.fields.length > 0);
881
+ }
882
+
883
+ // src/designer/utils/binding-registry.ts
884
+ var ELEMENT_BINDING_REGISTRY = {
885
+ text: [
886
+ { targetPath: "options.formatter", label: "\u5185\u5BB9", dataSource: "main" }
887
+ ],
888
+ longText: [
889
+ { targetPath: "options.formatter", label: "\u5185\u5BB9", dataSource: "main" },
890
+ { targetPath: "options.styler", label: "\u6837\u5F0F", dataSource: "main" }
891
+ ],
892
+ barcode: [
893
+ { targetPath: "options.formatter", label: "\u5185\u5BB9", dataSource: "main" }
894
+ ],
895
+ qrcode: [
896
+ { targetPath: "options.formatter", label: "\u5185\u5BB9", dataSource: "main" }
897
+ ],
898
+ image: [
899
+ { targetPath: "options.src", label: "\u5185\u5BB9", dataSource: "main", placeholder: "\u8F93\u5165\u56FE\u7247\u8DEF\u5F84\uFF0C\u652F\u6301 /docfiles/... \u76F8\u5BF9\u8DEF\u5F84\u3001\u7EDD\u5BF9 URL \u6216 {\u5B57\u6BB5} \u8868\u8FBE\u5F0F" }
900
+ ],
901
+ table: []
902
+ };
903
+ function getTableCellBindings(rowType, rowIndex, cellIndex, listField) {
904
+ if (rowType === "data") {
905
+ return [{
906
+ targetPath: `options.tableRows[${rowIndex}].cells[${cellIndex}].formatter`,
907
+ label: "\u5185\u5BB9",
908
+ dataSource: "list",
909
+ listField,
910
+ placeholder: "{name}"
911
+ }];
912
+ }
913
+ if (rowType === "subtotal") {
914
+ return [{
915
+ targetPath: `options.tableRows[${rowIndex}].cells[${cellIndex}].formatter`,
916
+ label: "\u5185\u5BB9",
917
+ dataSource: "subtotal",
918
+ placeholder: "{MONEY(SUM(amount))}"
919
+ }];
920
+ }
921
+ if (rowType === "summary") {
922
+ return [{
923
+ targetPath: `options.tableRows[${rowIndex}].cells[${cellIndex}].formatter`,
924
+ label: "\u5185\u5BB9",
925
+ dataSource: "summary",
926
+ placeholder: "{MONEY(SUM(amount))}"
927
+ }];
928
+ }
929
+ return [{
930
+ targetPath: `options.tableRows[${rowIndex}].cells[${cellIndex}].formatter`,
931
+ label: "\u5185\u5BB9",
932
+ dataSource: "main",
933
+ placeholder: "\u5408\u8BA1: {order.total}"
934
+ }];
935
+ }
936
+ function getElementBindings(element) {
937
+ const type = element.printElementType.type;
938
+ const staticBindings = ELEMENT_BINDING_REGISTRY[type] ?? [];
939
+ const dynamicBindings = [];
940
+ if (type === "table") {
941
+ const listField = element.options.dataSource;
942
+ for (const [ri, row] of (element.options.tableRows ?? []).entries()) {
943
+ for (const [ci, cell] of row.cells.entries()) {
944
+ if (cell.merged) continue;
945
+ dynamicBindings.push(...getTableCellBindings(row.type, ri, ci, listField));
946
+ }
947
+ }
948
+ }
949
+ return [...staticBindings, ...dynamicBindings];
950
+ }
951
+
952
+ // src/designer/utils/demo-data.ts
953
+ function getDemoData() {
954
+ return DEFAULT_DEMO_DATA;
955
+ }
956
+ var DEFAULT_DEMO_DATA = {
957
+ supplier: {
958
+ name: "\u946B\u8FBE\u4E94\u91D1\u6709\u9650\u516C\u53F8",
959
+ phone: "0571-88776655",
960
+ address: "\u676D\u5DDE\u5E02\u8427\u5C71\u533A\u7ECF\u6D4E\u5F00\u53D1\u533A88\u53F7"
961
+ },
962
+ receiver: {
963
+ name: "\u674E\u56DB",
964
+ phone: "13912345678",
965
+ address: "\u676D\u5DDE\u5E02\u4F59\u676D\u533A\u6587\u4E00\u897F\u8DEF998\u53F7"
966
+ },
967
+ order: {
968
+ no: "PO20260801001",
969
+ date: "2026-08-01",
970
+ total: 69543.75
971
+ },
972
+ // 50 条商品明细, 用于验证多页分页效果
973
+ goods: [
974
+ { name: "\u78B3\u94A2\u87BA\u4E1D M8", spec: "M14\xD710", unit: "\u4E2A", qty: 100, price: 0.5, amount: 50, remark: "A\u7EA7" },
975
+ { name: "\u4E0D\u9508\u94A2\u87BA\u6BCD M10", spec: "M16\xD711", unit: "\u5957", qty: 117, price: 0.75, amount: 87.75, remark: "" },
976
+ { name: "\u5F39\u7C27\u57AB\u5708 M12", spec: "M6\xD712", unit: "\u76D2", qty: 134, price: 1, amount: 134, remark: "" },
977
+ { name: "\u516D\u89D2\u87BA\u6813 M14", spec: "M5\xD713", unit: "\u652F", qty: 151, price: 1.25, amount: 188.75, remark: "" },
978
+ { name: "\u5185\u516D\u89D2\u87BA\u9489 M16", spec: "M20\xD714", unit: "\u888B", qty: 168, price: 1.5, amount: 252, remark: "" },
979
+ { name: "\u5E73\u57AB\u5708 M6", spec: "M4\xD715", unit: "\u4EF6", qty: 185, price: 1.75, amount: 323.75, remark: "" },
980
+ { name: "\u81EA\u653B\u87BA\u9489 M5", spec: "M3\xD716", unit: "\u6839", qty: 202, price: 2, amount: 404, remark: "" },
981
+ { name: "\u53CC\u5934\u87BA\u67F1 M20", spec: "M8\xD717", unit: "\u5305", qty: 219, price: 2.25, amount: 492.75, remark: "A\u7EA7" },
982
+ { name: "\u7D27\u5B9A\u87BA\u9489 M4", spec: "M10\xD718", unit: "\u53EA", qty: 236, price: 2.5, amount: 590, remark: "" },
983
+ { name: "\u5341\u5B57\u76D8\u5934\u9489 M3", spec: "M12\xD719", unit: "\u7EC4", qty: 253, price: 2.75, amount: 695.75, remark: "" },
984
+ { name: "\u78B3\u94A2\u87BA\u4E1D M8", spec: "M14\xD720", unit: "\u4E2A", qty: 270, price: 3, amount: 810, remark: "" },
985
+ { name: "\u4E0D\u9508\u94A2\u87BA\u6BCD M10", spec: "M16\xD721", unit: "\u5957", qty: 287, price: 3.25, amount: 932.75, remark: "" },
986
+ { name: "\u5F39\u7C27\u57AB\u5708 M12", spec: "M6\xD722", unit: "\u76D2", qty: 304, price: 3.5, amount: 1064, remark: "" },
987
+ { name: "\u516D\u89D2\u87BA\u6813 M14", spec: "M5\xD723", unit: "\u652F", qty: 321, price: 3.75, amount: 1203.75, remark: "" },
988
+ { name: "\u5185\u516D\u89D2\u87BA\u9489 M16", spec: "M20\xD724", unit: "\u888B", qty: 338, price: 4, amount: 1352, remark: "A\u7EA7" },
989
+ { name: "\u5E73\u57AB\u5708 M6", spec: "M4\xD725", unit: "\u4EF6", qty: 355, price: 4.25, amount: 1508.75, remark: "" },
990
+ { name: "\u81EA\u653B\u87BA\u9489 M5", spec: "M3\xD726", unit: "\u6839", qty: 372, price: 4.5, amount: 1674, remark: "" },
991
+ { name: "\u53CC\u5934\u87BA\u67F1 M20", spec: "M8\xD727", unit: "\u5305", qty: 389, price: 4.75, amount: 1847.75, remark: "" },
992
+ { name: "\u7D27\u5B9A\u87BA\u9489 M4", spec: "M10\xD728", unit: "\u53EA", qty: 406, price: 5, amount: 2030, remark: "" },
993
+ { name: "\u5341\u5B57\u76D8\u5934\u9489 M3", spec: "M12\xD729", unit: "\u7EC4", qty: 423, price: 5.25, amount: 2220.75, remark: "" },
994
+ { name: "\u78B3\u94A2\u87BA\u4E1D M8", spec: "M14\xD730", unit: "\u4E2A", qty: 440, price: 0.5, amount: 220, remark: "" },
995
+ { name: "\u4E0D\u9508\u94A2\u87BA\u6BCD M10", spec: "M16\xD731", unit: "\u5957", qty: 457, price: 0.75, amount: 342.75, remark: "A\u7EA7" },
996
+ { name: "\u5F39\u7C27\u57AB\u5708 M12", spec: "M6\xD732", unit: "\u76D2", qty: 474, price: 1, amount: 474, remark: "" },
997
+ { name: "\u516D\u89D2\u87BA\u6813 M14", spec: "M5\xD733", unit: "\u652F", qty: 491, price: 1.25, amount: 613.75, remark: "" },
998
+ { name: "\u5185\u516D\u89D2\u87BA\u9489 M16", spec: "M20\xD734", unit: "\u888B", qty: 508, price: 1.5, amount: 762, remark: "" },
999
+ { name: "\u5E73\u57AB\u5708 M6", spec: "M4\xD735", unit: "\u4EF6", qty: 525, price: 1.75, amount: 918.75, remark: "" },
1000
+ { name: "\u81EA\u653B\u87BA\u9489 M5", spec: "M3\xD736", unit: "\u6839", qty: 542, price: 2, amount: 1084, remark: "" },
1001
+ { name: "\u53CC\u5934\u87BA\u67F1 M20", spec: "M8\xD737", unit: "\u5305", qty: 559, price: 2.25, amount: 1257.75, remark: "" },
1002
+ { name: "\u7D27\u5B9A\u87BA\u9489 M4", spec: "M10\xD738", unit: "\u53EA", qty: 576, price: 2.5, amount: 1440, remark: "A\u7EA7" },
1003
+ { name: "\u5341\u5B57\u76D8\u5934\u9489 M3", spec: "M12\xD739", unit: "\u7EC4", qty: 593, price: 2.75, amount: 1630.75, remark: "" },
1004
+ { name: "\u78B3\u94A2\u87BA\u4E1D M8", spec: "M14\xD740", unit: "\u4E2A", qty: 610, price: 3, amount: 1830, remark: "" },
1005
+ { name: "\u4E0D\u9508\u94A2\u87BA\u6BCD M10", spec: "M16\xD741", unit: "\u5957", qty: 627, price: 3.25, amount: 2037.75, remark: "" },
1006
+ { name: "\u5F39\u7C27\u57AB\u5708 M12", spec: "M6\xD742", unit: "\u76D2", qty: 644, price: 3.5, amount: 2254, remark: "" },
1007
+ { name: "\u516D\u89D2\u87BA\u6813 M14", spec: "M5\xD743", unit: "\u652F", qty: 661, price: 3.75, amount: 2478.75, remark: "" },
1008
+ { name: "\u5185\u516D\u89D2\u87BA\u9489 M16", spec: "M20\xD744", unit: "\u888B", qty: 678, price: 4, amount: 2712, remark: "" },
1009
+ { name: "\u5E73\u57AB\u5708 M6", spec: "M4\xD745", unit: "\u4EF6", qty: 695, price: 4.25, amount: 2953.75, remark: "A\u7EA7" },
1010
+ { name: "\u81EA\u653B\u87BA\u9489 M5", spec: "M3\xD746", unit: "\u6839", qty: 712, price: 4.5, amount: 3204, remark: "" },
1011
+ { name: "\u53CC\u5934\u87BA\u67F1 M20", spec: "M8\xD747", unit: "\u5305", qty: 729, price: 4.75, amount: 3462.75, remark: "" },
1012
+ { name: "\u7D27\u5B9A\u87BA\u9489 M4", spec: "M10\xD748", unit: "\u53EA", qty: 746, price: 5, amount: 3730, remark: "" },
1013
+ { name: "\u5341\u5B57\u76D8\u5934\u9489 M3", spec: "M12\xD749", unit: "\u7EC4", qty: 763, price: 5.25, amount: 4005.75, remark: "" },
1014
+ { name: "\u78B3\u94A2\u87BA\u4E1D M8", spec: "M14\xD710", unit: "\u4E2A", qty: 780, price: 0.5, amount: 390, remark: "" },
1015
+ { name: "\u4E0D\u9508\u94A2\u87BA\u6BCD M10", spec: "M16\xD711", unit: "\u5957", qty: 797, price: 0.75, amount: 597.75, remark: "" },
1016
+ { name: "\u5F39\u7C27\u57AB\u5708 M12", spec: "M6\xD712", unit: "\u76D2", qty: 814, price: 1, amount: 814, remark: "A\u7EA7" },
1017
+ { name: "\u516D\u89D2\u87BA\u6813 M14", spec: "M5\xD713", unit: "\u652F", qty: 831, price: 1.25, amount: 1038.75, remark: "" },
1018
+ { name: "\u5185\u516D\u89D2\u87BA\u9489 M16", spec: "M20\xD714", unit: "\u888B", qty: 848, price: 1.5, amount: 1272, remark: "" },
1019
+ { name: "\u5E73\u57AB\u5708 M6", spec: "M4\xD715", unit: "\u4EF6", qty: 865, price: 1.75, amount: 1513.75, remark: "" },
1020
+ { name: "\u81EA\u653B\u87BA\u9489 M5", spec: "M3\xD716", unit: "\u6839", qty: 882, price: 2, amount: 1764, remark: "" },
1021
+ { name: "\u53CC\u5934\u87BA\u67F1 M20", spec: "M8\xD717", unit: "\u5305", qty: 899, price: 2.25, amount: 2022.75, remark: "" },
1022
+ { name: "\u7D27\u5B9A\u87BA\u9489 M4", spec: "M10\xD718", unit: "\u53EA", qty: 916, price: 2.5, amount: 2290, remark: "" },
1023
+ { name: "\u5341\u5B57\u76D8\u5934\u9489 M3", spec: "M12\xD719", unit: "\u7EC4", qty: 933, price: 2.75, amount: 2565.75, remark: "A\u7EA7" }
1024
+ ]
1025
+ };
1026
+
1027
+ // src/designer/utils/engine.ts
1028
+ var engine = new TemplateEngine();
1029
+
1030
+ // src/designer/utils/expression-eval.ts
1031
+ var EXTENDED_FUNCTIONS = {
1032
+ MONEY: formatMoney,
1033
+ CONCAT: (...args) => args.filter((v) => v != null).join(""),
1034
+ IF: ifFn,
1035
+ FORMAT: (value) => {
1036
+ if (typeof value !== "number") return String(value);
1037
+ return value.toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
1038
+ },
1039
+ SUBSTR: (str, start, len) => String(str).slice(start, len ? start + len : void 0),
1040
+ LEN: (str) => String(str).length,
1041
+ ROUND: (num, decimals) => Number(Number(num).toFixed(decimals)),
1042
+ NOW: () => (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
1043
+ IFEMPTY: (value, defaultVal) => value != null && value !== "" ? String(value) : defaultVal,
1044
+ PAD: (value, len, char) => String(value).padStart(len, char),
1045
+ REPLACE: (str, from, to) => String(str).replace(new RegExp(from, "g"), to),
1046
+ JSON: (value) => JSON.stringify(value, null, 2),
1047
+ DATE: formatDate,
1048
+ UPPER: toUpperCaseAmount
1049
+ };
1050
+ for (const [name, fn] of Object.entries(EXTENDED_FUNCTIONS)) {
1051
+ engine.registerFunction(name, fn);
1052
+ }
1053
+ function aggregate(fn, field, rows) {
1054
+ switch (fn.toUpperCase()) {
1055
+ case "SUM":
1056
+ return sum(rows, field);
1057
+ case "AVG":
1058
+ return avg(rows, field);
1059
+ case "COUNT":
1060
+ return count(rows, field);
1061
+ case "MIN":
1062
+ return min(rows, field);
1063
+ case "MAX":
1064
+ return max(rows, field);
1065
+ default:
1066
+ return 0;
1067
+ }
1068
+ }
1069
+ function safeEval(expr, context, _callableNames) {
1070
+ return engine.evaluate(expr, context);
1071
+ }
1072
+ function evaluateTemplate(text, ctx) {
1073
+ if (!text) return "";
1074
+ if (!text.includes("{")) return text;
1075
+ const rows = Array.isArray(ctx.rows) ? ctx.rows : [];
1076
+ let processedText = text.replace(/\{([^}]+)\}/g, (_match, inner) => {
1077
+ let expr = inner.trim();
1078
+ expr = expr.replace(/\b(SUM|AVG|COUNT|MIN|MAX)\(([^)]+)\)/g, (_m, fn, arg) => {
1079
+ const field = arg.trim().replace(/^['"]|['"]$/g, "");
1080
+ return String(aggregate(fn, field, rows));
1081
+ });
1082
+ return `{${expr}}`;
1083
+ });
1084
+ try {
1085
+ const result = engine.render(processedText, ctx);
1086
+ return result;
1087
+ } catch {
1088
+ return text;
1089
+ }
1090
+ }
1091
+
1092
+ // src/designer/utils/binding.ts
1093
+ function createFieldElement(field) {
1094
+ return createRuntimeElement("text", {
1095
+ formatter: `{${field.fieldKey}}`,
1096
+ title: field.fieldLabel
1097
+ });
1098
+ }
1099
+ function resolveTextBinding(options, data) {
1100
+ if (options.formatter) {
1101
+ if (!data || data.length === 0) return "";
1102
+ const row = data[0];
1103
+ try {
1104
+ return engine.render(options.formatter, row ?? {});
1105
+ } catch {
1106
+ return options.formatter;
1107
+ }
1108
+ }
1109
+ return "";
1110
+ }
1111
+ function lookupDemoPath(path) {
1112
+ let cur = DEFAULT_DEMO_DATA;
1113
+ for (const seg of path.split(".")) {
1114
+ if (cur == null) return void 0;
1115
+ cur = Array.isArray(cur) ? cur[0]?.[seg] : cur[seg];
1116
+ }
1117
+ return cur;
1118
+ }
1119
+ function resolveBarcodeDesignValue(formatter, listSource) {
1120
+ const first = listSource?.[0];
1121
+ const resolved = (formatter ?? "").replace(/\{([^{}]+)\}/g, (_, expr) => {
1122
+ const key = expr.trim();
1123
+ const v = key.includes(".") ? lookupDemoPath(key) : first?.[key] ?? lookupDemoPath(key);
1124
+ return v == null ? "" : String(v);
1125
+ }).trim();
1126
+ return resolved || "1234567890128";
1127
+ }
1128
+
1129
+ // src/designer/interactions/adsorb.ts
1130
+ var SNAP_COLORS = {
1131
+ left: "#3b82f6",
1132
+ center: "#10b981",
1133
+ right: "#f59e0b",
1134
+ top: "#3b82f6",
1135
+ middle: "#10b981",
1136
+ bottom: "#f59e0b"
1137
+ };
1138
+ function snapColor(mk) {
1139
+ if (mk === "vCenter") return SNAP_COLORS.center;
1140
+ if (mk === "hCenter") return SNAP_COLORS.middle;
1141
+ return SNAP_COLORS[mk] ?? SNAP_COLORS.left;
1142
+ }
1143
+ function computeAdsorb(movingRect, others, manualGuides, config = {}) {
1144
+ const adsorbMin = config.adsorbMin ?? 1;
1145
+ const adsorbLineMin = config.adsorbLineMin ?? 2;
1146
+ const showAdsorbLine = config.showAdsorbLine ?? true;
1147
+ let left = movingRect.left;
1148
+ let top = movingRect.top;
1149
+ const lines = [];
1150
+ const m = {
1151
+ left: movingRect.left,
1152
+ right: movingRect.left + movingRect.width,
1153
+ vCenter: movingRect.left + movingRect.width / 2,
1154
+ top: movingRect.top,
1155
+ bottom: movingRect.top + movingRect.height,
1156
+ hCenter: movingRect.top + movingRect.height / 2
1157
+ };
1158
+ let minDist = Infinity;
1159
+ for (const other of others) {
1160
+ if (other.id === movingRect.id) continue;
1161
+ const o = {
1162
+ left: other.left,
1163
+ right: other.left + other.width,
1164
+ vCenter: other.left + other.width / 2,
1165
+ top: other.top,
1166
+ bottom: other.top + other.height,
1167
+ hCenter: other.top + other.height / 2
1168
+ };
1169
+ const hPairs = [
1170
+ ["left", "left"],
1171
+ ["left", "vCenter"],
1172
+ ["left", "right"],
1173
+ ["vCenter", "left"],
1174
+ ["vCenter", "vCenter"],
1175
+ ["vCenter", "right"],
1176
+ ["right", "left"],
1177
+ ["right", "vCenter"],
1178
+ ["right", "right"]
1179
+ ];
1180
+ const vPairs = [
1181
+ ["top", "top"],
1182
+ ["top", "hCenter"],
1183
+ ["top", "bottom"],
1184
+ ["hCenter", "top"],
1185
+ ["hCenter", "hCenter"],
1186
+ ["hCenter", "bottom"],
1187
+ ["bottom", "top"],
1188
+ ["bottom", "hCenter"],
1189
+ ["bottom", "bottom"]
1190
+ ];
1191
+ for (const [mk, ok] of hPairs) {
1192
+ const dist = Math.abs(m[mk] - o[ok]);
1193
+ if (dist <= adsorbMin && dist < minDist) {
1194
+ minDist = dist;
1195
+ left = movingRect.left + (o[ok] - m[mk]);
1196
+ }
1197
+ }
1198
+ for (const [mk, ok] of vPairs) {
1199
+ const dist = Math.abs(m[mk] - o[ok]);
1200
+ if (dist <= adsorbMin && dist < minDist) {
1201
+ minDist = dist;
1202
+ top = movingRect.top + (o[ok] - m[mk]);
1203
+ }
1204
+ }
1205
+ }
1206
+ if (manualGuides) {
1207
+ const m2 = {
1208
+ left,
1209
+ right: left + movingRect.width,
1210
+ vCenter: left + movingRect.width / 2,
1211
+ top,
1212
+ bottom: top + movingRect.height,
1213
+ hCenter: top + movingRect.height / 2
1214
+ };
1215
+ const hKeys = ["left", "vCenter", "right"];
1216
+ for (const pos of manualGuides.vertical) {
1217
+ for (const k of hKeys) {
1218
+ const d = Math.abs(m2[k] - pos);
1219
+ if (d <= adsorbMin && d < minDist) {
1220
+ minDist = d;
1221
+ left = movingRect.left + (pos - m2[k]);
1222
+ }
1223
+ }
1224
+ }
1225
+ const vKeys = ["top", "hCenter", "bottom"];
1226
+ for (const pos of manualGuides.horizontal) {
1227
+ for (const k of vKeys) {
1228
+ const d = Math.abs(m2[k] - pos);
1229
+ if (d <= adsorbMin && d < minDist) {
1230
+ minDist = d;
1231
+ top = movingRect.top + (pos - m2[k]);
1232
+ }
1233
+ }
1234
+ }
1235
+ }
1236
+ const adjustedM = {
1237
+ left,
1238
+ right: left + movingRect.width,
1239
+ vCenter: left + movingRect.width / 2,
1240
+ top,
1241
+ bottom: top + movingRect.height,
1242
+ hCenter: top + movingRect.height / 2
1243
+ };
1244
+ if (showAdsorbLine) {
1245
+ const seen = /* @__PURE__ */ new Set();
1246
+ for (const other of others) {
1247
+ if (other.id === movingRect.id) continue;
1248
+ const o = {
1249
+ left: other.left,
1250
+ right: other.left + other.width,
1251
+ vCenter: other.left + other.width / 2,
1252
+ top: other.top,
1253
+ bottom: other.top + other.height,
1254
+ hCenter: other.top + other.height / 2
1255
+ };
1256
+ const hPairs = [
1257
+ ["left", "left"],
1258
+ ["left", "vCenter"],
1259
+ ["left", "right"],
1260
+ ["vCenter", "left"],
1261
+ ["vCenter", "vCenter"],
1262
+ ["vCenter", "right"],
1263
+ ["right", "left"],
1264
+ ["right", "vCenter"],
1265
+ ["right", "right"]
1266
+ ];
1267
+ const vPairs = [
1268
+ ["top", "top"],
1269
+ ["top", "hCenter"],
1270
+ ["top", "bottom"],
1271
+ ["hCenter", "top"],
1272
+ ["hCenter", "hCenter"],
1273
+ ["hCenter", "bottom"],
1274
+ ["bottom", "top"],
1275
+ ["bottom", "hCenter"],
1276
+ ["bottom", "bottom"]
1277
+ ];
1278
+ for (const [mk, ok] of hPairs) {
1279
+ const dist = Math.abs(adjustedM[mk] - o[ok]);
1280
+ if (dist <= adsorbLineMin) {
1281
+ const key = `v:${o[ok]}`;
1282
+ if (!seen.has(key)) {
1283
+ seen.add(key);
1284
+ lines.push({ type: "vertical", position: o[ok], id: other.id, color: snapColor(mk) });
1285
+ }
1286
+ }
1287
+ }
1288
+ for (const [mk, ok] of vPairs) {
1289
+ const dist = Math.abs(adjustedM[mk] - o[ok]);
1290
+ if (dist <= adsorbLineMin) {
1291
+ const key = `h:${o[ok]}`;
1292
+ if (!seen.has(key)) {
1293
+ seen.add(key);
1294
+ lines.push({ type: "horizontal", position: o[ok], id: other.id, color: snapColor(mk) });
1295
+ }
1296
+ }
1297
+ }
1298
+ }
1299
+ }
1300
+ if (manualGuides) {
1301
+ for (const pos of manualGuides.vertical) {
1302
+ if (!lines.some((l) => l.type === "vertical" && l.position === pos)) {
1303
+ lines.push({ type: "vertical", position: pos });
1304
+ }
1305
+ }
1306
+ for (const pos of manualGuides.horizontal) {
1307
+ if (!lines.some((l) => l.type === "horizontal" && l.position === pos)) {
1308
+ lines.push({ type: "horizontal", position: pos });
1309
+ }
1310
+ }
1311
+ }
1312
+ return { left, top, lines };
1313
+ }
1314
+
1315
+ // src/designer/interactions/useAlign.ts
1316
+ function useAlign() {
1317
+ function align(elements, mode) {
1318
+ if (elements.length < 2) return;
1319
+ const bounds = {
1320
+ minLeft: Math.min(...elements.map((e) => e.options.left)),
1321
+ maxRight: Math.max(...elements.map((e) => e.options.left + e.options.width)),
1322
+ minTop: Math.min(...elements.map((e) => e.options.top)),
1323
+ maxBottom: Math.max(...elements.map((e) => e.options.top + e.options.height))
1324
+ };
1325
+ switch (mode) {
1326
+ case "left":
1327
+ elements.forEach((e) => {
1328
+ e.options.left = bounds.minLeft;
1329
+ });
1330
+ break;
1331
+ case "right":
1332
+ elements.forEach((e) => {
1333
+ e.options.left = bounds.maxRight - e.options.width;
1334
+ });
1335
+ break;
1336
+ case "top":
1337
+ elements.forEach((e) => {
1338
+ e.options.top = bounds.minTop;
1339
+ });
1340
+ break;
1341
+ case "bottom":
1342
+ elements.forEach((e) => {
1343
+ e.options.top = bounds.maxBottom - e.options.height;
1344
+ });
1345
+ break;
1346
+ case "vertical": {
1347
+ const centerX = bounds.minLeft + (bounds.maxRight - bounds.minLeft) / 2;
1348
+ elements.forEach((e) => {
1349
+ e.options.left = centerX - e.options.width / 2;
1350
+ });
1351
+ break;
1352
+ }
1353
+ case "horizontal": {
1354
+ const centerY = bounds.minTop + (bounds.maxBottom - bounds.minTop) / 2;
1355
+ elements.forEach((e) => {
1356
+ e.options.top = centerY - e.options.height / 2;
1357
+ });
1358
+ break;
1359
+ }
1360
+ case "distributeHor": {
1361
+ const sorted = [...elements].sort((a, b) => a.options.left - b.options.left);
1362
+ const totalW = sorted.reduce((s, e) => s + e.options.width, 0);
1363
+ const gap = (bounds.maxRight - bounds.minLeft - totalW) / (sorted.length - 1);
1364
+ let x = bounds.minLeft;
1365
+ sorted.forEach((e) => {
1366
+ e.options.left = x;
1367
+ x += e.options.width + gap;
1368
+ });
1369
+ break;
1370
+ }
1371
+ case "distributeVer": {
1372
+ const sorted = [...elements].sort((a, b) => a.options.top - b.options.top);
1373
+ const totalH = sorted.reduce((s, e) => s + e.options.height, 0);
1374
+ const gap = (bounds.maxBottom - bounds.minTop - totalH) / (sorted.length - 1);
1375
+ let y = bounds.minTop;
1376
+ sorted.forEach((e) => {
1377
+ e.options.top = y;
1378
+ y += e.options.height + gap;
1379
+ });
1380
+ break;
1381
+ }
1382
+ }
1383
+ }
1384
+ return { align };
1385
+ }
1386
+
1387
+ // src/designer/interactions/useGroup.ts
1388
+ function generateGroupId() {
1389
+ return `grp-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
1390
+ }
1391
+ function useGroup() {
1392
+ function group(elements, selectedIds) {
1393
+ const selected = elements.filter((e) => selectedIds.has(e.id));
1394
+ if (selected.length < 2) return elements;
1395
+ const groupId = generateGroupId();
1396
+ return elements.map(
1397
+ (e) => selectedIds.has(e.id) ? { ...e, options: { ...e.options, groupId } } : e
1398
+ );
1399
+ }
1400
+ function ungroup(elements, selectedIds) {
1401
+ const selected = elements.filter((e) => selectedIds.has(e.id));
1402
+ const groupIds = new Set(selected.map((e) => e.options.groupId).filter(Boolean));
1403
+ return elements.map(
1404
+ (e) => e.options.groupId && groupIds.has(e.options.groupId) ? { ...e, options: { ...e.options, groupId: void 0 } } : e
1405
+ );
1406
+ }
1407
+ function getGroupedIds(elements, selectedId) {
1408
+ const el = elements.find((e) => e.id === selectedId);
1409
+ if (!el?.options.groupId) return /* @__PURE__ */ new Set([selectedId]);
1410
+ const ids = /* @__PURE__ */ new Set();
1411
+ for (const e of elements) {
1412
+ if (e.options.groupId === el.options.groupId) {
1413
+ ids.add(e.id);
1414
+ }
1415
+ }
1416
+ return ids;
1417
+ }
1418
+ return { group, ungroup, getGroupedIds };
1419
+ }
1420
+
1421
+ // src/designer/interactions/useKeyboard.ts
1422
+ var STEP = 1;
1423
+ var STEP_LARGE = 10;
1424
+ var STEP_SMALL = 0.5;
1425
+ function useKeyboard(handlers) {
1426
+ function onKeyDown(e) {
1427
+ const tag = e.target.tagName;
1428
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
1429
+ const ctrl = e.ctrlKey || e.metaKey;
1430
+ if (e.key.startsWith("Arrow")) {
1431
+ e.preventDefault();
1432
+ const step = e.shiftKey ? STEP_LARGE : e.ctrlKey ? STEP_SMALL : STEP;
1433
+ const dx = e.key === "ArrowLeft" ? -step : e.key === "ArrowRight" ? step : 0;
1434
+ const dy = e.key === "ArrowUp" ? -step : e.key === "ArrowDown" ? step : 0;
1435
+ handlers.onMove?.(dx, dy);
1436
+ return;
1437
+ }
1438
+ if (e.key === "Delete" || e.key === "Backspace") {
1439
+ e.preventDefault();
1440
+ handlers.onDelete?.();
1441
+ return;
1442
+ }
1443
+ if (ctrl) {
1444
+ const k = e.key.toLowerCase();
1445
+ if (k === "c") {
1446
+ e.preventDefault();
1447
+ handlers.onCopy?.();
1448
+ } else if (k === "v") {
1449
+ e.preventDefault();
1450
+ handlers.onPaste?.();
1451
+ } else if (k === "a") {
1452
+ e.preventDefault();
1453
+ handlers.onSelectAll?.();
1454
+ } else if (k === "d") {
1455
+ e.preventDefault();
1456
+ handlers.onDuplicate?.();
1457
+ } else if (k === "z") {
1458
+ e.preventDefault();
1459
+ e.shiftKey ? handlers.onRedo?.() : handlers.onUndo?.();
1460
+ } else if (k === "y") {
1461
+ e.preventDefault();
1462
+ handlers.onRedo?.();
1463
+ } else if (k === "1") {
1464
+ e.preventDefault();
1465
+ handlers.onResetZoom?.();
1466
+ } else if (k === "l") {
1467
+ e.preventDefault();
1468
+ handlers.onAlignLeft?.();
1469
+ } else if (k === "r") {
1470
+ e.preventDefault();
1471
+ handlers.onAlignRight?.();
1472
+ } else if (k === "e") {
1473
+ e.preventDefault();
1474
+ handlers.onAlignCenterH?.();
1475
+ } else if (k === "t") {
1476
+ e.preventDefault();
1477
+ handlers.onAlignTop?.();
1478
+ } else if (k === "b") {
1479
+ e.preventDefault();
1480
+ handlers.onAlignBottom?.();
1481
+ } else if (k === "g") {
1482
+ e.preventDefault();
1483
+ e.shiftKey ? handlers.onUngroup?.() : handlers.onGroup?.();
1484
+ }
1485
+ }
1486
+ }
1487
+ function setup() {
1488
+ document.addEventListener("keydown", onKeyDown);
1489
+ }
1490
+ function cleanup() {
1491
+ document.removeEventListener("keydown", onKeyDown);
1492
+ }
1493
+ return { setup, cleanup };
1494
+ }
1495
+
1496
+ // src/designer/interactions/useResize.ts
1497
+ var RESIZE_POINTS = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
1498
+ function calcResizeRect(point, startRect, dx, dy, minW, minH) {
1499
+ let { left, top, width, height } = startRect;
1500
+ switch (point) {
1501
+ case "e":
1502
+ width = Math.max(minW, startRect.width + dx);
1503
+ break;
1504
+ case "w":
1505
+ width = Math.max(minW, startRect.width - dx);
1506
+ left = startRect.left + startRect.width - width;
1507
+ break;
1508
+ case "s":
1509
+ height = Math.max(minH, startRect.height + dy);
1510
+ break;
1511
+ case "n":
1512
+ height = Math.max(minH, startRect.height - dy);
1513
+ top = startRect.top + startRect.height - height;
1514
+ break;
1515
+ case "se":
1516
+ width = Math.max(minW, startRect.width + dx);
1517
+ height = Math.max(minH, startRect.height + dy);
1518
+ break;
1519
+ case "sw":
1520
+ width = Math.max(minW, startRect.width - dx);
1521
+ left = startRect.left + startRect.width - width;
1522
+ height = Math.max(minH, startRect.height + dy);
1523
+ break;
1524
+ case "ne":
1525
+ width = Math.max(minW, startRect.width + dx);
1526
+ height = Math.max(minH, startRect.height - dy);
1527
+ top = startRect.top + startRect.height - height;
1528
+ break;
1529
+ case "nw":
1530
+ width = Math.max(minW, startRect.width - dx);
1531
+ left = startRect.left + startRect.width - width;
1532
+ height = Math.max(minH, startRect.height - dy);
1533
+ top = startRect.top + startRect.height - height;
1534
+ break;
1535
+ }
1536
+ return { left, top, width, height };
1537
+ }
1538
+ function useResize(options) {
1539
+ let startX = 0, startY = 0;
1540
+ let startRect = { left: 0, top: 0, width: 0, height: 0 };
1541
+ let activePoint = null;
1542
+ function startResize(point, e) {
1543
+ if (e.button !== 0) return;
1544
+ activePoint = point;
1545
+ startX = e.clientX;
1546
+ startY = e.clientY;
1547
+ startRect = { ...options.getRect() };
1548
+ options.onStart?.();
1549
+ document.addEventListener("mousemove", onResizeMove);
1550
+ document.addEventListener("mouseup", onResizeEnd);
1551
+ e.preventDefault();
1552
+ }
1553
+ function onResizeMove(e) {
1554
+ if (!activePoint) {
1555
+ return;
1556
+ }
1557
+ const scale = options.getScale?.() || 1;
1558
+ const dx = (e.clientX - startX) / scale;
1559
+ const dy = (e.clientY - startY) / scale;
1560
+ const minW = options.minWidth || 20;
1561
+ const minH = options.minHeight || 20;
1562
+ options.onResize(calcResizeRect(activePoint, startRect, dx, dy, minW, minH));
1563
+ }
1564
+ function onResizeEnd() {
1565
+ activePoint = null;
1566
+ options.onStop?.();
1567
+ document.removeEventListener("mousemove", onResizeMove);
1568
+ document.removeEventListener("mouseup", onResizeEnd);
1569
+ }
1570
+ function cleanup() {
1571
+ document.removeEventListener("mousemove", onResizeMove);
1572
+ document.removeEventListener("mouseup", onResizeEnd);
1573
+ }
1574
+ return { startResize, cleanup };
1575
+ }
1576
+
1577
+ // src/designer/interactions/useBindingDisplay.ts
1578
+ function getNestedValue(obj, path) {
1579
+ return path.split(".").reduce((current, key) => current?.[key], obj);
1580
+ }
1581
+ function useBindingDisplay() {
1582
+ function getBindingDisplayState(element) {
1583
+ const formatter = element.options.formatter;
1584
+ const title = element.options.title;
1585
+ if (!formatter) {
1586
+ return {
1587
+ status: "unbound",
1588
+ badge: title || "\u6587\u672C",
1589
+ badgeColor: "#909399",
1590
+ demoPreview: title || "\u6587\u672C"
1591
+ };
1592
+ }
1593
+ const demoData = getDemoData();
1594
+ const fieldMatch = formatter.match(/\{([^}]+)\}/);
1595
+ if (fieldMatch) {
1596
+ const fieldPath = fieldMatch[1];
1597
+ const demoValue = getNestedValue(demoData, fieldPath);
1598
+ return {
1599
+ status: "bound",
1600
+ badge: `fx ${formatter}`,
1601
+ badgeColor: "#409EFF",
1602
+ demoPreview: demoValue != null ? String(demoValue) : formatter
1603
+ };
1604
+ }
1605
+ return {
1606
+ status: "bound",
1607
+ badge: `fx ${formatter}`,
1608
+ badgeColor: "#409EFF",
1609
+ demoPreview: formatter
1610
+ };
1611
+ }
1612
+ return { getBindingDisplayState };
1613
+ }
1614
+ export {
1615
+ BARCODE_BAR_HEIGHT_MODULES,
1616
+ BARCODE_MARGIN_BOTTOM_MODULES,
1617
+ BARCODE_MODULE_WIDTH_MM,
1618
+ BARCODE_QUIET_ZONE_MODULES,
1619
+ BARCODE_TEXT_FONT_SIZE_MODULES,
1620
+ DEFAULT_DEMO_DATA,
1621
+ DEFAULT_SHRINK_MIN_FONT_SIZE_PT,
1622
+ ELEMENT_BINDING_REGISTRY,
1623
+ EXTENDED_FUNCTIONS,
1624
+ FIT_SCALE_MIN_PERCENT,
1625
+ GHOST_BORDER_CSS,
1626
+ LABEL_PAPER_GROUP,
1627
+ MIN_COL_WIDTH_MM,
1628
+ MIN_SCALE_PERCENT,
1629
+ MIN_SHRINK_FONT_SIZE_PT,
1630
+ MM_PER_INCH,
1631
+ PAPER_PRESETS,
1632
+ PRESET_COLORS,
1633
+ PROPERTY_REGISTRY,
1634
+ RESIZE_POINTS,
1635
+ RULER_THICKNESS,
1636
+ ZONE_ALLOWED_TYPES,
1637
+ applyBorderPreset,
1638
+ barcodeAvailableBoxMm,
1639
+ barcodePreferredModuleWidthMm,
1640
+ barcodeUnitsPerModule,
1641
+ buildRulerTicks,
1642
+ calcResizeRect,
1643
+ canMergeReason,
1644
+ cellFitCapMm,
1645
+ cellFitKey,
1646
+ cellFitWidthMm,
1647
+ chooseMajorStepMM,
1648
+ clampResizedColumnWidth,
1649
+ clampScalePercent,
1650
+ clampToZone,
1651
+ computeAdsorb,
1652
+ computeFitScale,
1653
+ createCell,
1654
+ createDefaultOptions,
1655
+ createDefaultTable,
1656
+ createFieldElement,
1657
+ createRuntimeElement,
1658
+ deleteCol,
1659
+ deleteRow,
1660
+ engine,
1661
+ evaluateTemplate,
1662
+ filterGroups,
1663
+ finalizeElementZone,
1664
+ findMainCell,
1665
+ floorFontSize,
1666
+ generateGroupId,
1667
+ generateId,
1668
+ getDemoData,
1669
+ getElementBindings,
1670
+ getPaperDimensions,
1671
+ getPaperSizeMM,
1672
+ getSpanRect,
1673
+ getTableCellBindings,
1674
+ getZoneRects,
1675
+ groupFields,
1676
+ insertCol,
1677
+ insertRow,
1678
+ isContinuousPaperSize,
1679
+ isLabelPaperSize,
1680
+ labelPaperDefaults,
1681
+ matchKeywords,
1682
+ mergeCells,
1683
+ minorStepMM,
1684
+ mmToPx,
1685
+ nextWheelScale,
1686
+ normalizeSelection,
1687
+ normalizeTemplateUnits,
1688
+ parseCellFitKey,
1689
+ ptToMm,
1690
+ pxToMm,
1691
+ resolveBarcodeDesignValue,
1692
+ resolveBarcodeSize,
1693
+ resolveCellBorderCss,
1694
+ resolveCellTextFit,
1695
+ resolveElementTextFit,
1696
+ resolveShrinkMinFontSize,
1697
+ resolveTextBinding,
1698
+ roundFontSize,
1699
+ safeEval,
1700
+ searchProperties,
1701
+ setRowType,
1702
+ splitCells,
1703
+ syncTableElementSize,
1704
+ useAlign,
1705
+ useBindingDisplay,
1706
+ useGroup,
1707
+ useKeyboard,
1708
+ useResize,
1709
+ zoneFromPaperPoint
1710
+ };