gm-printer 10.41.2 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-printer",
3
- "version": "10.41.2",
3
+ "version": "10.41.3-beta.0",
4
4
  "description": "diy printer",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -236,6 +236,12 @@ class EditorField extends React.Component {
236
236
  // 新分类
237
237
  const categoryStyle =
238
238
  toJS(editStore.computedTableSpecialConfig)?.categoryConfig?.style || {}
239
+ // 当前表格 dataKey tokens,用于判断商品分类/商品三级分类是否开启
240
+ const tableKeyArr = (
241
+ editStore.computedTableSpecialConfig?.dataKey || ''
242
+ ).split('_')
243
+ const isNewCategoryOn = tableKeyArr.includes('newCategory')
244
+ const isNewCategory3On = tableKeyArr.includes('newCategory3')
239
245
  // 小计样式,specialConfig可能是undefined
240
246
  const specialStyle =
241
247
  toJS(editStore.computedTableSpecialConfig)?.specialConfig?.style || {}
@@ -260,13 +266,18 @@ class EditorField extends React.Component {
260
266
  get(subtotal, 'isUpperLowerCaseSeparate')) ||
261
267
  false
262
268
  // 每页合计样式
263
- const overallOrderStyle =
264
- (overallOrder && overallOrder?.fields[0].style) || {}
269
+ // 兼容 overallOrder 无 fields 的模板(如仅存 {show:false}),链式取值避免渲染崩溃
270
+ const overallOrderStyle = overallOrder?.fields?.[0]?.style || {}
265
271
  // 每页合计自定义单元格
266
272
  const subtotalUpperCustomCell =
267
273
  (editStore.computedTableSpecialConfig?.subtotal &&
268
274
  get(subtotal, 'isCustomCells')) ||
269
275
  false
276
+ // 每页合计单元格拆分展示(与自定义单元格互斥的新配置)
277
+ const subtotalSplitCell =
278
+ (editStore.computedTableSpecialConfig?.subtotal &&
279
+ get(subtotal, 'isSplitCells')) ||
280
+ false
270
281
 
271
282
  // 打印账户总计金额
272
283
  const printAccount =
@@ -491,47 +502,48 @@ class EditorField extends React.Component {
491
502
  />
492
503
  <Gap />
493
504
 
494
- {!mergeClassificationAndLabel && (showCategory || showCategorySubtotal) && (
495
- <>
496
- {showCategory && (
497
- <Flex>
498
- <Flex>{i18next.t('分类设置')}:</Flex>
499
- <Fonter
500
- style={categoryStyle}
501
- onChange={this.handleCategoryStyleChange}
502
- />
503
- <Separator />
504
- <TextAlign
505
- style={categoryStyle}
506
- onChange={this.handleCategoryStyleChange}
507
- />
508
- </Flex>
509
- )}
510
-
511
- {showCategorySubtotal && (
512
- <>
505
+ {!mergeClassificationAndLabel &&
506
+ (showCategory || showCategorySubtotal) && (
507
+ <>
508
+ {showCategory && (
513
509
  <Flex>
514
- <Flex>{i18next.t('小计设置')}:</Flex>
510
+ <Flex>{i18next.t('分类设置')}:</Flex>
515
511
  <Fonter
516
- style={specialStyle}
517
- onChange={this.handleSpecialStyleChange}
512
+ style={categoryStyle}
513
+ onChange={this.handleCategoryStyleChange}
518
514
  />
519
515
  <Separator />
520
516
  <TextAlign
521
- style={specialStyle}
522
- onChange={this.handleSpecialStyleChange}
517
+ style={categoryStyle}
518
+ onChange={this.handleCategoryStyleChange}
523
519
  />
524
520
  </Flex>
525
- <EditorSubtotalCheck
526
- subtotalCheckDisabled
527
- subtotalChecked={specialTrNeedUpperCase}
528
- subtotalCheckOnChange={editStore.setSpecialUpperCase}
529
- subtotalCheckText='显示大写金额'
530
- />
531
- </>
532
- )}
533
- </>
534
- )}
521
+ )}
522
+
523
+ {showCategorySubtotal && (
524
+ <>
525
+ <Flex>
526
+ <Flex>{i18next.t('小计设置')}:</Flex>
527
+ <Fonter
528
+ style={specialStyle}
529
+ onChange={this.handleSpecialStyleChange}
530
+ />
531
+ <Separator />
532
+ <TextAlign
533
+ style={specialStyle}
534
+ onChange={this.handleSpecialStyleChange}
535
+ />
536
+ </Flex>
537
+ <EditorSubtotalCheck
538
+ subtotalCheckDisabled
539
+ subtotalChecked={specialTrNeedUpperCase}
540
+ subtotalCheckOnChange={editStore.setSpecialUpperCase}
541
+ subtotalCheckText='显示大写金额'
542
+ />
543
+ </>
544
+ )}
545
+ </>
546
+ )}
535
547
 
536
548
  {showPageSubtotal && pageSubtotalEnabled && (
537
549
  <>
@@ -570,7 +582,9 @@ class EditorField extends React.Component {
570
582
  key={fields.id}
571
583
  inputName='subtotalRadio'
572
584
  checked={subtotalFields === fields.id}
573
- radioChecked={() => editStore.subtotalRadioCheck(fields)}
585
+ radioChecked={() =>
586
+ editStore.subtotalRadioCheck(fields)
587
+ }
574
588
  />
575
589
  )
576
590
  })}
@@ -626,6 +640,24 @@ class EditorField extends React.Component {
626
640
  onChange={editStore.setSubtotalFields}
627
641
  style={{ width: '65px', margin: '5px 0 5px 80px' }}
628
642
  />
643
+ {/* 单元格拆分展示:每页合计/数值拆分为两个单元格,与开启自定义单元格互斥(默认未选) */}
644
+ <EditorSubtotalCheck
645
+ subtotalCheckDisabled
646
+ subtotalChecked={subtotalSplitCell}
647
+ subtotalCheckOnChange={editStore.setSubtotalSplitCells}
648
+ subtotalCheckText={i18next.t('单元格拆分展示')}
649
+ />
650
+ {/* 左侧文案修改:默认"每页合计",允许为空,每页合计统一展示输入框内容 */}
651
+ <Flex style={{ margin: '5px 0 5px 62px' }} alignCenter>
652
+ <EditorText
653
+ label={i18next.t('左侧文案修改')}
654
+ value={subtotal?.fields?.[0]?.name ?? ''}
655
+ // Text 组件的 onChange 实际收到事件对象(handleChange 被 spread 覆盖),需自行取 e.target.value
656
+ onChange={e =>
657
+ editStore.setSubtotalLeftText(e.target.value)
658
+ }
659
+ />
660
+ </Flex>
629
661
  </>
630
662
  )}
631
663
  </>
@@ -804,6 +836,23 @@ class EditorField extends React.Component {
804
836
  </div>
805
837
  </Flex>
806
838
  )}
839
+ {/* 分类/三级分类设置:选中商品分类或商品三级分类时展示,控制标题行字体及位置(共用 categoryConfig) */}
840
+ {mergeClassificationAndLabel && (isNewCategoryOn || isNewCategory3On) && (
841
+ <>
842
+ <Flex>{i18next.t('分类/三级分类设置')}:</Flex>
843
+ <Flex style={{ marginLeft: 57 }}>
844
+ <Fonter
845
+ style={categoryStyle}
846
+ onChange={this.handleCategoryStyleChange}
847
+ />
848
+ <Separator />
849
+ <TextAlign
850
+ style={categoryStyle}
851
+ onChange={this.handleCategoryStyleChange}
852
+ />
853
+ </Flex>
854
+ </>
855
+ )}
807
856
  {mergeClassificationAndLabel && (
808
857
  <>
809
858
  <Flex>{i18next.t('分类/标签小计')}:</Flex>
@@ -873,6 +922,29 @@ class EditorField extends React.Component {
873
922
  subtotalCheckText='大、小写金额分左右两边展示'
874
923
  marginLeft
875
924
  />
925
+ {/* 显示分类名称:选中时小计行展示"分类名+小计文案",未选中只展示"小计文案"(默认选中) */}
926
+ <EditorSubtotalCheck
927
+ subtotalCheckDisabled
928
+ subtotalChecked={
929
+ editStore.computedTableSpecialConfig?.specialConfig
930
+ ?.showCategoryName ?? true
931
+ }
932
+ subtotalCheckOnChange={editStore.setShowCategoryName}
933
+ subtotalCheckText={i18next.t('显示分类名称')}
934
+ marginLeft
935
+ />
936
+ {/* 小计文案修改:默认"小计",允许为空 */}
937
+ <Flex style={{ margin: '5px 0 5px 17px' }} alignCenter>
938
+ <EditorText
939
+ label={i18next.t('小计文案修改')}
940
+ value={
941
+ editStore.computedTableSpecialConfig?.specialConfig
942
+ ?.subtotalText ?? '小计'
943
+ }
944
+ // Text 组件的 onChange 实际收到事件对象(handleChange 被 spread 覆盖),需自行取 e.target.value
945
+ onChange={e => editStore.setSubtotalText(e.target.value)}
946
+ />
947
+ </Flex>
876
948
  </div>
877
949
  </Flex>
878
950
 
@@ -8,7 +8,7 @@ import {
8
8
  buildDataKey,
9
9
  getDataKeyPrefix,
10
10
  isDetailOneRowSelectKey,
11
- isIndependentRolDataKey,
11
+ isIndependentRolDataKey
12
12
  } from './data_key_util'
13
13
  import React from 'react'
14
14
  import { createDiySummaryStoreMethods } from './diy_summary_helper'
@@ -686,7 +686,6 @@ class EditorStore {
686
686
  // 切换的时候,要把对应table的多余空数据清掉
687
687
  this.clearExtraTableData(table.dataKey)
688
688
 
689
- this.setAutoFillingConfig(!this.isAutoFilling)
690
689
  // 获取td的colSpan
691
690
  const colSpanLength = getColSpanLength(table)
692
691
  set(table.subtotal, {
@@ -783,8 +782,24 @@ class EditorStore {
783
782
  if (table.overallOrder.showEachPage === undefined) {
784
783
  set(table.overallOrder, { showEachPage: true })
785
784
  }
786
- table.overallOrder.fields[0].colSpan =
787
- colSpanLength - (table.overallOrder.fields?.[1]?.colSpan ?? 0)
785
+ // 兼容存量/测试模板 overallOrder 只存了 {show:false} 没有 fields,补默认结构,否则 fields[0] 取值崩溃
786
+ if (!table.overallOrder.fields?.length) {
787
+ set(table.overallOrder, {
788
+ fields: [
789
+ {
790
+ name: '整单合计:',
791
+ valueField: '出库金额',
792
+ style: {
793
+ fontWeight: 'bold'
794
+ },
795
+ colSpan: colSpanLength
796
+ }
797
+ ]
798
+ })
799
+ } else {
800
+ table.overallOrder.fields[0].colSpan =
801
+ colSpanLength - (table.overallOrder.fields?.[1]?.colSpan ?? 0)
802
+ }
788
803
  } else {
789
804
  // 兼容已经存在的配送单据,他们的配置存在后端的,没有overallOrder这个配置,给加上
790
805
  set(table, {
@@ -922,6 +937,54 @@ class EditorStore {
922
937
  ) {
923
938
  newDataKey = _.without(newDataKey, 'tag', 'tagDiy')
924
939
  }
940
+ // 标签小计和三级分类小计互斥
941
+ if (
942
+ newDataKey.includes('category3') &&
943
+ (key === 'tag' || key === 'tagDiy')
944
+ ) {
945
+ newDataKey = _.without(newDataKey, 'category3')
946
+ } else if (
947
+ (newDataKey.includes('tag') || newDataKey.includes('tagDiy')) &&
948
+ key === 'category3'
949
+ ) {
950
+ newDataKey = _.without(newDataKey, 'tag', 'tagDiy')
951
+ }
952
+ // 标签小计和商品三级分类互斥
953
+ if (
954
+ newDataKey.includes('newCategory3') &&
955
+ (key === 'tag' || key === 'tagDiy')
956
+ ) {
957
+ newDataKey = _.without(newDataKey, 'newCategory3')
958
+ } else if (
959
+ (newDataKey.includes('tag') || newDataKey.includes('tagDiy')) &&
960
+ key === 'newCategory3'
961
+ ) {
962
+ newDataKey = _.without(newDataKey, 'tag', 'tagDiy')
963
+ }
964
+ // 商品三级分类和商品分类互斥(标题维度一级/三级二选一)
965
+ if (newDataKey.includes('newCategory3') && key === 'newCategory') {
966
+ newDataKey = _.without(newDataKey, 'newCategory3')
967
+ } else if (newDataKey.includes('newCategory') && key === 'newCategory3') {
968
+ newDataKey = _.without(newDataKey, 'newCategory')
969
+ }
970
+ // 三级分类小计和分类小计互斥(小计维度一级/三级二选一)
971
+ if (newDataKey.includes('category3') && key === 'category') {
972
+ newDataKey = _.without(newDataKey, 'category3')
973
+ } else if (newDataKey.includes('category') && key === 'category3') {
974
+ newDataKey = _.without(newDataKey, 'category')
975
+ }
976
+ // 跨层级混搭互斥:商品三级分类(三级标题)不配一级分类小计
977
+ if (newDataKey.includes('newCategory3') && key === 'category') {
978
+ newDataKey = _.without(newDataKey, 'newCategory3')
979
+ } else if (newDataKey.includes('category') && key === 'newCategory3') {
980
+ newDataKey = _.without(newDataKey, 'category')
981
+ }
982
+ // 跨层级混搭互斥:三级分类小计(三级小计)不配一级商品分类
983
+ if (newDataKey.includes('category3') && key === 'newCategory') {
984
+ newDataKey = _.without(newDataKey, 'category3')
985
+ } else if (newDataKey.includes('newCategory') && key === 'category3') {
986
+ newDataKey = _.without(newDataKey, 'newCategory')
987
+ }
925
988
 
926
989
  if (newDataKey.includes('categoryDiy')) {
927
990
  if (!diyCategorySubtotal?.show) {
@@ -948,7 +1011,10 @@ class EditorStore {
948
1011
  o => o === 'tag',
949
1012
  o => o === 'multi3',
950
1013
  o => o === 'multi',
1014
+ // 三级 token 紧跟对应一级 token 之后,保证拼串顺序 orders → category → category3 → newCategory → newCategory3 → multi → multi3 → tag → categoryDiy → tagDiy
1015
+ o => o === 'newCategory3',
951
1016
  o => o === 'newCategory',
1017
+ o => o === 'category3',
952
1018
  o => o === 'category',
953
1019
  o => o === 'orders'
954
1020
  ])
@@ -957,12 +1023,33 @@ class EditorStore {
957
1023
 
958
1024
  this.config.contents[arr[2]].dataKey = newDataKey.join('_')
959
1025
 
960
- // 整单合计不显示
961
- if (overallOrder?.show) overallOrder.show = false
962
- // 每页合计不显示
963
- if (subtotal?.show) subtotal.show = false
964
- // 自定义每页合计不显示
965
- if (diyOverallOrder?.show) diyOverallOrder.show = false
1026
+ // 三级分类相关 token(商品三级分类/三级分类小计)是纯分组、标题能力,
1027
+ // 与每页/整单合计可共存,切换时不再强制关闭合计开关;
1028
+ // 其余 token(多栏/一级分类/标签等)保持历史行为:切换时强制关闭
1029
+ if (!['newCategory3', 'category3'].includes(key)) {
1030
+ // 整单合计不显示
1031
+ if (overallOrder?.show) overallOrder.show = false
1032
+ // 每页合计不显示
1033
+ if (subtotal?.show) subtotal.show = false
1034
+ // 自定义每页合计不显示
1035
+ if (diyOverallOrder?.show) diyOverallOrder.show = false
1036
+ }
1037
+
1038
+ // 行数填充开启时切换 token:清掉旧 key 上的空行,并把 autoFillConfig.dataKey
1039
+ // 同步到新 key(printerStore 按两者相等才追加填充高度),交给 Printer 重挂载后重补
1040
+ if (this.isAutoFilling || this.config?.autoFillConfig?.checked) {
1041
+ this.clearAllTableEmptyData()
1042
+ this.setAutoFillingConfig(true)
1043
+ set(this.config, {
1044
+ autoFillConfig: {
1045
+ ...(this.config.autoFillConfig || {}),
1046
+ region: this.selectedRegion || this.config.autoFillConfig?.region,
1047
+ dataKey: newDataKey.join('_'),
1048
+ checked: true,
1049
+ fillIndex: this.fillIndex
1050
+ }
1051
+ })
1052
+ }
966
1053
  }
967
1054
 
968
1055
  @action
@@ -1313,7 +1400,7 @@ class EditorStore {
1313
1400
  dataKey === 'goods'
1314
1401
  ? buildDataKey(prefix, 'independent_rol_sku')
1315
1402
  : buildDataKey(prefix, 'independent_rol_address'),
1316
- detail_sort_type: 0,
1403
+ detail_sort_type: 0
1317
1404
  })
1318
1405
  }
1319
1406
 
@@ -1570,6 +1657,42 @@ class EditorStore {
1570
1657
  }
1571
1658
  }
1572
1659
 
1660
+ // 分类/标签 小计 - 显示分类名称(商品分类/商品三级分类小计行是否展示分类名,默认展示)
1661
+ @action.bound setShowCategoryName() {
1662
+ // 作用:触发组件的更新(小计行前缀由渲染层按 specialConfig 实时重拼,需触发预览重渲染)
1663
+ this.overallOrderShow = !this.overallOrderShow
1664
+ if (this.selectedRegion) {
1665
+ const arr = this.selectedRegion.split('.')
1666
+ const tableConfig = this.config.contents[arr[2]]
1667
+
1668
+ set(tableConfig, {
1669
+ specialConfig: {
1670
+ ...tableConfig.specialConfig,
1671
+ showCategoryName: !(
1672
+ tableConfig.specialConfig?.showCategoryName ?? true
1673
+ )
1674
+ }
1675
+ })
1676
+ }
1677
+ }
1678
+
1679
+ // 分类/标签 小计 - 小计文案修改(默认"小计",允许为空;缺失时由数据层兜底默认值)
1680
+ @action.bound setSubtotalText(value) {
1681
+ // 作用:触发组件的更新(小计行前缀由渲染层按 specialConfig 实时重拼,需触发预览重渲染)
1682
+ this.overallOrderShow = !this.overallOrderShow
1683
+ if (this.selectedRegion) {
1684
+ const arr = this.selectedRegion.split('.')
1685
+ const tableConfig = this.config.contents[arr[2]]
1686
+
1687
+ set(tableConfig, {
1688
+ specialConfig: {
1689
+ ...tableConfig.specialConfig,
1690
+ subtotalText: value
1691
+ }
1692
+ })
1693
+ }
1694
+ }
1695
+
1573
1696
  @action.bound
1574
1697
  setSpecialUpperCase() {
1575
1698
  if (this.selectedRegion) {
@@ -1765,6 +1888,8 @@ class EditorStore {
1765
1888
  }
1766
1889
  // 开启自定义单元格
1767
1890
  if (subtotalConfig?.isCustomCells) {
1891
+ // 互斥:开启自定义单元格时关闭单元格拆分展示
1892
+ set(subtotalConfig, { isSplitCells: false })
1768
1893
  // 同时还开启了自定义单元格选项
1769
1894
  if (subtotalConfig?.fields?.length === 2) {
1770
1895
  // fields添加自定义单元格
@@ -1812,6 +1937,8 @@ class EditorStore {
1812
1937
  }
1813
1938
 
1814
1939
  // 每页合计自定义单元格文本输入
1940
+ // Text 组件的 onChange 实际收到事件对象(handleChange 被 spread 覆盖),直接绑定时需防御式取 e.target.value,否则事件对象存进 fields[n].name 会导致页面崩溃
1941
+ // ⚠️ 不能用 `value?.target?.value || value`:清空输入时 e.target.value 为空串(falsy),|| 会错把事件对象存进去(清空即崩),必须用 value?.target 判别
1815
1942
  @action.bound
1816
1943
  setSubtotalFields(value) {
1817
1944
  if (this.selectedRegion) {
@@ -1820,10 +1947,64 @@ class EditorStore {
1820
1947
  const subtotalConfig = table?.subtotal
1821
1948
 
1822
1949
  if (subtotalConfig.isCustomCells) {
1950
+ const text = value?.target ? value.target.value : value ?? ''
1823
1951
  const subtotalConfig = table?.subtotal.fields
1824
1952
  subtotalConfig.length === 3
1825
- ? (subtotalConfig[2].name = value)
1826
- : (subtotalConfig[1].name = value)
1953
+ ? (subtotalConfig[2].name = text)
1954
+ : (subtotalConfig[1].name = text)
1955
+ }
1956
+ }
1957
+ }
1958
+
1959
+ // 每页合计单元格拆分展示("每页合计/数值"拆分为两个单元格,与开启自定义单元格互斥)
1960
+ @action.bound
1961
+ setSubtotalSplitCells() {
1962
+ if (this.selectedRegion) {
1963
+ // 作用:触发组件的更新
1964
+ this.overallOrderShow = !this.overallOrderShow
1965
+ const arr = this.selectedRegion.split('.')
1966
+ const table = this.config.contents[arr[2]]
1967
+ const subtotalConfig = table?.subtotal
1968
+ // 没有显示每页合计的时候不可以设置
1969
+ if (!subtotalConfig?.show) return
1970
+ // 多栏表格情况,计算colSpan
1971
+ const colSpanLength = getColSpanLength(table)
1972
+ const oldSplitCells = subtotalConfig?.isSplitCells
1973
+
1974
+ set(subtotalConfig, {
1975
+ isSplitCells: !oldSplitCells
1976
+ })
1977
+ // 兼容已经存在后端的模板,每页合计配置没有fields,手动补上
1978
+ if (!subtotalConfig.fields) {
1979
+ set(subtotalConfig, {
1980
+ fields: [
1981
+ {
1982
+ name: '每页合计:',
1983
+ valueField: 'real_item_price',
1984
+ colSpan: colSpanLength
1985
+ }
1986
+ ]
1987
+ })
1988
+ }
1989
+ // 互斥:开启拆分展示时关闭自定义单元格(复用其关闭时的 fields 重排逻辑)
1990
+ if (!oldSplitCells && subtotalConfig?.isCustomCells) {
1991
+ this.setSubtotalCustomCells()
1992
+ }
1993
+ }
1994
+ }
1995
+
1996
+ // 每页合计左侧文案修改(默认"每页合计",允许为空,每页合计统一展示输入框内容)
1997
+ @action.bound
1998
+ setSubtotalLeftText(value) {
1999
+ if (this.selectedRegion) {
2000
+ // 作用:触发组件的更新(预览渲染不直接观察 fields[0].name,需强制重渲染)
2001
+ this.overallOrderShow = !this.overallOrderShow
2002
+ const arr = this.selectedRegion.split('.')
2003
+ const table = this.config.contents[arr[2]]
2004
+ const subtotalConfig = table?.subtotal
2005
+
2006
+ if (subtotalConfig?.fields?.[0]) {
2007
+ set(subtotalConfig.fields[0], { name: value })
1827
2008
  }
1828
2009
  }
1829
2010
  }
@@ -1959,6 +2140,7 @@ class EditorStore {
1959
2140
  }
1960
2141
 
1961
2142
  // 整单合计自定义单元格文本输入
2143
+ // Text 组件直接绑定时收到事件对象(同 setSubtotalFields);⚠️ 不能用 `||` 兜底,清空输入时空串 falsy 会错存事件对象导致页面崩溃,用 value?.target 判别
1962
2144
  @action.bound
1963
2145
  setOverallOrderFields(value) {
1964
2146
  if (this.selectedRegion) {
@@ -1967,8 +2149,9 @@ class EditorStore {
1967
2149
  const overallOrderConfig = table?.overallOrder
1968
2150
 
1969
2151
  if (overallOrderConfig.isCustomCells) {
2152
+ const text = value?.target ? value.target.value : value ?? ''
1970
2153
  const overallOrderConfigFields = table?.overallOrder.fields
1971
- overallOrderConfigFields[1].name = value?.target?.value || value
2154
+ overallOrderConfigFields[1].name = text
1972
2155
  }
1973
2156
  }
1974
2157
  }
@@ -166,8 +166,11 @@ class ContextMenu extends React.Component {
166
166
  const isMultiActive = keyArr.includes('multi')
167
167
  const isThreeActive = keyArr.includes('multi3')
168
168
  const isCategoryActive = keyArr.includes('category')
169
+ // 三级分类小计(category3)/ 商品三级分类(newCategory3)token 状态
170
+ const isCategory3Active = keyArr.includes('category3')
169
171
  const isTagActive = keyArr.includes('tag')
170
172
  const isNewCategoryActive = keyArr.includes('newCategory')
173
+ const isNewCategory3Active = keyArr.includes('newCategory3')
171
174
  const isSubtotalActive = subtotal.show
172
175
  const isOverallOrder = overallOrder?.show
173
176
  const isDiyOverallOrder = diyOverallOrder?.show
@@ -208,6 +211,13 @@ class ContextMenu extends React.Component {
208
211
  >
209
212
  {i18next.t('分类小计')}
210
213
  </div>
214
+ {/* 三级分类小计:按三级分类(品类)拆分汇总小计,互斥规则见 changeTableDataKey */}
215
+ <div
216
+ onClick={this.handleChangeTableDataKey.bind(this, 'category3', name)}
217
+ className={isCategory3Active ? 'active' : ''}
218
+ >
219
+ {i18next.t('三级分类小计')}
220
+ </div>
211
221
  <div
212
222
  // onClick={() =>
213
223
  // this.handleDiySummary(
@@ -244,6 +254,13 @@ class ContextMenu extends React.Component {
244
254
  >
245
255
  {i18next.t('商品分类')}
246
256
  </div>
257
+ {/* 商品三级分类:按三级分类(品类)拆分并新增列表标题行,复用 categoryConfig 标题样式配置 */}
258
+ <div
259
+ onClick={this.handleCategoryConfig.bind(this, 'newCategory3', name)}
260
+ className={isNewCategory3Active ? 'active' : ''}
261
+ >
262
+ {i18next.t('商品三级分类')}
263
+ </div>
247
264
  <div
248
265
  onClick={this.handleSubtotal.bind(this, name)}
249
266
  className={isSubtotalActive ? 'active' : ''}
@@ -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>