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:
ajaxzheng 2023-12-07 10:08:11 +08:00 committed by GitHub
parent 26fd4df6ab
commit 019911a08c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 137 additions and 95 deletions

View File

@ -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>

View File

@ -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()
})

View File

@ -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
}
}
}
}

View File

@ -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']
},

View File

@ -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'
},

View File

@ -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)

View File

@ -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) {