diff --git a/packages/devui-vue/devui/form/src/directive/d-validate-rules.ts b/packages/devui-vue/devui/form/src/directive/d-validate-rules.ts index 0fd265013f..f8f08980f0 100644 --- a/packages/devui-vue/devui/form/src/directive/d-validate-rules.ts +++ b/packages/devui-vue/devui/form/src/directive/d-validate-rules.ts @@ -14,6 +14,7 @@ interface ValidateFnParam { messageShowType: MessageShowType dfcUID: string popPosition: PopPosition | Array + updateOn?: UpdateOn } interface CustomValidatorRuleObject { @@ -46,6 +47,7 @@ export interface ShowPopoverErrorMessageEventData { message?: string uid?: string, popPosition?: PopPosition + [prop : string]: any } type MessageShowType = 'popover' | 'text' | 'none' | 'toast'; @@ -210,7 +212,17 @@ function handleErrorStrategyPass(el: HTMLElement): void { el.setAttribute('class', classList.join(' ')); } -function handleValidateError({el, tipEl, message, isFormTag, messageShowType, dfcUID, popPosition = 'right-bottom'}: Partial): void { +function getFormControlUID(el: HTMLElement): string { + if(el.tagName.toLocaleLowerCase() === "body") return ""; + let uid = '' + if(el.parentElement.id.startsWith('dfc-')) { + return el.parentElement.id; + }else { + uid = getFormControlUID(el.parentElement); + } +} + +function handleValidateError({el, tipEl, message = "", isFormTag, messageShowType, dfcUID, popPosition = 'right-bottom', updateOn}: Partial): void { // 如果该指令用在form标签上,这里做特殊处理 if(isFormTag && messageShowType === MessageShowTypeEnum.toast) { // todo:待替换为toast @@ -218,9 +230,13 @@ function handleValidateError({el, tipEl, message, isFormTag, messageShowType, df return; } + if(!dfcUID) { + dfcUID = getFormControlUID(el); + } + // messageShowType为popover时,设置popover if(MessageShowTypeEnum.popover === messageShowType) { - EventBus.emit("showPopoverErrorMessage", {showPopover: true, message, uid: dfcUID, popPosition} as ShowPopoverErrorMessageEventData); + EventBus.emit("showPopoverErrorMessage", {showPopover: true, message, uid: dfcUID, popPosition, updateOn} as ShowPopoverErrorMessageEventData); return; } @@ -250,7 +266,7 @@ function getFormName(binding: DirectiveBinding): string { } // 校验处理函数 -function validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowType, dfcUID, popPosition}: Partial) { +function validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowType, dfcUID, popPosition, updateOn}: Partial) { validator.validate({modelName: modelValue}).then(() => { handleValidatePass(el, tipEl); }).catch((err) => { @@ -265,7 +281,7 @@ function validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowTyp msg = errors[0].message; } - handleValidateError({el, tipEl, message: msg, isFormTag, messageShowType, dfcUID, popPosition}); + handleValidateError({el, tipEl, message: msg, isFormTag, messageShowType, dfcUID, popPosition, updateOn}); }) } @@ -394,14 +410,21 @@ export default { const htmlEventValidateHandler = (e) => { const modelValue = e.target.value; if(messageShowType === MessageShowTypeEnum.popover) { - EventBus.emit("showPopoverErrorMessage", {showPopover: false, message: "", uid: dfcUID, popPosition} as ShowPopoverErrorMessageEventData); + EventBus.emit("showPopoverErrorMessage", {showPopover: false, message: "", uid: dfcUID, popPosition, updateOn} as ShowPopoverErrorMessageEventData); } - validateFn({validator, modelValue, el, tipEl, isFormTag: false, messageShowType, dfcUID, popPosition}); + validateFn({validator, modelValue, el, tipEl, isFormTag: false, messageShowType, dfcUID, popPosition, updateOn}); } // 监听事件验证 vnode.children[0].el.addEventListener(updateOn, htmlEventValidateHandler); + // 如果校验时机为change,则在focus时关闭popover + if(messageShowType === MessageShowTypeEnum.popover && updateOn === UpdateOnEnum.change) { + vnode.children[0].el.addEventListener('focus', () => { + EventBus.emit("showPopoverErrorMessage", {showPopover: false, uid: dfcUID, updateOn} as ShowPopoverErrorMessageEventData); + }); + } + // 设置errorStrategy if(errorStrategy === ErrorStrategyEnum.pristine) { handleErrorStrategy(el); @@ -415,7 +438,7 @@ export default { const modelValue = isFormTag ? '' : vnode.children[0].el.value; // 进行提交验证 - validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowType}); + validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowType, updateOn: 'submit'}); }); } diff --git a/packages/devui-vue/devui/form/src/form-control/form-control.tsx b/packages/devui-vue/devui/form/src/form-control/form-control.tsx index 55612f3a6f..c9907d3bd7 100644 --- a/packages/devui-vue/devui/form/src/form-control/form-control.tsx +++ b/packages/devui-vue/devui/form/src/form-control/form-control.tsx @@ -2,6 +2,7 @@ import { defineComponent, inject, ref, computed, reactive, onMounted, Teleport } import { uniqueId } from 'lodash-es'; import { IForm, formControlProps, formInjectionKey } from '../form-types'; import { ShowPopoverErrorMessageEventData } from '../directive/d-validate-rules' +import clickoutsideDirective from '../../../shared/devui-directive/clickoutside' import { EventBus, getElOffset } from '../util'; import Icon from '../../../icon/src/icon'; import Popover from '../../../popover/src/popover'; @@ -11,6 +12,9 @@ type positionType = 'top' | 'right' | 'bottom' | 'left'; export default defineComponent({ name: 'DFormControl', + directives: { + clickoutside: clickoutsideDirective + }, props: formControlProps, setup(props, ctx) { const formControl = ref(); @@ -19,6 +23,7 @@ export default defineComponent({ const isHorizontal = labelData.layout === 'horizontal'; const uid = uniqueId("dfc-"); const showPopover = ref(false); + const updateOn = ref('change'); const tipMessage = ref(""); const popPosition = ref("bottom"); let rectInfo: Partial = { @@ -38,11 +43,12 @@ export default defineComponent({ EventBus.on("showPopoverErrorMessage", (data: ShowPopoverErrorMessageEventData) => { if (uid === data.uid) { rectInfo = el.getBoundingClientRect(); - popoverLeftPosition = popPosition.value === "top" || popPosition.value === "bottom" ? rectInfo.right - (rectInfo.width / 2) : rectInfo.right; - popoverTopPosition = popPosition.value === "top" ? elOffset.top + (rectInfo.height / 2) - rectInfo.height : elOffset.top + (rectInfo.height / 2); showPopover.value = data.showPopover; tipMessage.value = data.message; popPosition.value = data.popPosition as any; // todo: 待popover组件positionType完善类型之后再替换类型 + popoverLeftPosition = popPosition.value === "top" || popPosition.value === "bottom" ? rectInfo.right - (rectInfo.width / 2) : rectInfo.right; + popoverTopPosition = popPosition.value === "top" ? elOffset.top + (rectInfo.height / 2) - rectInfo.height : elOffset.top + (rectInfo.height / 2); + updateOn.value = data.updateOn ?? 'change'; } }); }); @@ -60,13 +66,20 @@ export default defineComponent({ } }) + const handleClickOutside = () => { + if(updateOn.value !== 'change') { + showPopover.value = false; + } + } + return () => { const { feedbackStatus, extraInfo, } = props; - return
- + return
+ { showPopover.value && +
- +
+ }
{ctx.slots.default?.()}