vxe-table 4.18.4 → 4.18.6

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