From 2c5fb7357023c57df3c06c9a6dfad053bc9200df Mon Sep 17 00:00:00 2001 From: "1445654576@qq.com" <1445654576@qq.com> Date: Sat, 13 Nov 2021 14:04:40 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E5=BC=95=E5=85=A5popover=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=EF=BC=8CmessageShowType=E4=B8=BApopover=EF=BC=8C?= =?UTF-8?q?=E4=BD=BF=E7=94=A8popover=E8=BF=9B=E8=A1=8C=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../form/src/directive/d-validate-rules.ts | 55 +++++++++++++++++-- .../form/src/form-control/form-control.scss | 23 ++++++++ .../form/src/form-control/form-control.tsx | 31 ++++++++++- .../devui-vue/docs/components/form/index.md | 5 +- 4 files changed, 103 insertions(+), 11 deletions(-) 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 658bd0c3..f8a59f28 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 @@ -12,6 +12,8 @@ interface ValidateFnParam { isFormTag: boolean message: string messageShowType: MessageShowType + dfcUID: string + popPosition: PopPosition | Array } interface CustomValidatorRuleObject { @@ -24,6 +26,7 @@ interface DirectiveValidateRuleOptions { updateOn?: UpdateOn errorStrategy?: ErrorStrategy asyncDebounceTime?: number + popPosition?: PopPosition | Array } interface DirectiveBindingValue { @@ -38,9 +41,18 @@ interface DirectiveCustomRuleItem extends RuleItem { asyncValidators: CustomValidatorRuleObject[] } +export interface ShowPopoverErrorMessageEventData { + showPopover?: boolean + message?: string + uid?: string, + popPosition?: PopPosition +} + type MessageShowType = 'popover' | 'text' | 'none' | 'toast'; type UpdateOn = 'input' | 'focus' | 'change' | 'blur' | 'submit'; type ErrorStrategy = 'dirty' | 'pristine'; +type BasePopPosition = 'left' | 'right' | 'top' | 'bottom'; +type PopPosition = BasePopPosition | 'left-top' | 'left-bottom' | 'top-left' | 'top-right' | 'right-top' | 'right-bottom' | 'bottom-left' | 'bottom-right'; enum ErrorStrategyEnum { dirty = 'dirty', @@ -198,7 +210,7 @@ function handleErrorStrategyPass(el: HTMLElement): void { el.setAttribute('class', classList.join(' ')); } -function handleValidateError({el, tipEl, message, isFormTag, messageShowType}: Partial): void { +function handleValidateError({el, tipEl, message, isFormTag, messageShowType, dfcUID, popPosition = 'right-bottom'}: Partial): void { // 如果该指令用在form标签上,这里做特殊处理 if(isFormTag && messageShowType === MessageShowTypeEnum.toast) { // todo:待替换为toast @@ -206,6 +218,12 @@ function handleValidateError({el, tipEl, message, isFormTag, messageShowType}: P return; } + // messageShowType为popover时,设置popover + if(MessageShowTypeEnum.popover === messageShowType) { + EventBus.emit("showPopoverErrorMessage", {showPopover: true, message, uid: dfcUID, popPosition} as ShowPopoverErrorMessageEventData); + return; + } + tipEl.innerText = '' + message; tipEl.style.display = 'inline-flex'; tipEl.setAttribute('class', 'd-validate-tip'); @@ -225,7 +243,7 @@ function getFormName(binding: DirectiveBinding): string { } // 校验处理函数 -function validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowType}: Partial) { +function validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowType, dfcUID, popPosition}: Partial) { validator.validate({modelName: modelValue}).then(() => { handleValidatePass(el, tipEl); }).catch((err) => { @@ -240,13 +258,22 @@ function validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowTyp msg = errors[0].message; } - handleValidateError({el, tipEl, message: msg, isFormTag, messageShowType}); + handleValidateError({el, tipEl, message: msg, isFormTag, messageShowType, dfcUID, popPosition}); }) } +// 检测popover的position是否是正确值 +function checkValidPopsition(positionStr: string): boolean { + const validPosition = ['left', 'right', 'top', 'bottom', 'left-top', 'left-bottom', 'top-left', 'top-right', 'right-top', 'right-bottom', 'bottom-left', 'bottom-right']; + const isValid = validPosition.includes(positionStr); + !isValid && console.warn(`invalid popPosition value '${positionStr}'.`); + return isValid +} + export default { mounted(el: HTMLElement, binding: DirectiveBinding, vnode: VNode): void { const isFormTag = el.tagName === 'FORM'; + const dfcUID = el.parentNode.parentNode.parentElement.dataset.uid; const hasOptions = isObject(binding.value) && hasKey(binding.value, 'options'); @@ -259,12 +286,25 @@ export default { let { errorStrategy }: DirectiveBindingValue = binding.value; // errorStrategy可配置在options对象中 - const { + let { updateOn = UpdateOnEnum.change, errorStrategy: ErrorStrategy = ErrorStrategyEnum.dirty, - asyncDebounceTime = 300 + asyncDebounceTime = 300, + popPosition = ['right', 'bottom'] }: DirectiveValidateRuleOptions = options; + // 设置popover的位置 + if(messageShowType === MessageShowTypeEnum.popover) { + if(Array.isArray(popPosition)) { + popPosition = (popPosition.length > 1 ? popPosition.join('-') : popPosition[0]) as PopPosition; + if(!checkValidPopsition(popPosition)) { + popPosition = 'right-bottom'; + } + }else if(!checkValidPopsition(popPosition)) { + popPosition = 'right-bottom'; + } + } + if(!errorStrategy) { errorStrategy = ErrorStrategy; } @@ -340,7 +380,10 @@ export default { const htmlEventValidateHandler = (e) => { const modelValue = e.target.value; - validateFn({validator, modelValue, el, tipEl, isFormTag: false, messageShowType}); + if(messageShowType === MessageShowTypeEnum.popover) { + EventBus.emit("showPopoverErrorMessage", {showPopover: false, message: "", uid: dfcUID} as ShowPopoverErrorMessageEventData); + } + validateFn({validator, modelValue, el, tipEl, isFormTag: false, messageShowType, dfcUID, popPosition}); } // 监听事件验证 diff --git a/packages/devui-vue/devui/form/src/form-control/form-control.scss b/packages/devui-vue/devui/form/src/form-control/form-control.scss index 993bae7c..55b3f1bb 100644 --- a/packages/devui-vue/devui/form/src/form-control/form-control.scss +++ b/packages/devui-vue/devui/form/src/form-control/form-control.scss @@ -60,6 +60,29 @@ } } + + .devui-control-content-wrapper { + position: relative; + & > div { + z-index: 10; + } + .devui-popover-wrapper { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + z-index: 9; + + & > div { + width: inherit; + height: 100%; + } + } + } + + + .has-feedback { display: flex; align-items: center; 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 18150919..a1c0cc99 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 @@ -1,8 +1,14 @@ -import { defineComponent, inject, ref, computed, reactive } from 'vue'; +import { defineComponent, inject, ref, computed, reactive, onMounted } from 'vue'; +import { uniqueId } from 'lodash-es'; import {IForm, formControlProps, formInjectionKey} from '../form-types'; +import { ShowPopoverErrorMessageEventData } from '../directive/d-validate-rules' +import { EventBus } from '../util'; import Icon from '../../../icon/src/icon'; +import Popover from '../../../popover/src/popover'; import './form-control.scss'; +type positionType = 'top' | 'right' | 'bottom' | 'left'; + export default defineComponent({ name: 'DFormControl', props: formControlProps, @@ -11,6 +17,20 @@ export default defineComponent({ const dForm = reactive(inject(formInjectionKey, {} as IForm)); const labelData = reactive(dForm.labelData); const isHorizontal = labelData.layout === 'horizontal'; + const uid = uniqueId("dfc-"); + const showPopover = ref(false); + const tipMessage = ref(""); + const popPosition = ref("bottom"); + + onMounted(() => { + EventBus.on("showPopoverErrorMessage", (data: ShowPopoverErrorMessageEventData) => { + if(uid === data.uid) { + showPopover.value = data.showPopover; + tipMessage.value = data.message; + popPosition.value = data.popPosition as any; // todo: 待popover组件positionType完善类型之后再替换类型 + } + }); + }); const iconData = computed(() => { switch(props.feedbackStatus) { @@ -30,9 +50,14 @@ export default defineComponent({ feedbackStatus, extraInfo, } = props; - return
+ return
- {ctx.slots.default?.()} +
+ {ctx.slots.default?.()} +
+ { showPopover.value && } +
+
{ (feedbackStatus || ctx.slots.suffixTemplate?.()) &&