vxe-table 3.20.6 → 3.20.8

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 (144) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +106 -106
  3. package/es/index.esm.js +2 -1
  4. package/es/style.css +1 -1
  5. package/es/table/src/methods.js +75 -78
  6. package/es/table/src/table.js +14 -2
  7. package/es/table/src/util.js +6 -0
  8. package/es/table/style.css +18 -48
  9. package/es/ui/index.js +1 -1
  10. package/es/ui/src/dom.js +19 -20
  11. package/es/ui/src/log.js +1 -1
  12. package/es/vxe-table/style.css +18 -48
  13. package/lib/index.common.js +2 -1
  14. package/lib/index.umd.js +1915 -3227
  15. package/lib/index.umd.min.js +1 -1
  16. package/lib/style.css +1 -1
  17. package/lib/table/src/methods.js +12 -10
  18. package/lib/table/src/methods.min.js +1 -1
  19. package/lib/table/src/table.js +13 -1
  20. package/lib/table/src/table.min.js +1 -1
  21. package/lib/table/src/util.js +6 -0
  22. package/lib/table/src/util.min.js +1 -1
  23. package/lib/table/style/style.css +18 -48
  24. package/lib/ui/index.js +1 -1
  25. package/lib/ui/index.min.js +1 -1
  26. package/lib/ui/src/dom.js +21 -20
  27. package/lib/ui/src/dom.min.js +1 -1
  28. package/lib/ui/src/log.js +1 -1
  29. package/lib/ui/src/log.min.js +1 -1
  30. package/lib/v-x-e-table/index.js +2 -1
  31. package/lib/v-x-e-table/index.min.js +1 -1
  32. package/lib/vxe-colgroup/index.js +2 -1
  33. package/lib/vxe-colgroup/index.min.js +1 -1
  34. package/lib/vxe-column/index.js +2 -1
  35. package/lib/vxe-column/index.min.js +1 -1
  36. package/lib/vxe-grid/index.js +2 -1
  37. package/lib/vxe-grid/index.min.js +1 -1
  38. package/lib/vxe-table/index.js +2 -1
  39. package/lib/vxe-table/index.min.js +1 -1
  40. package/lib/vxe-table/style/style.css +18 -48
  41. package/lib/vxe-toolbar/index.js +2 -1
  42. package/lib/vxe-toolbar/index.min.js +1 -1
  43. package/lib/vxe-ui/index.js +2 -1
  44. package/lib/vxe-ui/index.min.js +1 -1
  45. package/lib/vxe-v-x-e-table/index.js +2 -1
  46. package/lib/vxe-v-x-e-table/index.min.js +1 -1
  47. package/package.json +93 -93
  48. package/packages/colgroup/index.ts +22 -22
  49. package/packages/column/index.ts +22 -22
  50. package/packages/components.ts +42 -42
  51. package/packages/grid/index.ts +22 -22
  52. package/packages/grid/src/grid.ts +1935 -1935
  53. package/packages/index.ts +4 -4
  54. package/packages/locale/lang/ar-EG.ts +832 -832
  55. package/packages/locale/lang/de-DE.ts +832 -832
  56. package/packages/locale/lang/en-US.ts +832 -832
  57. package/packages/locale/lang/es-ES.ts +832 -832
  58. package/packages/locale/lang/fr-FR.ts +832 -832
  59. package/packages/locale/lang/hu-HU.ts +832 -832
  60. package/packages/locale/lang/hy-AM.ts +832 -832
  61. package/packages/locale/lang/id-ID.ts +832 -832
  62. package/packages/locale/lang/it-IT.ts +832 -832
  63. package/packages/locale/lang/ja-JP.ts +832 -832
  64. package/packages/locale/lang/ko-KR.ts +832 -832
  65. package/packages/locale/lang/ms-MY.ts +832 -832
  66. package/packages/locale/lang/nb-NO.ts +832 -832
  67. package/packages/locale/lang/pt-BR.ts +832 -832
  68. package/packages/locale/lang/ru-RU.ts +832 -832
  69. package/packages/locale/lang/th-TH.ts +832 -832
  70. package/packages/locale/lang/ug-CN.ts +832 -832
  71. package/packages/locale/lang/uk-UA.ts +832 -832
  72. package/packages/locale/lang/uz-UZ.ts +832 -832
  73. package/packages/locale/lang/vi-VN.ts +832 -832
  74. package/packages/locale/lang/zh-CHT.ts +832 -832
  75. package/packages/locale/lang/zh-CN.ts +832 -832
  76. package/packages/locale/lang/zh-HK.ts +3 -3
  77. package/packages/locale/lang/zh-MO.ts +3 -3
  78. package/packages/locale/lang/zh-TC.ts +3 -3
  79. package/packages/locale/lang/zh-TW.ts +3 -3
  80. package/packages/table/index.ts +49 -49
  81. package/packages/table/module/custom/mixin.ts +369 -369
  82. package/packages/table/module/custom/panel.ts +1461 -1461
  83. package/packages/table/module/edit/mixin.ts +1123 -1123
  84. package/packages/table/module/export/export-panel.ts +658 -658
  85. package/packages/table/module/export/import-panel.ts +319 -319
  86. package/packages/table/module/export/mixin.ts +1693 -1693
  87. package/packages/table/module/export/util.ts +24 -24
  88. package/packages/table/module/filter/mixin.ts +507 -507
  89. package/packages/table/module/filter/panel.ts +329 -329
  90. package/packages/table/module/keyboard/mixin.ts +513 -513
  91. package/packages/table/module/menu/mixin.ts +329 -329
  92. package/packages/table/module/menu/panel.ts +212 -212
  93. package/packages/table/module/validator/mixin.ts +626 -626
  94. package/packages/table/render/index.ts +1456 -1456
  95. package/packages/table/src/body.ts +969 -969
  96. package/packages/table/src/cell.ts +1285 -1285
  97. package/packages/table/src/column.ts +205 -205
  98. package/packages/table/src/columnInfo.ts +228 -228
  99. package/packages/table/src/footer.ts +388 -388
  100. package/packages/table/src/group.ts +51 -51
  101. package/packages/table/src/header.ts +602 -602
  102. package/packages/table/src/methods.ts +12936 -12939
  103. package/packages/table/src/props.ts +311 -311
  104. package/packages/table/src/store.ts +15 -15
  105. package/packages/table/src/table.ts +2513 -2502
  106. package/packages/table/src/util.ts +1018 -1012
  107. package/packages/toolbar/index.ts +18 -18
  108. package/packages/toolbar/src/toolbar.ts +856 -856
  109. package/packages/ui/index.ts +530 -530
  110. package/packages/ui/src/anime.ts +52 -52
  111. package/packages/ui/src/comp.ts +3 -3
  112. package/packages/ui/src/dom.ts +237 -236
  113. package/packages/ui/src/log.ts +8 -8
  114. package/packages/ui/src/utils.ts +56 -56
  115. package/packages/ui/src/vn.ts +65 -65
  116. package/packages/v-x-e-table/index.ts +4 -4
  117. package/styles/all.scss +7 -7
  118. package/styles/base.scss +16 -16
  119. package/styles/components/grid.scss +89 -89
  120. package/styles/components/icon.scss +225 -225
  121. package/styles/components/old-icon.scss +715 -715
  122. package/styles/components/table-module/all.scss +6 -6
  123. package/styles/components/table-module/custom.scss +527 -527
  124. package/styles/components/table-module/export.scss +130 -130
  125. package/styles/components/table-module/filter.scss +130 -130
  126. package/styles/components/table-module/menu.scss +81 -81
  127. package/styles/components/table.scss +2679 -2679
  128. package/styles/components/toolbar.scss +119 -119
  129. package/styles/default.scss +2 -2
  130. package/styles/helpers/baseMixin.scss +95 -95
  131. package/styles/index.scss +3 -3
  132. package/styles/modules.scss +5 -5
  133. package/styles/theme/base.scss +93 -93
  134. package/styles/theme/dark.scss +49 -49
  135. package/styles/theme/light.scss +44 -44
  136. package/styles/variable.scss +43 -43
  137. package/types/all.d.ts +53 -53
  138. package/types/index.d.ts +4 -4
  139. /package/es/{iconfont.1773972972913.ttf → iconfont.1774240303597.ttf} +0 -0
  140. /package/es/{iconfont.1773972972913.woff → iconfont.1774240303597.woff} +0 -0
  141. /package/es/{iconfont.1773972972913.woff2 → iconfont.1774240303597.woff2} +0 -0
  142. /package/lib/{iconfont.1773972972913.ttf → iconfont.1774240303597.ttf} +0 -0
  143. /package/lib/{iconfont.1773972972913.woff → iconfont.1774240303597.woff} +0 -0
  144. /package/lib/{iconfont.1773972972913.woff2 → iconfont.1774240303597.woff2} +0 -0
@@ -1,626 +1,626 @@
1
- import XEUtils from 'xe-utils'
2
- import { VxeUI } from '../../../ui'
3
- import { eqEmptyValue, getFuncText } from '../../../ui/src/utils'
4
- import { scrollToView } from '../../../ui/src/dom'
5
- import { handleFieldOrColumn, getRowid } from '../../src/util'
6
- import { warnLog, errLog } from '../../../ui/src/log'
7
-
8
- import type { VxeTooltipInstance } from 'vxe-pc-ui'
9
- import type { VxeTableDefines, TableInternalData, TableReactData, VxeTableConstructor, VxeTablePrivateMethods } from '../../../../types'
10
-
11
- const { getConfig, validators } = VxeUI
12
-
13
- /**
14
- * 校验规则
15
- */
16
- class Rule {
17
- $options: any
18
- constructor (rule: any) {
19
- Object.assign(this, {
20
- $options: rule,
21
- required: rule.required,
22
- min: rule.min,
23
- max: rule.max,
24
- type: rule.type,
25
- pattern: rule.pattern,
26
- validator: rule.validator,
27
- trigger: rule.trigger,
28
- maxWidth: rule.maxWidth
29
- })
30
- }
31
-
32
- /**
33
- * 获取校验不通过的消息
34
- * 支持国际化翻译
35
- */
36
- get content () {
37
- return getFuncText(this.$options.content || this.$options.message)
38
- }
39
-
40
- get message () {
41
- return this.content
42
- }
43
- }
44
-
45
- // 如果存在 pattern,判断正则
46
- function validREValue (pattern: string | RegExp | undefined, val: string) {
47
- if (pattern && !(XEUtils.isRegExp(pattern) ? pattern : new RegExp(pattern)).test(val)) {
48
- return false
49
- }
50
- return true
51
- }
52
-
53
- // 如果存在 max,判断最大值
54
- function validMaxValue (max: string | number | undefined, num: number) {
55
- if (!XEUtils.eqNull(max) && num > XEUtils.toNumber(max)) {
56
- return false
57
- }
58
- return true
59
- }
60
-
61
- // 如果存在 min,判断最小值
62
- function validMinValue (min: string | number | undefined, num: number) {
63
- if (!XEUtils.eqNull(min) && num < XEUtils.toNumber(min)) {
64
- return false
65
- }
66
- return true
67
- }
68
-
69
- function validRuleValue (rule: VxeTableDefines.ValidatorRule, val: any, required: boolean | undefined) {
70
- const { type, min, max, pattern } = rule
71
- const isArrType = type === 'array'
72
- const isNumType = type === 'number'
73
- const isStrType = type === 'string'
74
- const strVal = `${val}`
75
- if (!validREValue(pattern, strVal)) {
76
- return false
77
- }
78
- if (isArrType) {
79
- if (!XEUtils.isArray(val)) {
80
- return false
81
- }
82
- if (required) {
83
- if (!val.length) {
84
- return false
85
- }
86
- }
87
- if (!validMinValue(min, val.length)) {
88
- return false
89
- }
90
- if (!validMaxValue(max, val.length)) {
91
- return false
92
- }
93
- } else if (isNumType) {
94
- const numVal = Number(val)
95
- if (isNaN(numVal)) {
96
- return false
97
- }
98
- if (!validMinValue(min, numVal)) {
99
- return false
100
- }
101
- if (!validMaxValue(max, numVal)) {
102
- return false
103
- }
104
- } else {
105
- if (isStrType) {
106
- if (!XEUtils.isString(val)) {
107
- return false
108
- }
109
- }
110
- if (required) {
111
- if (!strVal) {
112
- return false
113
- }
114
- }
115
- if (!validMinValue(min, strVal.length)) {
116
- return false
117
- }
118
- if (!validMaxValue(max, strVal.length)) {
119
- return false
120
- }
121
- }
122
- return true
123
- }
124
-
125
- function checkRuleStatus (rule: VxeTableDefines.ValidatorRule, row: any, val: any) {
126
- const { required, field } = rule
127
- const currVal = field ? XEUtils.get(row, field) : val
128
- const isEmptyVal = XEUtils.isArray(currVal) ? !currVal.length : eqEmptyValue(currVal)
129
- if (required) {
130
- if (isEmptyVal) {
131
- return false
132
- }
133
- if (!validRuleValue(rule, currVal, required)) {
134
- return false
135
- }
136
- } else {
137
- if (!isEmptyVal) {
138
- if (!validRuleValue(rule, currVal, required)) {
139
- return false
140
- }
141
- }
142
- }
143
- return true
144
- }
145
-
146
- /**
147
- * 聚焦到校验通过的单元格并弹出校验错误提示
148
- */
149
- function handleValidError ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, params: any) {
150
- return new Promise<void>(resolve => {
151
- const validOpts = $xeTable.computeValidOpts
152
- if (validOpts.autoPos === false) {
153
- $xeTable.dispatchEvent('valid-error', params, null)
154
- resolve()
155
- } else {
156
- $xeTable.handleEdit(params, { type: 'valid-error', trigger: 'call' }).then(() => {
157
- setTimeout(() => {
158
- resolve($xeTable.showValidTooltip(params))
159
- }, 10)
160
- })
161
- }
162
- })
163
- }
164
-
165
- function handleErrMsgMode ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, validErrMaps: any) {
166
- const validOpts = $xeTable.computeValidOpts
167
- if (validOpts.msgMode === 'single') {
168
- const keys = Object.keys(validErrMaps)
169
- const resMaps: Record<string, {
170
- row: any;
171
- column: any;
172
- rule: any;
173
- content: any;
174
- }> = {}
175
- if (keys.length) {
176
- const firstKey = keys[0]
177
- resMaps[firstKey] = validErrMaps[firstKey]
178
- }
179
- return resMaps
180
- }
181
- return validErrMaps
182
- }
183
-
184
- /**
185
- * 对表格数据进行校验
186
- * 如果不指定数据,则默认只校验临时变动的数据,例如新增或修改
187
- * 如果传 true 则校验当前表格数据
188
- * 如果传 row 指定行记录,则只验证传入的行
189
- * 如果传 rows 为多行记录,则只验证传入的行
190
- * 如果只传 callback 否则默认验证整个表格数据
191
- * 返回 Promise 对象,或者使用回调方式
192
- */
193
- function beginValidate ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, rows: any, cols: VxeTableDefines.ColumnInfo[] | null, cb: any, isFull?: boolean) {
194
- const props = $xeTable
195
- const reactData = $xeTable as unknown as TableReactData
196
- const internalData = $xeTable as unknown as TableInternalData
197
-
198
- const validRest: any = {}
199
- const { editRules, treeConfig } = props
200
- const { isRowGroupStatus } = reactData
201
- const { afterFullData, pendingRowMaps, removeRowMaps } = internalData
202
- const treeOpts = $xeTable.computeTreeOpts
203
- const aggregateOpts = $xeTable.computeAggregateOpts
204
- const validOpts = $xeTable.computeValidOpts
205
- let validList
206
- if (rows === true) {
207
- validList = afterFullData
208
- } else if (rows) {
209
- if (XEUtils.isFunction(rows)) {
210
- cb = rows
211
- } else {
212
- validList = XEUtils.isArray(rows) ? rows : [rows]
213
- }
214
- }
215
- if (!validList) {
216
- validList = $xeTable.getInsertRecords().concat($xeTable.getUpdateRecords())
217
- }
218
- const rowValidErrs: any = []
219
- internalData._lastCallTime = Date.now()
220
- internalData.validRuleErr = false // 如果为快速校验,当存在某列校验不通过时将终止执行
221
- $xeTable.clearValidate()
222
- const validErrMaps: Record<string, {
223
- row: any;
224
- column: any;
225
- rule: any;
226
- content: any;
227
- }> = {}
228
- if (editRules) {
229
- const columns = cols && cols.length ? cols : $xeTable.getColumns()
230
- const handleVaild = (row: any) => {
231
- const rowid = getRowid($xeTable, row)
232
- // 是否删除
233
- if (removeRowMaps[rowid]) {
234
- return
235
- }
236
- // 是否标记删除
237
- if (pendingRowMaps[rowid]) {
238
- return
239
- }
240
- if ($xeTable.isAggregateRecord(row)) {
241
- return
242
- }
243
- if (isFull || !internalData.validRuleErr) {
244
- const colVailds: any[] = []
245
- columns.forEach((column: any) => {
246
- const field = XEUtils.isString(column) ? column : column.field
247
- if ((isFull || !internalData.validRuleErr) && XEUtils.has(editRules, field)) {
248
- colVailds.push(
249
- $xeTable.validCellRules('all', row, column)
250
- .catch(({ rule, rules }: any) => {
251
- const rest = {
252
- rule,
253
- rules,
254
- rowIndex: $xeTable.getRowIndex(row),
255
- row,
256
- columnIndex: $xeTable.getColumnIndex(column),
257
- column,
258
- field,
259
- $table: $xeTable
260
- }
261
- if (!validRest[field]) {
262
- validRest[field] = []
263
- }
264
- validErrMaps[`${getRowid($xeTable, row)}:${column.id}`] = {
265
- column,
266
- row,
267
- rule,
268
- content: rule.content
269
- }
270
- validRest[field].push(rest)
271
- if (!isFull) {
272
- internalData.validRuleErr = true
273
- return Promise.reject(rest)
274
- }
275
- })
276
- )
277
- }
278
- })
279
- rowValidErrs.push(Promise.all(colVailds))
280
- }
281
- }
282
- if (isRowGroupStatus) {
283
- XEUtils.eachTree(validList, handleVaild, { children: aggregateOpts.mapChildrenField })
284
- } else if (treeConfig) {
285
- const childrenField = treeOpts.children || treeOpts.childrenField
286
- XEUtils.eachTree(validList, handleVaild, { children: childrenField })
287
- } else {
288
- validList.forEach(handleVaild)
289
- }
290
- return Promise.all(rowValidErrs).then(() => {
291
- const ruleProps = Object.keys(validRest)
292
- reactData.validErrorMaps = handleErrMsgMode($xeTable, validErrMaps)
293
- return $xeTable.$nextTick().then(() => {
294
- if (ruleProps.length) {
295
- return Promise.reject(validRest[ruleProps[0]][0])
296
- }
297
- if (cb) {
298
- cb()
299
- }
300
- })
301
- }).catch(firstErrParams => {
302
- return new Promise<void>((resolve, reject) => {
303
- const finish = () => {
304
- $xeTable.$nextTick(() => {
305
- if (cb) {
306
- cb(validRest)
307
- resolve()
308
- } else {
309
- if (getConfig().validToReject === 'obsolete') {
310
- // 已废弃,校验失败将不会执行catch
311
- reject(validRest)
312
- } else {
313
- resolve(validRest)
314
- }
315
- }
316
- })
317
- }
318
- const posAndFinish = () => {
319
- firstErrParams.cell = $xeTable.getCellElement(firstErrParams.row, firstErrParams.column)
320
- scrollToView(firstErrParams.cell)
321
- handleValidError($xeTable, firstErrParams).then(finish)
322
- }
323
- /**
324
- * 当校验不通过时
325
- * 将表格滚动到可视区
326
- * 由于提示信息至少需要占一行,定位向上偏移一行
327
- */
328
- if (validOpts.autoPos === false) {
329
- finish()
330
- } else {
331
- const row = firstErrParams.row
332
- const column = firstErrParams.column
333
- $xeTable.scrollToRow(row, column).then(posAndFinish)
334
- }
335
- })
336
- })
337
- } else {
338
- reactData.validErrorMaps = {}
339
- }
340
- return $xeTable.$nextTick().then(() => {
341
- if (cb) {
342
- cb()
343
- }
344
- })
345
- }
346
-
347
- export default {
348
- methods: {
349
- /**
350
- * 完整校验,和 validate 的区别就是会给有效数据中的每一行进行校验
351
- */
352
- _fullValidate (rows: any, cb: any) {
353
- const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
354
-
355
- if (XEUtils.isFunction(cb)) {
356
- warnLog('vxe.error.notValidators', ['fullValidate(rows, callback)', 'fullValidate(rows)'])
357
- }
358
- return beginValidate($xeTable, rows, null, cb, true)
359
- },
360
- /**
361
- * 快速校验,如果存在记录不通过的记录,则返回不再继续校验(异步校验除外)
362
- */
363
- _validate (rows: any, cb: any) {
364
- const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
365
-
366
- if (XEUtils.isFunction(cb)) {
367
- warnLog('vxe.error.notValidators', ['validate(rows, callback)', 'validate(rows)'])
368
- }
369
- return beginValidate($xeTable, rows, null, cb)
370
- },
371
- /**
372
- * 完整校验单元格,和 validateField 的区别就是会给有效数据中的每一行进行校验
373
- */
374
- _fullValidateField (rows: any, fieldOrColumn: any) {
375
- const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
376
-
377
- const colList = (XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : (fieldOrColumn ? [fieldOrColumn] : [])).map(column => handleFieldOrColumn(this, column))
378
- if (colList.length) {
379
- return beginValidate($xeTable, rows, colList, null, true)
380
- }
381
- return $xeTable.$nextTick()
382
- },
383
- /**
384
- * 快速校验单元格,如果存在记录不通过的记录,则返回不再继续校验(异步校验除外)
385
- */
386
- _validateField (rows: any, fieldOrColumn: any) {
387
- const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
388
-
389
- const colList = (XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : (fieldOrColumn ? [fieldOrColumn] : [])).map(column => handleFieldOrColumn(this, column))
390
- if (colList.length) {
391
- return beginValidate($xeTable, rows, colList, null)
392
- }
393
- return $xeTable.$nextTick()
394
- },
395
- hasCellRules (type: any, row: any, column: any) {
396
- const { editRules } = this
397
- const { property } = column
398
- if (property && editRules) {
399
- const rules = XEUtils.get(editRules, property)
400
- return rules && XEUtils.find(rules, rule => type === 'all' || !rule.trigger || type === rule.trigger)
401
- }
402
- return false
403
- },
404
- /**
405
- * 校验数据
406
- * 按表格行、列顺序依次校验(同步或异步)
407
- * 校验规则根据索引顺序依次校验,如果是异步则会等待校验完成才会继续校验下一列
408
- * 如果校验失败则,触发回调或者Promise<不通过列的错误消息>
409
- * 如果是传回调方式这返回一个校验不通过列的错误消息
410
- *
411
- * rule 配置:
412
- * required=Boolean 是否必填
413
- * min=Number 最小长度
414
- * max=Number 最大长度
415
- * validator=Function({ cellValue, rule, rules, row, column, rowIndex, columnIndex }) 自定义校验,接收一个 Promise
416
- * trigger=blur|change 触发方式(除非特殊场景,否则默认为空就行)
417
- */
418
- validCellRules (validType: any, row: any, column: any, val: any) {
419
- const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
420
- const props = $xeTable
421
- const internalData = $xeTable as unknown as TableInternalData
422
-
423
- const { editRules } = props
424
- const { field } = column
425
- const errorRules: Rule[] = []
426
- const syncValidList: Promise<any>[] = []
427
- if (field && editRules) {
428
- const rules = XEUtils.get(editRules, field)
429
- if (rules) {
430
- const cellValue = XEUtils.isUndefined(val) ? XEUtils.get(row, field) : val
431
- rules.forEach((rule: any) => {
432
- const { trigger, validator } = rule
433
- if (validType === 'all' || !trigger || validType === trigger) {
434
- if (validator) {
435
- const validParams: any = {
436
- cellValue,
437
- rule,
438
- rules,
439
- row,
440
- rowIndex: $xeTable.getRowIndex(row),
441
- column,
442
- columnIndex: $xeTable.getColumnIndex(column),
443
- field: column.field,
444
- $table: $xeTable
445
- }
446
- let customValid
447
- if (XEUtils.isString(validator)) {
448
- const gvItem = validators.get(validator)
449
- if (gvItem) {
450
- const tcvMethod = gvItem.tableCellValidatorMethod || gvItem.cellValidatorMethod
451
- if (tcvMethod) {
452
- customValid = tcvMethod(validParams)
453
- } else {
454
- errLog('vxe.error.notValidators', [validator])
455
- }
456
- } else {
457
- errLog('vxe.error.notValidators', [validator])
458
- }
459
- } else {
460
- customValid = validator(validParams)
461
- }
462
- if (customValid) {
463
- if (XEUtils.isError(customValid)) {
464
- internalData.validRuleErr = true
465
- errorRules.push(new Rule({ type: 'custom', trigger, content: customValid.message, rule: new Rule(rule) }))
466
- } else if (customValid.catch) {
467
- // 如果为异步校验(注:异步校验是并发无序的)
468
- syncValidList.push(
469
- customValid.catch((e: any) => {
470
- internalData.validRuleErr = true
471
- errorRules.push(new Rule({ type: 'custom', trigger, content: e && e.message ? e.message : (rule.content || rule.message), rule: new Rule(rule) }))
472
- })
473
- )
474
- }
475
- }
476
- } else {
477
- if (!checkRuleStatus(rule, row, cellValue)) {
478
- internalData.validRuleErr = true
479
- errorRules.push(new Rule(rule))
480
- }
481
- }
482
- }
483
- })
484
- }
485
- }
486
- return Promise.all(syncValidList).then(() => {
487
- if (errorRules.length) {
488
- const rest = { rules: errorRules, rule: errorRules[0] }
489
- return Promise.reject(rest)
490
- }
491
- })
492
- },
493
- _clearValidate (rows: any, fieldOrColumn: any) {
494
- const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
495
- const reactData = $xeTable as unknown as TableReactData
496
-
497
- const { validErrorMaps } = reactData
498
- const validTip = $xeTable.$refs.refValidTooltip as VxeTooltipInstance
499
- const validOpts = $xeTable.computeValidOpts
500
- const rowList = XEUtils.isArray(rows) ? rows : (rows ? [rows] : [])
501
- const colList = (XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : (fieldOrColumn ? [fieldOrColumn] : [])).map(column => handleFieldOrColumn($xeTable, column)) as VxeTableDefines.ColumnInfo<any>[]
502
- let validErrMaps: Record<string, {
503
- row: any;
504
- column: any;
505
- rule: any;
506
- content: any;
507
- }> = {}
508
- if (validTip && validTip.reactData.visible) {
509
- validTip.close()
510
- }
511
- // 如果是单个提示模式
512
- if (validOpts.msgMode === 'single') {
513
- reactData.validErrorMaps = {}
514
- return $xeTable.$nextTick()
515
- }
516
- if (rowList.length && colList.length) {
517
- validErrMaps = Object.assign({}, validErrorMaps)
518
- rowList.forEach(row => {
519
- colList.forEach((column) => {
520
- const validKey = `${getRowid($xeTable, row)}:${column.id}`
521
- if (validErrMaps[validKey]) {
522
- delete validErrMaps[validKey]
523
- }
524
- })
525
- })
526
- } else if (rowList.length) {
527
- const rowIdList = rowList.map(row => `${getRowid($xeTable, row)}`)
528
- XEUtils.each(validErrorMaps, (item, key) => {
529
- if (rowIdList.indexOf(key.split(':')[0]) > -1) {
530
- validErrMaps[key] = item
531
- }
532
- })
533
- } else if (colList.length) {
534
- const colidList = colList.map(column => `${column.id}`)
535
- XEUtils.each(validErrorMaps, (item, key) => {
536
- if (colidList.indexOf(key.split(':')[1]) > -1) {
537
- validErrMaps[key] = item
538
- }
539
- })
540
- }
541
- reactData.validErrorMaps = validErrMaps
542
- return $xeTable.$nextTick()
543
- },
544
- /**
545
- * 触发校验
546
- */
547
- triggerValidate (type: any) {
548
- const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
549
- const props = $xeTable
550
- const reactData = $xeTable as unknown as TableReactData
551
-
552
- const { editConfig, editRules } = props
553
- const { editStore } = reactData
554
- const { actived } = editStore
555
- const editOpts = $xeTable.computeEditOpts
556
- const validOpts = $xeTable.computeValidOpts
557
- // 检查清除校验消息
558
- if (editRules && validOpts.msgMode === 'single') {
559
- reactData.validErrorMaps = {}
560
- }
561
-
562
- // 校验单元格
563
- if (editConfig && editRules && actived.row) {
564
- const { row, column, cell } = actived.args
565
- if ($xeTable.hasCellRules(type, row, column)) {
566
- return $xeTable.validCellRules(type, row, column).then(() => {
567
- if (editOpts.mode === 'row') {
568
- $xeTable.clearValidate(row, column)
569
- }
570
- }).catch(({ rule }: any) => {
571
- // 如果校验不通过与触发方式一致,则聚焦提示错误,否则跳过并不作任何处理
572
- if (!rule.trigger || type === rule.trigger) {
573
- const rest = { rule, row, column, cell }
574
- $xeTable.showValidTooltip(rest)
575
- return Promise.reject(rest)
576
- }
577
- return Promise.resolve()
578
- })
579
- }
580
- }
581
- return Promise.resolve()
582
- },
583
- /**
584
- * 弹出校验错误提示
585
- */
586
- showValidTooltip (params: any) {
587
- const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
588
- const props = $xeTable
589
- const reactData = $xeTable as unknown as TableReactData
590
-
591
- const { height } = props
592
- const { tableData, validStore, validErrorMaps } = reactData
593
- const { rule, row, column, cell } = params
594
- const validOpts = $xeTable.computeValidOpts
595
- const validTip = $xeTable.$refs.refValidTooltip as VxeTooltipInstance
596
- const content = rule.content
597
- validStore.visible = true
598
- if (validOpts.msgMode === 'single') {
599
- reactData.validErrorMaps = {
600
- [`${getRowid($xeTable, row)}:${column.id}`]: {
601
- column,
602
- row,
603
- rule,
604
- content
605
- }
606
- }
607
- } else {
608
- reactData.validErrorMaps = Object.assign({}, validErrorMaps, {
609
- [`${getRowid($xeTable, row)}:${column.id}`]: {
610
- column,
611
- row,
612
- rule,
613
- content
614
- }
615
- })
616
- }
617
- $xeTable.dispatchEvent('valid-error', params, null)
618
- if (validTip) {
619
- if (validTip && (validOpts.message === 'tooltip' || (validOpts.message === 'default' && !height && tableData.length < 2))) {
620
- return validTip.open(cell, content)
621
- }
622
- }
623
- return $xeTable.$nextTick()
624
- }
625
- } as any
626
- }
1
+ import XEUtils from 'xe-utils'
2
+ import { VxeUI } from '../../../ui'
3
+ import { eqEmptyValue, getFuncText } from '../../../ui/src/utils'
4
+ import { scrollToView } from '../../../ui/src/dom'
5
+ import { handleFieldOrColumn, getRowid } from '../../src/util'
6
+ import { warnLog, errLog } from '../../../ui/src/log'
7
+
8
+ import type { VxeTooltipInstance } from 'vxe-pc-ui'
9
+ import type { VxeTableDefines, TableInternalData, TableReactData, VxeTableConstructor, VxeTablePrivateMethods } from '../../../../types'
10
+
11
+ const { getConfig, validators } = VxeUI
12
+
13
+ /**
14
+ * 校验规则
15
+ */
16
+ class Rule {
17
+ $options: any
18
+ constructor (rule: any) {
19
+ Object.assign(this, {
20
+ $options: rule,
21
+ required: rule.required,
22
+ min: rule.min,
23
+ max: rule.max,
24
+ type: rule.type,
25
+ pattern: rule.pattern,
26
+ validator: rule.validator,
27
+ trigger: rule.trigger,
28
+ maxWidth: rule.maxWidth
29
+ })
30
+ }
31
+
32
+ /**
33
+ * 获取校验不通过的消息
34
+ * 支持国际化翻译
35
+ */
36
+ get content () {
37
+ return getFuncText(this.$options.content || this.$options.message)
38
+ }
39
+
40
+ get message () {
41
+ return this.content
42
+ }
43
+ }
44
+
45
+ // 如果存在 pattern,判断正则
46
+ function validREValue (pattern: string | RegExp | undefined, val: string) {
47
+ if (pattern && !(XEUtils.isRegExp(pattern) ? pattern : new RegExp(pattern)).test(val)) {
48
+ return false
49
+ }
50
+ return true
51
+ }
52
+
53
+ // 如果存在 max,判断最大值
54
+ function validMaxValue (max: string | number | undefined, num: number) {
55
+ if (!XEUtils.eqNull(max) && num > XEUtils.toNumber(max)) {
56
+ return false
57
+ }
58
+ return true
59
+ }
60
+
61
+ // 如果存在 min,判断最小值
62
+ function validMinValue (min: string | number | undefined, num: number) {
63
+ if (!XEUtils.eqNull(min) && num < XEUtils.toNumber(min)) {
64
+ return false
65
+ }
66
+ return true
67
+ }
68
+
69
+ function validRuleValue (rule: VxeTableDefines.ValidatorRule, val: any, required: boolean | undefined) {
70
+ const { type, min, max, pattern } = rule
71
+ const isArrType = type === 'array'
72
+ const isNumType = type === 'number'
73
+ const isStrType = type === 'string'
74
+ const strVal = `${val}`
75
+ if (!validREValue(pattern, strVal)) {
76
+ return false
77
+ }
78
+ if (isArrType) {
79
+ if (!XEUtils.isArray(val)) {
80
+ return false
81
+ }
82
+ if (required) {
83
+ if (!val.length) {
84
+ return false
85
+ }
86
+ }
87
+ if (!validMinValue(min, val.length)) {
88
+ return false
89
+ }
90
+ if (!validMaxValue(max, val.length)) {
91
+ return false
92
+ }
93
+ } else if (isNumType) {
94
+ const numVal = Number(val)
95
+ if (isNaN(numVal)) {
96
+ return false
97
+ }
98
+ if (!validMinValue(min, numVal)) {
99
+ return false
100
+ }
101
+ if (!validMaxValue(max, numVal)) {
102
+ return false
103
+ }
104
+ } else {
105
+ if (isStrType) {
106
+ if (!XEUtils.isString(val)) {
107
+ return false
108
+ }
109
+ }
110
+ if (required) {
111
+ if (!strVal) {
112
+ return false
113
+ }
114
+ }
115
+ if (!validMinValue(min, strVal.length)) {
116
+ return false
117
+ }
118
+ if (!validMaxValue(max, strVal.length)) {
119
+ return false
120
+ }
121
+ }
122
+ return true
123
+ }
124
+
125
+ function checkRuleStatus (rule: VxeTableDefines.ValidatorRule, row: any, val: any) {
126
+ const { required, field } = rule
127
+ const currVal = field ? XEUtils.get(row, field) : val
128
+ const isEmptyVal = XEUtils.isArray(currVal) ? !currVal.length : eqEmptyValue(currVal)
129
+ if (required) {
130
+ if (isEmptyVal) {
131
+ return false
132
+ }
133
+ if (!validRuleValue(rule, currVal, required)) {
134
+ return false
135
+ }
136
+ } else {
137
+ if (!isEmptyVal) {
138
+ if (!validRuleValue(rule, currVal, required)) {
139
+ return false
140
+ }
141
+ }
142
+ }
143
+ return true
144
+ }
145
+
146
+ /**
147
+ * 聚焦到校验通过的单元格并弹出校验错误提示
148
+ */
149
+ function handleValidError ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, params: any) {
150
+ return new Promise<void>(resolve => {
151
+ const validOpts = $xeTable.computeValidOpts
152
+ if (validOpts.autoPos === false) {
153
+ $xeTable.dispatchEvent('valid-error', params, null)
154
+ resolve()
155
+ } else {
156
+ $xeTable.handleEdit(params, { type: 'valid-error', trigger: 'call' }).then(() => {
157
+ setTimeout(() => {
158
+ resolve($xeTable.showValidTooltip(params))
159
+ }, 10)
160
+ })
161
+ }
162
+ })
163
+ }
164
+
165
+ function handleErrMsgMode ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, validErrMaps: any) {
166
+ const validOpts = $xeTable.computeValidOpts
167
+ if (validOpts.msgMode === 'single') {
168
+ const keys = Object.keys(validErrMaps)
169
+ const resMaps: Record<string, {
170
+ row: any;
171
+ column: any;
172
+ rule: any;
173
+ content: any;
174
+ }> = {}
175
+ if (keys.length) {
176
+ const firstKey = keys[0]
177
+ resMaps[firstKey] = validErrMaps[firstKey]
178
+ }
179
+ return resMaps
180
+ }
181
+ return validErrMaps
182
+ }
183
+
184
+ /**
185
+ * 对表格数据进行校验
186
+ * 如果不指定数据,则默认只校验临时变动的数据,例如新增或修改
187
+ * 如果传 true 则校验当前表格数据
188
+ * 如果传 row 指定行记录,则只验证传入的行
189
+ * 如果传 rows 为多行记录,则只验证传入的行
190
+ * 如果只传 callback 否则默认验证整个表格数据
191
+ * 返回 Promise 对象,或者使用回调方式
192
+ */
193
+ function beginValidate ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, rows: any, cols: VxeTableDefines.ColumnInfo[] | null, cb: any, isFull?: boolean) {
194
+ const props = $xeTable
195
+ const reactData = $xeTable as unknown as TableReactData
196
+ const internalData = $xeTable as unknown as TableInternalData
197
+
198
+ const validRest: any = {}
199
+ const { editRules, treeConfig } = props
200
+ const { isRowGroupStatus } = reactData
201
+ const { afterFullData, pendingRowMaps, removeRowMaps } = internalData
202
+ const treeOpts = $xeTable.computeTreeOpts
203
+ const aggregateOpts = $xeTable.computeAggregateOpts
204
+ const validOpts = $xeTable.computeValidOpts
205
+ let validList
206
+ if (rows === true) {
207
+ validList = afterFullData
208
+ } else if (rows) {
209
+ if (XEUtils.isFunction(rows)) {
210
+ cb = rows
211
+ } else {
212
+ validList = XEUtils.isArray(rows) ? rows : [rows]
213
+ }
214
+ }
215
+ if (!validList) {
216
+ validList = $xeTable.getInsertRecords().concat($xeTable.getUpdateRecords())
217
+ }
218
+ const rowValidErrs: any = []
219
+ internalData._lastCallTime = Date.now()
220
+ internalData.validRuleErr = false // 如果为快速校验,当存在某列校验不通过时将终止执行
221
+ $xeTable.clearValidate()
222
+ const validErrMaps: Record<string, {
223
+ row: any;
224
+ column: any;
225
+ rule: any;
226
+ content: any;
227
+ }> = {}
228
+ if (editRules) {
229
+ const columns = cols && cols.length ? cols : $xeTable.getColumns()
230
+ const handleVaild = (row: any) => {
231
+ const rowid = getRowid($xeTable, row)
232
+ // 是否删除
233
+ if (removeRowMaps[rowid]) {
234
+ return
235
+ }
236
+ // 是否标记删除
237
+ if (pendingRowMaps[rowid]) {
238
+ return
239
+ }
240
+ if ($xeTable.isAggregateRecord(row)) {
241
+ return
242
+ }
243
+ if (isFull || !internalData.validRuleErr) {
244
+ const colVailds: any[] = []
245
+ columns.forEach((column: any) => {
246
+ const field = XEUtils.isString(column) ? column : column.field
247
+ if ((isFull || !internalData.validRuleErr) && XEUtils.has(editRules, field)) {
248
+ colVailds.push(
249
+ $xeTable.validCellRules('all', row, column)
250
+ .catch(({ rule, rules }: any) => {
251
+ const rest = {
252
+ rule,
253
+ rules,
254
+ rowIndex: $xeTable.getRowIndex(row),
255
+ row,
256
+ columnIndex: $xeTable.getColumnIndex(column),
257
+ column,
258
+ field,
259
+ $table: $xeTable
260
+ }
261
+ if (!validRest[field]) {
262
+ validRest[field] = []
263
+ }
264
+ validErrMaps[`${getRowid($xeTable, row)}:${column.id}`] = {
265
+ column,
266
+ row,
267
+ rule,
268
+ content: rule.content
269
+ }
270
+ validRest[field].push(rest)
271
+ if (!isFull) {
272
+ internalData.validRuleErr = true
273
+ return Promise.reject(rest)
274
+ }
275
+ })
276
+ )
277
+ }
278
+ })
279
+ rowValidErrs.push(Promise.all(colVailds))
280
+ }
281
+ }
282
+ if (isRowGroupStatus) {
283
+ XEUtils.eachTree(validList, handleVaild, { children: aggregateOpts.mapChildrenField })
284
+ } else if (treeConfig) {
285
+ const childrenField = treeOpts.children || treeOpts.childrenField
286
+ XEUtils.eachTree(validList, handleVaild, { children: childrenField })
287
+ } else {
288
+ validList.forEach(handleVaild)
289
+ }
290
+ return Promise.all(rowValidErrs).then(() => {
291
+ const ruleProps = Object.keys(validRest)
292
+ reactData.validErrorMaps = handleErrMsgMode($xeTable, validErrMaps)
293
+ return $xeTable.$nextTick().then(() => {
294
+ if (ruleProps.length) {
295
+ return Promise.reject(validRest[ruleProps[0]][0])
296
+ }
297
+ if (cb) {
298
+ cb()
299
+ }
300
+ })
301
+ }).catch(firstErrParams => {
302
+ return new Promise<void>((resolve, reject) => {
303
+ const finish = () => {
304
+ $xeTable.$nextTick(() => {
305
+ if (cb) {
306
+ cb(validRest)
307
+ resolve()
308
+ } else {
309
+ if (getConfig().validToReject === 'obsolete') {
310
+ // 已废弃,校验失败将不会执行catch
311
+ reject(validRest)
312
+ } else {
313
+ resolve(validRest)
314
+ }
315
+ }
316
+ })
317
+ }
318
+ const posAndFinish = () => {
319
+ firstErrParams.cell = $xeTable.getCellElement(firstErrParams.row, firstErrParams.column)
320
+ scrollToView(firstErrParams.cell)
321
+ handleValidError($xeTable, firstErrParams).then(finish)
322
+ }
323
+ /**
324
+ * 当校验不通过时
325
+ * 将表格滚动到可视区
326
+ * 由于提示信息至少需要占一行,定位向上偏移一行
327
+ */
328
+ if (validOpts.autoPos === false) {
329
+ finish()
330
+ } else {
331
+ const row = firstErrParams.row
332
+ const column = firstErrParams.column
333
+ $xeTable.scrollToRow(row, column).then(posAndFinish)
334
+ }
335
+ })
336
+ })
337
+ } else {
338
+ reactData.validErrorMaps = {}
339
+ }
340
+ return $xeTable.$nextTick().then(() => {
341
+ if (cb) {
342
+ cb()
343
+ }
344
+ })
345
+ }
346
+
347
+ export default {
348
+ methods: {
349
+ /**
350
+ * 完整校验,和 validate 的区别就是会给有效数据中的每一行进行校验
351
+ */
352
+ _fullValidate (rows: any, cb: any) {
353
+ const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
354
+
355
+ if (XEUtils.isFunction(cb)) {
356
+ warnLog('vxe.error.notValidators', ['fullValidate(rows, callback)', 'fullValidate(rows)'])
357
+ }
358
+ return beginValidate($xeTable, rows, null, cb, true)
359
+ },
360
+ /**
361
+ * 快速校验,如果存在记录不通过的记录,则返回不再继续校验(异步校验除外)
362
+ */
363
+ _validate (rows: any, cb: any) {
364
+ const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
365
+
366
+ if (XEUtils.isFunction(cb)) {
367
+ warnLog('vxe.error.notValidators', ['validate(rows, callback)', 'validate(rows)'])
368
+ }
369
+ return beginValidate($xeTable, rows, null, cb)
370
+ },
371
+ /**
372
+ * 完整校验单元格,和 validateField 的区别就是会给有效数据中的每一行进行校验
373
+ */
374
+ _fullValidateField (rows: any, fieldOrColumn: any) {
375
+ const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
376
+
377
+ const colList = (XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : (fieldOrColumn ? [fieldOrColumn] : [])).map(column => handleFieldOrColumn(this, column))
378
+ if (colList.length) {
379
+ return beginValidate($xeTable, rows, colList, null, true)
380
+ }
381
+ return $xeTable.$nextTick()
382
+ },
383
+ /**
384
+ * 快速校验单元格,如果存在记录不通过的记录,则返回不再继续校验(异步校验除外)
385
+ */
386
+ _validateField (rows: any, fieldOrColumn: any) {
387
+ const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
388
+
389
+ const colList = (XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : (fieldOrColumn ? [fieldOrColumn] : [])).map(column => handleFieldOrColumn(this, column))
390
+ if (colList.length) {
391
+ return beginValidate($xeTable, rows, colList, null)
392
+ }
393
+ return $xeTable.$nextTick()
394
+ },
395
+ hasCellRules (type: any, row: any, column: any) {
396
+ const { editRules } = this
397
+ const { property } = column
398
+ if (property && editRules) {
399
+ const rules = XEUtils.get(editRules, property)
400
+ return rules && XEUtils.find(rules, rule => type === 'all' || !rule.trigger || type === rule.trigger)
401
+ }
402
+ return false
403
+ },
404
+ /**
405
+ * 校验数据
406
+ * 按表格行、列顺序依次校验(同步或异步)
407
+ * 校验规则根据索引顺序依次校验,如果是异步则会等待校验完成才会继续校验下一列
408
+ * 如果校验失败则,触发回调或者Promise<不通过列的错误消息>
409
+ * 如果是传回调方式这返回一个校验不通过列的错误消息
410
+ *
411
+ * rule 配置:
412
+ * required=Boolean 是否必填
413
+ * min=Number 最小长度
414
+ * max=Number 最大长度
415
+ * validator=Function({ cellValue, rule, rules, row, column, rowIndex, columnIndex }) 自定义校验,接收一个 Promise
416
+ * trigger=blur|change 触发方式(除非特殊场景,否则默认为空就行)
417
+ */
418
+ validCellRules (validType: any, row: any, column: any, val: any) {
419
+ const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
420
+ const props = $xeTable
421
+ const internalData = $xeTable as unknown as TableInternalData
422
+
423
+ const { editRules } = props
424
+ const { field } = column
425
+ const errorRules: Rule[] = []
426
+ const syncValidList: Promise<any>[] = []
427
+ if (field && editRules) {
428
+ const rules = XEUtils.get(editRules, field)
429
+ if (rules) {
430
+ const cellValue = XEUtils.isUndefined(val) ? XEUtils.get(row, field) : val
431
+ rules.forEach((rule: any) => {
432
+ const { trigger, validator } = rule
433
+ if (validType === 'all' || !trigger || validType === trigger) {
434
+ if (validator) {
435
+ const validParams: any = {
436
+ cellValue,
437
+ rule,
438
+ rules,
439
+ row,
440
+ rowIndex: $xeTable.getRowIndex(row),
441
+ column,
442
+ columnIndex: $xeTable.getColumnIndex(column),
443
+ field: column.field,
444
+ $table: $xeTable
445
+ }
446
+ let customValid
447
+ if (XEUtils.isString(validator)) {
448
+ const gvItem = validators.get(validator)
449
+ if (gvItem) {
450
+ const tcvMethod = gvItem.tableCellValidatorMethod || gvItem.cellValidatorMethod
451
+ if (tcvMethod) {
452
+ customValid = tcvMethod(validParams)
453
+ } else {
454
+ errLog('vxe.error.notValidators', [validator])
455
+ }
456
+ } else {
457
+ errLog('vxe.error.notValidators', [validator])
458
+ }
459
+ } else {
460
+ customValid = validator(validParams)
461
+ }
462
+ if (customValid) {
463
+ if (XEUtils.isError(customValid)) {
464
+ internalData.validRuleErr = true
465
+ errorRules.push(new Rule({ type: 'custom', trigger, content: customValid.message, rule: new Rule(rule) }))
466
+ } else if (customValid.catch) {
467
+ // 如果为异步校验(注:异步校验是并发无序的)
468
+ syncValidList.push(
469
+ customValid.catch((e: any) => {
470
+ internalData.validRuleErr = true
471
+ errorRules.push(new Rule({ type: 'custom', trigger, content: e && e.message ? e.message : (rule.content || rule.message), rule: new Rule(rule) }))
472
+ })
473
+ )
474
+ }
475
+ }
476
+ } else {
477
+ if (!checkRuleStatus(rule, row, cellValue)) {
478
+ internalData.validRuleErr = true
479
+ errorRules.push(new Rule(rule))
480
+ }
481
+ }
482
+ }
483
+ })
484
+ }
485
+ }
486
+ return Promise.all(syncValidList).then(() => {
487
+ if (errorRules.length) {
488
+ const rest = { rules: errorRules, rule: errorRules[0] }
489
+ return Promise.reject(rest)
490
+ }
491
+ })
492
+ },
493
+ _clearValidate (rows: any, fieldOrColumn: any) {
494
+ const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
495
+ const reactData = $xeTable as unknown as TableReactData
496
+
497
+ const { validErrorMaps } = reactData
498
+ const validTip = $xeTable.$refs.refValidTooltip as VxeTooltipInstance
499
+ const validOpts = $xeTable.computeValidOpts
500
+ const rowList = XEUtils.isArray(rows) ? rows : (rows ? [rows] : [])
501
+ const colList = (XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : (fieldOrColumn ? [fieldOrColumn] : [])).map(column => handleFieldOrColumn($xeTable, column)) as VxeTableDefines.ColumnInfo<any>[]
502
+ let validErrMaps: Record<string, {
503
+ row: any;
504
+ column: any;
505
+ rule: any;
506
+ content: any;
507
+ }> = {}
508
+ if (validTip && validTip.reactData.visible) {
509
+ validTip.close()
510
+ }
511
+ // 如果是单个提示模式
512
+ if (validOpts.msgMode === 'single') {
513
+ reactData.validErrorMaps = {}
514
+ return $xeTable.$nextTick()
515
+ }
516
+ if (rowList.length && colList.length) {
517
+ validErrMaps = Object.assign({}, validErrorMaps)
518
+ rowList.forEach(row => {
519
+ colList.forEach((column) => {
520
+ const validKey = `${getRowid($xeTable, row)}:${column.id}`
521
+ if (validErrMaps[validKey]) {
522
+ delete validErrMaps[validKey]
523
+ }
524
+ })
525
+ })
526
+ } else if (rowList.length) {
527
+ const rowIdList = rowList.map(row => `${getRowid($xeTable, row)}`)
528
+ XEUtils.each(validErrorMaps, (item, key) => {
529
+ if (rowIdList.indexOf(key.split(':')[0]) > -1) {
530
+ validErrMaps[key] = item
531
+ }
532
+ })
533
+ } else if (colList.length) {
534
+ const colidList = colList.map(column => `${column.id}`)
535
+ XEUtils.each(validErrorMaps, (item, key) => {
536
+ if (colidList.indexOf(key.split(':')[1]) > -1) {
537
+ validErrMaps[key] = item
538
+ }
539
+ })
540
+ }
541
+ reactData.validErrorMaps = validErrMaps
542
+ return $xeTable.$nextTick()
543
+ },
544
+ /**
545
+ * 触发校验
546
+ */
547
+ triggerValidate (type: any) {
548
+ const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
549
+ const props = $xeTable
550
+ const reactData = $xeTable as unknown as TableReactData
551
+
552
+ const { editConfig, editRules } = props
553
+ const { editStore } = reactData
554
+ const { actived } = editStore
555
+ const editOpts = $xeTable.computeEditOpts
556
+ const validOpts = $xeTable.computeValidOpts
557
+ // 检查清除校验消息
558
+ if (editRules && validOpts.msgMode === 'single') {
559
+ reactData.validErrorMaps = {}
560
+ }
561
+
562
+ // 校验单元格
563
+ if (editConfig && editRules && actived.row) {
564
+ const { row, column, cell } = actived.args
565
+ if ($xeTable.hasCellRules(type, row, column)) {
566
+ return $xeTable.validCellRules(type, row, column).then(() => {
567
+ if (editOpts.mode === 'row') {
568
+ $xeTable.clearValidate(row, column)
569
+ }
570
+ }).catch(({ rule }: any) => {
571
+ // 如果校验不通过与触发方式一致,则聚焦提示错误,否则跳过并不作任何处理
572
+ if (!rule.trigger || type === rule.trigger) {
573
+ const rest = { rule, row, column, cell }
574
+ $xeTable.showValidTooltip(rest)
575
+ return Promise.reject(rest)
576
+ }
577
+ return Promise.resolve()
578
+ })
579
+ }
580
+ }
581
+ return Promise.resolve()
582
+ },
583
+ /**
584
+ * 弹出校验错误提示
585
+ */
586
+ showValidTooltip (params: any) {
587
+ const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
588
+ const props = $xeTable
589
+ const reactData = $xeTable as unknown as TableReactData
590
+
591
+ const { height } = props
592
+ const { tableData, validStore, validErrorMaps } = reactData
593
+ const { rule, row, column, cell } = params
594
+ const validOpts = $xeTable.computeValidOpts
595
+ const validTip = $xeTable.$refs.refValidTooltip as VxeTooltipInstance
596
+ const content = rule.content
597
+ validStore.visible = true
598
+ if (validOpts.msgMode === 'single') {
599
+ reactData.validErrorMaps = {
600
+ [`${getRowid($xeTable, row)}:${column.id}`]: {
601
+ column,
602
+ row,
603
+ rule,
604
+ content
605
+ }
606
+ }
607
+ } else {
608
+ reactData.validErrorMaps = Object.assign({}, validErrorMaps, {
609
+ [`${getRowid($xeTable, row)}:${column.id}`]: {
610
+ column,
611
+ row,
612
+ rule,
613
+ content
614
+ }
615
+ })
616
+ }
617
+ $xeTable.dispatchEvent('valid-error', params, null)
618
+ if (validTip) {
619
+ if (validTip && (validOpts.message === 'tooltip' || (validOpts.message === 'default' && !height && tableData.length < 2))) {
620
+ return validTip.open(cell, content)
621
+ }
622
+ }
623
+ return $xeTable.$nextTick()
624
+ }
625
+ } as any
626
+ }