1
0
mirror of synced 2025-12-11 16:48:13 +08:00

优化表格校验

This commit is contained in:
xuliangzhan
2023-07-02 16:26:25 +08:00
parent 1fb3c685b3
commit ff073ef49a
12 changed files with 187 additions and 186 deletions

View File

@@ -4,28 +4,28 @@
<div class="left"> <div class="left">
<a href="/vxe-table/"> <a href="/vxe-table/">
<img src="/vxe-table/logo.png" width="18"> <img src="/vxe-table/logo.png" width="18">
<span class="title">该文档已废弃官网文档地址<a href="https://github.com/x-extends/vxe-table-docs">https://github.com/x-extends/vxe-table-docs</a></span> <span class="title">官网文档地址<a href="https://github.com/x-extends/vxe-table-docs">https://github.com/x-extends/vxe-table-docs</a></span>
</a> </a>
</div> </div>
<div class="right"> <div class="right">
<div class="content"> <div class="content">
<span v-if="appData.usedJSHeapSize && appData.usedJSHeapSize !== '0'" class="performance">Memory used: {{ appData.usedJSHeapSize }} MB.</span> <span v-if="appData.usedJSHeapSize && appData.usedJSHeapSize !== '0'" class="performance">Memory used: {{ appData.usedJSHeapSize }} MB.</span>
<span>{{ $t('app.body.label.translations') }}:</span> <!-- <span>{{ $t('app.body.label.translations') }}:</span> -->
<vxe-select class="locale-switch" size="mini" v-model="$i18n.locale"> <!-- <vxe-select class="locale-switch" size="mini" v-model="$i18n.locale">
<vxe-option value="zh_CN" label="中文"></vxe-option> <vxe-option value="zh_CN" label="中文"></vxe-option>
<vxe-option value="zh_TC" label="繁體中文"></vxe-option> <vxe-option value="zh_TC" label="繁體中文"></vxe-option>
<vxe-option value="en_US" label="English"></vxe-option> <vxe-option value="en_US" label="English"></vxe-option> -->
<!-- <vxe-option value="ja_JP" label="ジャパン"></vxe-option> --> <!-- <vxe-option value="ja_JP" label="ジャパン"></vxe-option> -->
</vxe-select> <!-- </vxe-select> -->
<span>{{ $t('app.body.label.version') }}: </span> <!-- <span>{{ $t('app.body.label.version') }}: </span> -->
<vxe-select class="version-switch" size="mini" v-model="appData.version" @change="vChangeEvent"> <!-- <vxe-select class="version-switch" size="mini" v-model="appData.version" @change="vChangeEvent"> -->
<!-- <vxe-option value="4.5" :label="$t('app.body.other.v4d5')" disabled></vxe-option> --> <!-- <vxe-option value="4.5" :label="$t('app.body.other.v4d5')" disabled></vxe-option> -->
<vxe-option value="4" :label="$t('app.body.other.v4')"></vxe-option> <!-- <vxe-option value="4" :label="$t('app.body.other.v4')"></vxe-option> -->
<!-- <vxe-option value="3.5" :label="$t('app.body.other.v3d5')" disabled></vxe-option> --> <!-- <vxe-option value="3.5" :label="$t('app.body.other.v3d5')" disabled></vxe-option> -->
<vxe-option value="3" :label="$t('app.body.other.v3')"></vxe-option> <!-- <vxe-option value="3" :label="$t('app.body.other.v3')"></vxe-option>
<vxe-option value="2" :label="$t('app.body.other.v2')" class-name="due-to-stop"></vxe-option> <vxe-option value="2" :label="$t('app.body.other.v2')" class-name="due-to-stop"></vxe-option>
<vxe-option value="1" :label="$t('app.body.other.v1')" class-name="end-of-life"></vxe-option> <vxe-option value="1" :label="$t('app.body.other.v1')" class-name="end-of-life"></vxe-option>
</vxe-select> </vxe-select> -->
<router-link class="link donation" :title="$t('app.footer.donationDesc')" :to="{name: 'Donation'}">{{ $t('app.header.label.donation') }}</router-link> <router-link class="link donation" :title="$t('app.footer.donationDesc')" :to="{name: 'Donation'}">{{ $t('app.header.label.donation') }}</router-link>
<template v-if="appData.apiLoading && appData.showPlugin"> <template v-if="appData.apiLoading && appData.showPlugin">
<a v-if="appData.disabledPlugin" class="link support" href="/vxe-table/plugins" target="_blank">💡插件</a> <a v-if="appData.disabledPlugin" class="link support" href="/vxe-table/plugins" target="_blank">💡插件</a>
@@ -36,7 +36,7 @@
</header> </header>
<div class="page-container"> <div class="page-container">
<div class="aside" :class="{visible: appData.showLeft}"> <div class="aside" :class="{visible: appData.showLeft}">
<div class="header"> <!-- <div class="header">
<div class="version-list"> <div class="version-list">
<template v-if="appData.stableVersionList.length"> <template v-if="appData.stableVersionList.length">
<span class="title">{{ $t('app.body.label.stableVersion')}}</span> <span class="title">{{ $t('app.body.label.stableVersion')}}</span>
@@ -48,16 +48,16 @@
</template> </template>
</div> </div>
<vxe-input clearable v-model="appData.filterName" type="search" class="search-input" :placeholder="$t('app.body.search.searchPlaceholder')" @keyup="searchEvent" @clear="searchEvent"></vxe-input> <vxe-input clearable v-model="appData.filterName" type="search" class="search-input" :placeholder="$t('app.body.search.searchPlaceholder')" @keyup="searchEvent" @clear="searchEvent"></vxe-input>
</div> </div> -->
<div class="body"> <div class="body">
<div class="sponsors" v-if="appData.sponsorList.length"> <!-- <div class="sponsors" v-if="appData.sponsorList.length">
<h4 class="title">赞助商</h4> <h4 class="title">赞助商</h4>
<div v-for="(item, index) in appData.sponsorList" :key="index"> <div v-for="(item, index) in appData.sponsorList" :key="index">
<a :href="item.url" :title="item.title" target="_blank"> <a :href="item.url" :title="item.title" target="_blank">
<img :src="item.img" :style="{width: item.width, height: item.height}"> <img :src="item.img" :style="{width: item.width, height: item.height}">
</a> </a>
</div> </div>
</div> </div> -->
<div class="docs"> <div class="docs">
<template v-if="appData.apiList.length"> <template v-if="appData.apiList.length">
<ul class="nav-menu"> <ul class="nav-menu">
@@ -139,44 +139,44 @@ export default defineComponent({
locat: { locat: {
name: 'StartInstall' name: 'StartInstall'
} }
},
{
label: 'app.aside.nav.use',
locat: {
name: 'StartUse'
}
},
{
label: 'app.aside.nav.quick',
locat: {
name: 'StartQuick'
}
},
{
label: 'app.aside.nav.global',
locat: {
name: 'StartGlobal'
}
},
{
label: 'app.aside.nav.icons',
locat: {
name: 'StartIcons'
}
},
{
label: 'app.aside.nav.theme',
locat: {
name: 'StartTheme'
}
},
{
label: 'app.aside.nav.i18n',
demoUrl: 'https://jsrun.pro/SbfKp/edit',
locat: {
name: 'StartI18n'
}
} }
// {
// label: 'app.aside.nav.use',
// locat: {
// name: 'StartUse'
// }
// },
// {
// label: 'app.aside.nav.quick',
// locat: {
// name: 'StartQuick'
// }
// },
// {
// label: 'app.aside.nav.global',
// locat: {
// name: 'StartGlobal'
// }
// },
// {
// label: 'app.aside.nav.icons',
// locat: {
// name: 'StartIcons'
// }
// },
// {
// label: 'app.aside.nav.theme',
// locat: {
// name: 'StartTheme'
// }
// },
// {
// label: 'app.aside.nav.i18n',
// demoUrl: 'https://jsrun.pro/SbfKp/edit',
// locat: {
// name: 'StartI18n'
// }
// }
] ]
}, },
{ {

View File

@@ -30,7 +30,7 @@ import { defineComponent, ref } from 'vue'
export default defineComponent({ export default defineComponent({
setup () { setup () {
const allAlign = ref(null) const allAlign = ref('')
const tableData1 = ref([ const tableData1 = ref([
{ id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc' }, { id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc' },

View File

@@ -32,7 +32,7 @@ export default defineComponent({
const $xetable = inject('$xetable', {} as VxeTableConstructor & VxeTableMethods & VxeTablePrivateMethods) const $xetable = inject('$xetable', {} as VxeTableConstructor & VxeTableMethods & VxeTablePrivateMethods)
const { xID, props: tableProps, reactData: tableReactData, internalData: tableInternalData } = $xetable const { xID, props: tableProps, reactData: tableReactData, internalData: tableInternalData } = $xetable
const { refTableHeader, refTableBody, refValidTooltip } = $xetable.getRefMaps() const { refTableHeader, refTableBody } = $xetable.getRefMaps()
const { computeTooltipOpts, computeColumnOpts } = $xetable.getComputeMaps() const { computeTooltipOpts, computeColumnOpts } = $xetable.getComputeMaps()
const refElem = ref() as Ref<HTMLDivElement> const refElem = ref() as Ref<HTMLDivElement>
@@ -50,7 +50,6 @@ export default defineComponent({
const { fixedType } = props const { fixedType } = props
const { scrollXLoad } = tableReactData const { scrollXLoad } = tableReactData
const { lastScrollLeft } = tableInternalData const { lastScrollLeft } = tableInternalData
const validTip = refValidTooltip.value
const tableHeader = refTableHeader.value const tableHeader = refTableHeader.value
const tableBody = refTableBody.value const tableBody = refTableBody.value
const headerElem = tableHeader ? tableHeader.$el as HTMLDivElement : null const headerElem = tableHeader ? tableHeader.$el as HTMLDivElement : null
@@ -69,9 +68,6 @@ export default defineComponent({
if (scrollXLoad && isX) { if (scrollXLoad && isX) {
$xetable.triggerScrollXEvent(evnt) $xetable.triggerScrollXEvent(evnt)
} }
if (isX && validTip && validTip.reactData.visible) {
validTip.updatePlacement()
}
$xetable.dispatchEvent('scroll', { type: renderType, fixed: fixedType, scrollTop: bodyElem.scrollTop, scrollLeft, isX, isY: false }, evnt) $xetable.dispatchEvent('scroll', { type: renderType, fixed: fixedType, scrollTop: bodyElem.scrollTop, scrollLeft, isX, isY: false }, evnt)
} }

View File

@@ -222,12 +222,20 @@ export default defineComponent({
formMethods.dispatchEvent('collapse', { status, collapse: status, data: props.data }, evnt) formMethods.dispatchEvent('collapse', { status, collapse: status, data: props.data }, evnt)
} }
const clearValidate = (fieldOrItem?: VxeFormItemPropTypes.Field | VxeFormDefines.ItemInfo) => { const clearValidate = (fieldOrItem?: VxeFormItemPropTypes.Field | VxeFormItemPropTypes.Field[] | VxeFormDefines.ItemInfo | VxeFormDefines.ItemInfo[]) => {
if (fieldOrItem) { if (fieldOrItem) {
const item = handleFieldOrItem($xeform, fieldOrItem) let fields: any = fieldOrItem
if (item) { if (!XEUtils.isArray(fieldOrItem)) {
item.showError = false fields = [fieldOrItem]
} }
fields.forEach((field: any) => {
if (field) {
const item = handleFieldOrItem($xeform, field)
if (item) {
item.showError = false
}
}
})
} else { } else {
getItems().forEach((item) => { getItems().forEach((item) => {
item.showError = false item.showError = false
@@ -304,62 +312,81 @@ export default defineComponent({
* validator=Function({ itemValue, rule, rules, data, property }) 自定义校验,接收一个 Promise * validator=Function({ itemValue, rule, rules, data, property }) 自定义校验,接收一个 Promise
* trigger=change 触发方式 * trigger=change 触发方式
*/ */
const validItemRules = (validType: string, property: string, val?: any) => { const validItemRules = (validType: string, fields: string | string[], val?: any): Promise<VxeFormDefines.ValidateErrorMapParams | undefined> => {
const { data, rules: formRules } = props const { data, rules: formRules } = props
const errorRules: Rule[] = [] const errorMaps: VxeFormDefines.ValidateErrorMapParams = {}
const syncVailds: Promise<any>[] = [] if (!XEUtils.isArray(fields)) {
if (property && formRules) { fields = [fields]
const rules = XEUtils.get(formRules, property) }
if (rules) { return Promise.all(
const itemValue = XEUtils.isUndefined(val) ? XEUtils.get(data, property) : val fields.map((property) => {
rules.forEach((rule) => { const errorRules: Rule[] = []
const { type, trigger, required } = rule const syncVailds: Promise<any>[] = []
if (validType === 'all' || !trigger || validType === trigger) { if (property && formRules) {
if (XEUtils.isFunction(rule.validator)) { const rules = XEUtils.get(formRules, property)
const customValid = rule.validator({ if (rules) {
itemValue, const itemValue = XEUtils.isUndefined(val) ? XEUtils.get(data, property) : val
rule, rules.forEach((rule) => {
rules, const { type, trigger, required } = rule
data, if (validType === 'all' || !trigger || validType === trigger) {
field: property, if (XEUtils.isFunction(rule.validator)) {
property, const customValid = rule.validator({
$form: $xeform itemValue,
}) rule,
if (customValid) { rules,
if (XEUtils.isError(customValid)) { data,
errorRules.push(new Rule({ type: 'custom', trigger, content: customValid.message, rule: new Rule(rule) })) field: property,
} else if (customValid.catch) { property,
// 如果为异步校验(注:异步校验是并发无序的) $form: $xeform
syncVailds.push( })
customValid.catch((e: any) => { if (customValid) {
errorRules.push(new Rule({ type: 'custom', trigger, content: e ? e.message : (rule.content || rule.message), rule: new Rule(rule) })) if (XEUtils.isError(customValid)) {
}) errorRules.push(new Rule({ type: 'custom', trigger, content: customValid.message, rule: new Rule(rule) }))
) } else if (customValid.catch) {
// 如果为异步校验(注:异步校验是并发无序的)
syncVailds.push(
customValid.catch((e: any) => {
errorRules.push(new Rule({ type: 'custom', trigger, content: e ? e.message : (rule.content || rule.message), rule: new Rule(rule) }))
})
)
}
}
} else {
const isArrType = type === 'array'
const isArrVal = XEUtils.isArray(itemValue)
let hasEmpty = true
if (isArrType || isArrVal) {
hasEmpty = !isArrVal || !itemValue.length
} else if (XEUtils.isString(itemValue)) {
hasEmpty = eqEmptyValue(itemValue.trim())
} else {
hasEmpty = eqEmptyValue(itemValue)
}
if (required ? (hasEmpty || validErrorRuleValue(rule, itemValue)) : (!hasEmpty && validErrorRuleValue(rule, itemValue))) {
errorRules.push(new Rule(rule))
}
} }
} }
} else { })
const isArrType = type === 'array' }
const isArrVal = XEUtils.isArray(itemValue) }
let hasEmpty = true return Promise.all(syncVailds).then(() => {
if (isArrType || isArrVal) { if (errorRules.length) {
hasEmpty = !isArrVal || !itemValue.length errorMaps[property] = errorRules.map(rule => {
} else if (XEUtils.isString(itemValue)) { return {
hasEmpty = eqEmptyValue(itemValue.trim()) $form: $xeform,
} else { rule,
hasEmpty = eqEmptyValue(itemValue) data,
field: property,
property
} }
if (required ? (hasEmpty || validErrorRuleValue(rule, itemValue)) : (!hasEmpty && validErrorRuleValue(rule, itemValue))) { })
errorRules.push(new Rule(rule))
}
}
} }
}) })
} })
} ).then(() => {
return Promise.all(syncVailds).then(() => { if (!XEUtils.isEmpty(errorMaps)) {
if (errorRules.length) { return Promise.reject(errorMaps)
const rest = { rules: errorRules, rule: errorRules[0] }
return Promise.reject(rest)
} }
}) })
} }
@@ -380,14 +407,14 @@ export default defineComponent({
itemValids.push( itemValids.push(
validItemRules(type || 'all', field).then(() => { validItemRules(type || 'all', field).then(() => {
item.errRule = null item.errRule = null
}).catch(({ rule, rules }) => { }).catch((errorMaps: VxeFormDefines.ValidateErrorMapParams) => {
const rest: any = { rule, rules, data, field, property: field, $form: $xeform } const rest = errorMaps[field]
if (!validRest[field]) { if (!validRest[field]) {
validRest[field] = [] validRest[field] = []
} }
validRest[field].push(rest) validRest[field].push(rest)
validFields.push(field) validFields.push(field)
item.errRule = rule item.errRule = rest[0].rule
return Promise.reject(rest) return Promise.reject(rest)
}) })
) )
@@ -431,9 +458,12 @@ export default defineComponent({
return beginValidate(getItems(), '', callback) return beginValidate(getItems(), '', callback)
} }
const validateField = (fieldOrItem: VxeFormItemPropTypes.Field | VxeFormDefines.ItemInfo, callback: any) => { const validateField = (fieldOrItem: VxeFormItemPropTypes.Field | VxeFormItemPropTypes.Field[] | VxeFormDefines.ItemInfo | VxeFormDefines.ItemInfo[], callback: any) => {
const item = handleFieldOrItem($xeform, fieldOrItem) let fields: any[] = []
return beginValidate(item ? [item] : [], '', callback) if (!XEUtils.isArray(fieldOrItem)) {
fields = [fieldOrItem]
}
return beginValidate(fields.map(field => handleFieldOrItem($xeform, field) as VxeFormDefines.ItemInfo), '', callback)
} }
const submitEvent = (evnt: Event) => { const submitEvent = (evnt: Event) => {
@@ -513,11 +543,12 @@ export default defineComponent({
.then(() => { .then(() => {
clearValidate(field) clearValidate(field)
}) })
.catch(({ rule }) => { .catch((errorMaps: VxeFormDefines.ValidateErrorMapParams) => {
const rest = errorMaps[field]
const item = getItemByField(field) const item = getItemByField(field)
if (item) { if (rest && item) {
item.showError = true item.showError = true
item.errRule = rule item.errRule = rest[0].rule
} }
}) })
} }

View File

@@ -31,7 +31,7 @@ export default defineComponent({
const xesize = inject('xesize', null as ComputedRef<SizeType> | null) const xesize = inject('xesize', null as ComputedRef<SizeType> | null)
const { xID, props: tableProps, context: tableContext, reactData: tableReactData, internalData: tableInternalData } = $xetable const { xID, props: tableProps, context: tableContext, reactData: tableReactData, internalData: tableInternalData } = $xetable
const { refTableHeader, refTableBody, refTableFooter, refTableLeftBody, refTableRightBody, refValidTooltip } = $xetable.getRefMaps() const { refTableHeader, refTableBody, refTableFooter, refTableLeftBody, refTableRightBody } = $xetable.getRefMaps()
const { computeEditOpts, computeMouseOpts, computeSYOpts, computeEmptyOpts, computeKeyboardOpts, computeTooltipOpts, computeRadioOpts, computeExpandOpts, computeTreeOpts, computeCheckboxOpts, computeValidOpts, computeRowOpts, computeColumnOpts } = $xetable.getComputeMaps() const { computeEditOpts, computeMouseOpts, computeSYOpts, computeEmptyOpts, computeKeyboardOpts, computeTooltipOpts, computeRadioOpts, computeExpandOpts, computeTreeOpts, computeCheckboxOpts, computeValidOpts, computeRowOpts, computeColumnOpts } = $xetable.getComputeMaps()
const refElem = ref() as Ref<XEBodyScrollElement> const refElem = ref() as Ref<XEBodyScrollElement>
@@ -123,7 +123,7 @@ export default defineComponent({
* 渲染列 * 渲染列
*/ */
const renderColumn = (seq: number | string, rowid: string, fixedType: any, rowLevel: number, row: any, rowIndex: number, $rowIndex: number, _rowIndex: number, column: any, $columnIndex: number, columns: any, items: any[]) => { const renderColumn = (seq: number | string, rowid: string, fixedType: any, rowLevel: number, row: any, rowIndex: number, $rowIndex: number, _rowIndex: number, column: any, $columnIndex: number, columns: any, items: any[]) => {
const { columnKey, height, showOverflow: allColumnOverflow, cellClassName: allCellClassName, cellStyle, align: allAlign, spanMethod, mouseConfig, editConfig, editRules, tooltipConfig } = tableProps const { columnKey, showOverflow: allColumnOverflow, cellClassName: allCellClassName, cellStyle, align: allAlign, spanMethod, mouseConfig, editConfig, editRules, tooltipConfig } = tableProps
const { tableData, overflowX, scrollYLoad, currentColumn, mergeList, editStore, validStore, isAllOverflow } = tableReactData const { tableData, overflowX, scrollYLoad, currentColumn, mergeList, editStore, validStore, isAllOverflow } = tableReactData
const { afterFullData } = tableInternalData const { afterFullData } = tableInternalData
const validOpts = computeValidOpts.value const validOpts = computeValidOpts.value
@@ -155,7 +155,7 @@ export default defineComponent({
const tdOns: any = {} const tdOns: any = {}
const cellAlign = align || allAlign const cellAlign = align || allAlign
const hasValidError = validStore.row === row && validStore.column === column const hasValidError = validStore.row === row && validStore.column === column
const showValidTip = editRules && validOpts.showMessage && (validOpts.message === 'default' ? (height || tableData.length > 1) : validOpts.message === 'inline') const showValidTip = editRules && validOpts.showMessage
const attrs: any = { colid: column.id } const attrs: any = { colid: column.id }
const params: VxeTableDefines.CellRenderBodyParams = { $table: $xetable, $grid: $xetable.xegrid, seq, rowid, row, rowIndex, $rowIndex, _rowIndex, column, columnIndex, $columnIndex, _columnIndex, fixed: fixedType, type: renderType, isHidden: fixedHiddenColumn, level: rowLevel, visibleData: afterFullData, data: tableData, items } const params: VxeTableDefines.CellRenderBodyParams = { $table: $xetable, $grid: $xetable.xegrid, seq, rowid, row, rowIndex, $rowIndex, _rowIndex, column, columnIndex, $columnIndex, _columnIndex, fixed: fixedType, type: renderType, isHidden: fixedHiddenColumn, level: rowLevel, visibleData: afterFullData, data: tableData, items }
// 虚拟滚动不支持动态高度 // 虚拟滚动不支持动态高度
@@ -508,7 +508,6 @@ export default defineComponent({
const tableFooter = refTableFooter.value const tableFooter = refTableFooter.value
const leftBody = refTableLeftBody.value const leftBody = refTableLeftBody.value
const rightBody = refTableRightBody.value const rightBody = refTableRightBody.value
const validTip = refValidTooltip.value
const scrollBodyElem = refElem.value const scrollBodyElem = refElem.value
const headerElem = tableHeader ? tableHeader.$el as HTMLDivElement : null const headerElem = tableHeader ? tableHeader.$el as HTMLDivElement : null
const footerElem = tableFooter ? tableFooter.$el as HTMLDivElement : null const footerElem = tableFooter ? tableFooter.$el as HTMLDivElement : null
@@ -559,9 +558,6 @@ export default defineComponent({
if (scrollYLoad && isRollY) { if (scrollYLoad && isRollY) {
$xetable.triggerScrollYEvent(evnt) $xetable.triggerScrollYEvent(evnt)
} }
if (isRollX && validTip && validTip.reactData.visible) {
validTip.updatePlacement()
}
$xetable.dispatchEvent('scroll', { $xetable.dispatchEvent('scroll', {
type: renderType, type: renderType,
fixed: fixedType, fixed: fixedType,

View File

@@ -310,7 +310,6 @@ export default defineComponent({
const refElem = ref() as Ref<HTMLDivElement> const refElem = ref() as Ref<HTMLDivElement>
const refTooltip = ref() as Ref<VxeTooltipInstance> const refTooltip = ref() as Ref<VxeTooltipInstance>
const refCommTooltip = ref() as Ref<VxeTooltipInstance> const refCommTooltip = ref() as Ref<VxeTooltipInstance>
const refValidTooltip = ref() as Ref<VxeTooltipInstance>
const refTableFilter = ref() as Ref<ComponentPublicInstance> const refTableFilter = ref() as Ref<ComponentPublicInstance>
const refTableMenu = ref() as Ref<VxeMenuPanelInstance> const refTableMenu = ref() as Ref<VxeMenuPanelInstance>
@@ -396,11 +395,6 @@ export default defineComponent({
} }
}) })
const computeValidTipOpts = computed(() => {
const tooltipOpts = computeTooltipOpts.value
return Object.assign({ isArrow: false }, tooltipOpts)
})
const computeEditOpts = computed(() => { const computeEditOpts = computed(() => {
return Object.assign({}, GlobalConfig.table.editConfig, props.editConfig) as VxeTablePropTypes.EditOpts return Object.assign({}, GlobalConfig.table.editConfig, props.editConfig) as VxeTablePropTypes.EditOpts
}) })
@@ -546,7 +540,6 @@ export default defineComponent({
const refMaps: VxeTablePrivateRef = { const refMaps: VxeTablePrivateRef = {
refElem, refElem,
refTooltip, refTooltip,
refValidTooltip,
refTableFilter, refTableFilter,
refTableMenu, refTableMenu,
refTableHeader, refTableHeader,
@@ -4200,7 +4193,6 @@ export default defineComponent({
const el = refElem.value const el = refElem.value
const editOpts = computeEditOpts.value const editOpts = computeEditOpts.value
const { actived } = editStore const { actived } = editStore
const $validTooltip = refValidTooltip.value
const tableFilter = refTableFilter.value const tableFilter = refTableFilter.value
const tableMenu = refTableMenu.value const tableMenu = refTableMenu.value
if (tableFilter) { if (tableFilter) {
@@ -4220,9 +4212,7 @@ export default defineComponent({
// 如果是激活状态,点击了单元格之外 // 如果是激活状态,点击了单元格之外
const cell = actived.args.cell const cell = actived.args.cell
if ((!cell || !getEventTargetNode(evnt, cell).flag)) { if ((!cell || !getEventTargetNode(evnt, cell).flag)) {
if ($validTooltip && getEventTargetNode(evnt, $validTooltip.$el as HTMLDivElement).flag) { if (!internalData._lastCallTime || internalData._lastCallTime + 50 < Date.now()) {
// 如果是激活状态,且点击了校验提示框
} else if (!internalData._lastCallTime || internalData._lastCallTime + 50 < Date.now()) {
// 如果是激活状态,点击了单元格之外 // 如果是激活状态,点击了单元格之外
if (!getEventTargetNode(evnt, document.body, 'vxe-table--ignore-clear').flag) { if (!getEventTargetNode(evnt, document.body, 'vxe-table--ignore-clear').flag) {
// 如果手动调用了激活单元格,避免触发源被移除后导致重复关闭 // 如果手动调用了激活单元格,避免触发源被移除后导致重复关闭
@@ -6185,7 +6175,7 @@ export default defineComponent({
}) })
const renderVN = () => { const renderVN = () => {
const { loading, stripe, showHeader, height, treeConfig, mouseConfig, showFooter, highlightCell, highlightHoverRow, highlightHoverColumn, editConfig } = props const { loading, stripe, showHeader, treeConfig, mouseConfig, showFooter, highlightCell, highlightHoverRow, highlightHoverColumn, editConfig } = props
const { isGroup, overflowX, overflowY, scrollXLoad, scrollYLoad, scrollbarHeight, tableData, tableColumn, tableGroupColumn, footerTableData, initStore, columnStore, filterStore } = reactData const { isGroup, overflowX, overflowY, scrollXLoad, scrollYLoad, scrollbarHeight, tableData, tableColumn, tableGroupColumn, footerTableData, initStore, columnStore, filterStore } = reactData
const { leftList, rightList } = columnStore const { leftList, rightList } = columnStore
const loadingSlot = slots.loading const loadingSlot = slots.loading
@@ -6196,8 +6186,6 @@ export default defineComponent({
const vSize = computeSize.value const vSize = computeSize.value
const tableBorder = computeTableBorder.value const tableBorder = computeTableBorder.value
const mouseOpts = computeMouseOpts.value const mouseOpts = computeMouseOpts.value
const validOpts = computeValidOpts.value
const validTipOpts = computeValidTipOpts.value
const loadingOpts = computeLoadingOpts.value const loadingOpts = computeLoadingOpts.value
const isMenu = computeIsMenu.value const isMenu = computeIsMenu.value
return h('div', { return h('div', {
@@ -6352,14 +6340,6 @@ export default defineComponent({
isArrow: false, isArrow: false,
enterable: false enterable: false
}) : createCommentVNode(), }) : createCommentVNode(),
/**
* 校验提示
*/
hasUseTooltip && props.editRules && validOpts.showMessage && (validOpts.message === 'default' ? !height : validOpts.message === 'tooltip') ? h(resolveComponent('vxe-tooltip') as ComponentOptions, {
ref: refValidTooltip,
class: 'vxe-table--valid-error',
...(validOpts.message === 'tooltip' || tableData.length === 1 ? validTipOpts : {})
}) : createCommentVNode(),
/** /**
* 工具提示 * 工具提示
*/ */

View File

@@ -44,7 +44,6 @@ const tableValidatorMethodKeys: (keyof TableValidatorMethods)[] = ['fullValidate
const validatorHook: VxeGlobalHooksHandles.HookOptions = { const validatorHook: VxeGlobalHooksHandles.HookOptions = {
setupTable ($xetable) { setupTable ($xetable) {
const { props, reactData, internalData } = $xetable const { props, reactData, internalData } = $xetable
const { refValidTooltip } = $xetable.getRefMaps()
const { computeValidOpts, computeTreeOpts, computeEditOpts } = $xetable.getComputeMaps() const { computeValidOpts, computeTreeOpts, computeEditOpts } = $xetable.getComputeMaps()
let validatorMethods = {} as TableValidatorMethods let validatorMethods = {} as TableValidatorMethods
@@ -221,7 +220,6 @@ const validatorHook: VxeGlobalHooksHandles.HookOptions = {
}, },
clearValidate () { clearValidate () {
const { validStore } = reactData const { validStore } = reactData
const validTip = refValidTooltip.value
Object.assign(validStore, { Object.assign(validStore, {
visible: false, visible: false,
row: null, row: null,
@@ -229,9 +227,6 @@ const validatorHook: VxeGlobalHooksHandles.HookOptions = {
content: '', content: '',
rule: null rule: null
}) })
if (validTip && validTip.reactData.visible) {
validTip.close()
}
return nextTick() return nextTick()
} }
} }
@@ -276,13 +271,13 @@ const validatorHook: VxeGlobalHooksHandles.HookOptions = {
*/ */
validCellRules (validType, row, column, val) { validCellRules (validType, row, column, val) {
const { editRules } = props const { editRules } = props
const { property } = column const { field } = column
const errorRules: Rule[] = [] const errorRules: Rule[] = []
const syncVailds: Promise<any>[] = [] const syncVailds: Promise<any>[] = []
if (property && editRules) { if (field && editRules) {
const rules = XEUtils.get(editRules, property) const rules = XEUtils.get(editRules, field)
if (rules) { if (rules) {
const cellValue = XEUtils.isUndefined(val) ? XEUtils.get(row, property) : val const cellValue = XEUtils.isUndefined(val) ? XEUtils.get(row, field) : val
rules.forEach((rule) => { rules.forEach((rule) => {
const { type, trigger, required } = rule const { type, trigger, required } = rule
if (validType === 'all' || !trigger || validType === trigger) { if (validType === 'all' || !trigger || validType === trigger) {
@@ -382,11 +377,8 @@ const validatorHook: VxeGlobalHooksHandles.HookOptions = {
* 弹出校验错误提示 * 弹出校验错误提示
*/ */
showValidTooltip (params) { showValidTooltip (params) {
const { height } = props const { validStore } = reactData
const { tableData, validStore } = reactData const { rule, row, column } = params
const validOpts = computeValidOpts.value
const { rule, row, column, cell } = params
const validTip = refValidTooltip.value
const content = rule.content const content = rule.content
return nextTick().then(() => { return nextTick().then(() => {
Object.assign(validStore, { Object.assign(validStore, {
@@ -397,9 +389,6 @@ const validatorHook: VxeGlobalHooksHandles.HookOptions = {
visible: true visible: true
}) })
$xetable.dispatchEvent('valid-error', params, null) $xetable.dispatchEvent('valid-error', params, null)
if (validTip && (validOpts.message === 'tooltip' || (validOpts.message === 'default' && !height && tableData.length < 2))) {
return validTip.open(cell, content)
}
}) })
} }
} }

View File

@@ -135,8 +135,6 @@
/*validate*/ /*validate*/
--vxe-table-validate-error-color: #{$vxe-table-validate-error-color}; --vxe-table-validate-error-color: #{$vxe-table-validate-error-color};
--vxe-table-validate-tooltip-error-color: #{$vxe-table-validate-tooltip-error-color};
--vxe-table-validate-tooltip-error-background-color: #{$vxe-table-validate-tooltip-error-background-color};
/*grid*/ /*grid*/
--vxe-grid-maximize-background-color: #{$vxe-grid-maximize-background-color}; --vxe-grid-maximize-background-color: #{$vxe-grid-maximize-background-color};

View File

@@ -1177,8 +1177,9 @@
.vxe-cell--valid { .vxe-cell--valid {
width: 320px; width: 320px;
position: absolute; position: absolute;
bottom: calc(100% + 4px);
left: 50%; left: 50%;
font-size: 12px;
line-height: 1.2em;
transform: translateX(-50%); transform: translateX(-50%);
text-align: center; text-align: center;
pointer-events: none; pointer-events: none;
@@ -1186,9 +1187,7 @@
.vxe-cell--valid-msg { .vxe-cell--valid-msg {
display: inline-block; display: inline-block;
border-radius: var(--vxe-border-radius); border-radius: var(--vxe-border-radius);
padding: 8px 12px; color: var(--vxe-table-validate-error-color);
color: var(--vxe-table-validate-tooltip-error-color);
background-color: var(--vxe-table-validate-tooltip-error-background-color);
pointer-events: auto; pointer-events: auto;
} }
} }
@@ -1206,10 +1205,16 @@
} }
} }
.vxe-body--row { .vxe-body--row {
&:first-child { &:last-child {
.vxe-cell--valid { .vxe-cell--valid {
bottom: auto; top: -1.3em;
top: calc(100% + 4px); }
}
&:first-child {
&:last-child {
.vxe-cell--valid {
top: calc(100% - 1.3em);
}
} }
} }
} }

View File

@@ -132,8 +132,6 @@ $vxe-loading-z-index: 999 !default;
/*validate*/ /*validate*/
$vxe-table-validate-error-color: #f56c6c !default; $vxe-table-validate-error-color: #f56c6c !default;
$vxe-table-validate-tooltip-error-color: #fff !default;
$vxe-table-validate-tooltip-error-background-color: $vxe-table-validate-error-color !default;
/*grid*/ /*grid*/
$vxe-grid-maximize-background-color: #fff !default; $vxe-grid-maximize-background-color: #fff !default;

25
types/form.d.ts vendored
View File

@@ -1,4 +1,4 @@
import { RenderFunction, SetupContext, ComponentPublicInstance, Ref, ComputedRef, VNode } from 'vue' import { RenderFunction, SetupContext, ComponentPublicInstance, Ref, ComputedRef } from 'vue'
import { VXEComponent, VxeComponentBase, VxeEvent, SizeType, ValueOf, SlotVNodeType } from './component' import { VXEComponent, VxeComponentBase, VxeEvent, SizeType, ValueOf, SlotVNodeType } from './component'
import { VxeFormItemProps, VxeFormItemPropTypes } from './form-item' import { VxeFormItemProps, VxeFormItemPropTypes } from './form-item'
import { VxeGridConstructor } from './grid' import { VxeGridConstructor } from './grid'
@@ -148,17 +148,17 @@ export interface FormMethods {
* 对表单进行校验,参数为一个回调函数。该回调函数会在校验结束后被调用 callback(errMap)。若不传入回调函数,则会返回一个 promise * 对表单进行校验,参数为一个回调函数。该回调函数会在校验结束后被调用 callback(errMap)。若不传入回调函数,则会返回一个 promise
* @param callback 回调函数 * @param callback 回调函数
*/ */
validate(callback?: (errMap?: VxeFormDefines.ValidateErrorMapParams) => void): Promise<any> validate(callback?: (errMap?: VxeFormDefines.ValidateErrorMapParams) => void): Promise<VxeFormDefines.ValidateErrorMapParams>
/** /**
* 对表单指定项进行校验,参数为一个回调函数。该回调函数会在校验结束后被调用 callback(errMap)。若不传入回调函数,则会返回一个 promise * 对表单指定项进行校验,参数为一个回调函数。该回调函数会在校验结束后被调用 callback(errMap)。若不传入回调函数,则会返回一个 promise
* @param callback 回调函数 * @param callback 回调函数
*/ */
validateField(field: VxeFormItemPropTypes.Field | VxeFormDefines.ItemInfo, callback?: (errMap?: VxeFormDefines.ValidateErrorMapParams) => void): Promise<any> validateField(field: VxeFormItemPropTypes.Field | VxeFormItemPropTypes.Field[] | VxeFormDefines.ItemInfo | VxeFormDefines.ItemInfo[], callback?: (errMap?: VxeFormDefines.ValidateErrorMapParams) => void): Promise<VxeFormDefines.ValidateErrorMapParams>
/** /**
* 手动清除校验状态,如果指定 field 则清除指定的项,否则清除整个表单 * 手动清除校验状态,如果指定 field 则清除指定的项,否则清除整个表单
* @param field 字段名 * @param field 字段名
*/ */
clearValidate(field?: VxeFormItemPropTypes.Field | VxeFormDefines.ItemInfo): Promise<any> clearValidate(field?: VxeFormItemPropTypes.Field | VxeFormItemPropTypes.Field[] | VxeFormDefines.ItemInfo | VxeFormDefines.ItemInfo[]): Promise<any>
/** /**
* 更新项状态 * 更新项状态
* 当使用自定义渲染时可能会用到 * 当使用自定义渲染时可能会用到
@@ -262,7 +262,18 @@ export namespace VxeFormDefines {
* 使用自定义校验函数,接收一个 Promise * 使用自定义校验函数,接收一个 Promise
* @param params 参数 * @param params 参数
*/ */
validator?(params: ValidateErrorParams): void | Error | Promise<any> validator?(params: {
$form: VxeFormConstructor,
itemValue: any,
rule: VxeFormDefines.FormRule
rules: VxeFormDefines.FormRule[]
data: any
field: string,
/**
* @deprecated
*/
property: string
}): void | Error | Promise<any>
/** /**
* 提示消息 * 提示消息
*/ */
@@ -275,11 +286,9 @@ export namespace VxeFormDefines {
message?: string message?: string
} }
interface ValidateErrorParams { export interface ValidateErrorParams {
$form: VxeFormConstructor, $form: VxeFormConstructor,
itemValue: any,
rule: VxeFormDefines.FormRule rule: VxeFormDefines.FormRule
rules: VxeFormDefines.FormRule[]
data: any data: any
field: string field: string
/** /**

1
types/table.d.ts vendored
View File

@@ -40,7 +40,6 @@ export interface VxeTableConstructor<D = any> extends VxeComponentBase, VxeTable
export interface TablePrivateRef { export interface TablePrivateRef {
refElem: Ref<HTMLDivElement> refElem: Ref<HTMLDivElement>
refTooltip: Ref<VxeTooltipInstance> refTooltip: Ref<VxeTooltipInstance>
refValidTooltip: Ref<VxeTooltipInstance>
refTableFilter: Ref<ComponentPublicInstance> refTableFilter: Ref<ComponentPublicInstance>
refTableMenu: Ref<VxeMenuPanelInstance> refTableMenu: Ref<VxeMenuPanelInstance>
refTableHeader: Ref<ComponentPublicInstance> refTableHeader: Ref<ComponentPublicInstance>