gm-printer 10.42.3-beta.1 → 10.42.3-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/demo.js +1 -1
- package/package.json +1 -1
- package/src/printer/multi_column.js +98 -43
- package/src/printer/store.js +25 -36
package/package.json
CHANGED
|
@@ -106,7 +106,8 @@ export const flattenHorizontalPackedRows = (rows, colNumber) => {
|
|
|
106
106
|
export const flattenVerticalSegment = (rows, colNumber) => {
|
|
107
107
|
const columns = Array.from({ length: colNumber }, () => [])
|
|
108
108
|
rows.forEach(row => {
|
|
109
|
-
|
|
109
|
+
// _isEmptyData 行的序号可能被填充过,必须显式跳过,否则会被当真实商品吸收
|
|
110
|
+
if (isSpecialTableRow(row) || row._isEmptyData) return
|
|
110
111
|
splitPackedRow(row, colNumber).forEach((item, col) => {
|
|
111
112
|
if (isRealItem(item)) {
|
|
112
113
|
columns[col].push(stripMultiSuffixFields(item))
|
|
@@ -116,6 +117,37 @@ export const flattenVerticalSegment = (rows, colNumber) => {
|
|
|
116
117
|
return columns.reduce((list, col) => list.concat(col), [])
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
/** 按特殊行(分类小计/标题等)把整表切成若干「组」:相邻非特殊行的 run */
|
|
121
|
+
export const splitRowsIntoGroups = rows => {
|
|
122
|
+
const groups = []
|
|
123
|
+
let start = -1
|
|
124
|
+
for (let i = 0; i <= (rows || []).length; i++) {
|
|
125
|
+
const isBoundary = i === rows.length || isSpecialTableRow(rows[i])
|
|
126
|
+
if (!isBoundary && start < 0) {
|
|
127
|
+
start = i
|
|
128
|
+
}
|
|
129
|
+
if (isBoundary) {
|
|
130
|
+
if (start >= 0) {
|
|
131
|
+
groups.push({ begin: start, end: i })
|
|
132
|
+
}
|
|
133
|
+
start = -1
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return groups
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 源表(单栏/横向多栏)按特殊行分组后逐组拍平 → 每组一个商品队列。
|
|
141
|
+
* 源表不会被 repack 改写,队列是纯函数,重复执行结果不变(幂等)。
|
|
142
|
+
*/
|
|
143
|
+
export const buildSourceGroupQueues = (rows, colNumber) =>
|
|
144
|
+
splitRowsIntoGroups(rows).map(group =>
|
|
145
|
+
flattenHorizontalPackedRows(
|
|
146
|
+
(rows || []).slice(group.begin, group.end),
|
|
147
|
+
colNumber
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
|
|
119
151
|
const assignItemToRow = (row, item, suffix) => {
|
|
120
152
|
const clean = stripMultiSuffixFields(item) || {}
|
|
121
153
|
Object.keys(clean).forEach(key => {
|
|
@@ -442,39 +474,34 @@ export const renumberSegmentSerialsColumnMajor = (
|
|
|
442
474
|
|
|
443
475
|
/**
|
|
444
476
|
* 按分页区间把纵向多栏数据改成「每页先左后右」。
|
|
445
|
-
* -
|
|
446
|
-
*
|
|
447
|
-
*
|
|
477
|
+
* - 按特殊行(分类小计等)把整表切成若干组,每组一个商品队列:
|
|
478
|
+
* 商品只从本组队列取数,永不跨组 —— 奇数商品的组余数留在本组末段空格,
|
|
479
|
+
* 不会挤占下一组(修复分类合并错位)
|
|
480
|
+
* - 每组队列优先用外部传入的源表顺序(groupQueues;源表不被 repack 改写,
|
|
481
|
+
* 重复执行幂等);缺失时按「各页段贡献」从当前行拼接(重排的不动点,
|
|
482
|
+
* 同样幂等,但首趟不做每页重排)
|
|
483
|
+
* - 特殊行钉在原地;非特殊行都是新建对象,清除旧右列残留
|
|
484
|
+
* - fillIndex 时整段按列重编序号,有数据/无数据同一套规则,序号跨段跨组连续
|
|
448
485
|
*/
|
|
449
486
|
export const repackVerticalTableByPageRanges = (
|
|
450
487
|
rows,
|
|
451
488
|
colNumber,
|
|
452
489
|
pageRanges,
|
|
453
490
|
fillIndex = false,
|
|
454
|
-
|
|
491
|
+
groupQueues = null
|
|
455
492
|
) => {
|
|
456
493
|
if (!rows?.length || !pageRanges?.length || colNumber < 2) return rows
|
|
457
|
-
const items = (sourceItems && sourceItems.length
|
|
458
|
-
? sourceItems
|
|
459
|
-
: flattenVerticalSegment(rows, colNumber)
|
|
460
|
-
).map(stripMultiSuffixFields)
|
|
461
494
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const result = new Array(rows.length)
|
|
466
|
-
for (let k = 0; k < rows.length; k++) {
|
|
467
|
-
result[k] = rows[k]
|
|
468
|
-
}
|
|
495
|
+
const groups = splitRowsIntoGroups(rows)
|
|
496
|
+
// 全新数组,避免与旧行对象共享引用;特殊行沿用原引用(钉原地)
|
|
497
|
+
const result = rows.slice()
|
|
469
498
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
pageRanges.forEach(({ begin, end }) => {
|
|
499
|
+
// 页 range 与组边界取交集得到页段,并记录所属组
|
|
500
|
+
const segments = pageRanges.reduce((list, { begin, end }) => {
|
|
473
501
|
const packEnd = Math.min(end, rows.length)
|
|
474
502
|
let i = begin
|
|
475
503
|
while (i < packEnd) {
|
|
476
504
|
if (isSpecialTableRow(rows[i])) {
|
|
477
|
-
result[i] = rows[i]
|
|
478
505
|
i++
|
|
479
506
|
continue
|
|
480
507
|
}
|
|
@@ -482,32 +509,60 @@ export const repackVerticalTableByPageRanges = (
|
|
|
482
509
|
while (j < packEnd && !isSpecialTableRow(rows[j])) {
|
|
483
510
|
j++
|
|
484
511
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
512
|
+
if (j > i) {
|
|
513
|
+
list.push({
|
|
514
|
+
begin: i,
|
|
515
|
+
end: j,
|
|
516
|
+
groupIndex: groups.findIndex(g => i >= g.begin && i < g.end)
|
|
517
|
+
})
|
|
490
518
|
}
|
|
519
|
+
i = j
|
|
520
|
+
}
|
|
521
|
+
return list
|
|
522
|
+
}, [])
|
|
523
|
+
|
|
524
|
+
const externalQueues = groupQueues || []
|
|
525
|
+
const queues = groups.map((_, gi) => {
|
|
526
|
+
const external = externalQueues[gi]
|
|
527
|
+
if (external && external.length) {
|
|
528
|
+
return external
|
|
529
|
+
}
|
|
530
|
+
return segments
|
|
531
|
+
.filter(s => s.groupIndex === gi)
|
|
532
|
+
.reduce(
|
|
533
|
+
(list, s) =>
|
|
534
|
+
list.concat(
|
|
535
|
+
flattenVerticalSegment(rows.slice(s.begin, s.end), colNumber)
|
|
536
|
+
),
|
|
537
|
+
[]
|
|
538
|
+
)
|
|
539
|
+
})
|
|
491
540
|
|
|
492
|
-
|
|
493
|
-
const chunk = items.slice(consumed, consumed + capacity)
|
|
494
|
-
const packed = packVerticalNewspaper(chunk, colNumber, rowCount, false)
|
|
495
|
-
packed.forEach((row, offset) => {
|
|
496
|
-
result[i + offset] = row
|
|
497
|
-
})
|
|
498
|
-
consumed += chunk.length
|
|
499
|
-
|
|
500
|
-
if (fillIndex) {
|
|
501
|
-
nextSerial = renumberSegmentSerialsColumnMajor(
|
|
502
|
-
result,
|
|
503
|
-
i,
|
|
504
|
-
j,
|
|
505
|
-
colNumber,
|
|
506
|
-
nextSerial
|
|
507
|
-
)
|
|
508
|
-
}
|
|
541
|
+
if (!queues.some(q => q.length) && !fillIndex) return rows
|
|
509
542
|
|
|
510
|
-
|
|
543
|
+
const consumed = groups.map(() => 0)
|
|
544
|
+
let nextSerial = 1
|
|
545
|
+
segments.forEach(({ begin, end, groupIndex }) => {
|
|
546
|
+
const rowCount = end - begin
|
|
547
|
+
const queue = queues[groupIndex]
|
|
548
|
+
const chunk = queue.slice(
|
|
549
|
+
consumed[groupIndex],
|
|
550
|
+
consumed[groupIndex] + rowCount * colNumber
|
|
551
|
+
)
|
|
552
|
+
consumed[groupIndex] += chunk.length
|
|
553
|
+
packVerticalNewspaper(chunk, colNumber, rowCount, false).forEach(
|
|
554
|
+
(row, offset) => {
|
|
555
|
+
result[begin + offset] = row
|
|
556
|
+
}
|
|
557
|
+
)
|
|
558
|
+
if (fillIndex) {
|
|
559
|
+
nextSerial = renumberSegmentSerialsColumnMajor(
|
|
560
|
+
result,
|
|
561
|
+
begin,
|
|
562
|
+
end,
|
|
563
|
+
colNumber,
|
|
564
|
+
nextSerial
|
|
565
|
+
)
|
|
511
566
|
}
|
|
512
567
|
})
|
|
513
568
|
return result
|
package/src/printer/store.js
CHANGED
|
@@ -30,7 +30,9 @@ import {
|
|
|
30
30
|
packHorizontalRowMajor,
|
|
31
31
|
createEmptyRows,
|
|
32
32
|
createEmptyRowSkeleton,
|
|
33
|
-
|
|
33
|
+
isSpecialTableRow,
|
|
34
|
+
splitRowsIntoGroups,
|
|
35
|
+
buildSourceGroupQueues
|
|
34
36
|
} from './multi_column'
|
|
35
37
|
|
|
36
38
|
export const TR_BASE_HEIGHT = 23
|
|
@@ -760,13 +762,28 @@ class PrinterStore {
|
|
|
760
762
|
// 无商品时仍可能需要给空行按列编序号
|
|
761
763
|
if (!sourceItems.length && !this.fillIndex) return
|
|
762
764
|
|
|
765
|
+
// 源表按特殊行分组 → 每组一个商品队列:商品永不跨分类小计行
|
|
766
|
+
const groupQueues = buildSourceGroupQueues(sourceRows, sourceColNumber)
|
|
767
|
+
const verticalGroups = splitRowsIntoGroups(verticalRows)
|
|
768
|
+
const lastGroup = verticalGroups[verticalGroups.length - 1]
|
|
769
|
+
const lastGroupAllEmpty =
|
|
770
|
+
lastGroup &&
|
|
771
|
+
verticalRows
|
|
772
|
+
.slice(lastGroup.begin, lastGroup.end)
|
|
773
|
+
.every(row => row?._isEmptyData)
|
|
774
|
+
// 组数对齐(允许纵向表多一个纯空行尾组=行数填充产物)才用源表队列;
|
|
775
|
+
// 否则退回「按页段贡献」兜底:最坏不做每页重排,绝不会跨组串货
|
|
776
|
+
const aligned =
|
|
777
|
+
groupQueues.length === verticalGroups.length ||
|
|
778
|
+
(groupQueues.length === verticalGroups.length - 1 && lastGroupAllEmpty)
|
|
779
|
+
|
|
763
780
|
const pageRanges = this.getTablePageRanges(contentIndex, verticalRows)
|
|
764
781
|
this.data._table[verticalKey] = repackVerticalTableByPageRanges(
|
|
765
782
|
verticalRows,
|
|
766
783
|
colNumber,
|
|
767
784
|
pageRanges,
|
|
768
785
|
this.fillIndex,
|
|
769
|
-
|
|
786
|
+
aligned ? groupQueues : null
|
|
770
787
|
)
|
|
771
788
|
this.dataRevision += 1
|
|
772
789
|
})
|
|
@@ -1072,44 +1089,16 @@ class PrinterStore {
|
|
|
1072
1089
|
const emptyCount =
|
|
1073
1090
|
emptyTable.length > 0 ? emptyTable.length : toFillEmptyCount
|
|
1074
1091
|
|
|
1075
|
-
//
|
|
1092
|
+
// 纵向多栏:保留真实行(含分类小计等特殊行),仅追加空行补页;
|
|
1093
|
+
// 每页先左后右、按组落位由随后的 repackVerticalMultiByPages 完成
|
|
1076
1094
|
if (/_vertical$/.test(dataKey)) {
|
|
1077
1095
|
const sourceKey = dataKey.replace(/_vertical$/, '')
|
|
1078
|
-
const singleKey = sourceKey.replace(/_multi3?/, '')
|
|
1079
1096
|
const colNumber = getMultiNumber(sourceKey)
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
const sourceRows =
|
|
1085
|
-
this.data._table[singleKey] || this.data._table[sourceKey] || []
|
|
1086
|
-
const sourceColNumber = isMultiTable(singleKey)
|
|
1087
|
-
? getMultiNumber(singleKey)
|
|
1088
|
-
: isMultiTable(sourceKey)
|
|
1089
|
-
? colNumber
|
|
1090
|
-
: 1
|
|
1091
|
-
const sourceItems = flattenHorizontalPackedRows(
|
|
1092
|
-
sourceRows,
|
|
1093
|
-
sourceColNumber
|
|
1094
|
-
)
|
|
1095
|
-
const baseRowCount = sourceItems.length
|
|
1096
|
-
? Math.max(1, Math.ceil(sourceItems.length / colNumber))
|
|
1097
|
-
: 0
|
|
1098
|
-
// 钉死总行:left-first repack 后 _isEmptyData 变少,不能用 emptyTable.length 回推
|
|
1099
|
-
const emptyCount = resolveVerticalEmptyCount(
|
|
1100
|
-
tableData.length,
|
|
1101
|
-
baseRowCount,
|
|
1102
|
-
emptyTable.length > 0
|
|
1103
|
-
? emptyTable.length
|
|
1104
|
-
: this.getFilledTableData(table).length,
|
|
1105
|
-
emptyTable.length > 0
|
|
1097
|
+
const sample =
|
|
1098
|
+
table.find(row => row && !isSpecialTableRow(row)) || tableData[0]
|
|
1099
|
+
this.data._table[dataKey] = table.concat(
|
|
1100
|
+
createEmptyRows(emptyCount, sample, colNumber)
|
|
1106
1101
|
)
|
|
1107
|
-
const dataRows = baseRowCount
|
|
1108
|
-
? packVerticalNewspaper(sourceItems, colNumber, baseRowCount, false)
|
|
1109
|
-
: []
|
|
1110
|
-
const sample = dataRows[0] || sourceRows[0]
|
|
1111
|
-
const emptyRows = createEmptyRows(emptyCount, sample, colNumber)
|
|
1112
|
-
this.data._table[dataKey] = dataRows.concat(emptyRows)
|
|
1113
1102
|
return
|
|
1114
1103
|
}
|
|
1115
1104
|
|