forked from opentiny/tiny-vue
fix(grid): [grid] fix grid valid support promise (#1045)
* fix(grid): fix grid valid support promise * fix(grid): [grid] grid validation support Promsie * fix(grid): [grid] grid validation support Promsie * fix(grid): [grid] grid validation support Promsie
This commit is contained in:
parent
26fd4df6ab
commit
019911a08c
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="jsx">
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
Grid as TinyGrid,
|
||||
|
@ -57,7 +57,11 @@ const validRules = {
|
|||
const toolbarButtons = ref([
|
||||
{
|
||||
code: 'save',
|
||||
name: '保存'
|
||||
name: '提交数据'
|
||||
},
|
||||
{
|
||||
code: 'savePromise',
|
||||
name: '保存(Promise)'
|
||||
}
|
||||
])
|
||||
const tableData = ref([
|
||||
|
@ -166,6 +170,19 @@ function toolbarButtonClickEvent({ code }) {
|
|||
})
|
||||
break
|
||||
}
|
||||
case 'savePromise': {
|
||||
basicGridRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
TinyModal.alert('校验成功,触发了then!')
|
||||
})
|
||||
.catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
TinyModal.alert('校验不通过,触发了catch')
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -10,7 +10,10 @@ test('提交前校验', async ({ page }) => {
|
|||
})
|
||||
.getByRole('textbox')
|
||||
.fill('')
|
||||
await page.getByRole('button', { name: '保存' }).click()
|
||||
|
||||
await page.getByRole('button', { name: '提交数据' }).click()
|
||||
await expect(page.getByText('校验不通过', { exact: true })).toBeVisible()
|
||||
|
||||
await page.getByRole('button', { name: '确认' }).click()
|
||||
await page.getByRole('button', { name: '保存(Promise)' }).click()
|
||||
await expect(page.getByText('校验不通过,触发了catch', { exact: true })).toBeVisible()
|
||||
})
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
<script>
|
||||
import { Grid, GridColumn, GridToolbar, Modal as TinyModal } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
|
@ -59,7 +59,11 @@ export default {
|
|||
toolbarButtons: [
|
||||
{
|
||||
code: 'save',
|
||||
name: '保存'
|
||||
name: '提交数据'
|
||||
},
|
||||
{
|
||||
code: 'savePromise',
|
||||
name: '保存(Promise)'
|
||||
}
|
||||
],
|
||||
tableData: [
|
||||
|
@ -169,6 +173,19 @@ export default {
|
|||
})
|
||||
break
|
||||
}
|
||||
case 'savePromise': {
|
||||
this.$refs.basicGrid
|
||||
.validate()
|
||||
.then(() => {
|
||||
TinyModal.alert('校验成功,触发了then!')
|
||||
})
|
||||
.catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
TinyModal.alert('校验不通过,触发了catch')
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,9 +65,9 @@ export default {
|
|||
'name': { 'zh-CN': '开启响应式表格宽高', 'en-US': 'Enable responsive table width and height' },
|
||||
'desc': {
|
||||
'zh-CN':
|
||||
'<p>表格属性设置 autoResize 属性开启响应式表格宽高的同时,将高度<code>heigh</code>设置为<code>auto</code>就可以自动跟随父容器高度。tips:在自动高度场景,请确保表格或其父容器被设置了一个固定的高度。</p>\n',
|
||||
'<p>表格属性设置 autoResize 属性开启响应式表格宽高的同时,将高度<code>height</code>设置为<code>auto</code>就可以自动跟随父容器高度。tips:在自动高度场景,请确保表格或其父容器被设置了一个固定的高度。</p>\n',
|
||||
'en-US':
|
||||
'<p>Table property setting autoResize property enabling responsive table width and height, set height <code>heigh</code> to <code>auto</code> to automatically follow the height of the parent container. </p>\n'
|
||||
'<p>Table property setting autoResize property enabling responsive table width and height, set height <code>height</code> to <code>auto</code> to automatically follow the height of the parent container. </p>\n'
|
||||
},
|
||||
'codeFiles': ['size/auto-height.vue']
|
||||
},
|
||||
|
|
|
@ -65,7 +65,7 @@ export default {
|
|||
'name': { 'zh-CN': '提交前校验', 'en-US': 'Verify Before Submission' },
|
||||
'desc': {
|
||||
'zh-CN':
|
||||
'<p>grid 标签配置 edit-config 对象,并配置 edit-rules 对象来设置校验对象和校验规则,通过按钮点击事件调用 this.$refs.basicGrid.validate()方法来触发表格校验,具体参考下面示例。</p>\n',
|
||||
'<p>grid 标签配置 edit-config 对象,并配置 edit-rules 对象来设置校验对象和校验规则,通过按钮点击事件调用 this.$refs.basicGrid.validate()方法来触发表格校验,具体参考下面示例。注意:如果传递了 callback 回调就不能正常 catch 到 validate 捕获到的错误。</p>\n',
|
||||
'en-US':
|
||||
'The <p>grid tag configures the edit-config object, configures the edit-rules object to set the validation object and validation rule, and invokes the this.$refs.basicGrid.validate() method to trigger table validation through the button click event. For details, see the following example. </p>\n'
|
||||
},
|
||||
|
|
|
@ -26,11 +26,9 @@ import { t } from '@opentiny/vue-locale'
|
|||
import Validator from '@opentiny/vue-renderless/common/validate'
|
||||
import { getFuncText, emitEvent, getCell } from '@opentiny/vue-renderless/grid/utils'
|
||||
import { get, isFunction, isObject, isUndefined, find } from '@opentiny/vue-renderless/grid/static/'
|
||||
import { adjustParams, hasNoEditRules, realValid } from './utils/beginValidate'
|
||||
import { adjustParams, realValid } from './utils/beginValidate'
|
||||
import { extend } from '@opentiny/vue-renderless/common/object'
|
||||
|
||||
const isArr = Array.isArray
|
||||
|
||||
class Rule {
|
||||
constructor(rule) {
|
||||
Object.assign(this, {
|
||||
|
@ -54,15 +52,15 @@ class Rule {
|
|||
const onRejected = (opt, _this) => {
|
||||
const { isAll, validRest, cb, afterFullData, treeConfig } = opt
|
||||
return (params) => {
|
||||
let args = isAll ? validRest : { [params.column.property]: params }
|
||||
const args = isAll ? validRest : { [params.column.property]: params }
|
||||
|
||||
let funcFinish = (args, reject, resolve) => () => {
|
||||
const funcFinish = (args, reject, resolve) => () => {
|
||||
opt.status = false
|
||||
cb && cb(opt.status, args)
|
||||
cb ? resolve() : reject(args)
|
||||
}
|
||||
|
||||
let funcPosAndFinish = (params, finish) => () => {
|
||||
const funcPosAndFinish = (params, finish) => () => {
|
||||
getCell(_this, params).then((activeCell) => {
|
||||
params.cell = activeCell
|
||||
_this.handleValidError(params)
|
||||
|
@ -70,20 +68,24 @@ const onRejected = (opt, _this) => {
|
|||
})
|
||||
}
|
||||
|
||||
let getLocatRow = (params) => {
|
||||
let row = params.row
|
||||
let rowIndex = afterFullData.indexOf(row)
|
||||
const getLocatRow = (params) => {
|
||||
const row = params.row
|
||||
const rowIndex = afterFullData.indexOf(row)
|
||||
return rowIndex > 0 ? afterFullData[rowIndex - 1] : row
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let finish = funcFinish(args, reject, resolve)
|
||||
let posAndFinish = funcPosAndFinish(params, finish)
|
||||
let locatRow = getLocatRow(params)
|
||||
let isAutoPosFalse = _this.validOpts.autoPos === false
|
||||
const finish = funcFinish(args, reject, resolve)
|
||||
const posAndFinish = funcPosAndFinish(params, finish)
|
||||
|
||||
const locatRow = getLocatRow(params)
|
||||
// 是否触发校验时自动定位到当前校验的单元格
|
||||
const isAutoPosFalse = _this.validOpts.autoPos === false
|
||||
|
||||
isAutoPosFalse && finish()
|
||||
// 自动滚动到校验不通过的树表单元格
|
||||
!isAutoPosFalse && treeConfig && _this.scrollToTreeRow(locatRow).then(posAndFinish)
|
||||
// 自动滚动到校验不通过的表格单元格
|
||||
!isAutoPosFalse && !treeConfig && _this.scrollToRow(locatRow, true).then(posAndFinish)
|
||||
})
|
||||
}
|
||||
|
@ -100,10 +102,8 @@ export default {
|
|||
},
|
||||
// 聚焦到校验通过的单元格并弹出校验错误提示
|
||||
handleValidError(params) {
|
||||
let event = { type: 'valid-error', trigger: 'call' }
|
||||
let next = () => this.showValidTooltip(params)
|
||||
|
||||
this.handleActived(params, event).then(next)
|
||||
const event = { type: 'valid-error', trigger: 'call' }
|
||||
this.handleActived(params, event).then(() => this.showValidTooltip(params))
|
||||
},
|
||||
validatePromise(row, column, columnIndex, isAll, validRest) {
|
||||
function onrejected({ _vm, reject, resolve }) {
|
||||
|
@ -135,29 +135,33 @@ export default {
|
|||
* 如果传 row 指定行记录,则只验证传入的行
|
||||
* 如果传 rows 为多行记录,则只验证传入的行
|
||||
* 如果只传 callback 否则默认验证整个表格数据
|
||||
* isAll: 是否全量校验,如果为true会校验所有并返回所有不通过的列
|
||||
* 返回 Promise 对象,或者使用回调方式
|
||||
*/
|
||||
|
||||
beginValidate(rows, cb, isAll) {
|
||||
let { afterFullData, editRules, hasTreeExpand, treeConfig, treeOpts } = this
|
||||
let { status = true, vaildDatas = afterFullData, validRest = {} } = {}
|
||||
let ret = adjustParams(rows, cb, vaildDatas)
|
||||
cb = ret.cb
|
||||
vaildDatas = ret.vaildDatas
|
||||
beginValidate(rows, callback, isAll) {
|
||||
const { afterFullData, editRules, hasTreeExpand, treeConfig, treeOpts } = this as any
|
||||
const { status = true, validRest = {} } = {}
|
||||
// 处理用户传递的参数得到正确的校验数据和cb回调函数
|
||||
const { vaildDatas, cb } = adjustParams(rows, callback, afterFullData)
|
||||
const opt = { isAll, validRest, cb, afterFullData, treeConfig, status }
|
||||
|
||||
// 记录最后一次触发校验的时间
|
||||
this.lastCallTime = Date.now()
|
||||
this.clearValidate()
|
||||
|
||||
// 清空之前的校验状态
|
||||
this.clearValidate()
|
||||
if (!editRules) {
|
||||
hasNoEditRules(cb, opt.status)
|
||||
return Promise.resolve()
|
||||
if (cb) {
|
||||
cb(opt.status)
|
||||
}
|
||||
return Promise.resolve(opt.status)
|
||||
}
|
||||
|
||||
let validParams = { _vm: this, editRules, isAll, validRest, treeConfig, hasTreeExpand, vaildDatas, treeOpts }
|
||||
let rowValids = realValid(validParams)
|
||||
const validParams = { _vm: this, editRules, isAll, validRest, treeConfig, hasTreeExpand, vaildDatas, treeOpts }
|
||||
const rowValids = realValid(validParams)
|
||||
|
||||
let onFulfilled = () => {
|
||||
const onFulfilled = () => {
|
||||
let ruleKeys = Object.keys(validRest)
|
||||
|
||||
if (ruleKeys.length) {
|
||||
|
@ -166,8 +170,7 @@ export default {
|
|||
|
||||
cb && cb(opt.status)
|
||||
}
|
||||
Promise.all(rowValids).then(onFulfilled).catch(onRejected(opt, this))
|
||||
return Promise.resolve()
|
||||
return Promise.all(rowValids).then(onFulfilled).catch(onRejected(opt, this))
|
||||
},
|
||||
hasCellRules(type, row, { property }) {
|
||||
if (!property || !this.editRules) {
|
||||
|
@ -177,7 +180,7 @@ export default {
|
|||
let rules = get(this.editRules, property)
|
||||
let handler = (rule) => type === 'all' || !rule.trigger || type === rule.trigger
|
||||
|
||||
rules = !isArr(rules) && (isObject(rules) || isFunction(rules)) ? [rules] : rules
|
||||
rules = !Array.isArray(rules) && (isObject(rules) || isFunction(rules)) ? [rules] : rules
|
||||
|
||||
return rules && find(rules, handler)
|
||||
},
|
||||
|
@ -195,16 +198,22 @@ export default {
|
|||
* min为Number表示最小长度;
|
||||
* validator为Function(rule, value, callback, {rules, row, column, rowIndex, columnIndex})进行自定义校验;
|
||||
* trigger为blur|change表示触发方式(默认为空就行,除非特殊场景);
|
||||
* @param {'change' | 'all'} type 校验单元格的触发方式
|
||||
* @param { IRow } row 表格的行数据
|
||||
* @param { IColumnConfig } column 表格的行数据
|
||||
* @param { any } defaultValue 需要校验的默认值
|
||||
*/
|
||||
validCellRules(type, row, column, defVal) {
|
||||
let { editRules, rowId } = this
|
||||
let { property } = column
|
||||
let { descriptor = {}, model = {} } = {}
|
||||
validCellRules(type, row, column, defaultValue) {
|
||||
const { editRules, rowId } = this
|
||||
const { property } = column
|
||||
// model:存放校验的数据模型,descriptor: 存放数据模型对应的校验规则
|
||||
const { descriptor = {}, model = {} } = {}
|
||||
|
||||
if (property && editRules) {
|
||||
let rules = get(editRules, property)
|
||||
let cellValue = isUndefined(defVal) ? get(row, property) : defVal
|
||||
if (isArr(rules)) {
|
||||
// 获取本列的校验配置信息
|
||||
const rules = get(editRules, property)
|
||||
const cellValue = isUndefined(defaultValue) ? get(row, property) : defaultValue
|
||||
if (Array.isArray(rules)) {
|
||||
rules.forEach((rule, index) => {
|
||||
model[property + index] = cellValue
|
||||
descriptor[property + index] = rule
|
||||
|
@ -214,37 +223,36 @@ export default {
|
|||
descriptor[property] = rules
|
||||
}
|
||||
}
|
||||
|
||||
// 深度克隆,防止污染 editRules
|
||||
let _descriptor = extend(true, {}, descriptor)
|
||||
let validator = new Validator(_descriptor, t)
|
||||
let executor = (resolve, reject) => {
|
||||
let validArgs = {
|
||||
const _descriptor = extend(true, {}, descriptor)
|
||||
const validator = new Validator(_descriptor, t)
|
||||
const executor = (resolve, reject) => {
|
||||
const validArgs = {
|
||||
firstFields: true,
|
||||
first: true,
|
||||
custom: { row, column }
|
||||
}
|
||||
let onrejected1 = ({ fields }) => {
|
||||
let cellErrors = Object.keys(fields).map((prop) => {
|
||||
let rules = _descriptor[prop]
|
||||
const onRejected = ({ fields }) => {
|
||||
const cellErrors = Object.keys(fields).map((prop) => {
|
||||
const rules = _descriptor[prop]
|
||||
_descriptor[prop] = !rules.message ? Object.assign(rules, { message: fields[prop][0].message }) : rules
|
||||
return new Rule(_descriptor[prop])
|
||||
})
|
||||
|
||||
reject({ rules: cellErrors, rule: cellErrors[0] })
|
||||
}
|
||||
validator.validate(model, validArgs).then(resolve).catch(onrejected1)
|
||||
validator.validate(model, validArgs).then(resolve).catch(onRejected)
|
||||
}
|
||||
let onfulfilled = () => {
|
||||
const onFulfilled = () => {
|
||||
this.validatedMap[`${column.id}-${row[rowId]}`] = false
|
||||
return Promise.resolve()
|
||||
}
|
||||
let onrejected = (errors) => {
|
||||
const onRejected = (errors) => {
|
||||
this.validatedMap[`${column.id}-${row[rowId]}`] = true
|
||||
return Promise.reject(errors)
|
||||
}
|
||||
|
||||
return new Promise(executor).then(onfulfilled).catch(onrejected)
|
||||
return new Promise(executor).then(onFulfilled).catch(onRejected)
|
||||
},
|
||||
_clearValidate() {
|
||||
let src = {
|
||||
|
@ -332,7 +340,7 @@ export default {
|
|||
},
|
||||
// 关闭 validTip
|
||||
clostValidTooltip() {
|
||||
let validTip = this.$refs.validTip
|
||||
const validTip = this.$refs.validTip
|
||||
|
||||
if (validTip) {
|
||||
validTip.setExpectedState(false)
|
||||
|
|
|
@ -36,40 +36,35 @@ export function adjustParams(rows, cb, vaildDatas) {
|
|||
return { cb, vaildDatas }
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
export function columnHandler({ _vm, colVailds, editRules, isAll, row, validRest }) {
|
||||
function handler(column, columnIndex) {
|
||||
export const columnHandler = ({ _vm, colValidPromiseArr, editRules, isAll, row, validRest }) => {
|
||||
return (column, columnIndex) => {
|
||||
// 如果当前列是用户配置的校验列
|
||||
if (has(editRules, column.property)) {
|
||||
const p = new Promise((resolve, reject) => {
|
||||
_vm.validCellRules('all', row, column).then(resolve).catch(({ rule, rules }) => {
|
||||
const rowIndex = _vm.getRowIndex(row)
|
||||
let rest = { rule, rules, rowIndex, row, columnIndex, column, $table: _vm }
|
||||
colValidPromiseArr.push(
|
||||
new Promise((resolve, reject) => {
|
||||
_vm
|
||||
.validCellRules('all', row, column) // 校验某个单元格cell的数据
|
||||
.then(resolve)
|
||||
.catch(({ rule, rules }) => {
|
||||
const rowIndex = _vm.getRowIndex(row)
|
||||
const rest = { rule, rules, rowIndex, row, columnIndex, column, $table: _vm }
|
||||
// 如果是全量校验
|
||||
if (isAll) {
|
||||
if (!validRest[column.property]) {
|
||||
validRest[column.property] = []
|
||||
}
|
||||
|
||||
if (isAll) {
|
||||
if (!validRest[column.property]) {
|
||||
validRest[column.property] = []
|
||||
}
|
||||
validRest[column.property].push(rest)
|
||||
|
||||
validRest[column.property].push(rest)
|
||||
resolve()
|
||||
}
|
||||
|
||||
return resolve()
|
||||
}
|
||||
|
||||
return reject(rest)
|
||||
reject(rest)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
colVailds.push(p)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
export function hasNoEditRules(cb, status) {
|
||||
if (cb) {
|
||||
cb(status)
|
||||
}
|
||||
}
|
||||
|
||||
function validTree({ treeConfig, handleVaild, hasTreeExpand, vaildDatas, treeOpts }) {
|
||||
|
@ -87,15 +82,17 @@ function validTree({ treeConfig, handleVaild, hasTreeExpand, vaildDatas, treeOpt
|
|||
}
|
||||
}
|
||||
|
||||
export function realValid({ _vm, editRules, isAll, validRest, treeConfig, hasTreeExpand, vaildDatas, treeOpts }) {
|
||||
let rowValids = []
|
||||
let columns = _vm.getColumns()
|
||||
export const realValid = ({ _vm, editRules, isAll, validRest, treeConfig, hasTreeExpand, vaildDatas, treeOpts }) => {
|
||||
// 存放每行数据校验的处理结果
|
||||
const rowValids = []
|
||||
const columns = _vm.getColumns()
|
||||
|
||||
let handleVaild = (row) => {
|
||||
let colVailds = []
|
||||
const handleVaild = (row) => {
|
||||
// 存放每列校验的处理结果
|
||||
const colValidPromiseArr = []
|
||||
|
||||
columns.forEach(columnHandler({ _vm, colVailds, editRules, isAll, row, validRest }))
|
||||
rowValids.push(Promise.all(colVailds))
|
||||
columns.forEach(columnHandler({ _vm, colValidPromiseArr, editRules, isAll, row, validRest }))
|
||||
rowValids.push(Promise.all(colValidPromiseArr))
|
||||
}
|
||||
|
||||
if (treeConfig) {
|
||||
|
|
Loading…
Reference in New Issue