Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions packages/devui-vue/devui/form/src/directive/d-validate-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface ValidateFnParam {
messageShowType: MessageShowType
dfcUID: string
popPosition: PopPosition | Array<BasePopPosition>
updateOn?: UpdateOn
}

interface CustomValidatorRuleObject {
Expand Down Expand Up @@ -46,6 +47,7 @@ export interface ShowPopoverErrorMessageEventData {
message?: string
uid?: string,
popPosition?: PopPosition
[prop : string]: any
}

type MessageShowType = 'popover' | 'text' | 'none' | 'toast';
Expand Down Expand Up @@ -210,17 +212,31 @@ function handleErrorStrategyPass(el: HTMLElement): void {
el.setAttribute('class', classList.join(' '));
}

function handleValidateError({el, tipEl, message, isFormTag, messageShowType, dfcUID, popPosition = 'right-bottom'}: Partial<ValidateFnParam>): void {
function getDfcUID(el: HTMLElement): string {
if(el.tagName.toLocaleLowerCase() === "body") return "";
let uid = ''
if(el.parentElement.id.startsWith('dfc-')) {
return el.parentElement.id;
}else {
uid = getDfcUID(el.parentElement);
}
}

function handleValidateError({el, tipEl, message = "", isFormTag, messageShowType, dfcUID, popPosition = 'right-bottom', updateOn}: Partial<ValidateFnParam>): void {
// 如果该指令用在form标签上,这里做特殊处理
if(isFormTag && messageShowType === MessageShowTypeEnum.toast) {
// todo:待替换为toast
alert(message);
return;
}

if(!dfcUID) {
dfcUID = getDfcUID(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;
}

Expand Down Expand Up @@ -250,7 +266,7 @@ function getFormName(binding: DirectiveBinding): string {
}

// 校验处理函数
function validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowType, dfcUID, popPosition}: Partial<ValidateFnParam>) {
function validateFn({validator, modelValue, el, tipEl, isFormTag, messageShowType, dfcUID, popPosition, updateOn}: Partial<ValidateFnParam>) {
validator.validate({modelName: modelValue}).then(() => {
handleValidatePass(el, tipEl);
}).catch((err) => {
Expand All @@ -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});
})
}

Expand Down Expand Up @@ -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);
Expand All @@ -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'});
});

}
Expand Down
24 changes: 19 additions & 5 deletions packages/devui-vue/devui/form/src/form-control/form-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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();
Expand All @@ -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<positionType>("bottom");
let rectInfo: Partial<DOMRect> = {
Expand All @@ -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';
}
});
});
Expand All @@ -60,23 +66,31 @@ export default defineComponent({
}
})

const handleClickOutside = () => {
if(updateOn.value !== 'change') {
showPopover.value = false;
}
}

return () => {
const {
feedbackStatus,
extraInfo,
} = props;
return <div class="form-control" ref={formControl} data-uid={uid}>
<Teleport to="body">
return <div class="form-control" ref={formControl} data-uid={uid} v-clickoutside={handleClickOutside}>
{ showPopover.value &&
<Teleport to="body">
<div style={{
position: 'absolute',
left: popoverLeftPosition + 'px',
top: popoverTopPosition + 'px',
width: rectInfo.width + 'px',
height: rectInfo.height + 'px',
}}>
<Popover visible={showPopover.value} content={tipMessage.value} popType={"error"} position={popPosition.value} />
<Popover controlled={updateOn.value !== 'change'} visible={showPopover.value} content={tipMessage.value} popType={"error"} position={popPosition.value} />
</div>
</Teleport>
}
<div class={`devui-form-control-container${isHorizontal ? ' devui-form-control-container-horizontal' : ''}${feedbackStatus ? ' has-feedback' : ''}${feedbackStatus === 'error' ? ' feedback-error' : ''}`}>
<div class="devui-control-content-wrapper" id={uid}>
{ctx.slots.default?.()}
Expand Down