gm-printer 11.1.0-beta.0 → 11.1.0-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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-printer",
3
- "version": "11.1.0-beta.0",
3
+ "version": "11.1.0-beta.2",
4
4
  "description": "diy printer",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -125,6 +125,7 @@ class Table extends React.Component {
125
125
  <>
126
126
  <SubtotalTr
127
127
  range={range}
128
+ pageIndex={pageIndex}
128
129
  config={config}
129
130
  printerStore={printerStore}
130
131
  isSomeSubtotalTr={isSomeSubtotalTr}
@@ -140,6 +141,7 @@ class Table extends React.Component {
140
141
  <PageSummary {...this.props} />
141
142
  <SubtotalTr
142
143
  range={range}
144
+ pageIndex={pageIndex}
143
145
  config={config}
144
146
  printerStore={printerStore}
145
147
  isSomeSubtotalTr={isSomeSubtotalTr}
@@ -584,7 +584,7 @@ class PrinterStore {
584
584
  }
585
585
  }
586
586
 
587
- templateTable(text, dataKey, index, pageIndex) {
587
+ templateTable(text, dataKey, index, pageIndex, options = {}) {
588
588
  // 做好保护,出错就返回 text
589
589
  try {
590
590
  let list = this.data._table[dataKey] || this.data._table.orders
@@ -594,15 +594,18 @@ class PrinterStore {
594
594
  list = _.uniqBy(list, '序号')
595
595
  }
596
596
 
597
+ const { rowTransform, priceFn = price } = options
598
+ const sourceRow = list[index]
599
+ const row = rowTransform ? rowTransform(sourceRow) : sourceRow
597
600
  return _.template(text, {
598
601
  interpolate: /{{([\s\S]+?)}}/g
599
602
  })({
600
603
  ...this.data.common,
601
604
 
602
- [i18next.t('列')]: list[index],
605
+ [i18next.t('列')]: row,
603
606
  [i18next.t('当前页码')]: pageIndex + 1,
604
607
  [i18next.t('页码总数')]: this.pages.length,
605
- price: price // 提供一个价格处理函数
608
+ price: priceFn // 提供一个价格处理函数
606
609
  })
607
610
  } catch (err) {
608
611
  return text
@@ -185,6 +185,7 @@ class Table extends React.Component {
185
185
  <>
186
186
  <SubtotalTr
187
187
  range={range}
188
+ pageIndex={pageIndex}
188
189
  config={config}
189
190
  printerStore={printerStore}
190
191
  isSomeSubtotalTr={isSomeSubtotalTr}
@@ -200,6 +201,7 @@ class Table extends React.Component {
200
201
  <PageSummary {...this.props} />
201
202
  <SubtotalTr
202
203
  range={range}
204
+ pageIndex={pageIndex}
203
205
  config={config}
204
206
  printerStore={printerStore}
205
207
  isSomeSubtotalTr={isSomeSubtotalTr}
@@ -1,168 +1,17 @@
1
1
  import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import _ from 'lodash'
4
- // import Big from 'big.js'
5
- import {
6
- coverDigit2Uppercase,
7
- getDataKey,
8
- formatSumWithPrecision
9
- } from '../util'
10
- import { observer } from 'mobx-react'
11
- import classNames from 'classnames'
12
- import Big from 'big.js'
3
+ import DiySummary from './table_diy_summary_tr'
13
4
 
5
+ // 自定义整单合计复用通用自定义合计组件
14
6
  const DiyOverallOrder = props => {
15
- const {
16
- config: { diyOverallOrder, dataKey },
17
- printerStore,
18
- pageIndex
19
- } = props
20
- const tableData = printerStore.data._table[getDataKey(dataKey)] || []
21
-
22
- const formatter = printerStore.config?.payAmountFormatter
23
- const highPrecisionMapping = printerStore.config?.highPrecisionFieldMapping
24
-
25
- // 计算合计
26
- const sumData = field => {
27
- // 查找高精度字段,有则直接从原始数据取值,避免 templateTable 的精度损失
28
- const hpField = highPrecisionMapping?.[field]
29
- const sum = _.reduce(
30
- tableData,
31
- (a, b, i) => {
32
- let result = a
33
- if (hpField) {
34
- result = a.plus(b._origin?.[hpField] ?? b[hpField] ?? 0)
35
- } else {
36
- const bRes = printerStore
37
- .templateTable(field, dataKey, i, pageIndex)
38
- .replace(/\(\)/g, '')
39
- result = a.plus(+bRes || 0)
40
- }
41
- return result
42
- },
43
- Big(0)
44
- )
45
- return formatSumWithPrecision(sum, formatter)
46
- }
47
-
48
- if (!diyOverallOrder?.show || !printerStore?.ready) {
49
- return null
50
- }
51
-
52
- const leftField = diyOverallOrder.fields?.[0]
53
- if (!leftField) return null
54
- const rightName = leftField.rightName || ''
55
-
56
- // 左右显示分离
57
- const isUpperLowerCaseSeparate = diyOverallOrder?.isUpperLowerCaseSeparate
58
- // 大写金额在前
59
- const isUpperCaseBefore = diyOverallOrder?.isUpperCaseBefore
60
- // 大写金额
61
- const needUpperCase = diyOverallOrder?.needUpperCase
62
- // 小写金额
63
- const numericValue = sumData(leftField.valueField)
64
- // 大写金额
65
- const upperCaseValue = needUpperCase ? coverDigit2Uppercase(numericValue) : ''
66
-
67
- const leftText = () => {
68
- if (isUpperLowerCaseSeparate) {
69
- // 大写金额在前
70
- if (isUpperCaseBefore) {
71
- return upperCaseValue
72
- }
73
- // 小写金额在前
74
- return numericValue
75
- }
76
-
77
- // 大写金额在前且需要大写金额
78
- if (isUpperCaseBefore) {
79
- return `${upperCaseValue} ${numericValue}`
80
- }
81
-
82
- if (needUpperCase) {
83
- return `${numericValue} ${upperCaseValue}`
84
- }
85
-
86
- return numericValue
87
- }
88
-
89
- const rightText = () => {
90
- if (isUpperLowerCaseSeparate) {
91
- if (isUpperCaseBefore) {
92
- return `${rightName} ${numericValue}`
93
- }
94
- return `${rightName} ${upperCaseValue}`
95
- }
96
-
97
- return ''
98
- }
99
- return (
100
- <tr>
101
- <td colSpan={99}>
102
- <div style={{ ...leftField.style }} className='gm-flex-page'>
103
- {leftField.name}
104
- <div
105
- className={classNames('gm-flex-page', {
106
- 'gm-flex-justify-between-page': isUpperLowerCaseSeparate,
107
- 'gm-flex-grow-page': isUpperLowerCaseSeparate
108
- })}
109
- >
110
- {/* 左侧金额 */}
111
- <span>{leftText()}</span>
112
-
113
- <span>{rightText()}</span>
114
- </div>
115
- </div>
116
- </td>
117
- </tr>
118
- )
119
- // return (
120
- // diyOverallOrder?.show &&
121
- // printerStore?.ready && (
122
- // <tr>
123
- // {_.map(diyOverallOrder.fields, (item, index) => {
124
- // return (
125
- // <td colSpan={item.colSpan ?? 99} key={index}>
126
- // <div style={{ ...item.style }} className='gm-flex-page'>
127
- // {item.name}
128
- // <div
129
- // className={classNames('gm-flex-page', {
130
- // 'gm-flex-justify-between-page':
131
- // diyOverallOrder?.isUpperLowerCaseSeparate,
132
- // 'gm-flex-grow-page':
133
- // diyOverallOrder?.isUpperLowerCaseSeparate
134
- // })}
135
- // >
136
- // <span
137
- // className={
138
- // diyOverallOrder?.isUpperCaseBefore
139
- // ? 'gm-printer-subtotal-isUpperCaseBefore-inter'
140
- // : ''
141
- // }
142
- // >
143
- // {sumData(item.valueField)}
144
- // </span>
145
- // {diyOverallOrder?.needUpperCase && (
146
- // <span>
147
- // {coverDigit2Uppercase(sumData(item.valueField))
148
- // ? coverDigit2Uppercase(sumData(item.valueField))
149
- // : ''}
150
- // </span>
151
- // )}
152
- // </div>
153
- // </div>
154
- // </td>
155
- // )
156
- // })}
157
- // </tr>
158
- // )
159
- // )
7
+ return <DiySummary {...props} configKey='diyOverallOrder' />
160
8
  }
9
+
161
10
  DiyOverallOrder.propTypes = {
162
11
  config: PropTypes.object.isRequired,
163
- range: PropTypes.object.isRequired,
12
+ range: PropTypes.object,
164
13
  printerStore: PropTypes.object,
165
14
  pageIndex: PropTypes.number
166
15
  }
167
16
 
168
- export default observer(DiyOverallOrder)
17
+ export default DiyOverallOrder
@@ -2,6 +2,7 @@ import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import _ from 'lodash'
4
4
  import {
5
+ calculateDiySummary,
5
6
  coverDigit2Uppercase,
6
7
  getDataKey,
7
8
  isMultiTable,
@@ -53,67 +54,52 @@ const DiySummary = props => {
53
54
 
54
55
  const formatter = printerStore.config?.payAmountFormatter
55
56
  const highPrecisionMapping = printerStore.config?.highPrecisionFieldMapping
57
+ const isRoundFirst =
58
+ printerStore.data._origin?.precision_control?.is_round_first === true
56
59
 
57
- // 计算合计(逐行取原始列值,内置函数只作用在最终合计结果上)
60
+ // 单栏使用统一计算器;多栏保留原有后缀字段累加逻辑
58
61
  const sumData = field => {
59
- // 查找高精度字段,有则直接从原始数据取值,避免 templateTable 的精度损失
60
- // 兼容映射 key 为模板串或字段名两种写法
61
- const fieldKey = extractSumField(field)
62
- const hpField =
63
- highPrecisionMapping?.[field] || highPrecisionMapping?.[fieldKey]
64
-
65
- if (isMulti) {
66
- // 多栏表格暂不使用高精度字段,保持原有 templateTable 逻辑
67
- const sum = _.reduce(
62
+ if (!isMulti) {
63
+ return calculateDiySummary({
68
64
  tableData,
69
- (a, b, i) => {
70
- let result = a
71
- const bRes = getRes(field, i)
72
- result = a.plus(+bRes || 0)
73
-
74
- // 双栏
75
- const multiField = buildTemplateField(field, MULTI_SUFFIX)
76
- result = result.plus(+getRes(multiField, i) || 0)
77
-
78
- // 三栏
79
- const multiField3 = buildTemplateField(field, MULTI_SUFFIX3)
80
- result = result.plus(+getRes(multiField3, i) || 0)
81
-
82
- return result
83
- },
84
- Big(0)
85
- )
86
- return formatSumWithPrecision(sum, formatter)
65
+ valueField: field,
66
+ formatter,
67
+ highPrecisionMapping,
68
+ isRoundFirst,
69
+ renderTemplate: (template, index, options) =>
70
+ printerStore
71
+ .templateTable(
72
+ template,
73
+ dataKey,
74
+ range ? range.begin + index : index,
75
+ pageIndex,
76
+ options
77
+ )
78
+ .replace(/\(\)/g, '')
79
+ })
87
80
  }
88
81
 
89
- // 单栏:有高精度字段则直接从原始数据取值
90
- if (hpField) {
91
- const sum = _.reduce(
92
- tableData,
93
- (a, b) => {
94
- return a.plus(b._origin?.[hpField] ?? b[hpField] ?? 0)
95
- },
96
- Big(0)
97
- )
98
- return formatSumWithPrecision(sum, formatter)
99
- }
100
-
101
- // 兜底:无高精度字段,使用 templateTable
102
82
  const sum = _.reduce(
103
83
  tableData,
104
- (a, b, i) => {
105
- let result = a
106
- const bRes = getRes(field, i)
107
- result = a.plus(+bRes || 0)
108
- return result
84
+ (total, row, index) => {
85
+ let result = total.plus(+getRes(field, index) || 0)
86
+ const multiField = buildTemplateField(field, MULTI_SUFFIX)
87
+ result = result.plus(+getRes(multiField, index) || 0)
88
+ const multiField3 = buildTemplateField(field, MULTI_SUFFIX3)
89
+ return result.plus(+getRes(multiField3, index) || 0)
109
90
  },
110
91
  Big(0)
111
92
  )
112
93
  return formatSumWithPrecision(sum, formatter)
113
94
 
114
- function getRes(field, i) {
95
+ function getRes(template, index) {
115
96
  return printerStore
116
- .templateTable(field, dataKey, range ? range.begin + i : i, pageIndex)
97
+ .templateTable(
98
+ template,
99
+ dataKey,
100
+ range ? range.begin + index : index,
101
+ pageIndex
102
+ )
117
103
  .replace(/\(\)/g, '')
118
104
  }
119
105
  }
@@ -131,15 +117,14 @@ const DiySummary = props => {
131
117
  const isUpperLowerCaseSeparate = diyConfig?.isUpperLowerCaseSeparate
132
118
  const isUpperCaseBefore = diyConfig?.isUpperCaseBefore
133
119
  const needUpperCase = diyConfig?.needUpperCase
134
- const fieldKey = extractSumField(leftField.valueField)
135
- // 求和用去掉内置函数后的列模板,多栏/高精度逻辑保持不变
136
- const numericValue = sumData(toSumColTemplate(leftField.valueField))
137
- // 仅对合计结果做模板/内置函数渲染
138
- const displayValue = templateSumResult(
139
- leftField.valueField,
140
- numericValue,
141
- fieldKey
142
- )
120
+ // 单栏完整遵循统一舍入顺序;多栏保持原有求和后执行模板逻辑
121
+ const displayValue = isMulti
122
+ ? templateSumResult(
123
+ leftField.valueField,
124
+ sumData(toSumColTemplate(leftField.valueField)),
125
+ extractSumField(leftField.valueField)
126
+ )
127
+ : sumData(leftField.valueField)
143
128
  const upperCaseValue = needUpperCase ? coverDigit2Uppercase(displayValue) : ''
144
129
 
145
130
  const leftText = () => {
@@ -2,24 +2,56 @@ 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 } from '../util'
5
+ import {
6
+ calculateDiySummary,
7
+ coverDigit2Uppercase,
8
+ extractSumField,
9
+ getDataKey,
10
+ hasDiySummaryExpression
11
+ } from '../util'
6
12
  import { observer } from 'mobx-react'
7
13
  import classNames from 'classnames'
8
14
 
9
15
  const OverallOrder = props => {
10
16
  const {
11
- config: { overallOrder },
17
+ config: { overallOrder, dataKey, arrange },
12
18
  printerStore,
13
19
  printerStore: {
14
20
  data: { common }
15
21
  }
16
22
  } = props
23
+ const tableDataKey = getDataKey(dataKey, arrange)
24
+ const tableData = printerStore.data._table[tableDataKey] || []
25
+ const formatter = printerStore.config?.payAmountFormatter
26
+ const highPrecisionMapping = printerStore.config?.highPrecisionFieldMapping
27
+ const isRoundFirst =
28
+ printerStore.data._origin?.precision_control?.is_round_first === true
29
+
30
+ // 普通字段沿用服务端整单值,表达式使用统一明细计算器
31
+ const getOverallValue = valueField => {
32
+ const fieldKey = extractSumField(valueField)
33
+ if (!hasDiySummaryExpression(valueField)) {
34
+ return common?.[fieldKey] ?? ''
35
+ }
36
+ return calculateDiySummary({
37
+ tableData,
38
+ valueField,
39
+ formatter,
40
+ highPrecisionMapping,
41
+ isRoundFirst,
42
+ renderTemplate: (template, index, options) =>
43
+ printerStore
44
+ .templateTable(template, tableDataKey, index, undefined, options)
45
+ .replace(/\(\)/g, '')
46
+ })
47
+ }
17
48
 
18
49
  return (
19
50
  overallOrder?.show &&
20
51
  printerStore?.ready && (
21
52
  <tr>
22
53
  {_.map(overallOrder.fields, (item, index) => {
54
+ const displayValue = getOverallValue(item.valueField)
23
55
  return (
24
56
  <td colSpan={item.colSpan} key={index}>
25
57
  <div style={{ ...item.style }} className='gm-flex-page'>
@@ -38,13 +70,11 @@ const OverallOrder = props => {
38
70
  : ''
39
71
  }
40
72
  >
41
- {common?.[item.valueField] ?? ''}
73
+ {displayValue}
42
74
  </span>
43
75
  {overallOrder?.needUpperCase && (
44
76
  <span>
45
- {common?.[item.valueField]
46
- ? coverDigit2Uppercase(common[item.valueField])
47
- : ''}
77
+ {displayValue ? coverDigit2Uppercase(displayValue) : ''}
48
78
  </span>
49
79
  )}
50
80
  </div>
@@ -5,10 +5,12 @@ import _ from 'lodash'
5
5
  import { MULTI_SUFFIX, MULTI_SUFFIX3 } from '../config'
6
6
  import Big from 'big.js'
7
7
  import {
8
+ calculateDiySummary,
8
9
  coverDigit2Uppercase,
9
10
  getDataKey,
10
11
  formatSumWithPrecision,
11
12
  extractSumField,
13
+ isMultiTable,
12
14
  templateSumResult
13
15
  } from '../util'
14
16
  import { observer } from 'mobx-react'
@@ -47,10 +49,14 @@ const SubtotalTr = props => {
47
49
  }
48
50
  },
49
51
  range,
52
+ pageIndex,
50
53
  printerStore,
51
54
  isSomeSubtotalTr
52
55
  } = props
53
56
  const tableData = printerStore.data._table[getDataKey(dataKey, arrange)] || []
57
+ const isMulti = isMultiTable(dataKey)
58
+ const isRoundFirst =
59
+ printerStore.data._origin?.precision_control?.is_round_first === true
54
60
 
55
61
  // 计算合计
56
62
  const sumData2 = (list, field, formatter, highPrecisionMapping) => {
@@ -97,8 +103,28 @@ const SubtotalTr = props => {
97
103
  if (show && printerStore.ready) {
98
104
  const list = tableData.slice(range.begin, range.end)
99
105
 
100
- // 先按字段求和,再对结果做模板/内置函数渲染
106
+ // 单栏使用统一舍入逻辑,多栏保留原有后缀字段累加
101
107
  const formatSumValue = valueField => {
108
+ if (!isMulti) {
109
+ return calculateDiySummary({
110
+ tableData: list,
111
+ valueField,
112
+ formatter,
113
+ highPrecisionMapping,
114
+ isRoundFirst,
115
+ renderTemplate: (template, index, options) =>
116
+ printerStore
117
+ .templateTable(
118
+ template,
119
+ dataKey,
120
+ range.begin + index,
121
+ pageIndex,
122
+ options
123
+ )
124
+ .replace(/\(\)/g, '')
125
+ })
126
+ }
127
+
102
128
  const fieldKey = extractSumField(valueField)
103
129
  const sum = sumData2(list, fieldKey, formatter, highPrecisionMapping)
104
130
  return templateSumResult(valueField, sum, fieldKey)
@@ -201,7 +227,8 @@ const SubtotalTr = props => {
201
227
  SubtotalTr.propTypes = {
202
228
  config: PropTypes.object.isRequired,
203
229
  range: PropTypes.object.isRequired,
204
- printStore: PropTypes.object
230
+ printStore: PropTypes.object,
231
+ pageIndex: PropTypes.number
205
232
  }
206
233
 
207
234
  export default observer(SubtotalTr)
package/src/util.js CHANGED
@@ -450,6 +450,112 @@ const toSumColTemplate = text => {
450
450
  return `{{列.${key}}}`
451
451
  }
452
452
 
453
+ /**
454
+ * 判断自定义合计配置是否包含函数或运算表达式
455
+ * @param {string} valueField
456
+ * @returns {boolean}
457
+ */
458
+ const hasDiySummaryExpression = valueField => {
459
+ const expression =
460
+ typeof valueField === 'string'
461
+ ? valueField.match(/{{([\s\S]+?)}}/)?.[1]?.trim()
462
+ : ''
463
+ return expression ? !/^(?:列\.)?[\w㐀-鿿()]+$/.test(expression) : false
464
+ }
465
+
466
+ /**
467
+ * 按字段/表达式和舍入顺序计算自定义合计
468
+ * @param {Object} options
469
+ * @returns {string}
470
+ */
471
+ const calculateDiySummary = options => {
472
+ const {
473
+ tableData,
474
+ valueField,
475
+ formatter,
476
+ highPrecisionMapping,
477
+ isRoundFirst,
478
+ renderTemplate
479
+ } = options
480
+ const hasExpression = hasDiySummaryExpression(valueField)
481
+
482
+ // 用高精度字段构造公式上下文,不修改实际打印行
483
+ const getHighPrecisionRow = row => {
484
+ const formulaRow = { ...row }
485
+ _.forEach(highPrecisionMapping, (hpField, field) => {
486
+ const value = row._origin?.[hpField] ?? row[hpField]
487
+ if (value !== undefined && value !== null) {
488
+ formulaRow[field] = value
489
+ }
490
+ })
491
+ return formulaRow
492
+ }
493
+
494
+ if (!hasExpression) {
495
+ const fieldKey = extractSumField(valueField)
496
+ const mappedField = getHighPrecisionField(fieldKey, highPrecisionMapping)
497
+ const hpField = highPrecisionMapping?.[valueField] || mappedField
498
+ const hasHighPrecisionField = hpField !== fieldKey
499
+ const columnTemplate = valueField.includes('{{')
500
+ ? valueField
501
+ : toSumColTemplate(valueField)
502
+ const sum = _.reduce(
503
+ tableData,
504
+ (total, row, index) => {
505
+ const rawValue = hasHighPrecisionField
506
+ ? row._origin?.[hpField] ?? row[hpField] ?? 0
507
+ : renderTemplate(columnTemplate, index)
508
+ const value =
509
+ isRoundFirst && formatter ? formatter(rawValue).toFixed() : rawValue
510
+ const numericValue = value === '' || isNaN(Number(value)) ? 0 : value
511
+ return total.plus(numericValue)
512
+ },
513
+ Big(0)
514
+ )
515
+ return formatSumWithPrecision(sum, formatter)
516
+ }
517
+
518
+ // 表达式最多包含一个 price;后舍入模式下将 price 延迟到合计结果
519
+ const usesPrice = /\bprice\s*\(/.test(valueField)
520
+ let formulaPrecision = 2
521
+ let decimalPlaces = 0
522
+ const deferredPrice = (value, precision = 2) => {
523
+ formulaPrecision = precision
524
+ return value
525
+ }
526
+ const sum = _.reduce(
527
+ tableData,
528
+ (total, row, index) => {
529
+ const renderOptions = isRoundFirst
530
+ ? undefined
531
+ : {
532
+ rowTransform: getHighPrecisionRow,
533
+ priceFn: usesPrice ? deferredPrice : undefined
534
+ }
535
+ const result = String(
536
+ renderTemplate(valueField, index, renderOptions)
537
+ ).trim()
538
+ if (result === '' || isNaN(Number(result))) return total
539
+
540
+ const value =
541
+ isRoundFirst && !usesPrice && formatter
542
+ ? formatter(result).toFixed()
543
+ : result
544
+ const decimalPart = String(value).split('.')[1]
545
+ decimalPlaces = Math.max(decimalPlaces, decimalPart?.length || 0)
546
+ return total.plus(value)
547
+ },
548
+ Big(0)
549
+ )
550
+
551
+ if (!isRoundFirst && usesPrice) {
552
+ return price(sum, formulaPrecision)
553
+ }
554
+ return usesPrice
555
+ ? sum.toFixed(decimalPlaces)
556
+ : formatSumWithPrecision(sum, formatter)
557
+ }
558
+
453
559
  export {
454
560
  collectGroups,
455
561
  getHeight,
@@ -476,5 +582,7 @@ export {
476
582
  price,
477
583
  extractSumField,
478
584
  templateSumResult,
479
- toSumColTemplate
585
+ toSumColTemplate,
586
+ hasDiySummaryExpression,
587
+ calculateDiySummary
480
588
  }