gm-printer 10.41.1 → 10.41.3-beta.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.
@@ -1,55 +1,71 @@
1
1
  import EditorStore from '../common/editor_store'
2
2
  import { action, observable } from 'mobx'
3
3
  import i18next from '../../locales'
4
+ import { getDataKeySuffix, isLastColDataKey } from '../common/data_key_util'
5
+
6
+ const getPurchaseDefaultDetailText = () =>
7
+ i18next.t('{{采购数量_采购单位}}{{采购单位}}*{{商户名}}*{{商品备注}}')
8
+
9
+ /** 创建/切换「单列-最后一列」时,明细列 text 应与 template_text 一致 */
10
+ const resolveDetailTemplateText = (tableConfig, specialTableConfig) => {
11
+ const existing = tableConfig.specialConfig?.template_text
12
+ if (existing) return existing
13
+ if (specialTableConfig && specialTableConfig.defaultDetailText !== undefined) {
14
+ return specialTableConfig.defaultDetailText
15
+ }
16
+ return getPurchaseDefaultDetailText()
17
+ }
18
+
19
+ const syncLastColColumnText = (tableConfig) => {
20
+ if (!isLastColDataKey(tableConfig.dataKey)) return
21
+ const templateText = tableConfig.specialConfig?.template_text
22
+ if (!templateText) return
23
+ const specialCol = tableConfig.columns?.find((o) => o.isSpecialColumn)
24
+ if (specialCol) specialCol.text = templateText
25
+ }
4
26
 
5
27
  class Store extends EditorStore {
6
28
  constructor({ defaultTableDataKey }) {
7
29
  super()
8
- this.defaultTableDataKey = defaultTableDataKey // 修改默认dataKey
30
+ this.defaultTableDataKey = defaultTableDataKey
9
31
  }
10
32
 
11
33
  @observable
12
34
  customerTag = false
13
35
 
14
- /* start---------设置采购明细相关--------- */
15
36
  @action.bound
16
- setPurchaseTableKey(dataKey) {
17
- // 先移除选中项,安全第一
37
+ init(config, data) {
38
+ super.init(config, data)
39
+ config?.contents?.forEach((tableConfig) => {
40
+ if (tableConfig?.type === 'table') syncLastColColumnText(tableConfig)
41
+ })
42
+ }
43
+
44
+ @action.bound
45
+ setPurchaseTableKey(dataKey, specialTableConfig) {
18
46
  this.selected = null
19
47
  this.setTableDataKey(dataKey)
20
48
 
21
49
  const arr = this.selectedRegion.split('.')
22
50
  const tableConfig = this.config.contents[arr[2]]
23
51
 
24
- // 先去掉所有明细列
25
- const newCols = tableConfig.columns.filter(o => !o.isSpecialColumn)
52
+ const newCols = tableConfig.columns.filter((o) => !o.isSpecialColumn)
26
53
  tableConfig.columns.replace(newCols)
27
54
 
28
- // 单列-总表最后一列,在columns上修改
29
- if (
30
- dataKey === 'purchase_last_col' ||
31
- dataKey === 'purchase_last_col_noLineBreak'
32
- ) {
55
+ if (isLastColDataKey(dataKey)) {
56
+ const templateText = resolveDetailTemplateText(tableConfig, specialTableConfig)
33
57
  tableConfig.columns.push({
34
58
  head: i18next.t('明细'),
35
59
  headStyle: { textAlign: 'center' },
36
60
  style: { textAlign: 'left' },
37
61
  isSpecialColumn: true,
38
62
  separator: '+',
39
- // detailsType: 'purchase_last_col',
40
63
  specialDetailsKey: '__details',
41
- text: i18next.t(
42
- '{{采购数量_采购单位}}{{采购单位}}*{{商户名}}*{{商品备注}}'
43
- )
64
+ text: templateText,
44
65
  })
45
- // 通过detailsType属性区分单列-总表最后一列的数据是否换行展示
46
- dataKey === 'purchase_last_col'
47
- ? (tableConfig.columns[
48
- tableConfig.columns.length - 1
49
- ].detailLastColType = 'purchase_last_col')
50
- : (tableConfig.columns[
51
- tableConfig.columns.length - 1
52
- ].detailLastColType = 'purchase_last_col_noLineBreak')
66
+ const suffix = getDataKeySuffix(dataKey)
67
+ tableConfig.columns[tableConfig.columns.length - 1].detailLastColType =
68
+ suffix === 'last_col' ? 'purchase_last_col' : 'purchase_last_col_noLineBreak'
53
69
  }
54
70
  }
55
71
 
@@ -59,13 +75,9 @@ class Store extends EditorStore {
59
75
  const tableConfig = this.config.contents[arr[2]]
60
76
 
61
77
  tableConfig.specialConfig.template_text = value
62
- // 单列-总表最后一列,在columns上修改
63
- if (
64
- tableConfig.dataKey === 'purchase_last_col' ||
65
- tableConfig.dataKey === 'purchase_last_col_noLineBreak'
66
- ) {
67
- const specialCol = tableConfig.columns.find(o => o.isSpecialColumn)
68
- specialCol.text = value
78
+ if (isLastColDataKey(tableConfig.dataKey)) {
79
+ const specialCol = tableConfig.columns.find((o) => o.isSpecialColumn)
80
+ if (specialCol) specialCol.text = value
69
81
  }
70
82
  }
71
83
 
@@ -75,13 +87,9 @@ class Store extends EditorStore {
75
87
  const tableConfig = this.config.contents[arr[2]]
76
88
 
77
89
  tableConfig.specialConfig.style = value
78
- // 单列-总表最后一列,在columns上修改
79
- if (
80
- tableConfig.dataKey === 'purchase_last_col' ||
81
- tableConfig.dataKey === 'purchase_last_col_noLineBreak'
82
- ) {
83
- const specialCol = tableConfig.columns.find(o => o.isSpecialColumn)
84
- specialCol.style = value
90
+ if (isLastColDataKey(tableConfig.dataKey)) {
91
+ const specialCol = tableConfig.columns.find((o) => o.isSpecialColumn)
92
+ if (specialCol) specialCol.style = value
85
93
  }
86
94
  }
87
95
 
@@ -91,17 +99,12 @@ class Store extends EditorStore {
91
99
  const tableConfig = this.config.contents[arr[2]]
92
100
 
93
101
  tableConfig.specialConfig.template_text += fieldText
94
- // 单列-总表最后一列,在columns上修改
95
- if (
96
- tableConfig.dataKey === 'purchase_last_col' ||
97
- tableConfig.dataKey === 'purchase_last_col_noLineBreak'
98
- ) {
99
- const specialCol = tableConfig.columns.find(o => o.isSpecialColumn)
100
- specialCol.text += fieldText
102
+ if (isLastColDataKey(tableConfig.dataKey)) {
103
+ const specialCol = tableConfig.columns.find((o) => o.isSpecialColumn)
104
+ if (specialCol) specialCol.text += fieldText
101
105
  }
102
106
  }
103
107
 
104
- /* end---------设置采购明细相关--------- */
105
108
  @action.bound
106
109
  switchCustomerTag(val) {
107
110
  const arr = this.selectedRegion.split('.')
@@ -111,7 +111,8 @@ class Table extends React.Component {
111
111
  range,
112
112
  pageIndex,
113
113
  printerStore,
114
- isSomeSubtotalTr
114
+ isSomeSubtotalTr,
115
+ isLastTablePage
115
116
  } = this.props
116
117
  // 数据
117
118
  dataKey = getDataKey(dataKey, arrange)
@@ -202,7 +203,8 @@ class Table extends React.Component {
202
203
  ? printerStore.templateSpecialDetails(
203
204
  col,
204
205
  dataKey,
205
- i
206
+ i,
207
+ config.specialConfig?.template_text
206
208
  )
207
209
  : printerStore
208
210
  .templateTable(
@@ -227,6 +229,7 @@ class Table extends React.Component {
227
229
  range={range}
228
230
  config={config}
229
231
  printerStore={printerStore}
232
+ isLastTablePage={isLastTablePage}
230
233
  />
231
234
  <DiyOverallOrder
232
235
  range={range}
@@ -307,7 +310,8 @@ class Table extends React.Component {
307
310
  ? printerStore.templateSpecialDetails(
308
311
  col,
309
312
  dataKey,
310
- i
313
+ i,
314
+ config.specialConfig?.template_text
311
315
  )
312
316
  : printerStore
313
317
  .templateTable(col.text, dataKey, i, pageIndex)
@@ -327,6 +331,7 @@ class Table extends React.Component {
327
331
  range={range}
328
332
  config={config}
329
333
  printerStore={printerStore}
334
+ isLastTablePage={isLastTablePage}
330
335
  />
331
336
  <DiyOverallOrder
332
337
  range={range}
@@ -385,7 +390,12 @@ Table.propTypes = {
385
390
  pageIndex: PropTypes.number.isRequired,
386
391
  placeholder: PropTypes.string,
387
392
  printerStore: PropTypes.object,
388
- isSomeSubtotalTr: PropTypes.bool
393
+ isSomeSubtotalTr: PropTypes.bool,
394
+ isLastTablePage: PropTypes.bool
395
+ }
396
+
397
+ Table.defaultProps = {
398
+ isLastTablePage: true
389
399
  }
390
400
 
391
401
  export default Table
@@ -13,6 +13,20 @@ import MergePage from './merge_page'
13
13
  import { LONG_PRINT } from '../config'
14
14
  import Big from 'big.js'
15
15
 
16
+ /** 当前 panel 是否为该表格 content 在全部分页中的最后一次出现 */
17
+ const isLastTablePanel = (pages, pageIndex, panelIndexInPage, tableIndex) => {
18
+ for (let pi = pages.length - 1; pi >= 0; pi--) {
19
+ const page = pages[pi] || []
20
+ for (let pji = page.length - 1; pji >= 0; pji--) {
21
+ const p = page[pji]
22
+ if (p?.type === 'table' && p.index === tableIndex) {
23
+ return pi === pageIndex && pji === panelIndexInPage
24
+ }
25
+ }
26
+ }
27
+ return true
28
+ }
29
+
16
30
  // Header Sign Footer 相对特殊,要单独处理
17
31
  const Header = props => (
18
32
  <Panel {...props} name='header' placeholder={i18next.t('页眉')} />
@@ -272,6 +286,12 @@ class Printer extends React.Component {
272
286
  placeholder={`${i18next.t('区域')} ${panel.index}`}
273
287
  pageIndex={i}
274
288
  isSomeSubtotalTr={isSomeSubtotalTr}
289
+ isLastTablePage={isLastTablePanel(
290
+ printerStore.pages,
291
+ i,
292
+ ii,
293
+ panel.index
294
+ )}
275
295
  />
276
296
  )
277
297
 
@@ -7,6 +7,7 @@ import {
7
7
  getArrayMid,
8
8
  getOverallOrderTrHeight
9
9
  } from '../util'
10
+ import { isIndependentRolDataKey } from '../common/data_key_util'
10
11
  import _ from 'lodash'
11
12
  import Big from 'big.js'
12
13
  import { Tip } from '../components'
@@ -604,13 +605,14 @@ class PrinterStore {
604
605
  */
605
606
  templateTableRowSpan(text, dataKey, index, begin) {
606
607
  try {
607
- if (
608
- dataKey !== 'purchase_detail_one_row' &&
609
- dataKey !== 'purchase_independent_rol_sku' &&
610
- dataKey !== 'purchase_independent_rol_address' &&
611
- dataKey !== 'taxRateSales' &&
612
- dataKey !== 'ordinary'
613
- ) {
608
+ // 采购 / 分拣「按明细单行」展开后才做合并单元格(后缀 independent_rol_*)
609
+ const canMerge =
610
+ isIndependentRolDataKey(dataKey) ||
611
+ dataKey === 'taxRateSales' ||
612
+ dataKey === 'ordinary' ||
613
+ dataKey === 'purchase_detail_one_row' ||
614
+ dataKey === 'sorting_detail_detail_one_row'
615
+ if (!canMerge) {
614
616
  return null
615
617
  }
616
618
  const list = this.data._table[dataKey] || this.data._table.orders
@@ -696,12 +698,15 @@ class PrinterStore {
696
698
  }
697
699
  }
698
700
 
699
- templateSpecialDetails(col, dataKey, index) {
700
- // 做好保护,出错就返回 text
701
+ templateSpecialDetails(col, dataKey, index, fallbackText = '') {
701
702
  const { specialDetailsKey, text, detailLastColType, separator } = col
703
+ const templateText = text || fallbackText
704
+ if (!templateText) return ''
702
705
  try {
703
706
  const row = this.data._table[dataKey][index]
704
- const compiled = _.template(text, { interpolate: /{{([\s\S]+?)}}/g })
707
+ const compiled = _.template(templateText, {
708
+ interpolate: /{{([\s\S]+?)}}/g,
709
+ })
705
710
  let detailsList = row[specialDetailsKey] || []
706
711
 
707
712
  /** 简单处理下数据 */
@@ -724,7 +729,7 @@ class PrinterStore {
724
729
 
725
730
  return detailsList
726
731
  } catch (err) {
727
- return text
732
+ return templateText
728
733
  }
729
734
  }
730
735
 
@@ -169,7 +169,8 @@ class Table extends React.Component {
169
169
  range,
170
170
  pageIndex,
171
171
  printerStore,
172
- isSomeSubtotalTr
172
+ isSomeSubtotalTr,
173
+ isLastTablePage
173
174
  } = this.props
174
175
  // 数据
175
176
  dataKey = getDataKey(dataKey, arrange)
@@ -411,7 +412,8 @@ class Table extends React.Component {
411
412
  ? printerStore.templateSpecialDetails(
412
413
  col,
413
414
  dataKey,
414
- i
415
+ i,
416
+ config.specialConfig?.template_text
415
417
  )
416
418
  : printerStore
417
419
  .templateTable(col.text, dataKey, i, pageIndex)
@@ -445,6 +447,7 @@ class Table extends React.Component {
445
447
  range={range}
446
448
  config={config}
447
449
  printerStore={printerStore}
450
+ isLastTablePage={isLastTablePage}
448
451
  />
449
452
  <DiyOverallOrder
450
453
  range={range}
@@ -503,7 +506,12 @@ Table.propTypes = {
503
506
  pageIndex: PropTypes.number.isRequired,
504
507
  placeholder: PropTypes.string,
505
508
  printerStore: PropTypes.object,
506
- isSomeSubtotalTr: PropTypes.bool
509
+ isSomeSubtotalTr: PropTypes.bool,
510
+ isLastTablePage: PropTypes.bool
511
+ }
512
+
513
+ Table.defaultProps = {
514
+ isLastTablePage: true
507
515
  }
508
516
 
509
517
  export default Table
@@ -12,54 +12,66 @@ const OverallOrder = props => {
12
12
  printerStore,
13
13
  printerStore: {
14
14
  data: { common }
15
- }
15
+ },
16
+ isLastTablePage
16
17
  } = props
17
18
 
18
- return (
19
+ // showEachPage 默认 true(老模板缺省视为每页展示);false 时仅表格最后一页渲染
20
+ const showEachPage = overallOrder?.showEachPage !== false
21
+ const shouldShow =
19
22
  overallOrder?.show &&
20
- printerStore?.ready && (
21
- <tr>
22
- {_.map(overallOrder.fields, (item, index) => {
23
- return (
24
- <td colSpan={item.colSpan} key={index}>
25
- <div style={{ ...item.style }} className='gm-flex-page'>
26
- {item.name}
27
- <div
28
- className={classNames('gm-flex-page', {
29
- 'gm-flex-justify-between-page':
30
- overallOrder?.isUpperLowerCaseSeparate,
31
- 'gm-flex-grow-page': overallOrder?.isUpperLowerCaseSeparate
32
- })}
23
+ printerStore?.ready &&
24
+ (showEachPage || isLastTablePage)
25
+
26
+ if (!shouldShow) return null
27
+
28
+ return (
29
+ <tr>
30
+ {_.map(overallOrder.fields, (item, index) => {
31
+ return (
32
+ <td colSpan={item.colSpan} key={index}>
33
+ <div style={{ ...item.style }} className='gm-flex-page'>
34
+ {item.name}
35
+ <div
36
+ className={classNames('gm-flex-page', {
37
+ 'gm-flex-justify-between-page':
38
+ overallOrder?.isUpperLowerCaseSeparate,
39
+ 'gm-flex-grow-page': overallOrder?.isUpperLowerCaseSeparate
40
+ })}
41
+ >
42
+ <span
43
+ className={
44
+ overallOrder?.isUpperCaseBefore
45
+ ? 'gm-printer-subtotal-isUpperCaseBefore-inter'
46
+ : ''
47
+ }
33
48
  >
34
- <span
35
- className={
36
- overallOrder?.isUpperCaseBefore
37
- ? 'gm-printer-subtotal-isUpperCaseBefore-inter'
38
- : ''
39
- }
40
- >
41
- {common?.[item.valueField] ?? ''}
49
+ {common?.[item.valueField] ?? ''}
50
+ </span>
51
+ {overallOrder?.needUpperCase && (
52
+ <span>
53
+ {common?.[item.valueField]
54
+ ? coverDigit2Uppercase(common[item.valueField])
55
+ : ''}
42
56
  </span>
43
- {overallOrder?.needUpperCase && (
44
- <span>
45
- {common?.[item.valueField]
46
- ? coverDigit2Uppercase(common[item.valueField])
47
- : ''}
48
- </span>
49
- )}
50
- </div>
57
+ )}
51
58
  </div>
52
- </td>
53
- )
54
- })}
55
- </tr>
56
- )
59
+ </div>
60
+ </td>
61
+ )
62
+ })}
63
+ </tr>
57
64
  )
58
65
  }
59
66
  OverallOrder.propTypes = {
60
67
  config: PropTypes.object.isRequired,
61
68
  range: PropTypes.object.isRequired,
62
- printerStore: PropTypes.object
69
+ printerStore: PropTypes.object,
70
+ isLastTablePage: PropTypes.bool
71
+ }
72
+
73
+ OverallOrder.defaultProps = {
74
+ isLastTablePage: true
63
75
  }
64
76
 
65
77
  export default observer(OverallOrder)
@@ -25,6 +25,21 @@ const SpecialTr = ({ data, config }) => {
25
25
  } = specialConfig
26
26
  const { list, type, fixedSize } = data
27
27
 
28
+ // 「显示分类名称/小计文案修改」实时生效:数据行带 _prefix 原料时,按当前 specialConfig 重拼前缀
29
+ // 打印时配置与数据层拼前缀所用配置一致,重拼结果不变;编辑器预览改配置时实时刷新;导出(paint.js)不走本组件不受影响
30
+ const applySubtotalText = html => {
31
+ if (typeof html !== 'string' || data._prefix === undefined) {
32
+ return html
33
+ }
34
+ const showCategoryName = specialConfig?.showCategoryName ?? true
35
+ const subtotalText = specialConfig?.subtotalText ?? '小计'
36
+ const newPrefix = showCategoryName
37
+ ? `${data._categoryName ?? ''}${subtotalText}:`
38
+ : `${subtotalText}:`
39
+ // 前缀位于字符串头部,首次命中即替换;用函数替换避免 newPrefix 含 $ 序列时被特俗解释
40
+ return html.replace(data._prefix, () => newPrefix)
41
+ }
42
+
28
43
  let compiled
29
44
  try {
30
45
  compiled = _.template(template_text, { interpolate: /{{([\s\S]+?)}}/g })
@@ -119,7 +134,7 @@ const SpecialTr = ({ data, config }) => {
119
134
  className='gm-flex-page gm-flex-justify-between-page'
120
135
  style={{ 'justify-content': 'space-between' }}
121
136
  dangerouslySetInnerHTML={{
122
- __html: htmlStr || data.text
137
+ __html: applySubtotalText(htmlStr || data.text)
123
138
  }}
124
139
  />
125
140
  </td>
@@ -145,7 +160,7 @@ const SpecialTr = ({ data, config }) => {
145
160
  colSpan={99}
146
161
  style={Object.assign({ fontWeight: 'bold' }, style)}
147
162
  dangerouslySetInnerHTML={{
148
- __html: getHtml() || data.text
163
+ __html: applySubtotalText(getHtml() || data.text)
149
164
  }}
150
165
  />
151
166
  </tr>
@@ -134,6 +134,93 @@ const SubtotalTr = props => {
134
134
  </tr>
135
135
  )
136
136
  } else {
137
+ // 单元格拆分展示:第一格(左侧文案+数值)拆分为两个单元格,对齐复用每页合计设置的 textAlign;
138
+ // 与打印账户总计金额支持共存(2026-09 交互确认):拆分只作用于第一格,其余格按原 colSpan 渲染(与导出侧 paintSubtotal 行为一致)
139
+ if (get(subtotal, 'isSplitCells')) {
140
+ const item = fields[0]
141
+ // 第一格总 colSpan 拆一半给左侧文案,剩余给数值(colSpan 缺省 99 表示整行);
142
+ // 宽度分配沿用原有逻辑:各格按 store 侧 fields[n].colSpan 照画,渲染端不调宽度
143
+ const totalColSpan = item.colSpan ?? 99
144
+ const leftColSpan = Math.floor(totalColSpan / 2)
145
+ const cellStyle = {
146
+ fontWeight: 'bold',
147
+ justifyContent: flexStyle[subtotal.style?.textAlign],
148
+ ...subtotal.style
149
+ }
150
+
151
+ return (
152
+ <tr>
153
+ <td colSpan={leftColSpan}>
154
+ <div style={cellStyle} className='gm-flex-page'>
155
+ {item.name}
156
+ </div>
157
+ </td>
158
+ <td colSpan={totalColSpan - leftColSpan}>
159
+ <div style={cellStyle} className='gm-flex-page'>
160
+ <div
161
+ className={classNames('gm-flex-page', {
162
+ 'gm-flex-justify-between-page': isUpperLowerCaseSeparate,
163
+ 'gm-flex-grow-page': isUpperLowerCaseSeparate
164
+ })}
165
+ >
166
+ <span
167
+ className={
168
+ isUpperCaseBefore
169
+ ? 'gm-printer-subtotal-isUpperCaseBefore-inter'
170
+ : ''
171
+ }
172
+ >
173
+ {item.type === 'useSummarize'
174
+ ? getData(item.valueField)
175
+ : sumData2(list, item.valueField)}
176
+ </span>
177
+ {subtotal?.needUpperCase && (
178
+ <span>
179
+ {item.type === 'useSummarize'
180
+ ? '大写:' +
181
+ coverDigit2Uppercase(getData(item.valueField))
182
+ : '大写:' +
183
+ coverDigit2Uppercase(sumData2(list, item.valueField))}
184
+ </span>
185
+ )}
186
+ </div>
187
+ </div>
188
+ </td>
189
+ {/* 拆分只拆第一格;其余格(打印账户总计金额等)沿用普通多格渲染语义 */}
190
+ {_.map(fields.slice(1), (extra, extraIndex) => (
191
+ <td
192
+ colSpan={extra.colSpan ?? 99}
193
+ key={`split_extra_${extraIndex}`}
194
+ >
195
+ <div style={cellStyle} className='gm-flex-page'>
196
+ {extra.name}
197
+ <div
198
+ className={classNames('gm-flex-page', {
199
+ 'gm-flex-justify-between-page': isUpperLowerCaseSeparate,
200
+ 'gm-flex-grow-page': isUpperLowerCaseSeparate
201
+ })}
202
+ >
203
+ <span>
204
+ {/* 账户总计金额等固定取数字段走 useSummarize;拆分场景其余格不参与当页求和 */}
205
+ {extra.type === 'useSummarize'
206
+ ? getData(extra.valueField)
207
+ : ''}
208
+ </span>
209
+ {subtotal?.needUpperCase &&
210
+ extra.type === 'useSummarize' && (
211
+ <span>
212
+ {'大写:' +
213
+ coverDigit2Uppercase(getData(extra.valueField))}
214
+ </span>
215
+ )}
216
+ </div>
217
+ </div>
218
+ </td>
219
+ ))}
220
+ </tr>
221
+ )
222
+ }
223
+
137
224
  return (
138
225
  <tr>
139
226
  {_.map(fields, (item, index) => {