gm-printer 10.42.4-beta.1 → 10.42.4-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.
@@ -8,7 +8,12 @@ import BarCode from './barcode'
8
8
  import QrCode from './qrcode'
9
9
  import Tag from './tag'
10
10
 
11
- @inject('printerStore')
11
+ // editStore 用函数形式注入:打印态(do_print 的 iframe)没有 editStore Provider,
12
+ // 字符串形式的 inject 会直接抛错,这里降级为 null 关闭编辑能力
13
+ @inject(({ printerStore, editStore }) => ({
14
+ printerStore,
15
+ editStore: editStore || null
16
+ }))
12
17
  @observer
13
18
  class Block extends React.Component {
14
19
  constructor(props) {
@@ -16,8 +21,10 @@ class Block extends React.Component {
16
21
  this.state = {
17
22
  clientX: null,
18
23
  clientY: null,
19
- isEdit: false
24
+ isEdit: false,
25
+ isResizing: false
20
26
  }
27
+ this.resizeState = null
21
28
  }
22
29
 
23
30
  componentDidMount() {
@@ -32,6 +39,7 @@ class Block extends React.Component {
32
39
  'gm-printer-block-edit',
33
40
  this.handleBlockEdit
34
41
  )
42
+ this.teardownResize()
35
43
  }
36
44
 
37
45
  handleBlockEdit = e => {
@@ -112,6 +120,118 @@ class Block extends React.Component {
112
120
  })
113
121
  }
114
122
 
123
+ // 电子签章:图片四角等比例拖拽缩放
124
+ handleResizeStart = (direction, e) => {
125
+ // 阻止触发块的选中/拖动
126
+ e.stopPropagation()
127
+ e.preventDefault()
128
+
129
+ const {
130
+ config: { style, ratio }
131
+ } = this.props
132
+
133
+ const initW = parseFloat(style.width) || 0
134
+ const initH = parseFloat(style.height) || 0
135
+ // 旧模板无 ratio 时按当前显示宽高锁定;宽或高缺失(存量块无 width)时退回 1,
136
+ // 避免比例算出 0 导致高度 Infinity
137
+ const fallbackRatio = initW > 0 && initH > 0 ? initW / initH : 1
138
+ this.resizeState = {
139
+ direction,
140
+ startX: e.clientX,
141
+ startY: e.clientY,
142
+ initW,
143
+ initH,
144
+ initL: parseFloat(style.left) || 0,
145
+ initT: parseFloat(style.top) || 0,
146
+ // ratio = 原始宽/高
147
+ ratio: ratio || fallbackRatio,
148
+ baseStyle: style
149
+ }
150
+
151
+ this.setState({
152
+ isResizing: true
153
+ })
154
+
155
+ window.addEventListener('mousemove', this.handleResizeMove)
156
+ window.addEventListener('mouseup', this.handleResizeEnd)
157
+ }
158
+
159
+ handleResizeMove = e => {
160
+ if (!this.resizeState) return
161
+
162
+ // rAF 节流,避免高频 mousemove 打爆渲染
163
+ if (this.resizePending) return
164
+ this.resizePending = e
165
+ window.requestAnimationFrame(() => {
166
+ const evt = this.resizePending
167
+ this.resizePending = null
168
+ if (!evt || !this.resizeState) return
169
+ this.doResize(evt)
170
+ })
171
+ }
172
+
173
+ doResize = e => {
174
+ const {
175
+ direction,
176
+ startX,
177
+ initW,
178
+ initH,
179
+ initL,
180
+ initT,
181
+ ratio,
182
+ baseStyle
183
+ } = this.resizeState
184
+ const dx = e.clientX - startX
185
+
186
+ // 右侧角向右放大,左侧角向左放大
187
+ let width =
188
+ direction === 'rb' || direction === 'rt' ? initW + dx : initW - dx
189
+ // 最小尺寸兜底,防止翻转
190
+ width = Math.max(width, 10)
191
+ const height = Math.round(width / ratio)
192
+ width = Math.round(width)
193
+
194
+ // 锚定对角:左向角拖拽时右/下边不动,上向角拖拽时下边不动
195
+ let left = initL
196
+ let top = initT
197
+ if (direction === 'lb' || direction === 'lt') {
198
+ left = initL + initW - width
199
+ }
200
+ if (direction === 'rt' || direction === 'lt') {
201
+ top = initT + initH - height
202
+ }
203
+
204
+ dispatchMsg('gm-printer-block-style-set', {
205
+ style: {
206
+ ...baseStyle,
207
+ width: width + 'px',
208
+ height: height + 'px',
209
+ left: left + 'px',
210
+ top: top + 'px'
211
+ }
212
+ })
213
+ }
214
+
215
+ handleResizeEnd = () => {
216
+ // 先 flush 掉节流里排队中的最后一帧,避免松手回退
217
+ if (this.resizePending) {
218
+ const evt = this.resizePending
219
+ this.resizePending = null
220
+ this.doResize(evt)
221
+ }
222
+ this.teardownResize()
223
+ this.setState({
224
+ isResizing: false
225
+ })
226
+ }
227
+
228
+ teardownResize = () => {
229
+ window.removeEventListener('mousemove', this.handleResizeMove)
230
+ window.removeEventListener('mouseup', this.handleResizeEnd)
231
+ this.resizeState = null
232
+ this.resizePending = null
233
+ }
234
+
115
235
  render() {
116
236
  let {
117
237
  name,
@@ -119,6 +239,7 @@ class Block extends React.Component {
119
239
  pageIndex,
120
240
  className,
121
241
  printerStore,
242
+ editStore,
122
243
  ...rest
123
244
  } = this.props
124
245
  const { isEdit } = this.state
@@ -186,6 +307,14 @@ class Block extends React.Component {
186
307
  }
187
308
 
188
309
  const active = name === printerStore.selected
310
+ const { isResizing } = this.state
311
+ const showResizeHandles = !!(
312
+ active &&
313
+ type === 'image' &&
314
+ editStore &&
315
+ editStore.imageConfig &&
316
+ editStore.imageConfig.resizable
317
+ )
189
318
 
190
319
  return (
191
320
  <div
@@ -193,7 +322,7 @@ class Block extends React.Component {
193
322
  className={classNames('gm-printer-block', className, {
194
323
  active
195
324
  })}
196
- draggable
325
+ draggable={!isResizing}
197
326
  onDragStart={this.handleDragStart}
198
327
  onDragEnd={this.handleDragEnd}
199
328
  onClick={this.handleClick}
@@ -221,6 +350,26 @@ class Block extends React.Component {
221
350
  />
222
351
  )}
223
352
  {content}
353
+ {showResizeHandles && (
354
+ <>
355
+ <div
356
+ className='gm-printer-resize-handle gm-printer-resize-lt'
357
+ onMouseDown={this.handleResizeStart.bind(this, 'lt')}
358
+ />
359
+ <div
360
+ className='gm-printer-resize-handle gm-printer-resize-rt'
361
+ onMouseDown={this.handleResizeStart.bind(this, 'rt')}
362
+ />
363
+ <div
364
+ className='gm-printer-resize-handle gm-printer-resize-lb'
365
+ onMouseDown={this.handleResizeStart.bind(this, 'lb')}
366
+ />
367
+ <div
368
+ className='gm-printer-resize-handle gm-printer-resize-rb'
369
+ onMouseDown={this.handleResizeStart.bind(this, 'rb')}
370
+ />
371
+ </>
372
+ )}
224
373
  </div>
225
374
  )
226
375
  }
@@ -231,7 +380,8 @@ Block.propTypes = {
231
380
  config: PropTypes.object.isRequired,
232
381
  pageIndex: PropTypes.number.isRequired,
233
382
  className: PropTypes.string,
234
- printerStore: PropTypes.object
383
+ printerStore: PropTypes.object,
384
+ editStore: PropTypes.object
235
385
  }
236
386
 
237
387
  export default Block
@@ -62,7 +62,8 @@ function init() {
62
62
  * 影响合计行渲染与行高,进而影响分页边界)
63
63
  * @returns {Promise<{pages: Array, remainPageHeight: number, tableCustomerRowHeight: number}>}
64
64
  * pages: [[{type, index, begin, end}, ...], ...] 对 data._table[contents[i].dataKey] 行号的切片
65
- * remainPageHeight: 最后一页剩余高度(供行数填充换算)
65
+ * remainPageHeight: 行数填充基准高度(填充表格最后一段所在页的剩余高度,
66
+ * 无填充表格时为末页剩余;供行数填充换算)
66
67
  * tableCustomerRowHeight: 表格行高(供行数填充换算)
67
68
  */
68
69
  function computePages({ data, config, isSomeSubtotalTr }) {
@@ -5,7 +5,7 @@ export const INDEX_KEY = '序号'
5
5
 
6
6
  const SPECIAL_ROW_KEYS = [
7
7
  '_special',
8
- '_specialText',
8
+ '_specialText', // 分类标题行(三级分类/商品分类的品类名行),漏掉会在多栏重排时被当普通行丢弃
9
9
  '_diyCategorySubtotal',
10
10
  '_diyTagSubtotal',
11
11
  '_customTr'
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import _ from 'lodash'
3
- import { getDataKey, isMultiTable } from '../util'
3
+ import { getDataKey, isMultiTable, isMaskedValue, MASKED_VALUE } from '../util'
4
4
  import Big from 'big.js'
5
5
  import { observer } from 'mobx-react'
6
6
  import { get } from 'mobx'
@@ -19,6 +19,8 @@ const regExp = text => {
19
19
  * @returns {*|string}
20
20
  */
21
21
  const sumCol = (key, dataList, summaryFieldsResultToNumber = []) => {
22
+ // 任一值脱敏 '***' 则合计显 '***'(原逻辑 Big('***') 抛异常被 catch,合计显空串)
23
+ if (dataList.some(item => isMaskedValue(item[key]))) return MASKED_VALUE
22
24
  let result
23
25
  try {
24
26
  result = dataList
@@ -100,6 +100,13 @@ class Printer extends React.Component {
100
100
  await this.props.printerStore.setFillIndex(nextProps.fillIndex)
101
101
  await this.props.printerStore.setData(nextProps.data)
102
102
  await this.props.printerStore.setReady(true)
103
+ // 与 componentDidMount 同序:先按当前数据分页拿到行数填充基准,再补空行、
104
+ // 按补空后的数据重新分页。缺前置分页时 changeTableData 只能用上一轮
105
+ // (可能已含空行)的 remainPageHeight 换算行数,开关填充时行数会偏小或偏大
106
+ await this.props.printerStore.computedPages()
107
+ // 把「未补空」基准回报给编辑器,与挂载流程同口径
108
+ this.props.getremainpageHeight &&
109
+ this.props.getremainpageHeight(this.props.printerStore.remainPageHeight)
103
110
  await this.props.printerStore.changeTableData()
104
111
  await this.props.printerStore.computedPages()
105
112
  }
@@ -132,7 +139,7 @@ class Printer extends React.Component {
132
139
  printerStore.changeTableData()
133
140
  printerStore.computedPages()
134
141
  }
135
- // 获取剩余空白高度,传到editor
142
+ // 获取剩余空白高度,传到 editor
136
143
  getremainpageHeight && getremainpageHeight(printerStore.remainPageHeight)
137
144
  }
138
145
  printerStore.setReady(true)
@@ -7,7 +7,8 @@ import {
7
7
  getDataKey,
8
8
  caclSingleDetailsPageHeight,
9
9
  getArrayMid,
10
- getOverallOrderTrHeight
10
+ getOverallOrderTrHeight,
11
+ isMaskedValue
11
12
  } from '../util'
12
13
  import {
13
14
  isDetailOneRowSelectKey,
@@ -48,6 +49,8 @@ const canSplitRowDetails = (dataKey, row) => {
48
49
  }
49
50
 
50
51
  const price = (n, f = 2) => {
52
+ // 脱敏值 '***' 原样返回(不能走 isNaN 判空,空单元格仍需显空)
53
+ if (isMaskedValue(n)) return n
51
54
  if (isNaN(n)) return null
52
55
  return Big(n || 0).toFixed(f)
53
56
  }
@@ -67,7 +70,7 @@ class PrinterStore {
67
70
 
68
71
  @observable pages = [] // [{type, index, begin, end}]
69
72
 
70
- /** 当前页剩余空白高度 */
73
+ /** 行数填充基准:填充表格最后一段所在页的剩余空白高度;无填充表格时为末页剩余 */
71
74
  @observable remainPageHeight = 0
72
75
  @observable isSave = false
73
76
  /** data._table 非 observable,变更后递增以驱动预览重渲 */
@@ -326,6 +329,8 @@ class PrinterStore {
326
329
 
327
330
  // 使用本地数组累积分页结果,避免循环中反复触发 observable 更新
328
331
  const pages = []
332
+ // 各表格的行高数组(含明细拆分 splice 后的结果),供行数填充基准按同一口径复算
333
+ const splicedHeightsMap = {}
329
334
  // 每页必有 页眉header, 页脚footer , 签名
330
335
  const allPagesHaveThisHeight = heightMap.header + heightMap.footer
331
336
  // 退出计算! 因为页眉 + 页脚 > currentPageHeight,页面装不下其他东西
@@ -361,43 +366,15 @@ class PrinterStore {
361
366
  index++
362
367
  continue
363
368
  }
364
- const {
365
- subtotal,
366
- dataKey,
367
- summaryConfig,
368
- overallOrder,
369
- diySubtotal
370
- } = content
371
- // 如果显示每页合计,那么table高度多预留一行高度
372
- const subtotalTrHeight = subtotal.show ? getSumTrHeight(subtotal) : 0
373
- // 如果显示整单合计,那么table高度多预留一行高度
374
- const overallOrderTrHeight = overallOrder?.show
375
- ? getOverallOrderTrHeight(overallOrder)
376
- : 0
377
- // 如果每页合计(新的),那么table高度多预留一行高度
378
- const pageSummaryTrHeight =
379
- summaryConfig?.pageSummaryShow && !isMultiTable(dataKey) // 双栏table没有每页合计
380
- ? getSumTrHeight(summaryConfig)
381
- : 0
382
-
383
- // 如果显示自定义每页合计,那么table高度多预留一行高度
384
- const diySubtotalTrHeight = diySubtotal?.show
385
- ? getOverallOrderTrHeight(diySubtotal) // 使用相同的计算方式
386
- : 0
387
-
388
- // 每个表格都具有的高度
389
- const allTableHaveThisHeight =
390
- table.head.height +
391
- subtotalTrHeight +
392
- pageSummaryTrHeight +
393
- overallOrderTrHeight +
394
- diySubtotalTrHeight
369
+ const { dataKey } = content
370
+ // 每个表格都具有的高度(表头 + 各类合计行),提取成方法供填充基准复用同一口径
371
+ const allTableHaveThisHeight = this.getTableAllHaveHeight(index)
395
372
 
396
373
  /** 当前page页面的最小高度 */
397
374
  const currentPageMinimumHeight =
398
375
  allPagesHaveThisHeight + allTableHaveThisHeight
399
376
  // 最后一项是表格时,末页仍要渲染签名:装行可用高度预留签名,避免空行顶进签名区
400
- const isLastContent = index === this.config.contents.length - 1
377
+ const isLastContent = index === contentsLength - 1
401
378
  const signReserve = isLastContent ? this.getSignHeight() : 0
402
379
  /** 当前page可容纳的table高度 */
403
380
  let pageAccomodateTableHeight = roundHeight(
@@ -407,6 +384,8 @@ class PrinterStore {
407
384
  const heights = [
408
385
  ...this.getNormalTableBodyHeights(table.body.heights, dataKey)
409
386
  ]
387
+ // 保留引用:该数组会被明细拆分 splice,填充基准需要用拆分后的行高
388
+ splicedHeightsMap[index] = heights
410
389
  const tableData = tableDataMap[dataKey] || []
411
390
  // 表格行的索引,用于table.slice(begin, end), 分割到不同页面中
412
391
  let begin = 0
@@ -489,7 +468,7 @@ class PrinterStore {
489
468
  )
490
469
  break
491
470
  }
492
- // 行无法拆分(普通长文本列)或拆分失败
471
+ // 行无法拆分(普通长文本列)或拆分失败时,强制推进 end 防止死循环
493
472
  if (end === begin) {
494
473
  // 新一页可容纳的 table 高度(页高 - 页眉页脚)
495
474
  const fullPageAccomodateTableHeight = roundHeight(
@@ -608,15 +587,127 @@ class PrinterStore {
608
587
  currentPageHeight += this.getSignHeight()
609
588
  }
610
589
 
611
- this.remainPageHeight = Math.max(
612
- 0,
613
- Math.round(pageHeight - currentPageHeight)
590
+ // 行数填充基准:优先取「填充表格最后一段所在页」的剩余高度(表格底部与该页
591
+ // 可用底边之间的空白)。旧口径固定取末页剩余,当填充表格不在末页(下方内容
592
+ // 被挤到下一页)时,行数按末页空间换算会超出所在页容量:多出的空行被切到
593
+ // 下一页渲染成空表格,或把同页后续表格挤到下一页。
594
+ // 填充基准不做 Math.round 向上取整,避免整行边界多算 ≤1px 导致重分页时溢出
595
+ const fillRemainHeight = this.computeFillTableRemainHeight(
596
+ pages,
597
+ pageHeight,
598
+ heightMap,
599
+ splicedHeightsMap
614
600
  )
601
+ this.remainPageHeight =
602
+ fillRemainHeight != null
603
+ ? Math.max(0, fillRemainHeight)
604
+ : Math.max(0, Math.round(pageHeight - currentPageHeight))
615
605
  this.isSave = false
616
606
  this.config.isSave = false
617
607
  this.repackVerticalMultiByPages()
618
608
  }
619
609
 
610
+ /** 表格固定高度:表头 + 每页/整单/自定义合计行(不含数据行),与 computedPages 分页口径一致 */
611
+ getTableAllHaveHeight(contentIndex) {
612
+ const table = this.tablesInfo[`contents.table.${contentIndex}`]
613
+ if (!table?.head) return 0
614
+ const content = this.config.contents[contentIndex]
615
+ const {
616
+ subtotal,
617
+ dataKey,
618
+ summaryConfig,
619
+ overallOrder,
620
+ diySubtotal
621
+ } = content
622
+ // 如果显示每页合计,那么table高度多预留一行高度
623
+ const subtotalTrHeight = subtotal.show ? getSumTrHeight(subtotal) : 0
624
+ // 如果显示整单合计,那么table高度多预留一行高度
625
+ const overallOrderTrHeight = overallOrder?.show
626
+ ? getOverallOrderTrHeight(overallOrder)
627
+ : 0
628
+ // 如果每页合计(新的),那么table高度多预留一行高度
629
+ const pageSummaryTrHeight =
630
+ summaryConfig?.pageSummaryShow && !isMultiTable(dataKey) // 双栏table没有每页合计
631
+ ? getSumTrHeight(summaryConfig)
632
+ : 0
633
+ // 如果显示自定义每页合计,那么table高度多预留一行高度
634
+ const diySubtotalTrHeight = diySubtotal?.show
635
+ ? getOverallOrderTrHeight(diySubtotal) // 使用相同的计算方式
636
+ : 0
637
+
638
+ return (
639
+ table.head.height +
640
+ subtotalTrHeight +
641
+ pageSummaryTrHeight +
642
+ overallOrderTrHeight +
643
+ diySubtotalTrHeight
644
+ )
645
+ }
646
+
647
+ /** 行数填充目标表格在 contents 中的索引(对齐 tableConfig 的解析口径),无法解析返回 null */
648
+ getFillTableContentIndex() {
649
+ const { autoFillConfig } = this.config
650
+ // checked 为 false 时 region 也保留着(编辑器开关来回切),此时同样能定位,
651
+ // 让基准在「未开启填充」的分页里也保持正确口径
652
+ if (!this.selectedRegion && !autoFillConfig?.region) return null
653
+ const region = this.selectedRegion || autoFillConfig.region
654
+ const contentIndex = Number(region.split('.')[2])
655
+ const content = this.config.contents[contentIndex]
656
+ if (!content || !_.has(content, 'dataKey')) return null
657
+ return contentIndex
658
+ }
659
+
660
+ /**
661
+ * 填充表格最后一段所在页的剩余高度(表格底部到该页可用底边的空白)。
662
+ * 高度口径与 computedPages 一致:页眉页脚 + 该页各内容
663
+ * (表格固定高度 + 分段行高 / panel 实测高),所在页为末页时再扣签名区。
664
+ * 定位不到填充表格(未配置 / 表格无数据被跳过)时返回 null,由调用方退回旧口径。
665
+ */
666
+ computeFillTableRemainHeight(
667
+ pages,
668
+ pageHeight,
669
+ heightMap,
670
+ splicedHeightsMap
671
+ ) {
672
+ const fillIndex = this.getFillTableContentIndex()
673
+ if (fillIndex == null || !pages?.length) return null
674
+
675
+ // 定位填充表格最后一个分段所在的页(一个表格每页至多一个分段)
676
+ let pageIndex = -1
677
+ for (let i = pages.length - 1; i >= 0; i--) {
678
+ if (
679
+ (pages[i] || []).some(
680
+ panel => panel?.type === 'table' && panel.index === fillIndex
681
+ )
682
+ ) {
683
+ pageIndex = i
684
+ break
685
+ }
686
+ }
687
+ if (pageIndex < 0) return null
688
+
689
+ // 按分页同一套口径累计该页已用高度:表格下方还有内容时一并计入,
690
+ // 填充就只会补「表格与后续内容之间」的空隙,不会把它们挤到下一页
691
+ let used = heightMap.header + heightMap.footer
692
+ ;(pages[pageIndex] || []).forEach(panel => {
693
+ if (panel.type === 'table') {
694
+ const heights = splicedHeightsMap[panel.index] || []
695
+ used += this.getTableAllHaveHeight(panel.index)
696
+ for (let r = panel.begin; r < panel.end; r++) {
697
+ used += heights[r] || 0
698
+ }
699
+ } else {
700
+ used += heightMap[`contents.panel.${panel.index}`] || 0
701
+ }
702
+ })
703
+
704
+ // 签名只渲染在最后一页,填充所在页为末页时同样要预留
705
+ const signReserve =
706
+ pageIndex === pages.length - 1 ? this.getSignHeight() : 0
707
+ const remain = pageHeight - used - signReserve
708
+ return Number.isFinite(remain) ? remain : null
709
+ }
710
+
620
711
  getTablePageRanges(contentIndex, rows) {
621
712
  const ranges = []
622
713
  this.pages.forEach(page => {
@@ -2,10 +2,9 @@ import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import _ from 'lodash'
4
4
  // import Big from 'big.js'
5
- import { coverDigit2Uppercase, getDataKey } from '../util'
5
+ import { coverDigit2Uppercase, getDataKey, maskedSum } from '../util'
6
6
  import { observer } from 'mobx-react'
7
7
  import classNames from 'classnames'
8
- import Big from 'big.js'
9
8
 
10
9
  const DiyOverallOrder = props => {
11
10
  const {
@@ -15,20 +14,14 @@ const DiyOverallOrder = props => {
15
14
  } = props
16
15
  const tableData = printerStore.data._table[getDataKey(dataKey)] || []
17
16
 
18
- // 计算合计
17
+ // 计算合计:任一渲染结果脱敏 '***' 则合计显 '***'(整格判定假设同 table_diy_summary_tr)
19
18
  const sumData = field => {
20
- return _.reduce(
21
- tableData,
22
- (a, b, i) => {
23
- let result = a
24
- const bRes = printerStore
25
- .templateTable(field, dataKey, i, pageIndex)
26
- .replace(/\(\)/g, '')
27
- result = a.plus(+bRes || 0)
28
- return result
29
- },
30
- Big(0)
31
- ).toFixed(2)
19
+ const values = _.map(tableData, (b, i) =>
20
+ printerStore
21
+ .templateTable(field, dataKey, i, pageIndex)
22
+ .replace(/\(\)/g, '')
23
+ )
24
+ return maskedSum(values)
32
25
  }
33
26
 
34
27
  if (!diyOverallOrder?.show || !printerStore?.ready) {
@@ -1,11 +1,15 @@
1
1
  import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import _ from 'lodash'
4
- import { coverDigit2Uppercase, getDataKey, isMultiTable } from '../util'
4
+ import {
5
+ coverDigit2Uppercase,
6
+ getDataKey,
7
+ isMultiTable,
8
+ maskedSum
9
+ } from '../util'
5
10
  import { MULTI_SUFFIX, MULTI_SUFFIX3 } from '../config'
6
11
  import { observer } from 'mobx-react'
7
12
  import classNames from 'classnames'
8
- import Big from 'big.js'
9
13
 
10
14
  function buildTemplateField(fieldStr, suffix) {
11
15
  // 如果 field 是 "{{列.下单数}}",提取出 "下单数"
@@ -45,39 +49,18 @@ const DiySummary = props => {
45
49
 
46
50
  // 计算合计
47
51
  const sumData = field => {
48
- if (isMulti) {
49
- return _.reduce(
50
- tableData,
51
- (a, b, i) => {
52
- let result = a
53
- // 先通过 templateTable 获取原始字段的值
54
- const bRes = getRes(field, i)
55
- result = a.plus(+bRes || 0)
56
-
57
- // 双栏
58
- const multiField = buildTemplateField(field, MULTI_SUFFIX)
59
- result = result.plus(+getRes(multiField, i) || 0)
60
-
61
- // 三栏
62
- const multiField3 = buildTemplateField(field, MULTI_SUFFIX3)
63
- result = result.plus(+getRes(multiField3, i) || 0)
64
-
65
- return result
66
- },
67
- Big(0)
68
- ).toFixed(2)
69
- }
70
-
71
- return _.reduce(
72
- tableData,
73
- (a, b, i) => {
74
- let result = a
75
- const bRes = getRes(field, i)
76
- result = a.plus(+bRes || 0)
77
- return result
78
- },
79
- Big(0)
80
- ).toFixed(2)
52
+ // 收集本列渲染结果(含多栏后缀列),任一脱敏 '***' 则合计显 '***'
53
+ // 注意:基于渲染后字符串判定,假设模板整格就是脱敏值;带前后缀的模板(如 '{{列.金额}}元')无法识别
54
+ const values = []
55
+ _.each(tableData, (b, i) => {
56
+ values.push(getRes(field, i))
57
+ if (isMulti) {
58
+ // 双栏/三栏取后缀字段
59
+ values.push(getRes(buildTemplateField(field, MULTI_SUFFIX), i))
60
+ values.push(getRes(buildTemplateField(field, MULTI_SUFFIX3), i))
61
+ }
62
+ })
63
+ return maskedSum(values)
81
64
 
82
65
  function getRes(field, i) {
83
66
  return printerStore
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types'
4
4
  import _ from 'lodash'
5
5
  import { MULTI_SUFFIX, MULTI_SUFFIX3 } from '../config'
6
6
  import Big from 'big.js'
7
- import { coverDigit2Uppercase, getDataKey } from '../util'
7
+ import { coverDigit2Uppercase, getDataKey, maskedSum } from '../util'
8
8
  import { observer } from 'mobx-react'
9
9
  import { get } from 'mobx'
10
10
  import classNames from 'classnames'
@@ -76,21 +76,18 @@ const SubtotalTr = props => {
76
76
  if (field === 'real_item_price') field = '出库金额'
77
77
  if (field === 'settle_money2') field = '结算金额'
78
78
  if (field === 'total_money2') field = '金额'
79
- return _.reduce(
80
- list,
81
- (a, b) => {
82
- let result = a
83
- result = a.plus(b[field] || 0)
84
- if (b?.[field + MULTI_SUFFIX]) {
85
- result = result.plus(b?.[field + MULTI_SUFFIX])
86
- }
87
- if (b?.[field + MULTI_SUFFIX3]) {
88
- result = result.plus(b?.[field + MULTI_SUFFIX3])
89
- }
90
- return result
91
- },
92
- Big(0)
93
- ).toFixed(2)
79
+ // 收集本列所有值(含多栏后缀列),任一脱敏 '***' 则合计显 '***',否则 Big('***') 会崩溃
80
+ const values = []
81
+ _.each(list, b => {
82
+ values.push(b[field])
83
+ if (b?.[field + MULTI_SUFFIX]) {
84
+ values.push(b[field + MULTI_SUFFIX])
85
+ }
86
+ if (b?.[field + MULTI_SUFFIX3]) {
87
+ values.push(b[field + MULTI_SUFFIX3])
88
+ }
89
+ })
90
+ return maskedSum(values)
94
91
  }
95
92
 
96
93
  const getData = field => {