feffery_antd_components 0.4.3-rc6 → 0.4.3

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.
Files changed (462) hide show
  1. package/CONTRIBUTING.md +4 -4
  2. package/DESCRIPTION +1 -1
  3. package/LICENSE +21 -21
  4. package/MANIFEST.in +6 -6
  5. package/Project.toml +1 -1
  6. package/README-en_US.md +73 -73
  7. package/README.md +87 -87
  8. package/bug_fix.py +21 -36
  9. package/fac-logo.svg +151 -151
  10. package/feffery_antd_components/AntdAccordion.py +7 -7
  11. package/feffery_antd_components/AntdAffix.py +2 -2
  12. package/feffery_antd_components/AntdAlert.py +5 -5
  13. package/feffery_antd_components/AntdAnchor.py +5 -5
  14. package/feffery_antd_components/AntdAvatar.py +4 -4
  15. package/feffery_antd_components/AntdAvatarGroup.py +1 -1
  16. package/feffery_antd_components/AntdBackTop.py +4 -4
  17. package/feffery_antd_components/AntdBadge.py +5 -5
  18. package/feffery_antd_components/AntdBreadcrumb.py +4 -4
  19. package/feffery_antd_components/AntdButton.py +14 -14
  20. package/feffery_antd_components/AntdCalendar.py +4 -4
  21. package/feffery_antd_components/AntdCard.py +5 -5
  22. package/feffery_antd_components/AntdCardGrid.py +3 -3
  23. package/feffery_antd_components/AntdCardMeta.py +1 -1
  24. package/feffery_antd_components/AntdCarousel.py +13 -13
  25. package/feffery_antd_components/AntdCascader.py +16 -17
  26. package/feffery_antd_components/AntdCenter.py +3 -3
  27. package/feffery_antd_components/AntdCheckCard.py +7 -7
  28. package/feffery_antd_components/AntdCheckCardGroup.py +9 -9
  29. package/feffery_antd_components/AntdCheckableTag.py +2 -2
  30. package/feffery_antd_components/AntdCheckbox.py +8 -8
  31. package/feffery_antd_components/AntdCheckboxGroup.py +6 -6
  32. package/feffery_antd_components/AntdCol.py +5 -5
  33. package/feffery_antd_components/AntdCollapse.py +8 -8
  34. package/feffery_antd_components/AntdColorPicker.py +13 -13
  35. package/feffery_antd_components/AntdComment.py +7 -7
  36. package/feffery_antd_components/AntdCompact.py +3 -3
  37. package/feffery_antd_components/AntdConfigProvider.py +6 -6
  38. package/feffery_antd_components/AntdContent.py +1 -1
  39. package/feffery_antd_components/AntdCopyText.py +2 -2
  40. package/feffery_antd_components/AntdCountdown.py +1 -1
  41. package/feffery_antd_components/AntdCountup.py +1 -1
  42. package/feffery_antd_components/AntdCustomSkeleton.py +5 -5
  43. package/feffery_antd_components/AntdDatePicker.py +13 -13
  44. package/feffery_antd_components/AntdDateRangePicker.py +12 -12
  45. package/feffery_antd_components/AntdDescriptionItem.py +2 -2
  46. package/feffery_antd_components/AntdDescriptions.py +6 -6
  47. package/feffery_antd_components/AntdDivider.py +5 -5
  48. package/feffery_antd_components/AntdDraggerUpload.py +15 -15
  49. package/feffery_antd_components/AntdDrawer.py +12 -12
  50. package/feffery_antd_components/AntdDropdown.py +18 -18
  51. package/feffery_antd_components/AntdEmpty.py +2 -2
  52. package/feffery_antd_components/AntdFlex.py +6 -6
  53. package/feffery_antd_components/AntdFloatButton.py +5 -5
  54. package/feffery_antd_components/AntdFloatButtonGroup.py +3 -3
  55. package/feffery_antd_components/AntdFooter.py +1 -1
  56. package/feffery_antd_components/AntdForm.py +10 -6
  57. package/feffery_antd_components/AntdFormItem.py +4 -4
  58. package/feffery_antd_components/AntdFormRender.py +2 -2
  59. package/feffery_antd_components/AntdHappyProvider.py +2 -2
  60. package/feffery_antd_components/AntdHeader.py +1 -1
  61. package/feffery_antd_components/AntdIcon.py +3 -3
  62. package/feffery_antd_components/AntdImage.py +6 -6
  63. package/feffery_antd_components/AntdImageGroup.py +6 -6
  64. package/feffery_antd_components/AntdInput.py +17 -17
  65. package/feffery_antd_components/AntdInputNumber.py +12 -12
  66. package/feffery_antd_components/AntdLayout.py +1 -1
  67. package/feffery_antd_components/AntdMentions.py +9 -9
  68. package/feffery_antd_components/AntdMenu.py +9 -9
  69. package/feffery_antd_components/AntdMessage.py +3 -3
  70. package/feffery_antd_components/AntdModal.py +28 -28
  71. package/feffery_antd_components/AntdNotification.py +9 -9
  72. package/feffery_antd_components/AntdOTP.py +7 -7
  73. package/feffery_antd_components/AntdPageHeader.py +5 -5
  74. package/feffery_antd_components/AntdPagination.py +15 -15
  75. package/feffery_antd_components/AntdParagraph.py +2 -2
  76. package/feffery_antd_components/AntdPictureUpload.py +28 -28
  77. package/feffery_antd_components/AntdPopconfirm.py +21 -21
  78. package/feffery_antd_components/AntdPopover.py +9 -9
  79. package/feffery_antd_components/AntdPopupCard.py +9 -9
  80. package/feffery_antd_components/AntdProgress.py +9 -9
  81. package/feffery_antd_components/AntdQRCode.py +10 -10
  82. package/feffery_antd_components/AntdRadioGroup.py +9 -9
  83. package/feffery_antd_components/AntdRate.py +9 -9
  84. package/feffery_antd_components/AntdResult.py +1 -1
  85. package/feffery_antd_components/AntdRibbon.py +2 -2
  86. package/feffery_antd_components/AntdRow.py +3 -3
  87. package/feffery_antd_components/AntdSegmented.py +9 -9
  88. package/feffery_antd_components/AntdSegmentedColoring.py +11 -11
  89. package/feffery_antd_components/AntdSelect.py +19 -19
  90. package/feffery_antd_components/AntdSider.py +6 -6
  91. package/feffery_antd_components/AntdSkeleton.py +11 -11
  92. package/feffery_antd_components/AntdSkeletonAvatar.py +3 -3
  93. package/feffery_antd_components/AntdSkeletonButton.py +5 -5
  94. package/feffery_antd_components/AntdSkeletonImage.py +1 -1
  95. package/feffery_antd_components/AntdSkeletonInput.py +3 -3
  96. package/feffery_antd_components/AntdSlider.py +10 -10
  97. package/feffery_antd_components/AntdSpace.py +4 -4
  98. package/feffery_antd_components/AntdSpin.py +7 -8
  99. package/feffery_antd_components/AntdSplitter.py +5 -5
  100. package/feffery_antd_components/AntdSpoiler.py +5 -5
  101. package/feffery_antd_components/AntdStatistic.py +2 -2
  102. package/feffery_antd_components/AntdSteps.py +8 -8
  103. package/feffery_antd_components/AntdSwitch.py +8 -8
  104. package/feffery_antd_components/AntdTable.py +64 -63
  105. package/feffery_antd_components/AntdTabs.py +13 -14
  106. package/feffery_antd_components/AntdTag.py +4 -4
  107. package/feffery_antd_components/AntdText.py +2 -2
  108. package/feffery_antd_components/AntdTimePicker.py +16 -16
  109. package/feffery_antd_components/AntdTimeRangePicker.py +15 -15
  110. package/feffery_antd_components/AntdTimeline.py +2 -2
  111. package/feffery_antd_components/AntdTitle.py +2 -2
  112. package/feffery_antd_components/AntdTooltip.py +9 -9
  113. package/feffery_antd_components/AntdTour.py +9 -9
  114. package/feffery_antd_components/AntdTransfer.py +11 -11
  115. package/feffery_antd_components/AntdTree.py +17 -17
  116. package/feffery_antd_components/AntdTreeSelect.py +18 -18
  117. package/feffery_antd_components/AntdUpload.py +18 -18
  118. package/feffery_antd_components/AntdWatermark.py +6 -6
  119. package/feffery_antd_components/Fragment.py +1 -1
  120. package/feffery_antd_components/__init__.py +88 -88
  121. package/feffery_antd_components/_icons.py +559 -559
  122. package/feffery_antd_components/alias.py +222 -222
  123. package/feffery_antd_components/async-antd_table.js +2 -2
  124. package/feffery_antd_components/async-data_display.js +1 -1
  125. package/feffery_antd_components/async-data_entry.js +3 -3
  126. package/feffery_antd_components/async-upload.js +2 -2
  127. package/feffery_antd_components/feffery_antd_components.min.js +8 -8
  128. package/feffery_antd_components/metadata.json +1 -1
  129. package/feffery_antd_components/package-info.json +111 -96
  130. package/feffery_antd_components/utils.py +262 -262
  131. package/package.json +111 -96
  132. package/pytest.ini +5 -5
  133. package/release-to-test-pypi.sh +1 -1
  134. package/requirements.txt +2 -2
  135. package/review_checklist.md +47 -47
  136. package/ruff.toml +4 -4
  137. package/setup.py +33 -33
  138. package/src/jl/'feffery'_antdform.jl +2 -1
  139. package/src/jl/'feffery'_antdtable.jl +7 -6
  140. package/src/lib/components/dataDisplay/AntdAccordion.react.js +232 -232
  141. package/src/lib/components/dataDisplay/AntdAvatar.react.js +177 -177
  142. package/src/lib/components/dataDisplay/AntdAvatarGroup.react.js +133 -133
  143. package/src/lib/components/dataDisplay/AntdBadge.react.js +196 -196
  144. package/src/lib/components/dataDisplay/AntdCarousel.react.js +187 -187
  145. package/src/lib/components/dataDisplay/AntdCheckableTag.react.js +105 -105
  146. package/src/lib/components/dataDisplay/AntdCollapse.react.js +239 -239
  147. package/src/lib/components/dataDisplay/AntdComment.react.js +251 -251
  148. package/src/lib/components/dataDisplay/AntdCountdown.react.js +143 -143
  149. package/src/lib/components/dataDisplay/AntdCountup.react.js +133 -133
  150. package/src/lib/components/dataDisplay/AntdEmpty.react.js +162 -162
  151. package/src/lib/components/dataDisplay/AntdImage.react.js +202 -202
  152. package/src/lib/components/dataDisplay/AntdImageGroup.react.js +181 -181
  153. package/src/lib/components/dataDisplay/AntdPopover.react.js +220 -220
  154. package/src/lib/components/dataDisplay/AntdQRCode.react.js +191 -191
  155. package/src/lib/components/dataDisplay/AntdRibbon.react.js +105 -105
  156. package/src/lib/components/dataDisplay/AntdSegmented.react.js +234 -234
  157. package/src/lib/components/dataDisplay/AntdSpoiler.react.js +154 -154
  158. package/src/lib/components/dataDisplay/AntdStatistic.react.js +168 -168
  159. package/src/lib/components/dataDisplay/AntdTable.react.js +1859 -1858
  160. package/src/lib/components/dataDisplay/AntdTabs.react.js +352 -352
  161. package/src/lib/components/dataDisplay/AntdTag.react.js +135 -135
  162. package/src/lib/components/dataDisplay/AntdTimeline.react.js +136 -136
  163. package/src/lib/components/dataDisplay/AntdTooltip.react.js +230 -230
  164. package/src/lib/components/dataDisplay/AntdTree.react.js +641 -641
  165. package/src/lib/components/dataDisplay/card/AntdCard.react.js +249 -249
  166. package/src/lib/components/dataDisplay/card/AntdCardGrid.react.js +99 -99
  167. package/src/lib/components/dataDisplay/card/AntdCardMeta.react.js +97 -97
  168. package/src/lib/components/dataDisplay/descriptions/AntdDescriptionItem.react.js +130 -130
  169. package/src/lib/components/dataDisplay/descriptions/AntdDescriptions.react.js +237 -237
  170. package/src/lib/components/dataEntry/AntdCalendar.react.js +205 -205
  171. package/src/lib/components/dataEntry/AntdCascader.react.js +435 -435
  172. package/src/lib/components/dataEntry/AntdCheckbox.react.js +182 -182
  173. package/src/lib/components/dataEntry/AntdCheckboxGroup.react.js +193 -193
  174. package/src/lib/components/dataEntry/AntdColorPicker.react.js +240 -240
  175. package/src/lib/components/dataEntry/AntdDatePicker.react.js +432 -432
  176. package/src/lib/components/dataEntry/AntdDateRangePicker.react.js +439 -439
  177. package/src/lib/components/dataEntry/AntdInput.react.js +423 -423
  178. package/src/lib/components/dataEntry/AntdInputNumber.react.js +329 -329
  179. package/src/lib/components/dataEntry/AntdMentions.react.js +236 -236
  180. package/src/lib/components/dataEntry/AntdOTP.react.js +186 -186
  181. package/src/lib/components/dataEntry/AntdRadioGroup.react.js +241 -241
  182. package/src/lib/components/dataEntry/AntdRate.react.js +199 -199
  183. package/src/lib/components/dataEntry/AntdSegmentedColoring.react.js +251 -251
  184. package/src/lib/components/dataEntry/AntdSelect.react.js +492 -492
  185. package/src/lib/components/dataEntry/AntdSlider.react.js +325 -325
  186. package/src/lib/components/dataEntry/AntdSwitch.react.js +210 -210
  187. package/src/lib/components/dataEntry/AntdTimePicker.react.js +344 -344
  188. package/src/lib/components/dataEntry/AntdTimeRangePicker.react.js +325 -325
  189. package/src/lib/components/dataEntry/AntdTransfer.react.js +317 -317
  190. package/src/lib/components/dataEntry/AntdTreeSelect.react.js +573 -573
  191. package/src/lib/components/dataEntry/check-card/AntdCheckCard.react.js +186 -186
  192. package/src/lib/components/dataEntry/check-card/AntdCheckCardGroup.react.js +214 -214
  193. package/src/lib/components/dataEntry/form/AntdForm.react.js +201 -194
  194. package/src/lib/components/dataEntry/form/AntdFormItem.react.js +210 -210
  195. package/src/lib/components/dataEntry/upload/AntdDraggerUpload.react.js +504 -504
  196. package/src/lib/components/dataEntry/upload/AntdPictureUpload.react.js +530 -530
  197. package/src/lib/components/dataEntry/upload/AntdUpload.react.js +537 -537
  198. package/src/lib/components/feedback/AntdAlert.react.js +185 -185
  199. package/src/lib/components/feedback/AntdDrawer.react.js +306 -306
  200. package/src/lib/components/feedback/AntdMessage.react.js +143 -143
  201. package/src/lib/components/feedback/AntdModal.react.js +530 -530
  202. package/src/lib/components/feedback/AntdNotification.react.js +211 -211
  203. package/src/lib/components/feedback/AntdPopconfirm.react.js +433 -433
  204. package/src/lib/components/feedback/AntdPopupCard.react.js +362 -362
  205. package/src/lib/components/feedback/AntdProgress.react.js +300 -300
  206. package/src/lib/components/feedback/AntdResult.react.js +125 -125
  207. package/src/lib/components/feedback/AntdSpin.react.js +273 -273
  208. package/src/lib/components/feedback/skeleton/AntdCustomSkeleton.react.js +197 -197
  209. package/src/lib/components/feedback/skeleton/AntdSkeleton.react.js +291 -291
  210. package/src/lib/components/feedback/skeleton/AntdSkeletonAvatar.react.js +109 -109
  211. package/src/lib/components/feedback/skeleton/AntdSkeletonButton.react.js +114 -114
  212. package/src/lib/components/feedback/skeleton/AntdSkeletonImage.react.js +82 -82
  213. package/src/lib/components/feedback/skeleton/AntdSkeletonInput.react.js +98 -98
  214. package/src/lib/components/formRender/AntdFormRender.react.js +945 -945
  215. package/src/lib/components/general/AntdButton.react.js +328 -328
  216. package/src/lib/components/general/AntdFloatButton.react.js +157 -157
  217. package/src/lib/components/general/AntdFloatButtonGroup.react.js +162 -162
  218. package/src/lib/components/general/AntdIcon.react.js +176 -176
  219. package/src/lib/components/general/typography/AntdParagraph.react.js +202 -202
  220. package/src/lib/components/general/typography/AntdText.react.js +194 -194
  221. package/src/lib/components/general/typography/AntdTitle.react.js +186 -186
  222. package/src/lib/components/icons.react.js +1136 -1136
  223. package/src/lib/components/layout/AntdCenter.react.js +122 -122
  224. package/src/lib/components/layout/AntdCompact.react.js +108 -108
  225. package/src/lib/components/layout/AntdContent.react.js +92 -92
  226. package/src/lib/components/layout/AntdDivider.react.js +219 -219
  227. package/src/lib/components/layout/AntdFlex.react.js +143 -143
  228. package/src/lib/components/layout/AntdFooter.react.js +92 -92
  229. package/src/lib/components/layout/AntdHeader.react.js +92 -92
  230. package/src/lib/components/layout/AntdLayout.react.js +90 -90
  231. package/src/lib/components/layout/AntdSider.react.js +162 -162
  232. package/src/lib/components/layout/AntdSpace.react.js +213 -213
  233. package/src/lib/components/layout/AntdSplitter.react.js +173 -173
  234. package/src/lib/components/layout/grid/AntdCol.react.js +349 -349
  235. package/src/lib/components/layout/grid/AntdRow.react.js +152 -152
  236. package/src/lib/components/locales.react.js +197 -197
  237. package/src/lib/components/navigation/AntdAnchor.react.js +208 -208
  238. package/src/lib/components/navigation/AntdBreadcrumb.react.js +270 -270
  239. package/src/lib/components/navigation/AntdDropdown.react.js +469 -469
  240. package/src/lib/components/navigation/AntdMenu.react.js +573 -649
  241. package/src/lib/components/navigation/AntdPageHeader.react.js +139 -139
  242. package/src/lib/components/navigation/AntdPagination.react.js +303 -303
  243. package/src/lib/components/navigation/AntdSteps.react.js +199 -199
  244. package/src/lib/components/other/AntdAffix.react.js +120 -120
  245. package/src/lib/components/other/AntdBackTop.react.js +128 -128
  246. package/src/lib/components/other/AntdConfigProvider.react.js +246 -246
  247. package/src/lib/components/other/AntdCopyText.react.js +138 -138
  248. package/src/lib/components/other/AntdHappyProvider.react.js +50 -50
  249. package/src/lib/components/other/AntdTour.react.js +277 -277
  250. package/src/lib/components/other/AntdWatermark.react.js +179 -179
  251. package/src/lib/components/other/Fragment.react.js +50 -50
  252. package/src/lib/components/styles.css +513 -513
  253. package/src/lib/components/utils.js +51 -51
  254. package/src/lib/contexts/FormContext.js +4 -4
  255. package/src/lib/contexts/FormItemContext.js +4 -4
  256. package/src/lib/contexts/PropsContext.js +4 -4
  257. package/src/lib/fragments/AntdTable.react.js +2215 -2200
  258. package/src/lib/fragments/dataDisplay/AntdAccordion.react.js +101 -101
  259. package/src/lib/fragments/dataDisplay/AntdAvatar.react.js +127 -127
  260. package/src/lib/fragments/dataDisplay/AntdAvatarGroup.react.js +51 -51
  261. package/src/lib/fragments/dataDisplay/AntdBadge.react.js +73 -73
  262. package/src/lib/fragments/dataDisplay/AntdCarousel.react.js +71 -71
  263. package/src/lib/fragments/dataDisplay/AntdCheckableTag.react.js +57 -57
  264. package/src/lib/fragments/dataDisplay/AntdComment.react.js +178 -178
  265. package/src/lib/fragments/dataDisplay/AntdCountdown.react.js +83 -83
  266. package/src/lib/fragments/dataDisplay/AntdCountup.react.js +70 -70
  267. package/src/lib/fragments/dataDisplay/AntdEmpty.react.js +71 -71
  268. package/src/lib/fragments/dataDisplay/AntdImage.react.js +206 -206
  269. package/src/lib/fragments/dataDisplay/AntdImageGroup.react.js +91 -91
  270. package/src/lib/fragments/dataDisplay/AntdPopover.react.js +106 -106
  271. package/src/lib/fragments/dataDisplay/AntdQRCode.react.js +93 -93
  272. package/src/lib/fragments/dataDisplay/AntdRibbon.react.js +53 -53
  273. package/src/lib/fragments/dataDisplay/AntdSegmented.react.js +129 -129
  274. package/src/lib/fragments/dataDisplay/AntdSpoiler.react.js +96 -96
  275. package/src/lib/fragments/dataDisplay/AntdTabs.react.js +211 -211
  276. package/src/lib/fragments/dataDisplay/AntdTag.react.js +66 -66
  277. package/src/lib/fragments/dataDisplay/AntdTimeline.react.js +67 -67
  278. package/src/lib/fragments/dataDisplay/AntdTooltip.react.js +98 -98
  279. package/src/lib/fragments/dataDisplay/AntdTree.react.js +581 -581
  280. package/src/lib/fragments/dataDisplay/card/AntdCard.react.js +104 -104
  281. package/src/lib/fragments/dataDisplay/card/AntdCardGrid.react.js +54 -54
  282. package/src/lib/fragments/dataDisplay/card/AntdCardMeta.react.js +54 -54
  283. package/src/lib/fragments/dataDisplay/descriptions/AntdDescriptionItem.react.js +56 -56
  284. package/src/lib/fragments/dataDisplay/descriptions/AntdDescriptions.react.js +139 -139
  285. package/src/lib/fragments/dataEntry/AntdCalendar.react.js +178 -178
  286. package/src/lib/fragments/dataEntry/AntdCascader.react.js +319 -319
  287. package/src/lib/fragments/dataEntry/AntdCheckbox.react.js +119 -119
  288. package/src/lib/fragments/dataEntry/AntdCheckboxGroup.react.js +122 -122
  289. package/src/lib/fragments/dataEntry/AntdColorPicker.react.js +244 -244
  290. package/src/lib/fragments/dataEntry/AntdDatePicker.react.js +586 -586
  291. package/src/lib/fragments/dataEntry/AntdDateRangePicker.react.js +628 -628
  292. package/src/lib/fragments/dataEntry/AntdInput.react.js +450 -450
  293. package/src/lib/fragments/dataEntry/AntdInputNumber.react.js +194 -194
  294. package/src/lib/fragments/dataEntry/AntdMentions.react.js +180 -180
  295. package/src/lib/fragments/dataEntry/AntdOTP.react.js +127 -127
  296. package/src/lib/fragments/dataEntry/AntdRadioGroup.react.js +203 -203
  297. package/src/lib/fragments/dataEntry/AntdRate.react.js +131 -131
  298. package/src/lib/fragments/dataEntry/AntdSegmentedColoring.react.js +246 -246
  299. package/src/lib/fragments/dataEntry/AntdSelect.react.js +373 -373
  300. package/src/lib/fragments/dataEntry/AntdSlider.react.js +177 -177
  301. package/src/lib/fragments/dataEntry/AntdSwitch.react.js +132 -132
  302. package/src/lib/fragments/dataEntry/AntdTimePicker.react.js +191 -191
  303. package/src/lib/fragments/dataEntry/AntdTimeRangePicker.react.js +213 -213
  304. package/src/lib/fragments/dataEntry/AntdTransfer.react.js +183 -183
  305. package/src/lib/fragments/dataEntry/AntdTreeSelect.react.js +355 -355
  306. package/src/lib/fragments/dataEntry/check-card/AntdCheckCard.react.js +111 -111
  307. package/src/lib/fragments/dataEntry/check-card/AntdCheckCardGroup.react.js +122 -122
  308. package/src/lib/fragments/dataEntry/form/AntdForm.react.js +126 -120
  309. package/src/lib/fragments/dataEntry/form/AntdFormItem.react.js +85 -84
  310. package/src/lib/fragments/formRender/AntdFormRender.react.js +179 -179
  311. package/src/lib/fragments/upload/AntdDraggerUpload.react.js +622 -622
  312. package/src/lib/fragments/upload/AntdPictureUpload.react.js +577 -577
  313. package/src/lib/fragments/upload/AntdUpload.react.js +613 -613
  314. package/src/lib/hooks/useCss.js +28 -28
  315. package/src/lib/hooks/useStickyOffset.js +115 -115
  316. package/src/lib/index.js +240 -240
  317. package/src/lib/internal_components/UtilsLink.react.js +82 -0
  318. package/src/lib/store/formStore.js +54 -47
  319. package/tests/dash3.0tests/AntdQRCode/347/273/204/344/273/266loading/347/212/266/346/200/201/351/207/215/346/236/204/test.py +51 -51
  320. package/tests/dash3.0tests/AntdTable/345/207/275/346/225/260/345/274/217/346/224/271/351/200/240/test.py +62 -62
  321. package/tests/dash3.0tests/_dashprivate_layout/346/224/271/345/212/250/346/265/213/350/257/225/test.py +50 -50
  322. package/tests/dash3.0tests/loading_state/346/224/271/345/212/250/346/265/213/350/257/225/test.py +38 -38
  323. package/tests/dash3.0tests//345/207/275/346/225/260/345/274/217/347/273/204/344/273/266/351/273/230/350/256/244/345/200/274/351/207/215/346/236/204/test.py +30 -30
  324. package/tests/dash3.0tests//345/212/240/350/275/275/345/212/250/347/224/273/347/261/273/347/273/204/344/273/266/351/207/215/346/236/204/346/265/213/350/257/225/test.py +62 -62
  325. package/tests/dash3.0tests//347/273/204/344/273/266/345/261/236/346/200/247/345/237/272/347/241/200/346/214/201/344/271/205/345/214/226/351/207/215/346/236/204/346/265/213/350/257/225/test.py +25 -25
  326. package/tests/dataDisplay/AntdAccordion/feat_size.py +36 -36
  327. package/tests/dataDisplay/AntdAccordion/feat_styles.py +35 -35
  328. package/tests/dataDisplay/AntdCard/refactor_delete_props.py +83 -83
  329. package/tests/dataDisplay/AntdCarousel/feat_auto_play_dot_duration.py +34 -34
  330. package/tests/dataDisplay/AntdCollapse/feat_size.py +30 -30
  331. package/tests/dataDisplay/AntdCollapse/feat_styles.py +29 -29
  332. package/tests/dataDisplay/AntdCountdown/feat_finish_event.py +41 -41
  333. package/tests/dataDisplay/AntdCountdown/refactor_to_timer.py +57 -57
  334. package/tests/dataDisplay/AntdCountup/refactor_to_timer.py +57 -57
  335. package/tests/dataDisplay/AntdDescriptions/feat_span_filled.py +42 -42
  336. package/tests/dataDisplay/AntdImage/debug_preview_toolbar.py +23 -23
  337. package/tests/dataDisplay/AntdSegmented/feat_shape.py +39 -39
  338. package/tests/dataDisplay/AntdSegmented/feat_simple_options.py +43 -43
  339. package/tests/dataDisplay/AntdSegmented/feat_vertical.py +32 -32
  340. package/tests/dataDisplay/AntdTable/feat_button_mode_like_dcc_link.py +125 -0
  341. package/tests/dataDisplay/AntdTable/feat_button_mode_refactor.py +143 -143
  342. package/tests/dataDisplay/AntdTable/feat_button_mode_tooltip.py +47 -47
  343. package/tests/dataDisplay/AntdTable/feat_columns_fixed_bool.py +40 -40
  344. package/tests/dataDisplay/AntdTable/feat_columns_min_width.py +51 -51
  345. package/tests/dataDisplay/AntdTable/feat_columns_sort_order.py +131 -131
  346. package/tests/dataDisplay/AntdTable/feat_editable_disabled_keys.py +71 -71
  347. package/tests/dataDisplay/AntdTable/feat_header_align.py +38 -38
  348. package/tests/dataDisplay/AntdTable/feat_link_mode_like_dcc_link.py +49 -0
  349. package/tests/dataDisplay/AntdTable/feat_loading.py +76 -76
  350. package/tests/dataDisplay/AntdTable/feat_mini_chart_color.py +139 -139
  351. package/tests/dataDisplay/AntdTable/feat_row_class_name.py +39 -39
  352. package/tests/dataDisplay/AntdTable/feat_row_class_name_func.py +41 -41
  353. package/tests/dataDisplay/AntdTable/feat_select_mode_show_search.py +70 -70
  354. package/tests/dataDisplay/AntdTable/feat_sticky_header.py +81 -81
  355. package/tests/dataDisplay/AntdTable/feat_table_scroll.py +54 -54
  356. package/tests/dataDisplay/AntdTable/feat_tag_mode_tooltip.py +88 -88
  357. package/tests/dataDisplay/AntdTable/feat_title_popover_nodes.py +94 -79
  358. package/tests/dataDisplay/AntdTable/fix_context_locale.py +37 -37
  359. package/tests/dataDisplay/AntdTable/fix_field_bool.py +50 -50
  360. package/tests/dataDisplay/AntdTable/fix_nested_editable.py +115 -115
  361. package/tests/dataDisplay/AntdTable/fix_select_with_editable.py +84 -84
  362. package/tests/dataDisplay/AntdTable/fix_server_side_mode_filter_search.py +194 -194
  363. package/tests/dataDisplay/AntdTable/fix_summary.py +474 -474
  364. package/tests/dataDisplay/AntdTable/fix_summary_multi_level_header.py +85 -85
  365. package/tests/dataDisplay/AntdTable/fix_zero_value_filter.py +34 -34
  366. package/tests/dataDisplay/AntdTable/perf_button_mode_auto_wrap.py +39 -39
  367. package/tests/dataDisplay/AntdTable/perf_editable_column_format_constrait_message.py +65 -65
  368. package/tests/dataDisplay/AntdTable/refactor_mini_progress_mode.py +134 -134
  369. package/tests/dataDisplay/AntdTable/rollback_title_popover_info.py +46 -0
  370. package/tests/dataDisplay/AntdTabs/feat_item_icon.py +39 -39
  371. package/tests/dataDisplay/AntdTabs/feat_placeholder.py +124 -124
  372. package/tests/dataDisplay/AntdTabs/feat_tab_bar_style.py +40 -40
  373. package/tests/dataDisplay/AntdTree/feat_async_data_load.py +71 -71
  374. package/tests/dataDisplay/descriptions/AntdDescriptions/feat_extra.py +83 -83
  375. package/tests/dataEntry/AntdCalendar/feat_custom_cells.py +52 -52
  376. package/tests/dataEntry/AntdCascader/feat_prefix.py +62 -62
  377. package/tests/dataEntry/AntdCascader/feat_suffix_icon.py +62 -62
  378. package/tests/dataEntry/AntdCascader/fix_read_only.py +74 -74
  379. package/tests/dataEntry/AntdDatePicker/feat_custom_cells.py +79 -79
  380. package/tests/dataEntry/AntdDatePicker/feat_picker_value.py +38 -38
  381. package/tests/dataEntry/AntdDatePicker/feat_prefix.py +21 -21
  382. package/tests/dataEntry/AntdDatePicker/feat_suffix_icon.py +21 -21
  383. package/tests/dataEntry/AntdDateRangePicker/feat_custom_cells.py +79 -79
  384. package/tests/dataEntry/AntdDateRangePicker/feat_picker_value.py +38 -38
  385. package/tests/dataEntry/AntdDateRangePicker/feat_prefix.py +21 -21
  386. package/tests/dataEntry/AntdDateRangePicker/feat_suffix_icon.py +21 -21
  387. package/tests/dataEntry/AntdForm/feat_batch_control_tooltips.py +35 -0
  388. package/tests/dataEntry/AntdForm/feat_optional_batch_control.py +62 -62
  389. package/tests/dataEntry/AntdForm/fix_checkbox.py +65 -65
  390. package/tests/dataEntry/AntdRadioGroup/feat_block.py +40 -40
  391. package/tests/dataEntry/AntdSelect/feat_locale_ru.py +24 -24
  392. package/tests/dataEntry/AntdSelect/feat_max_count.py +28 -28
  393. package/tests/dataEntry/AntdSelect/feat_prefix.py +26 -26
  394. package/tests/dataEntry/AntdSelect/feat_show_search.py +32 -32
  395. package/tests/dataEntry/AntdSelect/feat_suffix_icon.py +46 -46
  396. package/tests/dataEntry/AntdSelect/fix_read_only.py +50 -50
  397. package/tests/dataEntry/AntdSelect/fix_read_only_with_allow_clear.py +29 -29
  398. package/tests/dataEntry/AntdSlider/feat_range_editable.py +49 -49
  399. package/tests/dataEntry/AntdTimePicker/feat_prefix.py +21 -21
  400. package/tests/dataEntry/AntdTimePicker/feat_suffix_icon.py +21 -21
  401. package/tests/dataEntry/AntdTimeRangePicker/feat_prefix.py +21 -21
  402. package/tests/dataEntry/AntdTimeRangePicker/feat_suffix_icon.py +21 -21
  403. package/tests/dataEntry/AntdTreeSelect/feat_async_data_load.py +69 -69
  404. package/tests/dataEntry/AntdTreeSelect/feat_max_count.py +71 -71
  405. package/tests/dataEntry/AntdTreeSelect/feat_prefix.py +43 -43
  406. package/tests/dataEntry/AntdTreeSelect/fix_read_only.py +50 -50
  407. package/tests/dataEntry/AntdUpload/feat_pastable.py +22 -22
  408. package/tests/dataEntry/AntdUpload/fix_follow_theme.py +28 -28
  409. package/tests/feat_variant_underlined.py +100 -100
  410. package/tests/feedback/AntdAlert/feat_icon.py +35 -35
  411. package/tests/feedback/AntdMessage/fix_follow_config_provider.py +44 -44
  412. package/tests/feedback/AntdModal/feat_force_render.py +57 -57
  413. package/tests/feedback/AntdModal/feat_loading_title.py +92 -92
  414. package/tests/feedback/AntdModal/feat_prevent_close.py +54 -54
  415. package/tests/feedback/AntdModal/feat_responsive_width.py +31 -31
  416. package/tests/feedback/AntdNotification/feat_component_content.py +64 -64
  417. package/tests/feedback/AntdNotification/feat_number_of_stack.py +40 -40
  418. package/tests/feedback/AntdNotification/feat_show_progress.py +40 -40
  419. package/tests/feedback/AntdNotification/fix_follow_config_provider.py +46 -46
  420. package/tests/feedback/AntdPopupCard/feat_force_render.py +61 -61
  421. package/tests/feedback/AntdProgress/feat_percent_position.py +39 -39
  422. package/tests/feedback/AntdProgress/perf_size.py +26 -26
  423. package/tests/feedback/AntdProgress/perf_steps.py +37 -37
  424. package/tests/feedback/AntdProgress/perf_stroke_color.py +58 -58
  425. package/tests/feedback/AntdResult/feat_extra.py +26 -26
  426. package/tests/feedback/AntdSkeleton/fix_dash3.py +43 -43
  427. package/tests/general/AntdButton/feat_color_and_variant.py +40 -40
  428. package/tests/general/AntdButton/feat_preset_color.py +46 -46
  429. package/tests/general/AntdFloatButtonGroup/feat_placement.py +83 -83
  430. package/tests/general/AntdFloatButtonGroup/feat_tooltip.py +34 -34
  431. package/tests/general/AntdIcon/feat_iconfont.py +41 -41
  432. package/tests/layout/AntdCenter/feat_use_token.py +41 -41
  433. package/tests/layout/AntdDivider/feat_plain.py +21 -21
  434. package/tests/layout/AntdDivider/feat_size.py +36 -36
  435. package/tests/layout/AntdSplitter/feat_AntdSplitter.py +210 -210
  436. package/tests/layout/AntdSplitter/feat_lazy.py +56 -56
  437. package/tests/layout/AntdSplitter/refactor.py +303 -303
  438. package/tests/navigation/AntdAnchor/feat_current_anchor.py +43 -43
  439. package/tests/navigation/AntdAnchor/feat_direction.py +43 -43
  440. package/tests/navigation/AntdAnchor/feat_replace.py +43 -43
  441. package/tests/navigation/AntdBreadcrumb/feat_menu_item_event.py +64 -64
  442. package/tests/navigation/AntdBreadcrumb/refactor_items.py +140 -140
  443. package/tests/navigation/AntdDropdown/feat_extra.py +29 -29
  444. package/tests/navigation/AntdMenu/feat_currentItem.py +85 -85
  445. package/tests/navigation/AntdMenu/feat_currentItemPath.py +85 -85
  446. package/tests/navigation/AntdMenu/feat_currentKeyPath.py +85 -85
  447. package/tests/navigation/AntdMenu/feat_custom_icon.py +57 -57
  448. package/tests/navigation/AntdMenu/feat_expandIcon.py +70 -70
  449. package/tests/navigation/AntdMenu/feat_triggerSubMenuAction.py +67 -67
  450. package/tests/navigation/AntdMenu/fix_onlyExpandCurrentSubMenu.py +57 -57
  451. package/tests/navigation/AntdMenu/fix_with_sider.py +71 -71
  452. package/tests/navigation/AntdPagination/feat_align.py +31 -31
  453. package/tests/other/AntdAffix/feat_listen_affixed.py +41 -41
  454. package/tests/other/AntdBackTop/refactor.py +18 -18
  455. package/tests/other/AntdConfigProvider/feat_use_css.py +67 -67
  456. package/tests/other/AntdCopyText/feat_copy_table.py +67 -67
  457. package/tests/other/AntdCopyText/feat_tooltips.py +61 -61
  458. package/tests/other/AntdHappyProvider/feat_component.py +60 -60
  459. package/tests/other/Fragment/feat_use_token.py +32 -32
  460. package/usage.py +34 -34
  461. package/webpack.config.js +181 -181
  462. package/webpack.serve.config.js +12 -12
@@ -1,2201 +1,2216 @@
1
- // react核心
2
- import React, {
3
- useContext,
4
- useState,
5
- useEffect,
6
- useRef
7
- } from 'react';
8
- // antd核心
9
- import {
10
- Table,
11
- Checkbox,
12
- Switch,
13
- Popover,
14
- Popconfirm,
15
- ConfigProvider,
16
- Typography,
17
- Menu,
18
- Dropdown,
19
- Input,
20
- Form,
21
- Tag,
22
- Button,
23
- Badge,
24
- Space,
25
- Image,
26
- Avatar,
27
- message,
28
- Select,
29
- Divider,
30
- Tooltip,
31
- Progress
32
- } from 'antd';
33
- import {
34
- TinyLine,
35
- TinyArea,
36
- TinyColumn,
37
- RingProgress
38
- } from '@ant-design/plots';
39
- import AntdIcon from '../components/general/AntdIcon.react';
40
- import {
41
- SearchOutlined,
42
- QuestionCircleOutlined,
43
- DownOutlined
44
- } from '@ant-design/icons';
45
- // 辅助库
46
- import Highlighter from 'react-highlight-words';
47
- import { isNumber, isEqual, isString, isBoolean, isEmpty, omitBy, isUndefined } from 'lodash';
48
- import { pickBy } from 'ramda';
49
- import { str2Locale, locale2text } from '../components/locales.react';
50
- import { useLoading } from '../components/utils';
51
- import useStickyOffset from '../hooks/useStickyOffset';
52
- // 上下文
53
- import PropsContext from '../contexts/PropsContext';
54
- // 参数类型
55
- import { propTypes, defaultProps } from '../components/dataDisplay/AntdTable.react';
56
-
57
- const { Text } = Typography;
58
-
59
- const insertNewColumnNode = (column, group, currentLevel, currentNode) => {
60
- // 若当前递归到的层级小于group数组长度
61
- if (currentLevel < group.length) {
62
- // 尝试在currentNode中搜索dataIndex等于当前group层级的元素
63
- let matchColumnIdx = currentNode.findIndex(item => item.dataIndex === group[currentLevel])
64
- // 若未在currentNode中搜索到当前group层级对应元素
65
- if (matchColumnIdx === -1) {
66
- // currentNode中push当前group层级对应元素
67
- currentNode.push({
68
- dataIndex: group[currentLevel],
69
- title: group[currentLevel],
70
- children: []
71
- })
72
- // 继续向下一层级递归
73
- insertNewColumnNode(column, group, currentLevel + 1, currentNode[currentNode.length - 1].children)
74
- } else {
75
- // 若在currentNode中搜索到当前group层级对应元素
76
- // 继续向下一层级递归
77
- insertNewColumnNode(column, group, currentLevel + 1, currentNode[matchColumnIdx].children)
78
- }
79
- } else {
80
- // 否则则视作到达最深层
81
- currentNode.push({ ...column })
82
- }
83
- }
84
-
85
- const splitSummaryRowContents = (summaryRowContents, columnCount, blankColumns) => {
86
- let summaryGroups = (
87
- blankColumns > 0 ?
88
- [Array.from({ length: blankColumns }, () => ({ empty: true }))] :
89
- [[]]
90
- );
91
- let currentGroupSpans = 0;
92
- for (let item of summaryRowContents) {
93
- // 检查当前字段追加到末尾分组后,是否超出总列数
94
- if (currentGroupSpans + (item.colSpan || 1) > columnCount) {
95
- currentGroupSpans = (item.colSpan || 1);
96
- // 处理前置空白列填充
97
- if (blankColumns > 0) {
98
- summaryGroups.push([...Array.from({ length: blankColumns }, () => ({ empty: true })), item])
99
- } else {
100
- summaryGroups.push([item])
101
- }
102
- } else {
103
- currentGroupSpans += (item.colSpan || 1)
104
- summaryGroups[summaryGroups.length - 1].push(item)
105
- }
106
- }
107
- return summaryGroups;
108
- }
109
-
110
- const findItemByKey = (array, key) => {
111
- let foundItem = null;
112
-
113
- const search = (item) => {
114
- if (item.key === key) {
115
- foundItem = item;
116
- return true;
117
- }
118
- return Array.isArray(item.children) && item.children.some(search);
119
- };
120
-
121
- array.some(search);
122
- return foundItem;
123
- };
124
-
125
- const replaceItemByKey = (array, key, replacement) => {
126
- for (let i = 0; i < array.length; i++) {
127
- const item = array[i];
128
- if (item && item.key === key) {
129
- array.splice(i, 1, replacement);
130
- return true;
131
- }
132
-
133
- if (item && Array.isArray(item.children)) {
134
- if (replaceItemByKey(item.children, key, replacement)) {
135
- return true;
136
- }
137
- }
138
- }
139
-
140
- return false;
141
- }
142
-
143
- // 定义不触发重绘的参数数组
144
- const preventUpdateProps = [
145
- 'recentlyMouseEnterColumnDataIndex',
146
- 'recentlyMouseEnterRowKey',
147
- 'recentlyMouseEnterRow'
148
- ];
149
-
150
- /**
151
- * 表格组件AntdTable
152
- */
153
- const AntdTable = (props) => {
154
- let {
155
- id,
156
- className,
157
- style,
158
- key,
159
- locale,
160
- containerId,
161
- columns,
162
- showHeader,
163
- rowHoverable,
164
- tableLayout,
165
- miniChartHeight,
166
- miniChartAnimation,
167
- rowSelectionType,
168
- selectedRowKeys,
169
- rowSelectionWidth,
170
- rowSelectionCheckStrictly,
171
- rowSelectionIgnoreRowKeys,
172
- sticky,
173
- titlePopoverInfo,
174
- columnsFormatConstraint,
175
- enableHoverListen,
176
- data,
177
- sortOptions,
178
- showSorterTooltip,
179
- showSorterTooltipTarget,
180
- filterOptions,
181
- defaultFilteredValues,
182
- pagination,
183
- bordered,
184
- maxHeight,
185
- maxWidth,
186
- scrollToFirstRowOnChange,
187
- size,
188
- mode,
189
- nClicksButton,
190
- nDoubleClicksCell,
191
- summaryRowContents,
192
- summaryRowBlankColumns,
193
- summaryRowFixed,
194
- customFormatFuncs,
195
- conditionalStyleFuncs,
196
- expandedRowKeyToContent,
197
- expandedRowWidth,
198
- expandRowByClick,
199
- defaultExpandedRowKeys,
200
- expandedRowKeys,
201
- enableCellClickListenColumns,
202
- nClicksCell,
203
- nContextMenuClicksCell,
204
- emptyContent,
205
- cellUpdateOptimize,
206
- nClicksDropdownItem,
207
- hiddenRowKeys,
208
- virtual,
209
- title,
210
- footer,
211
- loading,
212
- rowClassName,
213
- setProps,
214
- ...others
215
- } = props;
216
-
217
- const context = useContext(PropsContext)
218
- locale = (context && context.locale) || locale
219
-
220
- useEffect(() => {
221
- // 处理pagination参数的默认值问题
222
- if (isBoolean(pagination) && !pagination) {
223
- } else {
224
- setProps({
225
- pagination: {
226
- ...pagination,
227
- current: pagination?.current ? pagination?.current : 1
228
- }
229
- })
230
- }
231
- }, []);
232
-
233
- const [searchText, setSearchText] = useState('');
234
- const [searchedColumn, setSearchedColumn] = useState('');
235
-
236
- const stickyObj = (sticky && typeof sticky === 'object') ? sticky : {};
237
- const belowSelector = stickyObj.belowSelector ?? undefined;
238
- const offsetHeader = Number(stickyObj.offsetHeader || 0);
239
- const autoMeasuredOffset = belowSelector ? useStickyOffset({ selector: belowSelector, extra: offsetHeader }) : offsetHeader;
240
- const { belowSelector: _rm1,
241
- offsetHeader: _rm2,
242
- ...stickyRest
243
- } = stickyObj;
244
- const computedSticky = sticky === true || belowSelector || (sticky && typeof sticky === 'object')
245
- ? { ...stickyRest, offsetHeader: autoMeasuredOffset }
246
- : undefined;
247
-
248
- const onPageChange = (pagination, filter, sorter, currentData) => {
249
-
250
- // 当本次事件由翻页操作引发时
251
- if (currentData.action === 'paginate') {
252
- setProps({
253
- pagination: {
254
- ...pagination,
255
- pageSize: pagination.pageSize,
256
- current: pagination.current,
257
- position: pagination.position ?
258
- (
259
- Array.isArray(pagination.position) ?
260
- pagination.position[0] :
261
- pagination.position
262
- ) :
263
- pagination.position
264
- },
265
- currentData: currentData.currentDataSource
266
- })
267
- } else if (currentData.action === 'sort') {
268
- // 当sorter为数组时,即为多字段组合排序方式时
269
- if (Array.isArray(sorter)) {
270
- setProps(
271
- {
272
- sorter: {
273
- columns: sorter.map(item => item.column.dataIndex),
274
- orders: sorter.map(item => item.order)
275
- }
276
- }
277
- )
278
- } else if (sorter.order) {
279
- // 单字段排序方式
280
- setProps(
281
- {
282
- sorter: {
283
- columns: [sorter.column.dataIndex],
284
- orders: [sorter.order]
285
- }
286
- }
287
- )
288
- } else {
289
- // 非排序状态
290
- setProps(
291
- {
292
- sorter: {
293
- columns: [],
294
- orders: []
295
- }
296
- }
297
- )
298
- }
299
- } else if (currentData.action === 'filter') {
300
- setProps({ filter: filter })
301
- }
302
- }
303
-
304
- // 自定义关键词搜索过滤模式
305
- let searchInput;
306
- const getColumnSearchProps = (dataIndex, title) => ({
307
- filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
308
- <div style={{ padding: 8 }}>
309
- <Input
310
- ref={node => {
311
- searchInput = node;
312
- }}
313
- placeholder={`${locale2text.AntdTable[locale].filterKeywordPlaceholder} ${title}`}
314
- value={selectedKeys[0]}
315
- onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
316
- onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)}
317
- style={{ marginBottom: 8, display: 'block' }}
318
- />
319
- <Space>
320
- <Button
321
- type="primary"
322
- onClick={() => handleSearch(selectedKeys, confirm, dataIndex)}
323
- icon={<SearchOutlined />}
324
- size="small"
325
- style={{ width: 90 }}
326
- >
327
- {locale2text.AntdTable[locale].filterKeywordSearchButtonText}
328
- </Button>
329
- <Button onClick={() => handleSearchReset(clearFilters)} size="small" style={{ width: 90 }}>
330
- {locale2text.AntdTable[locale].filterKeywordResetButtonText}
331
- </Button>
332
- </Space>
333
- </div>
334
- ),
335
- filterIcon: filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
336
- // 搜索筛选
337
- onFilter: (value, record) => {
338
- if (mode === 'client-side') {
339
- // 仅支持非数组型合法输入值,对象型输入支持对content、text、label、tag属性进行搜索筛选
340
- if (record[dataIndex] && !Array.isArray(record[dataIndex])) {
341
- // 判断当前记录是否有content属性
342
- if (record[dataIndex]?.content) {
343
- return record[dataIndex].content.toString().toLowerCase().includes(value?.toLowerCase())
344
- } else if (record[dataIndex]?.text) {
345
- return record[dataIndex].text.toString().toLowerCase().includes(value?.toLowerCase())
346
- } else if (record[dataIndex]?.label) {
347
- return record[dataIndex].label.toString().toLowerCase().includes(value?.toLowerCase())
348
- } else if (record[dataIndex]?.tag) {
349
- return record[dataIndex].tag.toString().toLowerCase().includes(value?.toLowerCase())
350
- } else if (record[dataIndex]?.toString) {
351
- return record[dataIndex].toString().toLowerCase().includes(value?.toLowerCase())
352
- }
353
- } else if (Array.isArray(record[dataIndex])) {
354
- // 若当前记录为数组,分别检查数组元素对象是否具有content、tag、title属性
355
- if (record[dataIndex].some(item => item?.content)) {
356
- // 检查当前记录数组中是否至少有一个对象的content属性命中关键词
357
- return record[dataIndex].some(
358
- item => item?.content.toString().toLowerCase().includes(value?.toLowerCase())
359
- )
360
- } else if (record[dataIndex].some(item => item?.tag)) {
361
- // 检查当前记录数组中是否至少有一个对象的tag属性命中关键词
362
- return record[dataIndex].some(
363
- item => item?.tag.toString().toLowerCase().includes(value?.toLowerCase())
364
- )
365
- } else if (record[dataIndex].some(item => item?.title)) {
366
- // 检查当前记录数组中是否至少有一个对象的title属性命中关键词
367
- return record[dataIndex].some(
368
- item => item?.title.toString().toLowerCase().includes(value?.toLowerCase())
369
- )
370
- }
371
- }
372
- return false;
373
- } else {
374
- return true
375
- }
376
- },
377
- onFilterDropdownVisibleChange: visible => {
378
- if (visible) {
379
- setTimeout(() => searchInput.select(), 100);
380
- }
381
- },
382
- render: text =>
383
- searchedColumn === dataIndex ? (
384
- <Highlighter
385
- highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
386
- searchWords={[searchText]}
387
- autoEscape
388
- textToHighlight={text ? text.toString() : ''}
389
- />
390
- ) : (
391
- text
392
- ),
393
- });
394
-
395
- const handleSearch = (selectedKeys, confirm, dataIndex) => {
396
- confirm();
397
- setSearchText(selectedKeys[0]);
398
- setSearchedColumn(dataIndex);
399
- };
400
-
401
- const handleSearchReset = clearFilters => {
402
- clearFilters();
403
- setSearchText('');
404
- };
405
-
406
- if (!data) {
407
- data = []
408
- }
409
-
410
- // 当未设置行key时,自动以自增1的字符型结果作为key
411
- for (let i in data) {
412
- if (!data[i].hasOwnProperty('key')) {
413
- data[i]['key'] = i.toString()
414
- }
415
- }
416
-
417
- // 为pagination补充默认参数值
418
- if (isBoolean(pagination) && !pagination) {
419
-
420
- } else {
421
- pagination = {
422
- ...pagination,
423
- showTotalPrefix: pagination?.showTotalPrefix || locale2text.AntdTable[locale].showTotalPrefix,
424
- showTotalSuffix: pagination?.showTotalSuffix || locale2text.AntdTable[locale].showTotalSuffix
425
- }
426
- }
427
-
428
- // 根据columns中的hidden属性控制是否忽略对应字段
429
- columns = columns.filter(item => !item.hidden)
430
-
431
- // columns配置默认align参数
432
- for (let i in columns) {
433
- columns[i] = {
434
- align: 'center',
435
- ...columns[i]
436
- }
437
- }
438
-
439
- // 自定义可编辑单元格
440
- const EditableContext = React.createContext(null);
441
-
442
- const EditableRow = ({ index, ...props }) => {
443
- const [form] = Form.useForm();
444
- return (
445
- <Form form={form} component={false}>
446
- <EditableContext.Provider value={form}>
447
- <tr {...props} />
448
- </EditableContext.Provider>
449
- </Form>
450
- );
451
- };
452
-
453
- const EditableCell = ({
454
- title,
455
- editable,
456
- children,
457
- dataIndex,
458
- record,
459
- ...restProps
460
- }) => {
461
- const [editing, setEditing] = useState(false);
462
- const inputRef = useRef(null);
463
- const form = useContext(EditableContext);
464
- useEffect(() => {
465
- if (editing) {
466
- inputRef.current?.focus();
467
- }
468
- }, [editing]);
469
-
470
- const toggleEdit = () => {
471
- setEditing(!editing);
472
- form.setFieldsValue({
473
- [dataIndex]: record[dataIndex],
474
- });
475
- };
476
-
477
- const [dataSource, setDataSource] = useState(data)
478
-
479
- // 负责监听单元格内容修改动作从而进行相关值的更新
480
- const handleSave = (row, setProps, dataSource, setDataSource) => {
481
-
482
- const newData = [...dataSource];
483
- const item = findItemByKey(newData, row.key)
484
-
485
- const rowColumns = Object.getOwnPropertyNames(row)
486
-
487
- // 循环取出属性名,再判断属性值是否一致
488
- for (let i = 0; i < rowColumns.length; i++) {
489
- // 找到发生值修改的字段
490
- if (row[rowColumns[i]] !== item[rowColumns[i]] &&
491
- columnsFormatConstraint &&
492
- columnsFormatConstraint[rowColumns[i]] &&
493
- columnsFormatConstraint[rowColumns[i]].rule) {
494
- // 检查是否满足预设的正则表达式规则
495
- if (!eval(`/${columnsFormatConstraint[rowColumns[i]].rule}/`).test(row[rowColumns[i]])) {
496
- message.error(
497
- columnsFormatConstraint[rowColumns[i]]?.content ?
498
- columnsFormatConstraint[rowColumns[i]]?.content.replace('[VALUE]', row[rowColumns[i]]) :
499
- locale2text.AntdTable[locale].columnEditableFormatConstraintMessage.replace('[VALUE]', row[rowColumns[i]])
500
- );
501
- // 提前终止函数
502
- return
503
- }
504
- }
505
- }
506
-
507
- // #93 找到发生编辑行为的字段dataIndex
508
- let _changedColumn = null;
509
- for (let i = 0; i < rowColumns.length; i++) {
510
- if (row[rowColumns[i]] !== item[rowColumns[i]]) {
511
- _changedColumn = rowColumns[i]
512
- }
513
- }
514
-
515
- replaceItemByKey(newData, row.key, { ...item, ...row })
516
-
517
- setDataSource(newData);
518
-
519
- setProps({
520
- currentData: newData,
521
- // 忽略组件型字段键值对
522
- recentlyChangedRow: omitBy(row, value => value?.$$typeof),
523
- recentlyChangedColumn: _changedColumn,
524
- data: newData
525
- })
526
- };
527
-
528
- const save = async () => {
529
- try {
530
- const values = await form.validateFields();
531
- toggleEdit();
532
- handleSave({ ...record, ...values }, setProps, dataSource, setDataSource);
533
- } catch (errInfo) {
534
- console.log(errInfo)
535
- }
536
- };
537
-
538
- let childNode = children;
539
-
540
- if (editable) {
541
- let recordDisabled = (columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.disabledKeys || []).includes(record.key)
542
- childNode = editing ? (
543
- <Form.Item
544
- style={{
545
- margin: 0,
546
- }}
547
- name={dataIndex}
548
- rules={[
549
- {
550
- required: false,
551
- message: `${title} 为空!`,
552
- },
553
- ]}
554
- >
555
- {
556
- columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.mode === 'text-area' ?
557
- <Input.TextArea
558
- autoSize={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.autoSize}
559
- maxLength={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.maxLength}
560
- placeholder={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.placeholder}
561
- ref={inputRef}
562
- onBlur={save}
563
- onFocus={() => {
564
- // 移动光标至内容末尾
565
- inputRef.current?.focus({
566
- cursor: 'end',
567
- })
568
- }}
569
- disabled={recordDisabled}
570
- /> :
571
- <Input
572
- maxLength={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.maxLength}
573
- placeholder={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.placeholder}
574
- ref={inputRef}
575
- onPressEnter={save}
576
- onBlur={save}
577
- disabled={recordDisabled}
578
- />
579
- }
580
- </Form.Item>
581
- ) : (
582
- <div
583
- className="editable-cell-value-wrap"
584
- style={{
585
- whiteSpace: 'break-spaces',
586
- ...(recordDisabled ? { cursor: 'no-drop' } : {})
587
- }}
588
- onClick={recordDisabled ? undefined : toggleEdit}
589
- >
590
- {(children[1] || children[1] === 0) ? children : ' '}
591
- </div>
592
- );
593
- }
594
-
595
- return <td {...restProps}>{childNode}</td>;
596
- };
597
-
598
- // 数值比较函数
599
- const compareNumeric = (x, y) => {
600
- if (x.value < y.value) {
601
- return -1;
602
- } else if (x.value > y.value) {
603
- return 1;
604
- } else {
605
- return 0;
606
- }
607
- }
608
-
609
- // 多模式值筛选选项自动生成策略
610
- const generateFilterOptions = (inputData, columnDataIndex) => {
611
- let filterOptions = []
612
- for (let item of inputData) {
613
- // 若当前记录不为数组
614
- if ((item[columnDataIndex] || item[columnDataIndex] === 0) && !Array.isArray(item[columnDataIndex])) {
615
- if (item[columnDataIndex]?.content) {
616
- filterOptions.push(item[columnDataIndex].content)
617
- } else if (item[columnDataIndex]?.text) {
618
- filterOptions.push(item[columnDataIndex].text)
619
- } else if (item[columnDataIndex]?.label) {
620
- filterOptions.push(item[columnDataIndex].label)
621
- } else if (item[columnDataIndex]?.tag) {
622
- filterOptions.push(item[columnDataIndex].tag)
623
- } else if (item[columnDataIndex]?.toString) {
624
- filterOptions.push(item[columnDataIndex])
625
- }
626
- } else if (Array.isArray(item[columnDataIndex])) {
627
- // 若当前记录为数组,提取数组元素对象中存在的content或tag属性
628
- if (item[columnDataIndex].some(_item => _item?.content)) {
629
- filterOptions = filterOptions.concat(
630
- item[columnDataIndex]
631
- .filter(_item => _item?.content)
632
- .map(_item => _item.content)
633
- )
634
- } else if (item[columnDataIndex].some(_item => _item?.tag)) {
635
- filterOptions = filterOptions.concat(
636
- item[columnDataIndex]
637
- .filter(_item => _item?.tag)
638
- .map(_item => _item.tag)
639
- )
640
- }
641
- }
642
- }
643
-
644
- // 将提取到的合法筛选值去重结构化并排序
645
- return Array.from(
646
- new Set(filterOptions)
647
- ).map(
648
- value => ({ text: (value || value === 0) ? value.toString() : '', value: value })
649
- ).sort(compareNumeric)
650
- }
651
-
652
- // 处理可筛选特性
653
- // 若为前端渲染模式,在filterOptions中每个字段filterCustomItems缺失的情况下
654
- // 则会自动根据前端一次性加载的数据推算出所有添加过滤器字段的唯一值集合作为待选菜单
655
- if (mode !== 'server-side') {
656
- // 为filterOptions.filterDataIndexes中定义的每个字段添加过滤功能
657
- for (let i = 0; i < columns.length; i++) {
658
- // 若当前字段在filterOptions的keys()中
659
- if (Object.keys(filterOptions).indexOf(columns[i].dataIndex) !== -1) {
660
- // 若当前字段对应filterOptions子元素有filterMode.filterMode为'keyword'
661
- if (filterOptions[columns[i].dataIndex].filterMode === 'keyword') {
662
- columns[i] = {
663
- ...columns[i],
664
- ...getColumnSearchProps(columns[i].dataIndex, columns[i].title)
665
- }
666
- } else if (filterOptions[columns[i].dataIndex].filterMode === 'tree') {
667
- // 若当前字段筛选模式为'tree'模式
668
- // 若当前字段对应filterOptions子元素下有filterCustomTreeItems属性
669
- // 则为其添加自定义树形选项
670
- if (filterOptions[columns[i].dataIndex].filterCustomTreeItems) {
671
- columns[i] = {
672
- ...columns[i],
673
- defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
674
- filterMode: 'tree',
675
- // 直接使用自定义树形筛选菜单结构
676
- filters: filterOptions[columns[i].dataIndex].filterCustomTreeItems,
677
- // 针对不同再渲染模式设计值筛选逻辑
678
- onFilter: (value, record) => {
679
- // 仅支持非数组型合法输入值,对象型输入支持对content、text、label、tag属性进行值筛选
680
- if ((record[columns[i].dataIndex] || record[columns[i].dataIndex] === 0) && !Array.isArray(record[columns[i].dataIndex])) {
681
- // 判断当前记录是否有content属性
682
- if (record[columns[i].dataIndex]?.content) {
683
- return record[columns[i].dataIndex].content === value;
684
- } else if (record[columns[i].dataIndex]?.text) {
685
- return record[columns[i].dataIndex].text === value;
686
- } else if (record[columns[i].dataIndex]?.label) {
687
- return record[columns[i].dataIndex].label === value;
688
- } else if (record[columns[i].dataIndex]?.tag) {
689
- return record[columns[i].dataIndex].tag === value;
690
- } else if (record[columns[i].dataIndex]?.toString) {
691
- // 确保字符型、数值型均可稳定进行筛选
692
- return record[columns[i].dataIndex].toString() === value.toString();
693
- }
694
- } else if (Array.isArray(record[columns[i].dataIndex])) {
695
- // 若当前记录为数组,分别检查数组元素对象是否具有content、tag、title属性
696
- if (record[columns[i].dataIndex].some(item => item?.content)) {
697
- // 检查当前记录数组中是否至少有一个对象的content属性等于筛选值value
698
- return record[columns[i].dataIndex].some(
699
- item => item?.content.toString().toLowerCase() === value?.toLowerCase()
700
- )
701
- } else if (record[columns[i].dataIndex].some(item => item?.tag)) {
702
- // 检查当前记录数组中是否至少有一个对象的tag属性命中关键词
703
- return record[columns[i].dataIndex].some(
704
- item => item?.tag.toString().toLowerCase() === value?.toLowerCase()
705
- )
706
- } else if (record[columns[i].dataIndex].some(item => item?.title)) {
707
- // 检查当前记录数组中是否至少有一个对象的title属性命中关键词
708
- return record[columns[i].dataIndex].some(
709
- item => item?.title.toString().toLowerCase() === value?.toLowerCase()
710
- )
711
- }
712
- }
713
- return false;
714
- },
715
- filterSearch: filterOptions[columns[i].dataIndex].filterSearch
716
- }
717
- }
718
- } else {
719
- // 否则则一律视为'checkbox'模式
720
- // 若当前字段对应filterOptions子元素下有filterCustomItems属性
721
- // 则为其添加自定义选项
722
- if (filterOptions[columns[i].dataIndex].filterCustomItems) {
723
- columns[i] = {
724
- ...columns[i],
725
- defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
726
- filters: filterOptions[columns[i].dataIndex].filterCustomItems
727
- .map(value => ({ text: (value || value === 0) ? value.toString() : '', value: value })),
728
- // 针对不同再渲染模式设计值筛选逻辑
729
- onFilter: (value, record) => {
730
- // 仅支持非数组型合法输入值,对象型输入支持对content、text、label、tag属性进行值筛选
731
- if ((record[columns[i].dataIndex] || record[columns[i].dataIndex] === 0) && !Array.isArray(record[columns[i].dataIndex])) {
732
- // 判断当前记录是否有content属性
733
- if (record[columns[i].dataIndex]?.content) {
734
- return record[columns[i].dataIndex].content === value;
735
- } else if (record[columns[i].dataIndex]?.text) {
736
- return record[columns[i].dataIndex].text === value;
737
- } else if (record[columns[i].dataIndex]?.label) {
738
- return record[columns[i].dataIndex].label === value;
739
- } else if (record[columns[i].dataIndex]?.tag) {
740
- return record[columns[i].dataIndex].tag === value;
741
- } else if (record[columns[i].dataIndex]?.toString) {
742
- // 确保字符型、数值型均可稳定进行筛选
743
- return record[columns[i].dataIndex].toString() === value.toString();
744
- }
745
- } else if (Array.isArray(record[columns[i].dataIndex])) {
746
- // 若当前记录为数组,分别检查数组元素对象是否具有content、tag、title属性
747
- if (record[columns[i].dataIndex].some(item => item?.content)) {
748
- // 检查当前记录数组中是否至少有一个对象的content属性等于筛选值value
749
- return record[columns[i].dataIndex].some(
750
- item => item?.content.toString().toLowerCase() === value?.toLowerCase()
751
- )
752
- } else if (record[columns[i].dataIndex].some(item => item?.tag)) {
753
- // 检查当前记录数组中是否至少有一个对象的tag属性命中关键词
754
- return record[columns[i].dataIndex].some(
755
- item => item?.tag.toString().toLowerCase() === value?.toLowerCase()
756
- )
757
- } else if (record[columns[i].dataIndex].some(item => item?.title)) {
758
- // 检查当前记录数组中是否至少有一个对象的title属性命中关键词
759
- return record[columns[i].dataIndex].some(
760
- item => item?.title.toString().toLowerCase() === value?.toLowerCase()
761
- )
762
- }
763
- }
764
- return false;
765
- },
766
- filterMultiple: filterOptions[columns[i].dataIndex].filterMultiple,
767
- filterSearch: filterOptions[columns[i].dataIndex].filterSearch
768
- }
769
- } else {
770
- // 否则自动基于数据中的唯一值生成选项列表
771
- columns[i] = {
772
- ...columns[i],
773
- defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
774
- filters: generateFilterOptions(data, columns[i].dataIndex),
775
- // 针对不同再渲染模式设计值筛选逻辑
776
- onFilter: (value, record) => {
777
- // 仅支持非数组型合法输入值,对象型输入支持对content、text、label、tag属性进行值筛选
778
- if ((record[columns[i].dataIndex] || record[columns[i].dataIndex] === 0) && !Array.isArray(record[columns[i].dataIndex])) {
779
- if (record[columns[i].dataIndex]?.content) {
780
- return record[columns[i].dataIndex].content === value;
781
- } else if (record[columns[i].dataIndex]?.text) {
782
- return record[columns[i].dataIndex].text === value;
783
- } else if (record[columns[i].dataIndex]?.label) {
784
- return record[columns[i].dataIndex].label === value;
785
- } else if (record[columns[i].dataIndex]?.tag) {
786
- return record[columns[i].dataIndex].tag === value;
787
- } else if (record[columns[i].dataIndex]?.toString) {
788
- // 确保字符型、数值型均可稳定进行筛选
789
- return record[columns[i].dataIndex].toString() === value.toString();
790
- }
791
- } else if (Array.isArray(record[columns[i].dataIndex])) {
792
- // 若当前记录为数组,分别检查数组元素对象是否具有content、tag、title属性
793
- if (record[columns[i].dataIndex].some(item => item?.content)) {
794
- // 检查当前记录数组中是否至少有一个对象的content属性等于筛选值value
795
- return record[columns[i].dataIndex].some(
796
- item => item?.content.toString().toLowerCase() === value?.toLowerCase()
797
- )
798
- } else if (record[columns[i].dataIndex].some(item => item?.tag)) {
799
- // 检查当前记录数组中是否至少有一个对象的tag属性命中关键词
800
- return record[columns[i].dataIndex].some(
801
- item => item?.tag.toString().toLowerCase() === value?.toLowerCase()
802
- )
803
- } else if (record[columns[i].dataIndex].some(item => item?.title)) {
804
- // 检查当前记录数组中是否至少有一个对象的title属性命中关键词
805
- return record[columns[i].dataIndex].some(
806
- item => item?.title.toString().toLowerCase() === value?.toLowerCase()
807
- )
808
- }
809
- }
810
- return false;
811
- },
812
- filterMultiple: filterOptions[columns[i].dataIndex]?.filterMultiple,
813
- filterSearch: filterOptions[columns[i].dataIndex]?.filterSearch
814
- }
815
- }
816
- }
817
- }
818
- }
819
- } else {
820
- // 否则在server-side模式下
821
- // 为filterOptions.filterDataIndexes中定义的每个字段
822
- // 添加简单值选择过滤功能
823
- // 为filterOptions.filterDataIndexes中定义的每个字段添加过滤功能
824
- for (let i = 0; i < columns.length; i++) {
825
- // 若当前字段在filterOptions的keys()中
826
- if (Object.keys(filterOptions).indexOf(columns[i].dataIndex) !== -1) {
827
- // 若当前字段对应filterOptions子元素有filterMode属性且filterMode属性为'keyword'
828
- if (filterOptions[columns[i].dataIndex].filterMode === 'keyword') {
829
- columns[i] = {
830
- ...columns[i],
831
- ...getColumnSearchProps(columns[i].dataIndex, columns[i].title)
832
- }
833
- } else if (filterOptions[columns[i].dataIndex].filterMode === 'tree') {
834
- // 若当前字段筛选模式为'tree'模式
835
- // 若当前字段对应filterOptions子元素下有filterCustomTreeItems属性
836
- // 则为其添加自定义树形选项
837
- if (filterOptions[columns[i].dataIndex].filterCustomTreeItems) {
838
- columns[i] = {
839
- ...columns[i],
840
- defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
841
- filterMode: 'tree',
842
- filters: filterOptions[columns[i].dataIndex].filterCustomTreeItems,
843
- onFilter: (value, record) => true // 契合后端刷新模式
844
- }
845
- } else {
846
- columns[i] = {
847
- ...columns[i],
848
- filters: [],
849
- onFilter: (value, record) => true
850
- }
851
- }
852
- } else {
853
- // 否则则一律视为'checkbox'模式
854
- // 若当前字段对应filterOptions子元素下有filterCustomItems属性
855
- // 则为其添加自定义选项
856
- if (filterOptions[columns[i].dataIndex].hasOwnProperty('filterCustomItems')) {
857
- columns[i] = {
858
- ...columns[i],
859
- defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
860
- filters: filterOptions[columns[i].dataIndex].filterCustomItems
861
- .map(value => ({ text: (value || value === 0) ? value.toString() : '', value: value })),
862
- onFilter: (value, record) => true, // 契合后端刷新模式
863
- filterMultiple: filterOptions[columns[i].dataIndex].filterMultiple,
864
- filterSearch: filterOptions[columns[i].dataIndex].filterSearch
865
- }
866
- } else {
867
- columns[i] = {
868
- ...columns[i],
869
- filters: [],
870
- onFilter: (value, record) => true
871
- }
872
- }
873
- }
874
- }
875
- }
876
- }
877
-
878
- // 处理sortOptions参数的默认值问题
879
- sortOptions = {
880
- sortDataIndexes: [],
881
- multiple: false,
882
- forceCompareModes: {},
883
- customOrders: {},
884
- ...sortOptions
885
- }
886
-
887
- // 配置字段排序参数
888
- for (let i = 0; i < sortOptions.sortDataIndexes.length; i++) {
889
- for (let j = 0; j < columns.length; j++) {
890
- // sortOptions与data中本轮迭代到的dataIndex一致
891
- if (sortOptions.sortDataIndexes[i] === columns[j].dataIndex) {
892
- // 根据是否组合排序模式来决定当前字段的排序参数设置
893
- columns[j]['sorter'] = {
894
- compare: (a, b) => {
895
- // 当渲染模式为server-side时,禁用前端排序操作
896
- if (mode === 'server-side') {
897
- return 0
898
- } else {
899
- // 初始化排序直接比较值
900
- let valueA = null
901
- let valueB = null
902
- // 兼容各种具有单个值的再渲染模式,提取待比较的成对数据
903
- if (a[columns[j].dataIndex]?.content || b[columns[j].dataIndex]?.content) {
904
- valueA = a[columns[j].dataIndex]?.content
905
- valueB = b[columns[j].dataIndex]?.content
906
- } else if (a[columns[j].dataIndex]?.text || b[columns[j].dataIndex]?.text) {
907
- valueA = a[columns[j].dataIndex]?.text
908
- valueB = b[columns[j].dataIndex]?.text
909
- } else if (a[columns[j].dataIndex]?.label || b[columns[j].dataIndex]?.label) {
910
- valueA = a[columns[j].dataIndex]?.label
911
- valueB = b[columns[j].dataIndex]?.label
912
- } else if (a[columns[j].dataIndex]?.tag || b[columns[j].dataIndex]?.tag) {
913
- valueA = a[columns[j].dataIndex]?.tag
914
- valueB = b[columns[j].dataIndex]?.tag
915
- } else if (a[columns[j].dataIndex]?.toString || b[columns[j].dataIndex]?.toString) {
916
- valueA = a[columns[j].dataIndex]
917
- valueB = b[columns[j].dataIndex]
918
- }
919
- // 根据valueA、valueB的数据类型返回不同逻辑的判断结果
920
- // 若valueA、valueB至少有1个为数值型,则视作数值型比较
921
- // 若当前字段使用了强制比较模式
922
- if (sortOptions.forceCompareModes[columns[j].dataIndex]) {
923
- // 数值比较模式
924
- if (sortOptions.forceCompareModes[columns[j].dataIndex] === 'number') {
925
- // 强制转换比较值为数值型
926
- let numberA = Number(valueA)
927
- let numberB = Number(valueB)
928
- if (numberA < numberB) {
929
- return -1;
930
- } else if (numberA > numberB) {
931
- return 1;
932
- }
933
- return 0;
934
- } else if (sortOptions.forceCompareModes[columns[j].dataIndex] === 'custom' &&
935
- sortOptions.customOrders[columns[j].dataIndex]) {
936
- // 自定义顺序模式
937
- // 查询比较值在对应的自定义顺序中的索引
938
- let orderA = sortOptions.customOrders[columns[j].dataIndex].indexOf(valueA)
939
- let orderB = sortOptions.customOrders[columns[j].dataIndex].indexOf(valueB)
940
- if (orderA < orderB) {
941
- return 1;
942
- } else if (orderA > orderB) {
943
- return -1;
944
- }
945
- return 0;
946
- }
947
- } else {
948
- // 默认自动判断
949
- if (isNumber(valueA) || isNumber(valueB)) {
950
- return valueA - valueB
951
- } else {
952
- // 否则均视作字符型比较
953
- let stringA = valueA?.toString().toUpperCase()
954
- let stringB = valueB?.toString().toUpperCase()
955
- if (stringA < stringB) {
956
- return -1;
957
- } else if (stringA > stringB) {
958
- return 1;
959
- }
960
- return 0;
961
- }
962
- }
963
- }
964
- },
965
- multiple: sortOptions['multiple'] ?
966
- (sortOptions['multiple'] === 'auto' ? 1 : sortOptions.sortDataIndexes.length - i) :
967
- undefined
968
- }
969
- }
970
- }
971
- }
972
-
973
- // 配置各种再渲染模式
974
- for (let i = 0; i < columns.length; i++) {
975
- // 当前字段具有renderOptions参数时且renderOptions参数是字典时
976
- if (columns[i]['renderOptions'] && columns[i]['renderOptions']['renderType']) {
977
- // ellipsis模式
978
- if (columns[i]['renderOptions']['renderType'] === 'ellipsis') {
979
- columns[i]['ellipsis'] = {
980
- showTitle: false
981
- }
982
- columns[i]['render'] = content => (
983
- <Text ellipsis={{ tooltip: content }}>
984
- {content}
985
- </Text>
986
- )
987
- }
988
- // row-merge模式
989
- else if (columns[i]['renderOptions']['renderType'] === 'row-merge') {
990
- columns[i]['render'] = (content, record, index) => {
991
- return {
992
- children: content.content,
993
- props: {
994
- rowSpan: content.rowSpan
995
- }
996
- };
997
- }
998
- }
999
- // link模式
1000
- else if (columns[i]['renderOptions']['renderType'] === 'link') {
1001
- // 检查renderLinkText参数是否定义
1002
- if (columns[i]['renderOptions']['renderLinkText']) {
1003
- columns[i]['render'] = content => {
1004
- if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1005
- return null;
1006
- }
1007
- return (
1008
- <a href={content.disabled ? undefined : content.href}
1009
- target={content.target ? content.target : '_blank'}
1010
- disabled={content.disabled}>
1011
- {content.content ? content.content : columns[i]['renderOptions']['renderLinkText']}
1012
- </a>
1013
- )
1014
- }
1015
- } else {
1016
- columns[i]['render'] = content => {
1017
- if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1018
- return null;
1019
- }
1020
- return (
1021
- <a href={content.disabled ? undefined : content.href}
1022
- target={content.target ? content.target : '_blank'}
1023
- disabled={content.disabled}>
1024
- {content.content ? content.content : ' '}
1025
- </a>
1026
- )
1027
- }
1028
- }
1029
- }
1030
- // copyable模式
1031
- else if (columns[i]['renderOptions']['renderType'] === 'copyable') {
1032
- columns[i]['render'] = content => {
1033
- if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1034
- return null;
1035
- }
1036
- return (
1037
- <Text copyable={true}>
1038
- {content}
1039
- </Text>
1040
- )
1041
- }
1042
- }
1043
- // dropdown模式
1044
- else if (columns[i]['renderOptions']['renderType'] === 'dropdown') {
1045
- columns[i]['render'] = (menuItems, record) => {
1046
- // 针对空值进行错误处理
1047
- menuItems = menuItems || []
1048
- return (
1049
- <Dropdown
1050
- overlay={
1051
- <Menu onClick={(item, key, keyPath, domEvent) => {
1052
- setTimeout(function () {
1053
- setProps({
1054
- nClicksDropdownItem: nClicksDropdownItem + 1,
1055
- recentlyClickedDropdownItemTitle: item.key,
1056
- recentlyDropdownItemClickedDataIndex: columns[i].dataIndex,
1057
- // 忽略组件型字段键值对
1058
- recentlyDropdownItemClickedRow: omitBy(record, value => value?.$$typeof)
1059
- })
1060
- }, 200);
1061
- }}>
1062
- {
1063
- menuItems.map(
1064
- menuItem => (
1065
- menuItem ?
1066
- // 判断isDivider参数是否不为false
1067
- (
1068
- menuItem.isDivider ?
1069
- <Menu.Divider /> :
1070
- <Menu.Item
1071
- icon={
1072
- menuItem.icon && (
1073
- menuItem.iconRenderer === 'fontawesome' ?
1074
- (
1075
- React.createElement(
1076
- 'i',
1077
- {
1078
- className: menuItem.icon
1079
- }
1080
- )
1081
- ) :
1082
- (
1083
- <AntdIcon icon={menuItem.icon} />
1084
- )
1085
- )
1086
- }
1087
- disabled={menuItem.disabled}
1088
- key={menuItem.title}>
1089
- <a >{menuItem.title}</a>
1090
- </Menu.Item>
1091
- ) :
1092
- null
1093
- )
1094
- )
1095
- }
1096
- </Menu>
1097
- }
1098
- arrow={columns[i]['renderOptions']?.dropdownProps?.arrow}
1099
- disabled={columns[i]['renderOptions']?.dropdownProps?.disabled || menuItems.length === 0}
1100
- overlayClassName={columns[i]['renderOptions']?.dropdownProps?.overlayClassName}
1101
- overlayStyle={columns[i]['renderOptions']?.dropdownProps?.overlayStyle}
1102
- placement={columns[i]['renderOptions']?.dropdownProps?.placement}
1103
- trigger={
1104
- columns[i]['renderOptions']?.dropdownProps?.trigger ?
1105
- [columns[i]['renderOptions'].dropdownProps.trigger] : ['hover']
1106
- }
1107
- getPopupContainer={containerId ? () => (document.getElementById(containerId) ? document.getElementById(containerId) : document.body) : undefined}>
1108
- <a className="ant-dropdown-link"
1109
- onClick={e => e.preventDefault()}>
1110
- {columns[i]['renderOptions']?.dropdownProps?.title} <DownOutlined />
1111
- </a>
1112
- </Dropdown>
1113
- );
1114
- }
1115
- }
1116
- // dropdown-links模式
1117
- else if (columns[i]['renderOptions']['renderType'] === 'dropdown-links') {
1118
- columns[i]['render'] = menuItems => {
1119
- // 针对空值进行错误处理
1120
- menuItems = menuItems || []
1121
- return (
1122
- <Dropdown
1123
- overlay={
1124
- <Menu>
1125
- {
1126
- menuItems.map(
1127
- menuItem => (
1128
- menuItem ?
1129
- // 判断isDivider参数是否不为false
1130
- (
1131
- menuItem.isDivider ?
1132
- <Menu.Divider /> :
1133
- <Menu.Item
1134
- icon={
1135
- menuItem.icon && (
1136
- menuItem.iconRenderer === 'fontawesome' ?
1137
- (
1138
- React.createElement(
1139
- 'i',
1140
- {
1141
- className: menuItem.icon
1142
- }
1143
- )
1144
- ) :
1145
- (
1146
- <AntdIcon icon={menuItem.icon} />
1147
- )
1148
- )
1149
- }
1150
- disabled={menuItem.disabled}
1151
- key={menuItem.title}>
1152
- <a href={menuItem.href}
1153
- target={'_blank'}>
1154
- {menuItem.title}
1155
- </a>
1156
- </Menu.Item>
1157
- ) :
1158
- null
1159
- )
1160
- )
1161
- }
1162
- </Menu>
1163
- }
1164
- arrow={columns[i]['renderOptions']?.dropdownProps?.arrow}
1165
- disabled={columns[i]['renderOptions']?.dropdownProps?.disabled || menuItems.length === 0}
1166
- overlayClassName={columns[i]['renderOptions']?.dropdownProps?.overlayClassName}
1167
- overlayStyle={columns[i]['renderOptions']?.dropdownProps?.overlayStyle}
1168
- placement={columns[i]['renderOptions']?.dropdownProps?.placement}
1169
- trigger={
1170
- columns[i]['renderOptions']?.dropdownProps?.trigger ?
1171
- [columns[i]['renderOptions'].dropdownProps.trigger] : ['hover']
1172
- }
1173
- getPopupContainer={containerId ? () => (document.getElementById(containerId) ? document.getElementById(containerId) : document.body) : undefined}>
1174
- <a className="ant-dropdown-link"
1175
- onClick={e => e.preventDefault()}>
1176
- {columns[i]['renderOptions']?.dropdownProps?.title} <DownOutlined />
1177
- </a>
1178
- </Dropdown>
1179
- );
1180
- }
1181
- }
1182
- // ellipsis-copyable模式
1183
- else if (columns[i]['renderOptions']['renderType'] === 'ellipsis-copyable') {
1184
- columns[i]['ellipsis'] = {
1185
- showTitle: false
1186
- }
1187
- columns[i]['render'] = content => {
1188
- if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1189
- return null;
1190
- }
1191
- return (
1192
- <Text copyable={true} ellipsis={{ tooltip: content }}>
1193
- {content}
1194
- </Text>
1195
- )
1196
- }
1197
- }
1198
- // corner-mark模式
1199
- else if (columns[i]['renderOptions']['renderType'] === 'corner-mark') {
1200
- columns[i]['render'] = content => {
1201
- if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1202
- return null;
1203
- }
1204
- return (
1205
- <div className={content.placement ? 'ant-corner-mark-' + content.placement : 'ant-corner-mark-top-right'}
1206
- style={{
1207
- '--ant-corner-mark-color': content.hide ? 'transparent' : (content.color ? content.color : '#1890ff'),
1208
- '--ant-corner-mark-transform': `translate(${content.offsetX ? content.offsetX : 0}px, ${content.offsetY ? content.offsetY : 0}px)`
1209
- }}>
1210
- {content.content}
1211
- </div>
1212
- )
1213
- }
1214
- }
1215
- // status-badge模式
1216
- else if (columns[i]['renderOptions']['renderType'] === 'status-badge') {
1217
- columns[i]['render'] = content => {
1218
- if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1219
- return null;
1220
- }
1221
- return (
1222
- <Badge status={content.status} text={content.text} />
1223
- )
1224
- }
1225
- }
1226
- // image模式
1227
- else if (columns[i]['renderOptions']['renderType'] === 'image') {
1228
- columns[i]['render'] = content => {
1229
- if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1230
- return null;
1231
- }
1232
- return (
1233
- <Image src={content.src} height={content.height} preview={content.preview} />
1234
- )
1235
- }
1236
- }
1237
- // image-avatar模式
1238
- else if (columns[i]['renderOptions']['renderType'] === 'image-avatar') {
1239
- columns[i]['render'] = content => {
1240
- if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1241
- return null;
1242
- }
1243
- return (
1244
- <Avatar
1245
- src={content?.src}
1246
- size={content?.size}
1247
- shape={content?.shape}
1248
- />
1249
- )
1250
- }
1251
- }
1252
- // checkbox模式
1253
- else if (columns[i]['renderOptions']['renderType'] === 'checkbox') {
1254
- columns[i]['render'] = (content, record) => {
1255
- const currentDataIndex = columns[i]['dataIndex']
1256
- return (
1257
- <Checkbox checked={content.checked}
1258
- disabled={content.disabled}
1259
- onChange={(e) => {
1260
- // 修改对应行对应字段item.checked值
1261
- try {
1262
- data.forEach(function (item, i) {
1263
- // 命中后,修改值并利用错误抛出来跳出循环
1264
- if (item.key === record.key) {
1265
- data[i][currentDataIndex] = {
1266
- ...record[currentDataIndex],
1267
- checked: e.target.checked
1268
- }
1269
- throw new Error("目标已修改");
1270
- }
1271
- });
1272
- } catch (e) {
1273
- };
1274
-
1275
- setTimeout(function () {
1276
- setProps({
1277
- data: data,
1278
- // 忽略组件型字段键值对
1279
- recentlyCheckedRow: omitBy(record, value => value?.$$typeof),
1280
- recentlyCheckedLabel: content.label,
1281
- recentlyCheckedDataIndex: columns[i]['dataIndex'],
1282
- recentlyCheckedStatus: e.target.checked
1283
- })
1284
- }, 200);
1285
- }}>
1286
- {content.label}
1287
- </Checkbox>
1288
- );
1289
- }
1290
- }
1291
- // switch模式
1292
- else if (columns[i]['renderOptions']['renderType'] === 'switch') {
1293
- columns[i]['render'] = (content, record) => {
1294
- const currentDataIndex = columns[i]['dataIndex']
1295
- return (
1296
- <Switch checked={content.checked}
1297
- disabled={content.disabled}
1298
- checkedChildren={content.checkedChildren}
1299
- unCheckedChildren={content.unCheckedChildren}
1300
- onChange={(checked) => {
1301
- // 修改对应行对应字段item.checked值
1302
- try {
1303
- data.forEach(function (item, i) {
1304
- // 命中后,修改值并利用错误抛出来跳出循环
1305
- if (item.key === record.key) {
1306
- data[i][currentDataIndex] = {
1307
- ...record[currentDataIndex],
1308
- checked: checked
1309
- }
1310
- throw new Error("目标已修改");
1311
- }
1312
- });
1313
- } catch (e) {
1314
- };
1315
-
1316
- setTimeout(function () {
1317
- setProps({
1318
- data: data,
1319
- // 忽略组件型字段键值对
1320
- recentlySwitchRow: omitBy(record, value => value?.$$typeof),
1321
- recentlySwitchDataIndex: columns[i]['dataIndex'],
1322
- recentlySwitchStatus: checked
1323
- })
1324
- }, 200);
1325
- }} />
1326
- );
1327
- }
1328
- }
1329
- // select模式
1330
- else if (columns[i]['renderOptions']['renderType'] === 'select') {
1331
- columns[i]['render'] = (content, record) => {
1332
- const currentDataIndex = columns[i]['dataIndex']
1333
- // 针对空值进行错误处理
1334
- content = content || {}
1335
- return (
1336
- <Select
1337
- className={content.className}
1338
- style={{
1339
- width: '100%',
1340
- textAlign: 'left',
1341
- ...content.style
1342
- }}
1343
- showSearch={isUndefined(content.showSearch) ? true : content.showSearch}
1344
- options={content.options}
1345
- listHeight={content.listHeight}
1346
- mode={content.mode}
1347
- disabled={content.disabled}
1348
- size={content.size}
1349
- bordered={content.bordered}
1350
- placeholder={content.placeholder}
1351
- placement={content.placement}
1352
- defaultValue={content.value}
1353
- maxTagCount={content.maxTagCount}
1354
- optionFilterProp={content.optionFilterProp}
1355
- allowClear={content.allowClear}
1356
- onChange={(value) => {
1357
- // 修改对应行对应字段item.value值
1358
- try {
1359
- data.forEach(function (item, i) {
1360
- // 命中后,修改值并利用错误抛出来跳出循环
1361
- if (item.key === record.key) {
1362
- data[i][currentDataIndex] = {
1363
- ...record[currentDataIndex],
1364
- value: value
1365
- }
1366
- // 提前打断循环过程
1367
- throw new Error("目标已修改");
1368
- }
1369
- });
1370
- } catch (e) {
1371
- };
1372
-
1373
- setTimeout(
1374
- () => {
1375
- setProps({
1376
- data: data,
1377
- // 忽略组件型字段键值对
1378
- recentlySelectRow: omitBy(record, value => value?.$$typeof),
1379
- recentlySelectDataIndex: columns[i]['dataIndex'],
1380
- recentlySelectValue: value
1381
- })
1382
- },
1383
- 200
1384
- );
1385
- }} />
1386
- );
1387
- }
1388
- }
1389
- // button模式
1390
- else if (columns[i]['renderOptions']['renderType'] === 'button') {
1391
- // 根据参数配置生成按钮
1392
- columns[i]['render'] = (content, record) => {
1393
- // 合并单按钮/多按钮渲染逻辑
1394
- content = Array.isArray(content) ? content : [content];
1395
- return (
1396
- <Space split={columns[i]['renderOptions'].renderButtonSplit && <Divider type={"vertical"} />} wrap={true}>
1397
- {
1398
- content.map(
1399
- (content_, idx) => {
1400
- // 若当前按钮需要附带气泡确认框
1401
- if (columns[i]['renderOptions']['renderButtonPopConfirmProps'] || content_['popConfirmProps']) {
1402
- let popConfirmProps = columns[i]['renderOptions']['renderButtonPopConfirmProps'] || content_['popConfirmProps'];
1403
- let buttonElement = (
1404
- <Button
1405
- size={'small'}
1406
- type={content_.type}
1407
- color={content_.color}
1408
- variant={content_.variant}
1409
- danger={content_.danger}
1410
- disabled={content_.disabled}
1411
- style={content_.style}
1412
- icon={
1413
- content_.icon && (
1414
- content_.iconRenderer === 'fontawesome' ?
1415
- (
1416
- React.createElement(
1417
- 'i',
1418
- {
1419
- className: content_.icon
1420
- }
1421
- )
1422
- ) :
1423
- (
1424
- <AntdIcon icon={content_.icon} />
1425
- )
1426
- )
1427
- }
1428
- onClick={(e) => {
1429
- // 阻止事件冒泡
1430
- e.stopPropagation();
1431
- }}>
1432
- {content_.content}
1433
- </Button>
1434
- );
1435
- return (
1436
- <Popconfirm
1437
- key={idx}
1438
- title={popConfirmProps.title}
1439
- okText={popConfirmProps.okText}
1440
- cancelText={popConfirmProps.cancelText}
1441
- disabled={content_.disabled}
1442
- getPopupContainer={containerId ? () => (document.getElementById(containerId) ? document.getElementById(containerId) : document.body) : undefined}
1443
- onConfirm={(e) => {
1444
- // 阻止事件冒泡
1445
- e.stopPropagation();
1446
- setProps({
1447
- // 忽略组件型字段键值对
1448
- recentlyButtonClickedRow: omitBy(record, value => value?.$$typeof),
1449
- nClicksButton: nClicksButton + 1,
1450
- clickedContent: content_.content,
1451
- clickedCustom: content_.custom,
1452
- recentlyButtonClickedDataIndex: columns[i].dataIndex
1453
- })
1454
- }}
1455
- onCancel={(e) => {
1456
- // 阻止事件冒泡
1457
- e.stopPropagation();
1458
- }}>
1459
- {
1460
- content_.tooltip ?
1461
- (
1462
- <Tooltip title={content_.tooltip?.title} placement={content_.tooltip?.placement}>
1463
- {buttonElement}
1464
- </Tooltip>
1465
- ) :
1466
- (
1467
- buttonElement
1468
- )
1469
- }
1470
- </Popconfirm>
1471
- );
1472
- }
1473
- let buttonElement = (
1474
- <Button
1475
- key={idx}
1476
- onClick={(e) => {
1477
- // 阻止事件冒泡
1478
- e.stopPropagation();
1479
- setProps({
1480
- // 忽略组件型字段键值对
1481
- recentlyButtonClickedRow: omitBy(record, value => value?.$$typeof),
1482
- nClicksButton: nClicksButton + 1,
1483
- clickedContent: content_.content,
1484
- clickedCustom: content_.custom,
1485
- recentlyButtonClickedDataIndex: columns[i].dataIndex
1486
- })
1487
- }}
1488
- size={'small'}
1489
- type={content_.type}
1490
- color={content_.color}
1491
- variant={content_.variant}
1492
- danger={content_.danger}
1493
- disabled={content_.disabled}
1494
- href={content_.href}
1495
- target={content_.target}
1496
- style={content_.style}
1497
- icon={
1498
- content_.icon && (
1499
- content_.iconRenderer === 'fontawesome' ?
1500
- (
1501
- React.createElement(
1502
- 'i',
1503
- {
1504
- className: content_.icon
1505
- }
1506
- )
1507
- ) :
1508
- (
1509
- <AntdIcon icon={content_.icon} />
1510
- )
1511
- )
1512
- }>
1513
- {content_.content}
1514
- </Button>
1515
- );
1516
- // 否则仅渲染按钮
1517
- return (
1518
- content_.tooltip ?
1519
- (
1520
- <Tooltip title={content_.tooltip?.title} placement={content_.tooltip?.placement}>
1521
- {buttonElement}
1522
- </Tooltip>
1523
- ) :
1524
- (
1525
- buttonElement
1526
- )
1527
- );
1528
- }
1529
- )
1530
- }
1531
- </Space>
1532
- );
1533
- }
1534
- }
1535
- // tags模式
1536
- else if (columns[i]['renderOptions']['renderType'] === 'tags') {
1537
-
1538
- columns[i]['render'] = tags => {
1539
- if ((!tags || isEmpty(tags)) && tags !== 0 && tags !== '') {
1540
- return null;
1541
- }
1542
- return (
1543
- // 兼容单标签/多标签输入
1544
- <>
1545
- {(Array.isArray(tags) ? tags : [tags]).map(tag => {
1546
- const tagElement = (
1547
- <Tag color={tag.color}>
1548
- {tag.tag}
1549
- </Tag>
1550
- );
1551
- // 若当前标签需要附带文字提示
1552
- return tag.tooltip ? (
1553
- <Tooltip title={tag.tooltip?.title} placement={tag.tooltip?.placement}>
1554
- {tagElement}
1555
- </Tooltip>
1556
- ) : (
1557
- tagElement
1558
- );
1559
- })}
1560
- </>
1561
- )
1562
- }
1563
- }
1564
- // custom-format模式
1565
- else if (columns[i]['renderOptions']['renderType'] === 'custom-format') {
1566
- // 若customFormatFuncs对应当前字段的属性值存在
1567
- if (customFormatFuncs[columns[i]['dataIndex']]) {
1568
- columns[i]['render'] = eval(customFormatFuncs[columns[i]['dataIndex']])
1569
- }
1570
- }
1571
- // mini-line模式
1572
- else if (columns[i]['renderOptions']['renderType'] === 'mini-line') {
1573
- columns[i]['render'] = data => {
1574
- let config = {
1575
- autoFit: true,
1576
- padding: 0,
1577
- data: data,
1578
- animation: miniChartAnimation,
1579
- smooth: true
1580
- };
1581
- // 检查是否设置了tooltipCustomContent参数
1582
- if (columns[i]['renderOptions']?.tooltipCustomContent) {
1583
- config = {
1584
- ...config,
1585
- tooltip: {
1586
- customContent: eval(columns[i]['renderOptions'].tooltipCustomContent)
1587
- }
1588
- }
1589
- }
1590
- // 检查是否设置了miniChartColor参数
1591
- if (columns[i]['renderOptions']?.miniChartColor) {
1592
- config = {
1593
- ...config,
1594
- color: columns[i]['renderOptions'].miniChartColor
1595
- }
1596
- }
1597
- return <div style={{ height: miniChartHeight }}><TinyLine {...config} /></div>;
1598
- }
1599
- }
1600
- // mini-bar模式
1601
- else if (columns[i]['renderOptions']['renderType'] === 'mini-bar') {
1602
- columns[i]['render'] = data => {
1603
- let config = {
1604
- padding: 0,
1605
- autoFit: true,
1606
- data: data,
1607
- animation: miniChartAnimation,
1608
- };
1609
- // 检查是否设置了tooltipCustomContent参数
1610
- if (columns[i]['renderOptions']?.tooltipCustomContent) {
1611
- config = {
1612
- ...config,
1613
- tooltip: {
1614
- customContent: eval(columns[i]['renderOptions'].tooltipCustomContent)
1615
- }
1616
- }
1617
- }
1618
- // 检查是否设置了miniChartColor参数
1619
- if (columns[i]['renderOptions']?.miniChartColor) {
1620
- config = {
1621
- ...config,
1622
- color: columns[i]['renderOptions'].miniChartColor
1623
- }
1624
- }
1625
- return <div style={{ height: miniChartHeight }}><TinyColumn {...config} /></div>;
1626
- }
1627
- }
1628
- // mini-progress模式
1629
- else if (columns[i]['renderOptions']['renderType'] === 'mini-progress') {
1630
- columns[i]['render'] = data => {
1631
- return (
1632
- <div style={{ height: miniChartHeight, alignItems: 'center', display: 'flex' }}>
1633
- <Progress percent={(data * 100)}
1634
- format={
1635
- (percent) => {
1636
- if (isUndefined(columns[i]['renderOptions']['progressPercentPrecision'])) {
1637
- return percent + '%';
1638
- }
1639
- return percent.toFixed(columns[i]['renderOptions']['progressPercentPrecision']) + '%';
1640
- }
1641
- }
1642
- strokeColor={
1643
- data * 100 === 100 ?
1644
- columns[i]['renderOptions']['progressOneHundredPercentColor'] :
1645
- columns[i]['renderOptions']['progressColor']
1646
- }
1647
- trailColor={'#E8EDF3'}
1648
- status={data * 100 >= 100 ? undefined : 'normal'}
1649
- showInfo={Boolean(columns[i]['renderOptions']['progressShowPercent'])}
1650
- strokeLinecap={columns[i]['renderOptions']['progressStrokeLinecap'] || 'square'}
1651
- size={
1652
- columns[i]['renderOptions']['progressSize'] ?
1653
- ['100%', columns[i]['renderOptions']['progressSize']] :
1654
- ['100%', 15]
1655
- }
1656
- percentPosition={columns[i]['renderOptions']['progressPercentPosition']}
1657
- />
1658
- </div>
1659
- );
1660
- }
1661
- }
1662
- // mini-ring-progress模式
1663
- else if (columns[i]['renderOptions']['renderType'] === 'mini-ring-progress') {
1664
- columns[i]['render'] = data => {
1665
- let config = {
1666
- autoFit: true,
1667
- padding: 0,
1668
- percent: data,
1669
- animation: miniChartAnimation,
1670
- color: [
1671
- data === 1 ?
1672
- (columns[i]['renderOptions']['progressOneHundredPercentColor'] || '#52c41a') :
1673
- '#5B8FF9',
1674
- '#E8EDF3'
1675
- ],
1676
- statistic: {
1677
- content: {
1678
- style: {
1679
- fontSize: columns[i]['renderOptions']['ringProgressFontSize']
1680
- }
1681
- }
1682
- }
1683
- };
1684
- return <div style={{ height: miniChartHeight }}><RingProgress {...config} /></div>;
1685
- }
1686
- }
1687
- // mini-area模式
1688
- else if (columns[i]['renderOptions']['renderType'] === 'mini-area') {
1689
- columns[i]['render'] = data => {
1690
- let config = {
1691
- autoFit: true,
1692
- padding: 0,
1693
- data: data,
1694
- animation: miniChartAnimation,
1695
- smooth: true,
1696
- };
1697
- // 检查是否设置了tooltipCustomContent参数
1698
- if (columns[i]['renderOptions']?.tooltipCustomContent) {
1699
- config = {
1700
- ...config,
1701
- tooltip: {
1702
- customContent: eval(columns[i]['renderOptions'].tooltipCustomContent)
1703
- }
1704
- }
1705
- }
1706
- // 检查是否设置了miniChartColor参数
1707
- if (columns[i]['renderOptions']?.miniChartColor) {
1708
- config = {
1709
- ...config,
1710
- areaStyle: {
1711
- fill: columns[i]['renderOptions'].miniChartColor,
1712
- fillOpacity: 0.4
1713
- },
1714
- line: {
1715
- color: columns[i]['renderOptions'].miniChartColor
1716
- }
1717
- }
1718
- }
1719
- return <div style={{ height: miniChartHeight }}><TinyArea {...config} /></div>;
1720
- }
1721
- }
1722
- }
1723
- }
1724
-
1725
- // 处理columns.title的增广功能设置
1726
- if (titlePopoverInfo && Array.isArray(titlePopoverInfo)) {
1727
- for (let i = 0; i < columns.length; i++) {
1728
- const currentDataIndex = columns[i].dataIndex;
1729
- // Find popover config for current column from array
1730
- const popoverConfig = titlePopoverInfo.find(item => item.dataIndex === currentDataIndex);
1731
-
1732
- // Validate that popoverConfig exists and is a valid object
1733
- if (popoverConfig && typeof popoverConfig === 'object') {
1734
-
1735
- // Preserve original title if not already preserved
1736
- if (!columns[i].title_) {
1737
- columns[i]['title_'] = columns[i]['title']
1738
- }
1739
-
1740
- const originalTitle = columns[i].title_;
1741
- const popoverTitle = popoverConfig.title;
1742
- const popoverContent = popoverConfig.content;
1743
- const popoverPlacement = popoverConfig.placement || 'bottom';
1744
- const popoverOverlayStyle = popoverConfig.overlayStyle;
1745
-
1746
- // Create the title render function
1747
- columns[i]['title'] = () => {
1748
- // Check if content is a React element or needs wrapping
1749
- const isContentNode = React.isValidElement(popoverContent);
1750
- const contentToRender = isContentNode ?
1751
- popoverContent :
1752
- <div style={{
1753
- maxWidth: '250px',
1754
- wordWrap: 'break-word',
1755
- whiteSpace: 'normal',
1756
- wordBreak: 'break-all'
1757
- }}>{popoverContent}</div>;
1758
-
1759
- return <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
1760
- {originalTitle}
1761
- <Popover
1762
- title={popoverTitle}
1763
- content={contentToRender}
1764
- overlayStyle={popoverOverlayStyle}
1765
- placement={popoverPlacement}
1766
- getPopupContainer={containerId ? () => (document.getElementById(containerId) ? document.getElementById(containerId) : document.body) : undefined}>
1767
- <QuestionCircleOutlined
1768
- style={{
1769
- color: '#8c8c8c',
1770
- paddingLeft: '4px',
1771
- cursor: 'pointer'
1772
- }} />
1773
- </Popover>
1774
- </div>
1775
- }
1776
- }
1777
- }
1778
- }
1779
-
1780
- // 添加表头单元格监听事件
1781
- columns = columns.map(
1782
- (item, idx) => (
1783
- {
1784
- ...item,
1785
- ...{
1786
- onHeaderCell: (e) => {
1787
- return {
1788
- islast: idx - (columns.length - 1),
1789
- ...(
1790
- enableHoverListen ?
1791
- {
1792
- onMouseEnter: event => { setProps({ recentlyMouseEnterColumnDataIndex: e.dataIndex }) }
1793
- } :
1794
- {}
1795
- ),
1796
- // 独立控制表头对齐
1797
- ...(
1798
- e.headerAlign ?
1799
- {
1800
- style: { textAlign: e.headerAlign }
1801
- } :
1802
- {}
1803
- )
1804
- };
1805
- }
1806
- }
1807
- }
1808
- )
1809
- )
1810
-
1811
- let rowSelection
1812
- // 处理行选择功能设置
1813
- if (rowSelectionType) {
1814
-
1815
- rowSelection = {
1816
- columnWidth: rowSelectionWidth,
1817
- checkStrictly: rowSelectionCheckStrictly,
1818
- fixed: true,
1819
- type: rowSelectionType,
1820
- selectedRowKeys: selectedRowKeys,
1821
- selections: [
1822
- Table.SELECTION_ALL,
1823
- Table.SELECTION_INVERT,
1824
- Table.SELECTION_NONE
1825
- ],
1826
- onChange: (selectedRowKeys, selectedRows) => {
1827
- setProps({
1828
- selectedRowKeys: (
1829
- // 排除被忽略选择的行
1830
- rowSelectionIgnoreRowKeys ?
1831
- selectedRowKeys.filter(item => !rowSelectionIgnoreRowKeys.includes(item)) :
1832
- selectedRowKeys
1833
- ),
1834
- selectedRows: (
1835
- // 排除被忽略选择的行
1836
- rowSelectionIgnoreRowKeys ?
1837
- selectedRows.map(
1838
- // 忽略组件型字段键值对
1839
- record => omitBy(record, value => value?.$$typeof)
1840
- ).filter(
1841
- record => !rowSelectionIgnoreRowKeys.includes(record.key)
1842
- ) :
1843
- selectedRows.map(
1844
- record => omitBy(record, value => value?.$$typeof)
1845
- )
1846
- )
1847
- })
1848
- },
1849
- renderCell: (checked, record, index, originNode) => {
1850
- // 排除不可选择的行
1851
- if (rowSelectionIgnoreRowKeys && rowSelectionIgnoreRowKeys.includes(record.key)) {
1852
- return null;
1853
- }
1854
- return originNode;
1855
- }
1856
- }
1857
- }
1858
-
1859
- // 处理行可展开内容功能
1860
- let rowExpandedRowRender
1861
- if (expandedRowKeyToContent && Array.isArray(expandedRowKeyToContent)) {
1862
- rowExpandedRowRender = new Map(
1863
- expandedRowKeyToContent.map(
1864
- item => [item.key, item.content]
1865
- )
1866
- )
1867
- }
1868
-
1869
- // 检查当前是否至少有一个字段是可编辑的
1870
- let atLeastOneColumnEditable = columns.some(item => item.editable)
1871
-
1872
- // 减少不必要的单元格重绘
1873
- if (cellUpdateOptimize) {
1874
- columns = columns.map(
1875
- item => {
1876
- return {
1877
- ...item,
1878
- // 减少不必要的单元格重绘
1879
- shouldCellUpdate: (record, prevRecord) => {
1880
- if (isEqual(record, prevRecord)) {
1881
- return false;
1882
- }
1883
- return true;
1884
- }
1885
- };
1886
- }
1887
- )
1888
- }
1889
-
1890
- // 配置自定义组件
1891
- const components = {}
1892
-
1893
- // 若至少有一个字段开启编辑功能
1894
- if (atLeastOneColumnEditable) {
1895
- components.body = {
1896
- row: EditableRow,
1897
- cell: EditableCell,
1898
- }
1899
- }
1900
-
1901
- // 统一合并处理onCell自定义函数逻辑
1902
- columns = [...columns].map(
1903
- item => {
1904
- return {
1905
- ...item,
1906
- ...{
1907
- onCell: (record, index) => {
1908
- // 初始化onCell返回值
1909
- let returnValue = {}
1910
- // 处理自定义样式特性
1911
- if (conditionalStyleFuncs && conditionalStyleFuncs[item.dataIndex]) {
1912
- try {
1913
- returnValue = {
1914
- ...returnValue,
1915
- ...eval(conditionalStyleFuncs[item.dataIndex])(record, index)
1916
- }
1917
- } catch (e) {
1918
- console.error(e)
1919
- }
1920
- }
1921
- // 处理单元格单击、双击、右键事件
1922
- if (enableCellClickListenColumns && enableCellClickListenColumns.includes(item.dataIndex)) {
1923
- try {
1924
- returnValue = {
1925
- ...returnValue,
1926
- onClick: e => {
1927
- setProps({
1928
- recentlyCellClickColumn: item.dataIndex,
1929
- recentlyCellClickRecord: omitBy(record, value => value?.$$typeof),
1930
- nClicksCell: nClicksCell + 1,
1931
- cellClickEvent: {
1932
- pageX: e.pageX,
1933
- pageY: e.pageY,
1934
- clientX: e.clientX,
1935
- clientY: e.clientY,
1936
- screenX: e.screenX,
1937
- screenY: e.screenY,
1938
- timestamp: Date.now()
1939
- }
1940
- })
1941
- },
1942
- onDoubleClick: e => {
1943
- setProps({
1944
- recentlyCellDoubleClickColumn: item.dataIndex,
1945
- recentlyCellDoubleClickRecord: omitBy(record, value => value?.$$typeof),
1946
- nDoubleClicksCell: nDoubleClicksCell + 1,
1947
- cellDoubleClickEvent: {
1948
- pageX: e.pageX,
1949
- pageY: e.pageY,
1950
- clientX: e.clientX,
1951
- clientY: e.clientY,
1952
- screenX: e.screenX,
1953
- screenY: e.screenY,
1954
- timestamp: Date.now()
1955
- }
1956
- })
1957
- },
1958
- onContextMenu: e => {
1959
- // 阻止浏览器默认右键菜单
1960
- e.preventDefault()
1961
- setProps({
1962
- recentlyContextMenuClickColumn: item.dataIndex,
1963
- recentlyContextMenuClickRecord: omitBy(record, value => value?.$$typeof),
1964
- nContextMenuClicksCell: nContextMenuClicksCell + 1,
1965
- cellContextMenuClickEvent: {
1966
- pageX: e.pageX,
1967
- pageY: e.pageY,
1968
- clientX: e.clientX,
1969
- clientY: e.clientY,
1970
- screenX: e.screenX,
1971
- screenY: e.screenY,
1972
- timestamp: Date.now()
1973
- }
1974
- })
1975
- }
1976
- }
1977
- } catch (e) {
1978
- console.error(e)
1979
- }
1980
- }
1981
-
1982
- // 处理可编辑特性
1983
- if (item.editable) {
1984
- try {
1985
- returnValue = {
1986
- ...returnValue,
1987
- record,
1988
- editable: item.editable,
1989
- dataIndex: item.dataIndex,
1990
- title: item.title
1991
- }
1992
- } catch (e) {
1993
- console.error(e)
1994
- }
1995
- }
1996
-
1997
- return returnValue;
1998
- }
1999
- }
2000
- };
2001
- }
2002
- )
2003
-
2004
- // 若存在至少一个字段有group参数,则对columns进行重构以支持多层表头
2005
- let tempColumns = []
2006
- if (columns.some(e => e.group)) {
2007
- // 新逻辑
2008
- for (let column of columns) {
2009
- if (column.group) {
2010
- if (isString(column.group)) {
2011
- insertNewColumnNode(column, [column.group], 0, tempColumns)
2012
- } else {
2013
- insertNewColumnNode(column, column.group, 0, tempColumns)
2014
- }
2015
- } else {
2016
- tempColumns.push(column)
2017
- }
2018
- }
2019
- } else {
2020
- tempColumns = [...columns]
2021
- }
2022
-
2023
- return (
2024
- <ConfigProvider
2025
- locale={str2Locale.get(locale)}
2026
- renderEmpty={emptyContent ? () => emptyContent : undefined}
2027
- >
2028
- <Table
2029
- // 提取具有data-*或aria-*通配格式的属性
2030
- {...pickBy((_, k) => k.startsWith('data-') || k.startsWith('aria-'), others)}
2031
- id={id}
2032
- className={className}
2033
- style={style}
2034
- key={key}
2035
- components={components}
2036
- rowClassName={
2037
- rowClassName ?
2038
- (record, index) => {
2039
- // 初始化rowClassName返回值
2040
- let _rowClassName = atLeastOneColumnEditable ? 'editable-row' : '';
2041
- // 若rowClassName参数通过func进行javascript函数定义
2042
- if (rowClassName.func) {
2043
- _rowClassName += ` ${eval(rowClassName.func)(record, index)}`
2044
- } else {
2045
- _rowClassName += ` ${rowClassName}`
2046
- }
2047
- return _rowClassName.trimStart()
2048
- } :
2049
- (atLeastOneColumnEditable ? () => 'editable-row' : undefined)
2050
- }
2051
- dataSource={
2052
- // 根据hiddenRowKeys参数情况进行指定行记录的隐藏
2053
- data.filter(e => !hiddenRowKeys.includes(e.key))
2054
- }
2055
- columns={tempColumns}
2056
- showHeader={showHeader}
2057
- rowHoverable={rowHoverable}
2058
- tableLayout={tableLayout}
2059
- size={size}
2060
- rowSelection={rowSelection}
2061
- sticky={computedSticky}
2062
- pagination={
2063
- // 确保pagination=false生效
2064
- pagination &&
2065
- {
2066
- ...pagination,
2067
- ...{
2068
- showTotal: total => `${pagination.showTotalPrefix} ${total} ${pagination.showTotalSuffix}`
2069
- },
2070
- position: (pagination.position && !Array.isArray(pagination.position))
2071
- ? [pagination.position] : pagination.position
2072
- }
2073
- }
2074
- bordered={bordered}
2075
- scroll={{ x: maxWidth, y: maxHeight, scrollToFirstRowOnChange: scrollToFirstRowOnChange }}
2076
- onChange={onPageChange}
2077
- onRow={
2078
- enableHoverListen ?
2079
- (record, index) => {
2080
- return {
2081
- onMouseEnter: event => {
2082
- setProps({
2083
- recentlyMouseEnterRowKey: record.key,
2084
- // 忽略组件型字段键值对
2085
- recentlyMouseEnterRow: omitBy(record, value => value?.$$typeof)
2086
- })
2087
- }, // 鼠标移入行
2088
- };
2089
- } : undefined
2090
- }
2091
- summary={summaryRowContents ? () => (
2092
- <Table.Summary fixed={summaryRowFixed}>
2093
- {
2094
- splitSummaryRowContents(summaryRowContents, columns.length, summaryRowBlankColumns).map(
2095
- (group, idx) => (
2096
- <Table.Summary.Row key={idx}>
2097
- {group.map((item, i) =>
2098
- (
2099
- item.empty ?
2100
- (
2101
- <Table.Summary.Cell index={i} />
2102
- ) :
2103
- (
2104
- <Table.Summary.Cell index={i} colSpan={item.colSpan} align={item.align}>
2105
- {item.content}
2106
- </Table.Summary.Cell>
2107
- )
2108
- )
2109
- )}
2110
- </Table.Summary.Row>
2111
- )
2112
- )
2113
- }
2114
- </Table.Summary>
2115
- ) : undefined}
2116
- expandable={
2117
- rowExpandedRowRender ? {
2118
- defaultExpandedRowKeys: defaultExpandedRowKeys,
2119
- expandedRowKeys: expandedRowKeys || defaultExpandedRowKeys,
2120
- expandedRowRender: (record) => rowExpandedRowRender.get(record.key),
2121
- rowExpandable: (record) => rowExpandedRowRender.has(record.key),
2122
- columnWidth: expandedRowWidth,
2123
- expandRowByClick: expandRowByClick,
2124
- onExpandedRowsChange: (e) => setProps({
2125
- expandedRowKeys: e
2126
- })
2127
- } : {
2128
- // 兼容行记录自带children的自动展开情况
2129
- defaultExpandedRowKeys: defaultExpandedRowKeys,
2130
- expandedRowKeys: expandedRowKeys || defaultExpandedRowKeys,
2131
- columnWidth: expandedRowWidth,
2132
- expandRowByClick: expandRowByClick,
2133
- onExpandedRowsChange: (e) => setProps({
2134
- expandedRowKeys: e
2135
- })
2136
- }
2137
- }
2138
- virtual={virtual}
2139
- title={title && (() => title)}
2140
- footer={footer && (() => footer)}
2141
- data-dash-is-loading={useLoading()}
2142
- loading={loading}
2143
- getPopupContainer={
2144
- containerId ?
2145
- () => (
2146
- document.getElementById(containerId) ?
2147
- document.getElementById(containerId) :
2148
- document.body
2149
- ) :
2150
- undefined}
2151
- showSorterTooltip={showSorterTooltip && showSorterTooltipTarget ? { target: showSorterTooltipTarget } : showSorterTooltip}
2152
- />
2153
- </ConfigProvider>
2154
- );
2155
- };
2156
-
2157
- export default React.memo(
2158
- AntdTable,
2159
- (prevProps, nextProps) => {
2160
- // 计算发生变化的参数名
2161
- const changedProps = Object.keys(nextProps)
2162
- .filter(key => !isEqual(prevProps[key], nextProps[key]))
2163
-
2164
- // 特殊处理:
2165
- // 当recentlySelectValue发生变动且不涉及data变动时时,阻止本次重绘
2166
- if (changedProps.includes('recentlySelectValue') && !changedProps.includes('data') && !changedProps.includes('currentData')) {
2167
- return true;
2168
- }
2169
-
2170
- // #80
2171
- // selectedRowsSyncWithData=true时,当data发生更新,在selectedRowKeys有效时,对selectedRows进行同步更新
2172
- if (
2173
- nextProps['selectedRowsSyncWithData'] &&
2174
- nextProps['selectedRowKeys'] &&
2175
- changedProps.includes('data')
2176
- ) {
2177
- // 同步更新selectedRows的值
2178
- nextProps.setProps({
2179
- // 忽略组件型字段键值对
2180
- selectedRows: nextProps['data'].filter(item => nextProps['selectedRowKeys'].includes(item.key))
2181
- .map(
2182
- record => omitBy(record, value => value?.$$typeof)
2183
- )
2184
- })
2185
- }
2186
-
2187
- // // 判断当前轮次变更的prop是否均在preventUpdateProps中
2188
- // console.log({ changedProps })
2189
- // console.log(
2190
- // '免重绘props:',
2191
- // changedProps.every(propName => preventUpdateProps.includes(propName))
2192
- // )
2193
-
2194
- // changedProps中全部变化的prop都在preventUpdateProps中声明时
2195
- // 阻止本次重绘
2196
- return changedProps.every(propName => preventUpdateProps.includes(propName));
2197
- }
2198
- );
2199
-
2200
- AntdTable.defaultProps = defaultProps;
1
+ // react核心
2
+ import React, {
3
+ useContext,
4
+ useState,
5
+ useEffect,
6
+ useRef
7
+ } from 'react';
8
+ // antd核心
9
+ import {
10
+ Table,
11
+ Checkbox,
12
+ Switch,
13
+ Popover,
14
+ Popconfirm,
15
+ ConfigProvider,
16
+ Typography,
17
+ Menu,
18
+ Dropdown,
19
+ Input,
20
+ Form,
21
+ Tag,
22
+ Button,
23
+ Badge,
24
+ Space,
25
+ Image,
26
+ Avatar,
27
+ message,
28
+ Select,
29
+ Divider,
30
+ Tooltip,
31
+ Progress
32
+ } from 'antd';
33
+ import {
34
+ TinyLine,
35
+ TinyArea,
36
+ TinyColumn,
37
+ RingProgress
38
+ } from '@ant-design/plots';
39
+ import AntdIcon from '../components/general/AntdIcon.react';
40
+ import {
41
+ SearchOutlined,
42
+ QuestionCircleOutlined,
43
+ DownOutlined
44
+ } from '@ant-design/icons';
45
+ // 辅助库
46
+ import Highlighter from 'react-highlight-words';
47
+ import { isNumber, isEqual, isString, isBoolean, isEmpty, omitBy, isUndefined } from 'lodash';
48
+ import { pickBy } from 'ramda';
49
+ import { str2Locale, locale2text } from '../components/locales.react';
50
+ import { useLoading } from '../components/utils';
51
+ import useStickyOffset from '../hooks/useStickyOffset';
52
+ // 上下文
53
+ import PropsContext from '../contexts/PropsContext';
54
+ // 参数类型
55
+ import { propTypes, defaultProps } from '../components/dataDisplay/AntdTable.react';
56
+ // 内部组件
57
+ import { UtilsLink } from '../internal_components/UtilsLink.react';
58
+
59
+ const { Text } = Typography;
60
+
61
+ const insertNewColumnNode = (column, group, currentLevel, currentNode) => {
62
+ // 若当前递归到的层级小于group数组长度
63
+ if (currentLevel < group.length) {
64
+ // 尝试在currentNode中搜索dataIndex等于当前group层级的元素
65
+ let matchColumnIdx = currentNode.findIndex(item => item.dataIndex === group[currentLevel])
66
+ // 若未在currentNode中搜索到当前group层级对应元素
67
+ if (matchColumnIdx === -1) {
68
+ // 向currentNode中push当前group层级对应元素
69
+ currentNode.push({
70
+ dataIndex: group[currentLevel],
71
+ title: group[currentLevel],
72
+ children: []
73
+ })
74
+ // 继续向下一层级递归
75
+ insertNewColumnNode(column, group, currentLevel + 1, currentNode[currentNode.length - 1].children)
76
+ } else {
77
+ // 若在currentNode中搜索到当前group层级对应元素
78
+ // 继续向下一层级递归
79
+ insertNewColumnNode(column, group, currentLevel + 1, currentNode[matchColumnIdx].children)
80
+ }
81
+ } else {
82
+ // 否则则视作到达最深层
83
+ currentNode.push({ ...column })
84
+ }
85
+ }
86
+
87
+ const splitSummaryRowContents = (summaryRowContents, columnCount, blankColumns) => {
88
+ let summaryGroups = (
89
+ blankColumns > 0 ?
90
+ [Array.from({ length: blankColumns }, () => ({ empty: true }))] :
91
+ [[]]
92
+ );
93
+ let currentGroupSpans = 0;
94
+ for (let item of summaryRowContents) {
95
+ // 检查当前字段追加到末尾分组后,是否超出总列数
96
+ if (currentGroupSpans + (item.colSpan || 1) > columnCount) {
97
+ currentGroupSpans = (item.colSpan || 1);
98
+ // 处理前置空白列填充
99
+ if (blankColumns > 0) {
100
+ summaryGroups.push([...Array.from({ length: blankColumns }, () => ({ empty: true })), item])
101
+ } else {
102
+ summaryGroups.push([item])
103
+ }
104
+ } else {
105
+ currentGroupSpans += (item.colSpan || 1)
106
+ summaryGroups[summaryGroups.length - 1].push(item)
107
+ }
108
+ }
109
+ return summaryGroups;
110
+ }
111
+
112
+ const findItemByKey = (array, key) => {
113
+ let foundItem = null;
114
+
115
+ const search = (item) => {
116
+ if (item.key === key) {
117
+ foundItem = item;
118
+ return true;
119
+ }
120
+ return Array.isArray(item.children) && item.children.some(search);
121
+ };
122
+
123
+ array.some(search);
124
+ return foundItem;
125
+ };
126
+
127
+ const replaceItemByKey = (array, key, replacement) => {
128
+ for (let i = 0; i < array.length; i++) {
129
+ const item = array[i];
130
+ if (item && item.key === key) {
131
+ array.splice(i, 1, replacement);
132
+ return true;
133
+ }
134
+
135
+ if (item && Array.isArray(item.children)) {
136
+ if (replaceItemByKey(item.children, key, replacement)) {
137
+ return true;
138
+ }
139
+ }
140
+ }
141
+
142
+ return false;
143
+ }
144
+
145
+ // 定义不触发重绘的参数数组
146
+ const preventUpdateProps = [
147
+ 'recentlyMouseEnterColumnDataIndex',
148
+ 'recentlyMouseEnterRowKey',
149
+ 'recentlyMouseEnterRow'
150
+ ];
151
+
152
+ /**
153
+ * 表格组件AntdTable
154
+ */
155
+ const AntdTable = (props) => {
156
+ let {
157
+ id,
158
+ className,
159
+ style,
160
+ key,
161
+ locale,
162
+ containerId,
163
+ columns,
164
+ showHeader,
165
+ rowHoverable,
166
+ tableLayout,
167
+ miniChartHeight,
168
+ miniChartAnimation,
169
+ rowSelectionType,
170
+ selectedRowKeys,
171
+ rowSelectionWidth,
172
+ rowSelectionCheckStrictly,
173
+ rowSelectionIgnoreRowKeys,
174
+ sticky,
175
+ titlePopoverInfo,
176
+ columnsFormatConstraint,
177
+ enableHoverListen,
178
+ data,
179
+ sortOptions,
180
+ showSorterTooltip,
181
+ showSorterTooltipTarget,
182
+ filterOptions,
183
+ defaultFilteredValues,
184
+ pagination,
185
+ bordered,
186
+ maxHeight,
187
+ maxWidth,
188
+ scrollToFirstRowOnChange,
189
+ size,
190
+ mode,
191
+ nClicksButton,
192
+ nDoubleClicksCell,
193
+ summaryRowContents,
194
+ summaryRowBlankColumns,
195
+ summaryRowFixed,
196
+ customFormatFuncs,
197
+ conditionalStyleFuncs,
198
+ expandedRowKeyToContent,
199
+ expandedRowWidth,
200
+ expandRowByClick,
201
+ defaultExpandedRowKeys,
202
+ expandedRowKeys,
203
+ enableCellClickListenColumns,
204
+ nClicksCell,
205
+ nContextMenuClicksCell,
206
+ emptyContent,
207
+ cellUpdateOptimize,
208
+ nClicksDropdownItem,
209
+ hiddenRowKeys,
210
+ virtual,
211
+ title,
212
+ footer,
213
+ loading,
214
+ rowClassName,
215
+ setProps,
216
+ ...others
217
+ } = props;
218
+
219
+ const context = useContext(PropsContext)
220
+ locale = (context && context.locale) || locale
221
+
222
+ useEffect(() => {
223
+ // 处理pagination参数的默认值问题
224
+ if (isBoolean(pagination) && !pagination) {
225
+ } else {
226
+ setProps({
227
+ pagination: {
228
+ ...pagination,
229
+ current: pagination?.current ? pagination?.current : 1
230
+ }
231
+ })
232
+ }
233
+ }, []);
234
+
235
+ const [searchText, setSearchText] = useState('');
236
+ const [searchedColumn, setSearchedColumn] = useState('');
237
+
238
+ const stickyObj = (sticky && typeof sticky === 'object') ? sticky : {};
239
+ const belowSelector = stickyObj.belowSelector ?? undefined;
240
+ const offsetHeader = Number(stickyObj.offsetHeader || 0);
241
+ const autoMeasuredOffset = belowSelector ? useStickyOffset({ selector: belowSelector, extra: offsetHeader }) : offsetHeader;
242
+ const { belowSelector: _rm1,
243
+ offsetHeader: _rm2,
244
+ ...stickyRest
245
+ } = stickyObj;
246
+ const computedSticky = sticky === true || belowSelector || (sticky && typeof sticky === 'object')
247
+ ? { ...stickyRest, offsetHeader: autoMeasuredOffset }
248
+ : undefined;
249
+
250
+ const onPageChange = (pagination, filter, sorter, currentData) => {
251
+
252
+ // 当本次事件由翻页操作引发时
253
+ if (currentData.action === 'paginate') {
254
+ setProps({
255
+ pagination: {
256
+ ...pagination,
257
+ pageSize: pagination.pageSize,
258
+ current: pagination.current,
259
+ position: pagination.position ?
260
+ (
261
+ Array.isArray(pagination.position) ?
262
+ pagination.position[0] :
263
+ pagination.position
264
+ ) :
265
+ pagination.position
266
+ },
267
+ currentData: currentData.currentDataSource
268
+ })
269
+ } else if (currentData.action === 'sort') {
270
+ // 当sorter为数组时,即为多字段组合排序方式时
271
+ if (Array.isArray(sorter)) {
272
+ setProps(
273
+ {
274
+ sorter: {
275
+ columns: sorter.map(item => item.column.dataIndex),
276
+ orders: sorter.map(item => item.order)
277
+ }
278
+ }
279
+ )
280
+ } else if (sorter.order) {
281
+ // 单字段排序方式
282
+ setProps(
283
+ {
284
+ sorter: {
285
+ columns: [sorter.column.dataIndex],
286
+ orders: [sorter.order]
287
+ }
288
+ }
289
+ )
290
+ } else {
291
+ // 非排序状态
292
+ setProps(
293
+ {
294
+ sorter: {
295
+ columns: [],
296
+ orders: []
297
+ }
298
+ }
299
+ )
300
+ }
301
+ } else if (currentData.action === 'filter') {
302
+ setProps({ filter: filter })
303
+ }
304
+ }
305
+
306
+ // 自定义关键词搜索过滤模式
307
+ let searchInput;
308
+ const getColumnSearchProps = (dataIndex, title) => ({
309
+ filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
310
+ <div style={{ padding: 8 }}>
311
+ <Input
312
+ ref={node => {
313
+ searchInput = node;
314
+ }}
315
+ placeholder={`${locale2text.AntdTable[locale].filterKeywordPlaceholder} ${title}`}
316
+ value={selectedKeys[0]}
317
+ onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
318
+ onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)}
319
+ style={{ marginBottom: 8, display: 'block' }}
320
+ />
321
+ <Space>
322
+ <Button
323
+ type="primary"
324
+ onClick={() => handleSearch(selectedKeys, confirm, dataIndex)}
325
+ icon={<SearchOutlined />}
326
+ size="small"
327
+ style={{ width: 90 }}
328
+ >
329
+ {locale2text.AntdTable[locale].filterKeywordSearchButtonText}
330
+ </Button>
331
+ <Button onClick={() => handleSearchReset(clearFilters)} size="small" style={{ width: 90 }}>
332
+ {locale2text.AntdTable[locale].filterKeywordResetButtonText}
333
+ </Button>
334
+ </Space>
335
+ </div>
336
+ ),
337
+ filterIcon: filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
338
+ // 搜索筛选
339
+ onFilter: (value, record) => {
340
+ if (mode === 'client-side') {
341
+ // 仅支持非数组型合法输入值,对象型输入支持对content、text、label、tag属性进行搜索筛选
342
+ if (record[dataIndex] && !Array.isArray(record[dataIndex])) {
343
+ // 判断当前记录是否有content属性
344
+ if (record[dataIndex]?.content) {
345
+ return record[dataIndex].content.toString().toLowerCase().includes(value?.toLowerCase())
346
+ } else if (record[dataIndex]?.text) {
347
+ return record[dataIndex].text.toString().toLowerCase().includes(value?.toLowerCase())
348
+ } else if (record[dataIndex]?.label) {
349
+ return record[dataIndex].label.toString().toLowerCase().includes(value?.toLowerCase())
350
+ } else if (record[dataIndex]?.tag) {
351
+ return record[dataIndex].tag.toString().toLowerCase().includes(value?.toLowerCase())
352
+ } else if (record[dataIndex]?.toString) {
353
+ return record[dataIndex].toString().toLowerCase().includes(value?.toLowerCase())
354
+ }
355
+ } else if (Array.isArray(record[dataIndex])) {
356
+ // 若当前记录为数组,分别检查数组元素对象是否具有content、tag、title属性
357
+ if (record[dataIndex].some(item => item?.content)) {
358
+ // 检查当前记录数组中是否至少有一个对象的content属性命中关键词
359
+ return record[dataIndex].some(
360
+ item => item?.content.toString().toLowerCase().includes(value?.toLowerCase())
361
+ )
362
+ } else if (record[dataIndex].some(item => item?.tag)) {
363
+ // 检查当前记录数组中是否至少有一个对象的tag属性命中关键词
364
+ return record[dataIndex].some(
365
+ item => item?.tag.toString().toLowerCase().includes(value?.toLowerCase())
366
+ )
367
+ } else if (record[dataIndex].some(item => item?.title)) {
368
+ // 检查当前记录数组中是否至少有一个对象的title属性命中关键词
369
+ return record[dataIndex].some(
370
+ item => item?.title.toString().toLowerCase().includes(value?.toLowerCase())
371
+ )
372
+ }
373
+ }
374
+ return false;
375
+ } else {
376
+ return true
377
+ }
378
+ },
379
+ onFilterDropdownVisibleChange: visible => {
380
+ if (visible) {
381
+ setTimeout(() => searchInput.select(), 100);
382
+ }
383
+ },
384
+ render: text =>
385
+ searchedColumn === dataIndex ? (
386
+ <Highlighter
387
+ highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
388
+ searchWords={[searchText]}
389
+ autoEscape
390
+ textToHighlight={text ? text.toString() : ''}
391
+ />
392
+ ) : (
393
+ text
394
+ ),
395
+ });
396
+
397
+ const handleSearch = (selectedKeys, confirm, dataIndex) => {
398
+ confirm();
399
+ setSearchText(selectedKeys[0]);
400
+ setSearchedColumn(dataIndex);
401
+ };
402
+
403
+ const handleSearchReset = clearFilters => {
404
+ clearFilters();
405
+ setSearchText('');
406
+ };
407
+
408
+ if (!data) {
409
+ data = []
410
+ }
411
+
412
+ // 当未设置行key时,自动以自增1的字符型结果作为key
413
+ for (let i in data) {
414
+ if (!data[i].hasOwnProperty('key')) {
415
+ data[i]['key'] = i.toString()
416
+ }
417
+ }
418
+
419
+ // 为pagination补充默认参数值
420
+ if (isBoolean(pagination) && !pagination) {
421
+
422
+ } else {
423
+ pagination = {
424
+ ...pagination,
425
+ showTotalPrefix: pagination?.showTotalPrefix || locale2text.AntdTable[locale].showTotalPrefix,
426
+ showTotalSuffix: pagination?.showTotalSuffix || locale2text.AntdTable[locale].showTotalSuffix
427
+ }
428
+ }
429
+
430
+ // 根据columns中的hidden属性控制是否忽略对应字段
431
+ columns = columns.filter(item => !item.hidden)
432
+
433
+ // 为columns配置默认align参数
434
+ for (let i in columns) {
435
+ columns[i] = {
436
+ align: 'center',
437
+ ...columns[i]
438
+ }
439
+ }
440
+
441
+ // 自定义可编辑单元格
442
+ const EditableContext = React.createContext(null);
443
+
444
+ const EditableRow = ({ index, ...props }) => {
445
+ const [form] = Form.useForm();
446
+ return (
447
+ <Form form={form} component={false}>
448
+ <EditableContext.Provider value={form}>
449
+ <tr {...props} />
450
+ </EditableContext.Provider>
451
+ </Form>
452
+ );
453
+ };
454
+
455
+ const EditableCell = ({
456
+ title,
457
+ editable,
458
+ children,
459
+ dataIndex,
460
+ record,
461
+ ...restProps
462
+ }) => {
463
+ const [editing, setEditing] = useState(false);
464
+ const inputRef = useRef(null);
465
+ const form = useContext(EditableContext);
466
+ useEffect(() => {
467
+ if (editing) {
468
+ inputRef.current?.focus();
469
+ }
470
+ }, [editing]);
471
+
472
+ const toggleEdit = () => {
473
+ setEditing(!editing);
474
+ form.setFieldsValue({
475
+ [dataIndex]: record[dataIndex],
476
+ });
477
+ };
478
+
479
+ const [dataSource, setDataSource] = useState(data)
480
+
481
+ // 负责监听单元格内容修改动作从而进行相关值的更新
482
+ const handleSave = (row, setProps, dataSource, setDataSource) => {
483
+
484
+ const newData = [...dataSource];
485
+ const item = findItemByKey(newData, row.key)
486
+
487
+ const rowColumns = Object.getOwnPropertyNames(row)
488
+
489
+ // 循环取出属性名,再判断属性值是否一致
490
+ for (let i = 0; i < rowColumns.length; i++) {
491
+ // 找到发生值修改的字段
492
+ if (row[rowColumns[i]] !== item[rowColumns[i]] &&
493
+ columnsFormatConstraint &&
494
+ columnsFormatConstraint[rowColumns[i]] &&
495
+ columnsFormatConstraint[rowColumns[i]].rule) {
496
+ // 检查是否满足预设的正则表达式规则
497
+ if (!eval(`/${columnsFormatConstraint[rowColumns[i]].rule}/`).test(row[rowColumns[i]])) {
498
+ message.error(
499
+ columnsFormatConstraint[rowColumns[i]]?.content ?
500
+ columnsFormatConstraint[rowColumns[i]]?.content.replace('[VALUE]', row[rowColumns[i]]) :
501
+ locale2text.AntdTable[locale].columnEditableFormatConstraintMessage.replace('[VALUE]', row[rowColumns[i]])
502
+ );
503
+ // 提前终止函数
504
+ return
505
+ }
506
+ }
507
+ }
508
+
509
+ // #93 找到发生编辑行为的字段dataIndex
510
+ let _changedColumn = null;
511
+ for (let i = 0; i < rowColumns.length; i++) {
512
+ if (row[rowColumns[i]] !== item[rowColumns[i]]) {
513
+ _changedColumn = rowColumns[i]
514
+ }
515
+ }
516
+
517
+ replaceItemByKey(newData, row.key, { ...item, ...row })
518
+
519
+ setDataSource(newData);
520
+
521
+ setProps({
522
+ currentData: newData,
523
+ // 忽略组件型字段键值对
524
+ recentlyChangedRow: omitBy(row, value => value?.$$typeof),
525
+ recentlyChangedColumn: _changedColumn,
526
+ data: newData
527
+ })
528
+ };
529
+
530
+ const save = async () => {
531
+ try {
532
+ const values = await form.validateFields();
533
+ toggleEdit();
534
+ handleSave({ ...record, ...values }, setProps, dataSource, setDataSource);
535
+ } catch (errInfo) {
536
+ console.log(errInfo)
537
+ }
538
+ };
539
+
540
+ let childNode = children;
541
+
542
+ if (editable) {
543
+ let recordDisabled = (columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.disabledKeys || []).includes(record.key)
544
+ childNode = editing ? (
545
+ <Form.Item
546
+ style={{
547
+ margin: 0,
548
+ }}
549
+ name={dataIndex}
550
+ rules={[
551
+ {
552
+ required: false,
553
+ message: `${title} 为空!`,
554
+ },
555
+ ]}
556
+ >
557
+ {
558
+ columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.mode === 'text-area' ?
559
+ <Input.TextArea
560
+ autoSize={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.autoSize}
561
+ maxLength={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.maxLength}
562
+ placeholder={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.placeholder}
563
+ ref={inputRef}
564
+ onBlur={save}
565
+ onFocus={() => {
566
+ // 移动光标至内容末尾
567
+ inputRef.current?.focus({
568
+ cursor: 'end',
569
+ })
570
+ }}
571
+ disabled={recordDisabled}
572
+ /> :
573
+ <Input
574
+ maxLength={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.maxLength}
575
+ placeholder={columns.filter(e => e.dataIndex === dataIndex)[0].editOptions?.placeholder}
576
+ ref={inputRef}
577
+ onPressEnter={save}
578
+ onBlur={save}
579
+ disabled={recordDisabled}
580
+ />
581
+ }
582
+ </Form.Item>
583
+ ) : (
584
+ <div
585
+ className="editable-cell-value-wrap"
586
+ style={{
587
+ whiteSpace: 'break-spaces',
588
+ ...(recordDisabled ? { cursor: 'no-drop' } : {})
589
+ }}
590
+ onClick={recordDisabled ? undefined : toggleEdit}
591
+ >
592
+ {(children[1] || children[1] === 0) ? children : ' '}
593
+ </div>
594
+ );
595
+ }
596
+
597
+ return <td {...restProps}>{childNode}</td>;
598
+ };
599
+
600
+ // 数值比较函数
601
+ const compareNumeric = (x, y) => {
602
+ if (x.value < y.value) {
603
+ return -1;
604
+ } else if (x.value > y.value) {
605
+ return 1;
606
+ } else {
607
+ return 0;
608
+ }
609
+ }
610
+
611
+ // 多模式值筛选选项自动生成策略
612
+ const generateFilterOptions = (inputData, columnDataIndex) => {
613
+ let filterOptions = []
614
+ for (let item of inputData) {
615
+ // 若当前记录不为数组
616
+ if ((item[columnDataIndex] || item[columnDataIndex] === 0) && !Array.isArray(item[columnDataIndex])) {
617
+ if (item[columnDataIndex]?.content) {
618
+ filterOptions.push(item[columnDataIndex].content)
619
+ } else if (item[columnDataIndex]?.text) {
620
+ filterOptions.push(item[columnDataIndex].text)
621
+ } else if (item[columnDataIndex]?.label) {
622
+ filterOptions.push(item[columnDataIndex].label)
623
+ } else if (item[columnDataIndex]?.tag) {
624
+ filterOptions.push(item[columnDataIndex].tag)
625
+ } else if (item[columnDataIndex]?.toString) {
626
+ filterOptions.push(item[columnDataIndex])
627
+ }
628
+ } else if (Array.isArray(item[columnDataIndex])) {
629
+ // 若当前记录为数组,提取数组元素对象中存在的content或tag属性
630
+ if (item[columnDataIndex].some(_item => _item?.content)) {
631
+ filterOptions = filterOptions.concat(
632
+ item[columnDataIndex]
633
+ .filter(_item => _item?.content)
634
+ .map(_item => _item.content)
635
+ )
636
+ } else if (item[columnDataIndex].some(_item => _item?.tag)) {
637
+ filterOptions = filterOptions.concat(
638
+ item[columnDataIndex]
639
+ .filter(_item => _item?.tag)
640
+ .map(_item => _item.tag)
641
+ )
642
+ }
643
+ }
644
+ }
645
+
646
+ // 将提取到的合法筛选值去重结构化并排序
647
+ return Array.from(
648
+ new Set(filterOptions)
649
+ ).map(
650
+ value => ({ text: (value || value === 0) ? value.toString() : '', value: value })
651
+ ).sort(compareNumeric)
652
+ }
653
+
654
+ // 处理可筛选特性
655
+ // 若为前端渲染模式,在filterOptions中每个字段filterCustomItems缺失的情况下
656
+ // 则会自动根据前端一次性加载的数据推算出所有添加过滤器字段的唯一值集合作为待选菜单
657
+ if (mode !== 'server-side') {
658
+ // filterOptions.filterDataIndexes中定义的每个字段添加过滤功能
659
+ for (let i = 0; i < columns.length; i++) {
660
+ // 若当前字段在filterOptions的keys()中
661
+ if (Object.keys(filterOptions).indexOf(columns[i].dataIndex) !== -1) {
662
+ // 若当前字段对应filterOptions子元素有filterMode.filterMode为'keyword'
663
+ if (filterOptions[columns[i].dataIndex].filterMode === 'keyword') {
664
+ columns[i] = {
665
+ ...columns[i],
666
+ ...getColumnSearchProps(columns[i].dataIndex, columns[i].title)
667
+ }
668
+ } else if (filterOptions[columns[i].dataIndex].filterMode === 'tree') {
669
+ // 若当前字段筛选模式为'tree'模式
670
+ // 若当前字段对应filterOptions子元素下有filterCustomTreeItems属性
671
+ // 则为其添加自定义树形选项
672
+ if (filterOptions[columns[i].dataIndex].filterCustomTreeItems) {
673
+ columns[i] = {
674
+ ...columns[i],
675
+ defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
676
+ filterMode: 'tree',
677
+ // 直接使用自定义树形筛选菜单结构
678
+ filters: filterOptions[columns[i].dataIndex].filterCustomTreeItems,
679
+ // 针对不同再渲染模式设计值筛选逻辑
680
+ onFilter: (value, record) => {
681
+ // 仅支持非数组型合法输入值,对象型输入支持对content、text、label、tag属性进行值筛选
682
+ if ((record[columns[i].dataIndex] || record[columns[i].dataIndex] === 0) && !Array.isArray(record[columns[i].dataIndex])) {
683
+ // 判断当前记录是否有content属性
684
+ if (record[columns[i].dataIndex]?.content) {
685
+ return record[columns[i].dataIndex].content === value;
686
+ } else if (record[columns[i].dataIndex]?.text) {
687
+ return record[columns[i].dataIndex].text === value;
688
+ } else if (record[columns[i].dataIndex]?.label) {
689
+ return record[columns[i].dataIndex].label === value;
690
+ } else if (record[columns[i].dataIndex]?.tag) {
691
+ return record[columns[i].dataIndex].tag === value;
692
+ } else if (record[columns[i].dataIndex]?.toString) {
693
+ // 确保字符型、数值型均可稳定进行筛选
694
+ return record[columns[i].dataIndex].toString() === value.toString();
695
+ }
696
+ } else if (Array.isArray(record[columns[i].dataIndex])) {
697
+ // 若当前记录为数组,分别检查数组元素对象是否具有content、tag、title属性
698
+ if (record[columns[i].dataIndex].some(item => item?.content)) {
699
+ // 检查当前记录数组中是否至少有一个对象的content属性等于筛选值value
700
+ return record[columns[i].dataIndex].some(
701
+ item => item?.content.toString().toLowerCase() === value?.toLowerCase()
702
+ )
703
+ } else if (record[columns[i].dataIndex].some(item => item?.tag)) {
704
+ // 检查当前记录数组中是否至少有一个对象的tag属性命中关键词
705
+ return record[columns[i].dataIndex].some(
706
+ item => item?.tag.toString().toLowerCase() === value?.toLowerCase()
707
+ )
708
+ } else if (record[columns[i].dataIndex].some(item => item?.title)) {
709
+ // 检查当前记录数组中是否至少有一个对象的title属性命中关键词
710
+ return record[columns[i].dataIndex].some(
711
+ item => item?.title.toString().toLowerCase() === value?.toLowerCase()
712
+ )
713
+ }
714
+ }
715
+ return false;
716
+ },
717
+ filterSearch: filterOptions[columns[i].dataIndex].filterSearch
718
+ }
719
+ }
720
+ } else {
721
+ // 否则则一律视为'checkbox'模式
722
+ // 若当前字段对应filterOptions子元素下有filterCustomItems属性
723
+ // 则为其添加自定义选项
724
+ if (filterOptions[columns[i].dataIndex].filterCustomItems) {
725
+ columns[i] = {
726
+ ...columns[i],
727
+ defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
728
+ filters: filterOptions[columns[i].dataIndex].filterCustomItems
729
+ .map(value => ({ text: (value || value === 0) ? value.toString() : '', value: value })),
730
+ // 针对不同再渲染模式设计值筛选逻辑
731
+ onFilter: (value, record) => {
732
+ // 仅支持非数组型合法输入值,对象型输入支持对content、text、label、tag属性进行值筛选
733
+ if ((record[columns[i].dataIndex] || record[columns[i].dataIndex] === 0) && !Array.isArray(record[columns[i].dataIndex])) {
734
+ // 判断当前记录是否有content属性
735
+ if (record[columns[i].dataIndex]?.content) {
736
+ return record[columns[i].dataIndex].content === value;
737
+ } else if (record[columns[i].dataIndex]?.text) {
738
+ return record[columns[i].dataIndex].text === value;
739
+ } else if (record[columns[i].dataIndex]?.label) {
740
+ return record[columns[i].dataIndex].label === value;
741
+ } else if (record[columns[i].dataIndex]?.tag) {
742
+ return record[columns[i].dataIndex].tag === value;
743
+ } else if (record[columns[i].dataIndex]?.toString) {
744
+ // 确保字符型、数值型均可稳定进行筛选
745
+ return record[columns[i].dataIndex].toString() === value.toString();
746
+ }
747
+ } else if (Array.isArray(record[columns[i].dataIndex])) {
748
+ // 若当前记录为数组,分别检查数组元素对象是否具有content、tag、title属性
749
+ if (record[columns[i].dataIndex].some(item => item?.content)) {
750
+ // 检查当前记录数组中是否至少有一个对象的content属性等于筛选值value
751
+ return record[columns[i].dataIndex].some(
752
+ item => item?.content.toString().toLowerCase() === value?.toLowerCase()
753
+ )
754
+ } else if (record[columns[i].dataIndex].some(item => item?.tag)) {
755
+ // 检查当前记录数组中是否至少有一个对象的tag属性命中关键词
756
+ return record[columns[i].dataIndex].some(
757
+ item => item?.tag.toString().toLowerCase() === value?.toLowerCase()
758
+ )
759
+ } else if (record[columns[i].dataIndex].some(item => item?.title)) {
760
+ // 检查当前记录数组中是否至少有一个对象的title属性命中关键词
761
+ return record[columns[i].dataIndex].some(
762
+ item => item?.title.toString().toLowerCase() === value?.toLowerCase()
763
+ )
764
+ }
765
+ }
766
+ return false;
767
+ },
768
+ filterMultiple: filterOptions[columns[i].dataIndex].filterMultiple,
769
+ filterSearch: filterOptions[columns[i].dataIndex].filterSearch
770
+ }
771
+ } else {
772
+ // 否则自动基于数据中的唯一值生成选项列表
773
+ columns[i] = {
774
+ ...columns[i],
775
+ defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
776
+ filters: generateFilterOptions(data, columns[i].dataIndex),
777
+ // 针对不同再渲染模式设计值筛选逻辑
778
+ onFilter: (value, record) => {
779
+ // 仅支持非数组型合法输入值,对象型输入支持对content、text、label、tag属性进行值筛选
780
+ if ((record[columns[i].dataIndex] || record[columns[i].dataIndex] === 0) && !Array.isArray(record[columns[i].dataIndex])) {
781
+ if (record[columns[i].dataIndex]?.content) {
782
+ return record[columns[i].dataIndex].content === value;
783
+ } else if (record[columns[i].dataIndex]?.text) {
784
+ return record[columns[i].dataIndex].text === value;
785
+ } else if (record[columns[i].dataIndex]?.label) {
786
+ return record[columns[i].dataIndex].label === value;
787
+ } else if (record[columns[i].dataIndex]?.tag) {
788
+ return record[columns[i].dataIndex].tag === value;
789
+ } else if (record[columns[i].dataIndex]?.toString) {
790
+ // 确保字符型、数值型均可稳定进行筛选
791
+ return record[columns[i].dataIndex].toString() === value.toString();
792
+ }
793
+ } else if (Array.isArray(record[columns[i].dataIndex])) {
794
+ // 若当前记录为数组,分别检查数组元素对象是否具有content、tag、title属性
795
+ if (record[columns[i].dataIndex].some(item => item?.content)) {
796
+ // 检查当前记录数组中是否至少有一个对象的content属性等于筛选值value
797
+ return record[columns[i].dataIndex].some(
798
+ item => item?.content.toString().toLowerCase() === value?.toLowerCase()
799
+ )
800
+ } else if (record[columns[i].dataIndex].some(item => item?.tag)) {
801
+ // 检查当前记录数组中是否至少有一个对象的tag属性命中关键词
802
+ return record[columns[i].dataIndex].some(
803
+ item => item?.tag.toString().toLowerCase() === value?.toLowerCase()
804
+ )
805
+ } else if (record[columns[i].dataIndex].some(item => item?.title)) {
806
+ // 检查当前记录数组中是否至少有一个对象的title属性命中关键词
807
+ return record[columns[i].dataIndex].some(
808
+ item => item?.title.toString().toLowerCase() === value?.toLowerCase()
809
+ )
810
+ }
811
+ }
812
+ return false;
813
+ },
814
+ filterMultiple: filterOptions[columns[i].dataIndex]?.filterMultiple,
815
+ filterSearch: filterOptions[columns[i].dataIndex]?.filterSearch
816
+ }
817
+ }
818
+ }
819
+ }
820
+ }
821
+ } else {
822
+ // 否则在server-side模式下
823
+ // 为filterOptions.filterDataIndexes中定义的每个字段
824
+ // 添加简单值选择过滤功能
825
+ // filterOptions.filterDataIndexes中定义的每个字段添加过滤功能
826
+ for (let i = 0; i < columns.length; i++) {
827
+ // 若当前字段在filterOptions的keys()中
828
+ if (Object.keys(filterOptions).indexOf(columns[i].dataIndex) !== -1) {
829
+ // 若当前字段对应filterOptions子元素有filterMode属性且filterMode属性为'keyword'
830
+ if (filterOptions[columns[i].dataIndex].filterMode === 'keyword') {
831
+ columns[i] = {
832
+ ...columns[i],
833
+ ...getColumnSearchProps(columns[i].dataIndex, columns[i].title)
834
+ }
835
+ } else if (filterOptions[columns[i].dataIndex].filterMode === 'tree') {
836
+ // 若当前字段筛选模式为'tree'模式
837
+ // 若当前字段对应filterOptions子元素下有filterCustomTreeItems属性
838
+ // 则为其添加自定义树形选项
839
+ if (filterOptions[columns[i].dataIndex].filterCustomTreeItems) {
840
+ columns[i] = {
841
+ ...columns[i],
842
+ defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
843
+ filterMode: 'tree',
844
+ filters: filterOptions[columns[i].dataIndex].filterCustomTreeItems,
845
+ onFilter: (value, record) => true // 契合后端刷新模式
846
+ }
847
+ } else {
848
+ columns[i] = {
849
+ ...columns[i],
850
+ filters: [],
851
+ onFilter: (value, record) => true
852
+ }
853
+ }
854
+ } else {
855
+ // 否则则一律视为'checkbox'模式
856
+ // 若当前字段对应filterOptions子元素下有filterCustomItems属性
857
+ // 则为其添加自定义选项
858
+ if (filterOptions[columns[i].dataIndex].hasOwnProperty('filterCustomItems')) {
859
+ columns[i] = {
860
+ ...columns[i],
861
+ defaultFilteredValue: defaultFilteredValues[columns[i].dataIndex],
862
+ filters: filterOptions[columns[i].dataIndex].filterCustomItems
863
+ .map(value => ({ text: (value || value === 0) ? value.toString() : '', value: value })),
864
+ onFilter: (value, record) => true, // 契合后端刷新模式
865
+ filterMultiple: filterOptions[columns[i].dataIndex].filterMultiple,
866
+ filterSearch: filterOptions[columns[i].dataIndex].filterSearch
867
+ }
868
+ } else {
869
+ columns[i] = {
870
+ ...columns[i],
871
+ filters: [],
872
+ onFilter: (value, record) => true
873
+ }
874
+ }
875
+ }
876
+ }
877
+ }
878
+ }
879
+
880
+ // 处理sortOptions参数的默认值问题
881
+ sortOptions = {
882
+ sortDataIndexes: [],
883
+ multiple: false,
884
+ forceCompareModes: {},
885
+ customOrders: {},
886
+ ...sortOptions
887
+ }
888
+
889
+ // 配置字段排序参数
890
+ for (let i = 0; i < sortOptions.sortDataIndexes.length; i++) {
891
+ for (let j = 0; j < columns.length; j++) {
892
+ // 若sortOptions与data中本轮迭代到的dataIndex一致
893
+ if (sortOptions.sortDataIndexes[i] === columns[j].dataIndex) {
894
+ // 根据是否组合排序模式来决定当前字段的排序参数设置
895
+ columns[j]['sorter'] = {
896
+ compare: (a, b) => {
897
+ // 当渲染模式为server-side时,禁用前端排序操作
898
+ if (mode === 'server-side') {
899
+ return 0
900
+ } else {
901
+ // 初始化排序直接比较值
902
+ let valueA = null
903
+ let valueB = null
904
+ // 兼容各种具有单个值的再渲染模式,提取待比较的成对数据
905
+ if (a[columns[j].dataIndex]?.content || b[columns[j].dataIndex]?.content) {
906
+ valueA = a[columns[j].dataIndex]?.content
907
+ valueB = b[columns[j].dataIndex]?.content
908
+ } else if (a[columns[j].dataIndex]?.text || b[columns[j].dataIndex]?.text) {
909
+ valueA = a[columns[j].dataIndex]?.text
910
+ valueB = b[columns[j].dataIndex]?.text
911
+ } else if (a[columns[j].dataIndex]?.label || b[columns[j].dataIndex]?.label) {
912
+ valueA = a[columns[j].dataIndex]?.label
913
+ valueB = b[columns[j].dataIndex]?.label
914
+ } else if (a[columns[j].dataIndex]?.tag || b[columns[j].dataIndex]?.tag) {
915
+ valueA = a[columns[j].dataIndex]?.tag
916
+ valueB = b[columns[j].dataIndex]?.tag
917
+ } else if (a[columns[j].dataIndex]?.toString || b[columns[j].dataIndex]?.toString) {
918
+ valueA = a[columns[j].dataIndex]
919
+ valueB = b[columns[j].dataIndex]
920
+ }
921
+ // 根据valueA、valueB的数据类型返回不同逻辑的判断结果
922
+ // 若valueA、valueB至少有1个为数值型,则视作数值型比较
923
+ // 若当前字段使用了强制比较模式
924
+ if (sortOptions.forceCompareModes[columns[j].dataIndex]) {
925
+ // 数值比较模式
926
+ if (sortOptions.forceCompareModes[columns[j].dataIndex] === 'number') {
927
+ // 强制转换比较值为数值型
928
+ let numberA = Number(valueA)
929
+ let numberB = Number(valueB)
930
+ if (numberA < numberB) {
931
+ return -1;
932
+ } else if (numberA > numberB) {
933
+ return 1;
934
+ }
935
+ return 0;
936
+ } else if (sortOptions.forceCompareModes[columns[j].dataIndex] === 'custom' &&
937
+ sortOptions.customOrders[columns[j].dataIndex]) {
938
+ // 自定义顺序模式
939
+ // 查询比较值在对应的自定义顺序中的索引
940
+ let orderA = sortOptions.customOrders[columns[j].dataIndex].indexOf(valueA)
941
+ let orderB = sortOptions.customOrders[columns[j].dataIndex].indexOf(valueB)
942
+ if (orderA < orderB) {
943
+ return 1;
944
+ } else if (orderA > orderB) {
945
+ return -1;
946
+ }
947
+ return 0;
948
+ }
949
+ } else {
950
+ // 默认自动判断
951
+ if (isNumber(valueA) || isNumber(valueB)) {
952
+ return valueA - valueB
953
+ } else {
954
+ // 否则均视作字符型比较
955
+ let stringA = valueA?.toString().toUpperCase()
956
+ let stringB = valueB?.toString().toUpperCase()
957
+ if (stringA < stringB) {
958
+ return -1;
959
+ } else if (stringA > stringB) {
960
+ return 1;
961
+ }
962
+ return 0;
963
+ }
964
+ }
965
+ }
966
+ },
967
+ multiple: sortOptions['multiple'] ?
968
+ (sortOptions['multiple'] === 'auto' ? 1 : sortOptions.sortDataIndexes.length - i) :
969
+ undefined
970
+ }
971
+ }
972
+ }
973
+ }
974
+
975
+ // 配置各种再渲染模式
976
+ for (let i = 0; i < columns.length; i++) {
977
+ // 当前字段具有renderOptions参数时且renderOptions参数是字典时
978
+ if (columns[i]['renderOptions'] && columns[i]['renderOptions']['renderType']) {
979
+ // ellipsis模式
980
+ if (columns[i]['renderOptions']['renderType'] === 'ellipsis') {
981
+ columns[i]['ellipsis'] = {
982
+ showTitle: false
983
+ }
984
+ columns[i]['render'] = content => (
985
+ <Text ellipsis={{ tooltip: content }}>
986
+ {content}
987
+ </Text>
988
+ )
989
+ }
990
+ // row-merge模式
991
+ else if (columns[i]['renderOptions']['renderType'] === 'row-merge') {
992
+ columns[i]['render'] = (content, record, index) => {
993
+ return {
994
+ children: content.content,
995
+ props: {
996
+ rowSpan: content.rowSpan
997
+ }
998
+ };
999
+ }
1000
+ }
1001
+ // link模式
1002
+ else if (columns[i]['renderOptions']['renderType'] === 'link') {
1003
+ // 检查renderLinkText参数是否定义
1004
+ if (columns[i]['renderOptions']['renderLinkText']) {
1005
+ columns[i]['render'] = content => {
1006
+ if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1007
+ return null;
1008
+ }
1009
+ return (
1010
+ columns[i]['renderOptions']['likeDccLink'] ?
1011
+ (
1012
+ <UtilsLink href={content.disabled ? undefined : content.href}
1013
+ target={content.target ? content.target : '_blank'}
1014
+ disabled={content.disabled}>
1015
+ {content.content ? content.content : columns[i]['renderOptions']['renderLinkText']}
1016
+ </UtilsLink>
1017
+ ) :
1018
+ (
1019
+ <a href={content.disabled ? undefined : content.href}
1020
+ target={content.target ? content.target : '_blank'}
1021
+ disabled={content.disabled}>
1022
+ {content.content ? content.content : columns[i]['renderOptions']['renderLinkText']}
1023
+ </a>
1024
+ )
1025
+ );
1026
+ }
1027
+ } else {
1028
+ columns[i]['render'] = content => {
1029
+ if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1030
+ return null;
1031
+ }
1032
+ return (
1033
+ columns[i]['renderOptions']['likeDccLink'] ?
1034
+ (
1035
+ <UtilsLink href={content.disabled ? undefined : content.href}
1036
+ target={content.target ? content.target : '_blank'}
1037
+ disabled={content.disabled}>
1038
+ {content.content ? content.content : ' '}
1039
+ </UtilsLink>
1040
+ ) :
1041
+ (
1042
+ <a href={content.disabled ? undefined : content.href}
1043
+ target={content.target ? content.target : '_blank'}
1044
+ disabled={content.disabled}>
1045
+ {content.content ? content.content : ' '}
1046
+ </a>
1047
+ )
1048
+ );
1049
+ }
1050
+ }
1051
+ }
1052
+ // copyable模式
1053
+ else if (columns[i]['renderOptions']['renderType'] === 'copyable') {
1054
+ columns[i]['render'] = content => {
1055
+ if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1056
+ return null;
1057
+ }
1058
+ return (
1059
+ <Text copyable={true}>
1060
+ {content}
1061
+ </Text>
1062
+ )
1063
+ }
1064
+ }
1065
+ // dropdown模式
1066
+ else if (columns[i]['renderOptions']['renderType'] === 'dropdown') {
1067
+ columns[i]['render'] = (menuItems, record) => {
1068
+ // 针对空值进行错误处理
1069
+ menuItems = menuItems || []
1070
+ return (
1071
+ <Dropdown
1072
+ overlay={
1073
+ <Menu onClick={(item, key, keyPath, domEvent) => {
1074
+ setTimeout(function () {
1075
+ setProps({
1076
+ nClicksDropdownItem: nClicksDropdownItem + 1,
1077
+ recentlyClickedDropdownItemTitle: item.key,
1078
+ recentlyDropdownItemClickedDataIndex: columns[i].dataIndex,
1079
+ // 忽略组件型字段键值对
1080
+ recentlyDropdownItemClickedRow: omitBy(record, value => value?.$$typeof)
1081
+ })
1082
+ }, 200);
1083
+ }}>
1084
+ {
1085
+ menuItems.map(
1086
+ menuItem => (
1087
+ menuItem ?
1088
+ // 判断isDivider参数是否不为false
1089
+ (
1090
+ menuItem.isDivider ?
1091
+ <Menu.Divider /> :
1092
+ <Menu.Item
1093
+ icon={
1094
+ menuItem.icon && (
1095
+ menuItem.iconRenderer === 'fontawesome' ?
1096
+ (
1097
+ React.createElement(
1098
+ 'i',
1099
+ {
1100
+ className: menuItem.icon
1101
+ }
1102
+ )
1103
+ ) :
1104
+ (
1105
+ <AntdIcon icon={menuItem.icon} />
1106
+ )
1107
+ )
1108
+ }
1109
+ disabled={menuItem.disabled}
1110
+ key={menuItem.title}>
1111
+ <a >{menuItem.title}</a>
1112
+ </Menu.Item>
1113
+ ) :
1114
+ null
1115
+ )
1116
+ )
1117
+ }
1118
+ </Menu>
1119
+ }
1120
+ arrow={columns[i]['renderOptions']?.dropdownProps?.arrow}
1121
+ disabled={columns[i]['renderOptions']?.dropdownProps?.disabled || menuItems.length === 0}
1122
+ overlayClassName={columns[i]['renderOptions']?.dropdownProps?.overlayClassName}
1123
+ overlayStyle={columns[i]['renderOptions']?.dropdownProps?.overlayStyle}
1124
+ placement={columns[i]['renderOptions']?.dropdownProps?.placement}
1125
+ trigger={
1126
+ columns[i]['renderOptions']?.dropdownProps?.trigger ?
1127
+ [columns[i]['renderOptions'].dropdownProps.trigger] : ['hover']
1128
+ }
1129
+ getPopupContainer={containerId ? () => (document.getElementById(containerId) ? document.getElementById(containerId) : document.body) : undefined}>
1130
+ <a className="ant-dropdown-link"
1131
+ onClick={e => e.preventDefault()}>
1132
+ {columns[i]['renderOptions']?.dropdownProps?.title} <DownOutlined />
1133
+ </a>
1134
+ </Dropdown>
1135
+ );
1136
+ }
1137
+ }
1138
+ // dropdown-links模式
1139
+ else if (columns[i]['renderOptions']['renderType'] === 'dropdown-links') {
1140
+ columns[i]['render'] = menuItems => {
1141
+ // 针对空值进行错误处理
1142
+ menuItems = menuItems || []
1143
+ return (
1144
+ <Dropdown
1145
+ overlay={
1146
+ <Menu>
1147
+ {
1148
+ menuItems.map(
1149
+ menuItem => (
1150
+ menuItem ?
1151
+ // 判断isDivider参数是否不为false
1152
+ (
1153
+ menuItem.isDivider ?
1154
+ <Menu.Divider /> :
1155
+ <Menu.Item
1156
+ icon={
1157
+ menuItem.icon && (
1158
+ menuItem.iconRenderer === 'fontawesome' ?
1159
+ (
1160
+ React.createElement(
1161
+ 'i',
1162
+ {
1163
+ className: menuItem.icon
1164
+ }
1165
+ )
1166
+ ) :
1167
+ (
1168
+ <AntdIcon icon={menuItem.icon} />
1169
+ )
1170
+ )
1171
+ }
1172
+ disabled={menuItem.disabled}
1173
+ key={menuItem.title}>
1174
+ <a href={menuItem.href}
1175
+ target={'_blank'}>
1176
+ {menuItem.title}
1177
+ </a>
1178
+ </Menu.Item>
1179
+ ) :
1180
+ null
1181
+ )
1182
+ )
1183
+ }
1184
+ </Menu>
1185
+ }
1186
+ arrow={columns[i]['renderOptions']?.dropdownProps?.arrow}
1187
+ disabled={columns[i]['renderOptions']?.dropdownProps?.disabled || menuItems.length === 0}
1188
+ overlayClassName={columns[i]['renderOptions']?.dropdownProps?.overlayClassName}
1189
+ overlayStyle={columns[i]['renderOptions']?.dropdownProps?.overlayStyle}
1190
+ placement={columns[i]['renderOptions']?.dropdownProps?.placement}
1191
+ trigger={
1192
+ columns[i]['renderOptions']?.dropdownProps?.trigger ?
1193
+ [columns[i]['renderOptions'].dropdownProps.trigger] : ['hover']
1194
+ }
1195
+ getPopupContainer={containerId ? () => (document.getElementById(containerId) ? document.getElementById(containerId) : document.body) : undefined}>
1196
+ <a className="ant-dropdown-link"
1197
+ onClick={e => e.preventDefault()}>
1198
+ {columns[i]['renderOptions']?.dropdownProps?.title} <DownOutlined />
1199
+ </a>
1200
+ </Dropdown>
1201
+ );
1202
+ }
1203
+ }
1204
+ // ellipsis-copyable模式
1205
+ else if (columns[i]['renderOptions']['renderType'] === 'ellipsis-copyable') {
1206
+ columns[i]['ellipsis'] = {
1207
+ showTitle: false
1208
+ }
1209
+ columns[i]['render'] = content => {
1210
+ if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1211
+ return null;
1212
+ }
1213
+ return (
1214
+ <Text copyable={true} ellipsis={{ tooltip: content }}>
1215
+ {content}
1216
+ </Text>
1217
+ )
1218
+ }
1219
+ }
1220
+ // corner-mark模式
1221
+ else if (columns[i]['renderOptions']['renderType'] === 'corner-mark') {
1222
+ columns[i]['render'] = content => {
1223
+ if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1224
+ return null;
1225
+ }
1226
+ return (
1227
+ <div className={content.placement ? 'ant-corner-mark-' + content.placement : 'ant-corner-mark-top-right'}
1228
+ style={{
1229
+ '--ant-corner-mark-color': content.hide ? 'transparent' : (content.color ? content.color : '#1890ff'),
1230
+ '--ant-corner-mark-transform': `translate(${content.offsetX ? content.offsetX : 0}px, ${content.offsetY ? content.offsetY : 0}px)`
1231
+ }}>
1232
+ {content.content}
1233
+ </div>
1234
+ )
1235
+ }
1236
+ }
1237
+ // status-badge模式
1238
+ else if (columns[i]['renderOptions']['renderType'] === 'status-badge') {
1239
+ columns[i]['render'] = content => {
1240
+ if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1241
+ return null;
1242
+ }
1243
+ return (
1244
+ <Badge status={content.status} text={content.text} />
1245
+ )
1246
+ }
1247
+ }
1248
+ // image模式
1249
+ else if (columns[i]['renderOptions']['renderType'] === 'image') {
1250
+ columns[i]['render'] = content => {
1251
+ if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1252
+ return null;
1253
+ }
1254
+ return (
1255
+ <Image src={content.src} height={content.height} preview={content.preview} />
1256
+ )
1257
+ }
1258
+ }
1259
+ // image-avatar模式
1260
+ else if (columns[i]['renderOptions']['renderType'] === 'image-avatar') {
1261
+ columns[i]['render'] = content => {
1262
+ if ((!content || isEmpty(content)) && content !== 0 && content !== '') {
1263
+ return null;
1264
+ }
1265
+ return (
1266
+ <Avatar
1267
+ src={content?.src}
1268
+ size={content?.size}
1269
+ shape={content?.shape}
1270
+ />
1271
+ )
1272
+ }
1273
+ }
1274
+ // checkbox模式
1275
+ else if (columns[i]['renderOptions']['renderType'] === 'checkbox') {
1276
+ columns[i]['render'] = (content, record) => {
1277
+ const currentDataIndex = columns[i]['dataIndex']
1278
+ return (
1279
+ <Checkbox checked={content.checked}
1280
+ disabled={content.disabled}
1281
+ onChange={(e) => {
1282
+ // 修改对应行对应字段item.checked
1283
+ try {
1284
+ data.forEach(function (item, i) {
1285
+ // 命中后,修改值并利用错误抛出来跳出循环
1286
+ if (item.key === record.key) {
1287
+ data[i][currentDataIndex] = {
1288
+ ...record[currentDataIndex],
1289
+ checked: e.target.checked
1290
+ }
1291
+ throw new Error("目标已修改");
1292
+ }
1293
+ });
1294
+ } catch (e) {
1295
+ };
1296
+
1297
+ setTimeout(function () {
1298
+ setProps({
1299
+ data: data,
1300
+ // 忽略组件型字段键值对
1301
+ recentlyCheckedRow: omitBy(record, value => value?.$$typeof),
1302
+ recentlyCheckedLabel: content.label,
1303
+ recentlyCheckedDataIndex: columns[i]['dataIndex'],
1304
+ recentlyCheckedStatus: e.target.checked
1305
+ })
1306
+ }, 200);
1307
+ }}>
1308
+ {content.label}
1309
+ </Checkbox>
1310
+ );
1311
+ }
1312
+ }
1313
+ // switch模式
1314
+ else if (columns[i]['renderOptions']['renderType'] === 'switch') {
1315
+ columns[i]['render'] = (content, record) => {
1316
+ const currentDataIndex = columns[i]['dataIndex']
1317
+ return (
1318
+ <Switch checked={content.checked}
1319
+ disabled={content.disabled}
1320
+ checkedChildren={content.checkedChildren}
1321
+ unCheckedChildren={content.unCheckedChildren}
1322
+ onChange={(checked) => {
1323
+ // 修改对应行对应字段item.checked值
1324
+ try {
1325
+ data.forEach(function (item, i) {
1326
+ // 命中后,修改值并利用错误抛出来跳出循环
1327
+ if (item.key === record.key) {
1328
+ data[i][currentDataIndex] = {
1329
+ ...record[currentDataIndex],
1330
+ checked: checked
1331
+ }
1332
+ throw new Error("目标已修改");
1333
+ }
1334
+ });
1335
+ } catch (e) {
1336
+ };
1337
+
1338
+ setTimeout(function () {
1339
+ setProps({
1340
+ data: data,
1341
+ // 忽略组件型字段键值对
1342
+ recentlySwitchRow: omitBy(record, value => value?.$$typeof),
1343
+ recentlySwitchDataIndex: columns[i]['dataIndex'],
1344
+ recentlySwitchStatus: checked
1345
+ })
1346
+ }, 200);
1347
+ }} />
1348
+ );
1349
+ }
1350
+ }
1351
+ // select模式
1352
+ else if (columns[i]['renderOptions']['renderType'] === 'select') {
1353
+ columns[i]['render'] = (content, record) => {
1354
+ const currentDataIndex = columns[i]['dataIndex']
1355
+ // 针对空值进行错误处理
1356
+ content = content || {}
1357
+ return (
1358
+ <Select
1359
+ className={content.className}
1360
+ style={{
1361
+ width: '100%',
1362
+ textAlign: 'left',
1363
+ ...content.style
1364
+ }}
1365
+ showSearch={isUndefined(content.showSearch) ? true : content.showSearch}
1366
+ options={content.options}
1367
+ listHeight={content.listHeight}
1368
+ mode={content.mode}
1369
+ disabled={content.disabled}
1370
+ size={content.size}
1371
+ bordered={content.bordered}
1372
+ placeholder={content.placeholder}
1373
+ placement={content.placement}
1374
+ defaultValue={content.value}
1375
+ maxTagCount={content.maxTagCount}
1376
+ optionFilterProp={content.optionFilterProp}
1377
+ allowClear={content.allowClear}
1378
+ onChange={(value) => {
1379
+ // 修改对应行对应字段item.value值
1380
+ try {
1381
+ data.forEach(function (item, i) {
1382
+ // 命中后,修改值并利用错误抛出来跳出循环
1383
+ if (item.key === record.key) {
1384
+ data[i][currentDataIndex] = {
1385
+ ...record[currentDataIndex],
1386
+ value: value
1387
+ }
1388
+ // 提前打断循环过程
1389
+ throw new Error("目标已修改");
1390
+ }
1391
+ });
1392
+ } catch (e) {
1393
+ };
1394
+
1395
+ setTimeout(
1396
+ () => {
1397
+ setProps({
1398
+ data: data,
1399
+ // 忽略组件型字段键值对
1400
+ recentlySelectRow: omitBy(record, value => value?.$$typeof),
1401
+ recentlySelectDataIndex: columns[i]['dataIndex'],
1402
+ recentlySelectValue: value
1403
+ })
1404
+ },
1405
+ 200
1406
+ );
1407
+ }} />
1408
+ );
1409
+ }
1410
+ }
1411
+ // button模式
1412
+ else if (columns[i]['renderOptions']['renderType'] === 'button') {
1413
+ // 根据参数配置生成按钮
1414
+ columns[i]['render'] = (content, record) => {
1415
+ // 合并单按钮/多按钮渲染逻辑
1416
+ content = Array.isArray(content) ? content : [content];
1417
+ return (
1418
+ <Space split={columns[i]['renderOptions'].renderButtonSplit && <Divider type={"vertical"} />} wrap={true}>
1419
+ {
1420
+ content.map(
1421
+ (content_, idx) => {
1422
+ // 若当前按钮需要附带气泡确认框
1423
+ if (columns[i]['renderOptions']['renderButtonPopConfirmProps'] || content_['popConfirmProps']) {
1424
+ let popConfirmProps = columns[i]['renderOptions']['renderButtonPopConfirmProps'] || content_['popConfirmProps'];
1425
+ let buttonElement = (
1426
+ <Button
1427
+ size={'small'}
1428
+ type={content_.type}
1429
+ color={content_.color}
1430
+ variant={content_.variant}
1431
+ danger={content_.danger}
1432
+ disabled={content_.disabled}
1433
+ style={content_.style}
1434
+ icon={
1435
+ content_.icon && (
1436
+ content_.iconRenderer === 'fontawesome' ?
1437
+ (
1438
+ React.createElement(
1439
+ 'i',
1440
+ {
1441
+ className: content_.icon
1442
+ }
1443
+ )
1444
+ ) :
1445
+ (
1446
+ <AntdIcon icon={content_.icon} />
1447
+ )
1448
+ )
1449
+ }
1450
+ onClick={(e) => {
1451
+ // 阻止事件冒泡
1452
+ e.stopPropagation();
1453
+ }}>
1454
+ {content_.content}
1455
+ </Button>
1456
+ );
1457
+ return (
1458
+ <Popconfirm
1459
+ key={idx}
1460
+ title={popConfirmProps.title}
1461
+ okText={popConfirmProps.okText}
1462
+ cancelText={popConfirmProps.cancelText}
1463
+ disabled={content_.disabled}
1464
+ getPopupContainer={containerId ? () => (document.getElementById(containerId) ? document.getElementById(containerId) : document.body) : undefined}
1465
+ onConfirm={(e) => {
1466
+ // 阻止事件冒泡
1467
+ e.stopPropagation();
1468
+ setProps({
1469
+ // 忽略组件型字段键值对
1470
+ recentlyButtonClickedRow: omitBy(record, value => value?.$$typeof),
1471
+ nClicksButton: nClicksButton + 1,
1472
+ clickedContent: content_.content,
1473
+ clickedCustom: content_.custom,
1474
+ recentlyButtonClickedDataIndex: columns[i].dataIndex
1475
+ })
1476
+ }}
1477
+ onCancel={(e) => {
1478
+ // 阻止事件冒泡
1479
+ e.stopPropagation();
1480
+ }}>
1481
+ {
1482
+ content_.tooltip ?
1483
+ (
1484
+ <Tooltip title={content_.tooltip?.title} placement={content_.tooltip?.placement}>
1485
+ {buttonElement}
1486
+ </Tooltip>
1487
+ ) :
1488
+ (
1489
+ buttonElement
1490
+ )
1491
+ }
1492
+ </Popconfirm>
1493
+ );
1494
+ }
1495
+ let buttonElement = (
1496
+ <Button
1497
+ key={idx}
1498
+ onClick={(e) => {
1499
+ // 阻止事件冒泡
1500
+ if ( !columns[i]['renderOptions']['likeDccLink'] ) {
1501
+ e.stopPropagation();
1502
+ }
1503
+ setProps({
1504
+ // 忽略组件型字段键值对
1505
+ recentlyButtonClickedRow: omitBy(record, value => value?.$$typeof),
1506
+ nClicksButton: nClicksButton + 1,
1507
+ clickedContent: content_.content,
1508
+ clickedCustom: content_.custom,
1509
+ recentlyButtonClickedDataIndex: columns[i].dataIndex
1510
+ })
1511
+ }}
1512
+ size={'small'}
1513
+ type={content_.type}
1514
+ color={content_.color}
1515
+ variant={content_.variant}
1516
+ danger={content_.danger}
1517
+ disabled={content_.disabled}
1518
+ href={columns[i]['renderOptions']['likeDccLink'] ? undefined : content_.href}
1519
+ target={content_.target}
1520
+ style={content_.style}
1521
+ icon={
1522
+ content_.icon && (
1523
+ content_.iconRenderer === 'fontawesome' ?
1524
+ (
1525
+ React.createElement(
1526
+ 'i',
1527
+ {
1528
+ className: content_.icon
1529
+ }
1530
+ )
1531
+ ) :
1532
+ (
1533
+ <AntdIcon icon={content_.icon} />
1534
+ )
1535
+ )
1536
+ }>
1537
+ {content_.content}
1538
+ </Button>
1539
+ );
1540
+ // 否则仅渲染按钮
1541
+ return (
1542
+ content_.tooltip ?
1543
+ (
1544
+ <Tooltip title={content_.tooltip?.title} placement={content_.tooltip?.placement}>
1545
+ {
1546
+ (
1547
+ columns[i]['renderOptions']['likeDccLink'] ?
1548
+ (
1549
+ <UtilsLink key={idx} href={content_.href}>{buttonElement}</UtilsLink>
1550
+ ) :
1551
+ buttonElement
1552
+ )
1553
+ }
1554
+ </Tooltip>
1555
+ ) :
1556
+ (
1557
+ columns[i]['renderOptions']['likeDccLink'] ?
1558
+ (
1559
+ <UtilsLink key={idx} href={content_.href}>{buttonElement}</UtilsLink>
1560
+ ) :
1561
+ buttonElement
1562
+ )
1563
+ );
1564
+ }
1565
+ )
1566
+ }
1567
+ </Space>
1568
+ );
1569
+ }
1570
+ }
1571
+ // tags模式
1572
+ else if (columns[i]['renderOptions']['renderType'] === 'tags') {
1573
+
1574
+ columns[i]['render'] = tags => {
1575
+ if ((!tags || isEmpty(tags)) && tags !== 0 && tags !== '') {
1576
+ return null;
1577
+ }
1578
+ return (
1579
+ // 兼容单标签/多标签输入
1580
+ <>
1581
+ {(Array.isArray(tags) ? tags : [tags]).map(tag => {
1582
+ const tagElement = (
1583
+ <Tag color={tag.color}>
1584
+ {tag.tag}
1585
+ </Tag>
1586
+ );
1587
+ // 若当前标签需要附带文字提示
1588
+ return tag.tooltip ? (
1589
+ <Tooltip title={tag.tooltip?.title} placement={tag.tooltip?.placement}>
1590
+ {tagElement}
1591
+ </Tooltip>
1592
+ ) : (
1593
+ tagElement
1594
+ );
1595
+ })}
1596
+ </>
1597
+ )
1598
+ }
1599
+ }
1600
+ // custom-format模式
1601
+ else if (columns[i]['renderOptions']['renderType'] === 'custom-format') {
1602
+ // 若customFormatFuncs对应当前字段的属性值存在
1603
+ if (customFormatFuncs[columns[i]['dataIndex']]) {
1604
+ columns[i]['render'] = eval(customFormatFuncs[columns[i]['dataIndex']])
1605
+ }
1606
+ }
1607
+ // mini-line模式
1608
+ else if (columns[i]['renderOptions']['renderType'] === 'mini-line') {
1609
+ columns[i]['render'] = data => {
1610
+ let config = {
1611
+ autoFit: true,
1612
+ padding: 0,
1613
+ data: data,
1614
+ animation: miniChartAnimation,
1615
+ smooth: true
1616
+ };
1617
+ // 检查是否设置了tooltipCustomContent参数
1618
+ if (columns[i]['renderOptions']?.tooltipCustomContent) {
1619
+ config = {
1620
+ ...config,
1621
+ tooltip: {
1622
+ customContent: eval(columns[i]['renderOptions'].tooltipCustomContent)
1623
+ }
1624
+ }
1625
+ }
1626
+ // 检查是否设置了miniChartColor参数
1627
+ if (columns[i]['renderOptions']?.miniChartColor) {
1628
+ config = {
1629
+ ...config,
1630
+ color: columns[i]['renderOptions'].miniChartColor
1631
+ }
1632
+ }
1633
+ return <div style={{ height: miniChartHeight }}><TinyLine {...config} /></div>;
1634
+ }
1635
+ }
1636
+ // mini-bar模式
1637
+ else if (columns[i]['renderOptions']['renderType'] === 'mini-bar') {
1638
+ columns[i]['render'] = data => {
1639
+ let config = {
1640
+ padding: 0,
1641
+ autoFit: true,
1642
+ data: data,
1643
+ animation: miniChartAnimation,
1644
+ };
1645
+ // 检查是否设置了tooltipCustomContent参数
1646
+ if (columns[i]['renderOptions']?.tooltipCustomContent) {
1647
+ config = {
1648
+ ...config,
1649
+ tooltip: {
1650
+ customContent: eval(columns[i]['renderOptions'].tooltipCustomContent)
1651
+ }
1652
+ }
1653
+ }
1654
+ // 检查是否设置了miniChartColor参数
1655
+ if (columns[i]['renderOptions']?.miniChartColor) {
1656
+ config = {
1657
+ ...config,
1658
+ color: columns[i]['renderOptions'].miniChartColor
1659
+ }
1660
+ }
1661
+ return <div style={{ height: miniChartHeight }}><TinyColumn {...config} /></div>;
1662
+ }
1663
+ }
1664
+ // mini-progress模式
1665
+ else if (columns[i]['renderOptions']['renderType'] === 'mini-progress') {
1666
+ columns[i]['render'] = data => {
1667
+ return (
1668
+ <div style={{ height: miniChartHeight, alignItems: 'center', display: 'flex' }}>
1669
+ <Progress percent={(data * 100)}
1670
+ format={
1671
+ (percent) => {
1672
+ if (isUndefined(columns[i]['renderOptions']['progressPercentPrecision'])) {
1673
+ return percent + '%';
1674
+ }
1675
+ return percent.toFixed(columns[i]['renderOptions']['progressPercentPrecision']) + '%';
1676
+ }
1677
+ }
1678
+ strokeColor={
1679
+ data * 100 === 100 ?
1680
+ columns[i]['renderOptions']['progressOneHundredPercentColor'] :
1681
+ columns[i]['renderOptions']['progressColor']
1682
+ }
1683
+ trailColor={'#E8EDF3'}
1684
+ status={data * 100 >= 100 ? undefined : 'normal'}
1685
+ showInfo={Boolean(columns[i]['renderOptions']['progressShowPercent'])}
1686
+ strokeLinecap={columns[i]['renderOptions']['progressStrokeLinecap'] || 'square'}
1687
+ size={
1688
+ columns[i]['renderOptions']['progressSize'] ?
1689
+ ['100%', columns[i]['renderOptions']['progressSize']] :
1690
+ ['100%', 15]
1691
+ }
1692
+ percentPosition={columns[i]['renderOptions']['progressPercentPosition']}
1693
+ />
1694
+ </div>
1695
+ );
1696
+ }
1697
+ }
1698
+ // mini-ring-progress模式
1699
+ else if (columns[i]['renderOptions']['renderType'] === 'mini-ring-progress') {
1700
+ columns[i]['render'] = data => {
1701
+ let config = {
1702
+ autoFit: true,
1703
+ padding: 0,
1704
+ percent: data,
1705
+ animation: miniChartAnimation,
1706
+ color: [
1707
+ data === 1 ?
1708
+ (columns[i]['renderOptions']['progressOneHundredPercentColor'] || '#52c41a') :
1709
+ '#5B8FF9',
1710
+ '#E8EDF3'
1711
+ ],
1712
+ statistic: {
1713
+ content: {
1714
+ style: {
1715
+ fontSize: columns[i]['renderOptions']['ringProgressFontSize']
1716
+ }
1717
+ }
1718
+ }
1719
+ };
1720
+ return <div style={{ height: miniChartHeight }}><RingProgress {...config} /></div>;
1721
+ }
1722
+ }
1723
+ // mini-area模式
1724
+ else if (columns[i]['renderOptions']['renderType'] === 'mini-area') {
1725
+ columns[i]['render'] = data => {
1726
+ let config = {
1727
+ autoFit: true,
1728
+ padding: 0,
1729
+ data: data,
1730
+ animation: miniChartAnimation,
1731
+ smooth: true,
1732
+ };
1733
+ // 检查是否设置了tooltipCustomContent参数
1734
+ if (columns[i]['renderOptions']?.tooltipCustomContent) {
1735
+ config = {
1736
+ ...config,
1737
+ tooltip: {
1738
+ customContent: eval(columns[i]['renderOptions'].tooltipCustomContent)
1739
+ }
1740
+ }
1741
+ }
1742
+ // 检查是否设置了miniChartColor参数
1743
+ if (columns[i]['renderOptions']?.miniChartColor) {
1744
+ config = {
1745
+ ...config,
1746
+ areaStyle: {
1747
+ fill: columns[i]['renderOptions'].miniChartColor,
1748
+ fillOpacity: 0.4
1749
+ },
1750
+ line: {
1751
+ color: columns[i]['renderOptions'].miniChartColor
1752
+ }
1753
+ }
1754
+ }
1755
+ return <div style={{ height: miniChartHeight }}><TinyArea {...config} /></div>;
1756
+ }
1757
+ }
1758
+ }
1759
+ }
1760
+
1761
+ if (titlePopoverInfo) {
1762
+ for (let i = 0; i < columns.length; i++) {
1763
+ if (Object.keys(titlePopoverInfo).includes(columns[i].dataIndex)) {
1764
+
1765
+ if (!columns[i].title_) {
1766
+ columns[i]['title_'] = columns[i]['title']
1767
+ }
1768
+ columns[i]['title'] = () => {
1769
+ return <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
1770
+ {columns[i].title_}
1771
+ <Popover
1772
+ title={titlePopoverInfo[columns[i].dataIndex].title}
1773
+ content={<div style={{
1774
+ maxWidth: '250px',
1775
+ wordWrap: 'break-word',
1776
+ whiteSpace: 'normal',
1777
+ wordBreak: 'break-all'
1778
+ }}>{titlePopoverInfo[columns[i].dataIndex].content}</div>}
1779
+ overlayStyle={titlePopoverInfo[columns[i].dataIndex].overlayStyle}
1780
+ placement={titlePopoverInfo[columns[i].dataIndex].placement || 'bottom'}
1781
+ getPopupContainer={containerId ? () => (document.getElementById(containerId) ? document.getElementById(containerId) : document.body) : undefined}>
1782
+ <QuestionCircleOutlined
1783
+ style={{
1784
+ color: '#8c8c8c',
1785
+ paddingLeft: '4px',
1786
+ cursor: 'pointer'
1787
+ }} />
1788
+ </Popover>
1789
+ </div>
1790
+ }
1791
+ }
1792
+ }
1793
+ }
1794
+
1795
+ // 添加表头单元格监听事件
1796
+ columns = columns.map(
1797
+ (item, idx) => (
1798
+ {
1799
+ ...item,
1800
+ ...{
1801
+ onHeaderCell: (e) => {
1802
+ return {
1803
+ islast: idx - (columns.length - 1),
1804
+ ...(
1805
+ enableHoverListen ?
1806
+ {
1807
+ onMouseEnter: event => { setProps({ recentlyMouseEnterColumnDataIndex: e.dataIndex }) }
1808
+ } :
1809
+ {}
1810
+ ),
1811
+ // 独立控制表头对齐
1812
+ ...(
1813
+ e.headerAlign ?
1814
+ {
1815
+ style: { textAlign: e.headerAlign }
1816
+ } :
1817
+ {}
1818
+ )
1819
+ };
1820
+ }
1821
+ }
1822
+ }
1823
+ )
1824
+ )
1825
+
1826
+ let rowSelection
1827
+ // 处理行选择功能设置
1828
+ if (rowSelectionType) {
1829
+
1830
+ rowSelection = {
1831
+ columnWidth: rowSelectionWidth,
1832
+ checkStrictly: rowSelectionCheckStrictly,
1833
+ fixed: true,
1834
+ type: rowSelectionType,
1835
+ selectedRowKeys: selectedRowKeys,
1836
+ selections: [
1837
+ Table.SELECTION_ALL,
1838
+ Table.SELECTION_INVERT,
1839
+ Table.SELECTION_NONE
1840
+ ],
1841
+ onChange: (selectedRowKeys, selectedRows) => {
1842
+ setProps({
1843
+ selectedRowKeys: (
1844
+ // 排除被忽略选择的行
1845
+ rowSelectionIgnoreRowKeys ?
1846
+ selectedRowKeys.filter(item => !rowSelectionIgnoreRowKeys.includes(item)) :
1847
+ selectedRowKeys
1848
+ ),
1849
+ selectedRows: (
1850
+ // 排除被忽略选择的行
1851
+ rowSelectionIgnoreRowKeys ?
1852
+ selectedRows.map(
1853
+ // 忽略组件型字段键值对
1854
+ record => omitBy(record, value => value?.$$typeof)
1855
+ ).filter(
1856
+ record => !rowSelectionIgnoreRowKeys.includes(record.key)
1857
+ ) :
1858
+ selectedRows.map(
1859
+ record => omitBy(record, value => value?.$$typeof)
1860
+ )
1861
+ )
1862
+ })
1863
+ },
1864
+ renderCell: (checked, record, index, originNode) => {
1865
+ // 排除不可选择的行
1866
+ if (rowSelectionIgnoreRowKeys && rowSelectionIgnoreRowKeys.includes(record.key)) {
1867
+ return null;
1868
+ }
1869
+ return originNode;
1870
+ }
1871
+ }
1872
+ }
1873
+
1874
+ // 处理行可展开内容功能
1875
+ let rowExpandedRowRender
1876
+ if (expandedRowKeyToContent && Array.isArray(expandedRowKeyToContent)) {
1877
+ rowExpandedRowRender = new Map(
1878
+ expandedRowKeyToContent.map(
1879
+ item => [item.key, item.content]
1880
+ )
1881
+ )
1882
+ }
1883
+
1884
+ // 检查当前是否至少有一个字段是可编辑的
1885
+ let atLeastOneColumnEditable = columns.some(item => item.editable)
1886
+
1887
+ // 减少不必要的单元格重绘
1888
+ if (cellUpdateOptimize) {
1889
+ columns = columns.map(
1890
+ item => {
1891
+ return {
1892
+ ...item,
1893
+ // 减少不必要的单元格重绘
1894
+ shouldCellUpdate: (record, prevRecord) => {
1895
+ if (isEqual(record, prevRecord)) {
1896
+ return false;
1897
+ }
1898
+ return true;
1899
+ }
1900
+ };
1901
+ }
1902
+ )
1903
+ }
1904
+
1905
+ // 配置自定义组件
1906
+ const components = {}
1907
+
1908
+ // 若至少有一个字段开启编辑功能
1909
+ if (atLeastOneColumnEditable) {
1910
+ components.body = {
1911
+ row: EditableRow,
1912
+ cell: EditableCell,
1913
+ }
1914
+ }
1915
+
1916
+ // 统一合并处理onCell自定义函数逻辑
1917
+ columns = [...columns].map(
1918
+ item => {
1919
+ return {
1920
+ ...item,
1921
+ ...{
1922
+ onCell: (record, index) => {
1923
+ // 初始化onCell返回值
1924
+ let returnValue = {}
1925
+ // 处理自定义样式特性
1926
+ if (conditionalStyleFuncs && conditionalStyleFuncs[item.dataIndex]) {
1927
+ try {
1928
+ returnValue = {
1929
+ ...returnValue,
1930
+ ...eval(conditionalStyleFuncs[item.dataIndex])(record, index)
1931
+ }
1932
+ } catch (e) {
1933
+ console.error(e)
1934
+ }
1935
+ }
1936
+ // 处理单元格单击、双击、右键事件
1937
+ if (enableCellClickListenColumns && enableCellClickListenColumns.includes(item.dataIndex)) {
1938
+ try {
1939
+ returnValue = {
1940
+ ...returnValue,
1941
+ onClick: e => {
1942
+ setProps({
1943
+ recentlyCellClickColumn: item.dataIndex,
1944
+ recentlyCellClickRecord: omitBy(record, value => value?.$$typeof),
1945
+ nClicksCell: nClicksCell + 1,
1946
+ cellClickEvent: {
1947
+ pageX: e.pageX,
1948
+ pageY: e.pageY,
1949
+ clientX: e.clientX,
1950
+ clientY: e.clientY,
1951
+ screenX: e.screenX,
1952
+ screenY: e.screenY,
1953
+ timestamp: Date.now()
1954
+ }
1955
+ })
1956
+ },
1957
+ onDoubleClick: e => {
1958
+ setProps({
1959
+ recentlyCellDoubleClickColumn: item.dataIndex,
1960
+ recentlyCellDoubleClickRecord: omitBy(record, value => value?.$$typeof),
1961
+ nDoubleClicksCell: nDoubleClicksCell + 1,
1962
+ cellDoubleClickEvent: {
1963
+ pageX: e.pageX,
1964
+ pageY: e.pageY,
1965
+ clientX: e.clientX,
1966
+ clientY: e.clientY,
1967
+ screenX: e.screenX,
1968
+ screenY: e.screenY,
1969
+ timestamp: Date.now()
1970
+ }
1971
+ })
1972
+ },
1973
+ onContextMenu: e => {
1974
+ // 阻止浏览器默认右键菜单
1975
+ e.preventDefault()
1976
+ setProps({
1977
+ recentlyContextMenuClickColumn: item.dataIndex,
1978
+ recentlyContextMenuClickRecord: omitBy(record, value => value?.$$typeof),
1979
+ nContextMenuClicksCell: nContextMenuClicksCell + 1,
1980
+ cellContextMenuClickEvent: {
1981
+ pageX: e.pageX,
1982
+ pageY: e.pageY,
1983
+ clientX: e.clientX,
1984
+ clientY: e.clientY,
1985
+ screenX: e.screenX,
1986
+ screenY: e.screenY,
1987
+ timestamp: Date.now()
1988
+ }
1989
+ })
1990
+ }
1991
+ }
1992
+ } catch (e) {
1993
+ console.error(e)
1994
+ }
1995
+ }
1996
+
1997
+ // 处理可编辑特性
1998
+ if (item.editable) {
1999
+ try {
2000
+ returnValue = {
2001
+ ...returnValue,
2002
+ record,
2003
+ editable: item.editable,
2004
+ dataIndex: item.dataIndex,
2005
+ title: item.title
2006
+ }
2007
+ } catch (e) {
2008
+ console.error(e)
2009
+ }
2010
+ }
2011
+
2012
+ return returnValue;
2013
+ }
2014
+ }
2015
+ };
2016
+ }
2017
+ )
2018
+
2019
+ // 若存在至少一个字段有group参数,则对columns进行重构以支持多层表头
2020
+ let tempColumns = []
2021
+ if (columns.some(e => e.group)) {
2022
+ // 新逻辑
2023
+ for (let column of columns) {
2024
+ if (column.group) {
2025
+ if (isString(column.group)) {
2026
+ insertNewColumnNode(column, [column.group], 0, tempColumns)
2027
+ } else {
2028
+ insertNewColumnNode(column, column.group, 0, tempColumns)
2029
+ }
2030
+ } else {
2031
+ tempColumns.push(column)
2032
+ }
2033
+ }
2034
+ } else {
2035
+ tempColumns = [...columns]
2036
+ }
2037
+
2038
+ return (
2039
+ <ConfigProvider
2040
+ locale={str2Locale.get(locale)}
2041
+ renderEmpty={emptyContent ? () => emptyContent : undefined}
2042
+ >
2043
+ <Table
2044
+ // 提取具有data-*或aria-*通配格式的属性
2045
+ {...pickBy((_, k) => k.startsWith('data-') || k.startsWith('aria-'), others)}
2046
+ id={id}
2047
+ className={className}
2048
+ style={style}
2049
+ key={key}
2050
+ components={components}
2051
+ rowClassName={
2052
+ rowClassName ?
2053
+ (record, index) => {
2054
+ // 初始化rowClassName返回值
2055
+ let _rowClassName = atLeastOneColumnEditable ? 'editable-row' : '';
2056
+ // 若rowClassName参数通过func进行javascript函数定义
2057
+ if (rowClassName.func) {
2058
+ _rowClassName += ` ${eval(rowClassName.func)(record, index)}`
2059
+ } else {
2060
+ _rowClassName += ` ${rowClassName}`
2061
+ }
2062
+ return _rowClassName.trimStart()
2063
+ } :
2064
+ (atLeastOneColumnEditable ? () => 'editable-row' : undefined)
2065
+ }
2066
+ dataSource={
2067
+ // 根据hiddenRowKeys参数情况进行指定行记录的隐藏
2068
+ data.filter(e => !hiddenRowKeys.includes(e.key))
2069
+ }
2070
+ columns={tempColumns}
2071
+ showHeader={showHeader}
2072
+ rowHoverable={rowHoverable}
2073
+ tableLayout={tableLayout}
2074
+ size={size}
2075
+ rowSelection={rowSelection}
2076
+ sticky={computedSticky}
2077
+ pagination={
2078
+ // 确保pagination=false生效
2079
+ pagination &&
2080
+ {
2081
+ ...pagination,
2082
+ ...{
2083
+ showTotal: total => `${pagination.showTotalPrefix} ${total} ${pagination.showTotalSuffix}`
2084
+ },
2085
+ position: (pagination.position && !Array.isArray(pagination.position))
2086
+ ? [pagination.position] : pagination.position
2087
+ }
2088
+ }
2089
+ bordered={bordered}
2090
+ scroll={{ x: maxWidth, y: maxHeight, scrollToFirstRowOnChange: scrollToFirstRowOnChange }}
2091
+ onChange={onPageChange}
2092
+ onRow={
2093
+ enableHoverListen ?
2094
+ (record, index) => {
2095
+ return {
2096
+ onMouseEnter: event => {
2097
+ setProps({
2098
+ recentlyMouseEnterRowKey: record.key,
2099
+ // 忽略组件型字段键值对
2100
+ recentlyMouseEnterRow: omitBy(record, value => value?.$$typeof)
2101
+ })
2102
+ }, // 鼠标移入行
2103
+ };
2104
+ } : undefined
2105
+ }
2106
+ summary={summaryRowContents ? () => (
2107
+ <Table.Summary fixed={summaryRowFixed}>
2108
+ {
2109
+ splitSummaryRowContents(summaryRowContents, columns.length, summaryRowBlankColumns).map(
2110
+ (group, idx) => (
2111
+ <Table.Summary.Row key={idx}>
2112
+ {group.map((item, i) =>
2113
+ (
2114
+ item.empty ?
2115
+ (
2116
+ <Table.Summary.Cell index={i} />
2117
+ ) :
2118
+ (
2119
+ <Table.Summary.Cell index={i} colSpan={item.colSpan} align={item.align}>
2120
+ {item.content}
2121
+ </Table.Summary.Cell>
2122
+ )
2123
+ )
2124
+ )}
2125
+ </Table.Summary.Row>
2126
+ )
2127
+ )
2128
+ }
2129
+ </Table.Summary>
2130
+ ) : undefined}
2131
+ expandable={
2132
+ rowExpandedRowRender ? {
2133
+ defaultExpandedRowKeys: defaultExpandedRowKeys,
2134
+ expandedRowKeys: expandedRowKeys || defaultExpandedRowKeys,
2135
+ expandedRowRender: (record) => rowExpandedRowRender.get(record.key),
2136
+ rowExpandable: (record) => rowExpandedRowRender.has(record.key),
2137
+ columnWidth: expandedRowWidth,
2138
+ expandRowByClick: expandRowByClick,
2139
+ onExpandedRowsChange: (e) => setProps({
2140
+ expandedRowKeys: e
2141
+ })
2142
+ } : {
2143
+ // 兼容行记录自带children的自动展开情况
2144
+ defaultExpandedRowKeys: defaultExpandedRowKeys,
2145
+ expandedRowKeys: expandedRowKeys || defaultExpandedRowKeys,
2146
+ columnWidth: expandedRowWidth,
2147
+ expandRowByClick: expandRowByClick,
2148
+ onExpandedRowsChange: (e) => setProps({
2149
+ expandedRowKeys: e
2150
+ })
2151
+ }
2152
+ }
2153
+ virtual={virtual}
2154
+ title={title && (() => title)}
2155
+ footer={footer && (() => footer)}
2156
+ data-dash-is-loading={useLoading()}
2157
+ loading={loading}
2158
+ getPopupContainer={
2159
+ containerId ?
2160
+ () => (
2161
+ document.getElementById(containerId) ?
2162
+ document.getElementById(containerId) :
2163
+ document.body
2164
+ ) :
2165
+ undefined}
2166
+ showSorterTooltip={showSorterTooltip && showSorterTooltipTarget ? { target: showSorterTooltipTarget } : showSorterTooltip}
2167
+ />
2168
+ </ConfigProvider>
2169
+ );
2170
+ };
2171
+
2172
+ export default React.memo(
2173
+ AntdTable,
2174
+ (prevProps, nextProps) => {
2175
+ // 计算发生变化的参数名
2176
+ const changedProps = Object.keys(nextProps)
2177
+ .filter(key => !isEqual(prevProps[key], nextProps[key]))
2178
+
2179
+ // 特殊处理:
2180
+ // 当recentlySelectValue发生变动且不涉及data变动时时,阻止本次重绘
2181
+ if (changedProps.includes('recentlySelectValue') && !changedProps.includes('data') && !changedProps.includes('currentData')) {
2182
+ return true;
2183
+ }
2184
+
2185
+ // #80
2186
+ // selectedRowsSyncWithData=true时,当data发生更新,在selectedRowKeys有效时,对selectedRows进行同步更新
2187
+ if (
2188
+ nextProps['selectedRowsSyncWithData'] &&
2189
+ nextProps['selectedRowKeys'] &&
2190
+ changedProps.includes('data')
2191
+ ) {
2192
+ // 同步更新selectedRows的值
2193
+ nextProps.setProps({
2194
+ // 忽略组件型字段键值对
2195
+ selectedRows: nextProps['data'].filter(item => nextProps['selectedRowKeys'].includes(item.key))
2196
+ .map(
2197
+ record => omitBy(record, value => value?.$$typeof)
2198
+ )
2199
+ })
2200
+ }
2201
+
2202
+ // // 判断当前轮次变更的prop是否均在preventUpdateProps中
2203
+ // console.log({ changedProps })
2204
+ // console.log(
2205
+ // '免重绘props:',
2206
+ // changedProps.every(propName => preventUpdateProps.includes(propName))
2207
+ // )
2208
+
2209
+ // changedProps中全部变化的prop都在preventUpdateProps中声明时
2210
+ // 阻止本次重绘
2211
+ return changedProps.every(propName => preventUpdateProps.includes(propName));
2212
+ }
2213
+ );
2214
+
2215
+ AntdTable.defaultProps = defaultProps;
2201
2216
  AntdTable.propTypes = propTypes;