vxe-table 4.23.8 → 4.23.9

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