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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 12 additions & 32 deletions packages/devui-vue/devui/form/index.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,10 @@
import type { App } from 'vue';
import Form from './src/form';
import FormLabel from './src/form-label/form-label';
import FormItem from './src/form-item/form-item';
import FormControl from './src/form-control/form-control';
import FormOperation from './src/form-operation/form-operation';
import dValidateRules from './src/directive/d-validate-rules';

Form.install = function(app: App) {
app.component(Form.name, Form);
app.directive('d-validate-rules', dValidateRules);
};

FormLabel.install = function(app: App) {
app.component(FormLabel.name, FormLabel);
};

FormItem.install = function(app: App) {
app.component(FormItem.name, FormItem);
};

FormControl.install = function(app: App) {
app.component(FormControl.name, FormControl);
};

FormOperation.install = function(app: App) {
app.component(FormOperation.name, FormOperation);
};
import FormLabel from './src/components/form-label/form-label';
import FormItem from './src/components/form-item/form-item';
import FormControl from './src/components/form-control/form-control';
import FormOperation from './src/components/form-operation/form-operation';
import dValidateRules from './src/directives/d-validate-rules';

export { Form, FormLabel, FormItem, FormControl, FormOperation };

Expand All @@ -34,10 +13,11 @@ export default {
category: '数据录入',
status: '75%',
install(app: App): void {
app.use(Form as any);
app.use(FormLabel as any);
app.use(FormItem as any);
app.use(FormControl as any);
app.use(FormOperation as any);
}
app.component(Form.name, Form);
app.directive('d-validate-rules', dValidateRules);
app.component(FormLabel.name, FormLabel);
app.component(FormItem.name, FormItem);
app.component(FormControl.name, FormControl);
app.component(FormOperation.name, FormOperation);
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { defineComponent, inject, ref, computed, reactive, onMounted, Teleport } from 'vue';
import { uniqueId } from 'lodash';
import { IForm, formControlProps, formInjectionKey } from '../../form-types';
import { ShowPopoverErrorMessageEventData } from '../../directives/d-validate-rules';
import clickoutsideDirective from '../../../../shared/devui-directive/clickoutside';
import { EventBus, getElOffset } from '../../utils';
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',
directives: {
clickoutside: clickoutsideDirective,
},
props: formControlProps,
setup(props, ctx) {
const formControl = ref();
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 updateOn = ref('change');
const tipMessage = ref('');
const popPosition = ref<positionType>('bottom');
let rectInfo: Partial<DOMRect> = {
width: 0,
height: 0,
};
let elOffset = {
left: 0,
top: 0,
};
let popoverLeftPosition = 0;
let popoverTopPosition = 0;

onMounted(() => {
const el = document.getElementById(uid);
elOffset = getElOffset(el);
EventBus.on('showPopoverErrorMessage', (data: ShowPopoverErrorMessageEventData) => {
if (uid === data.uid) {
rectInfo = el.getBoundingClientRect();
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';
}
});
});

const iconData = computed(() => {
switch (props.feedbackStatus) {
case 'pending':
return { name: 'priority', color: '#e9edfa' };
case 'success':
return { name: 'right-o', color: 'rgb(61, 204, 166)' };
case 'error':
return { name: 'error-o', color: 'rgb(249, 95, 91)' };
default:
return { name: '', color: '' };
}
});

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

return () => {
const { feedbackStatus, extraInfo } = props;
return (
<div class="devui-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
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 ? ' devui-has-feedback' : ''
}${feedbackStatus === 'error' ? ' devui-feedback-error' : ''}`}>
<div class="devui-control-content-wrapper" id={uid}>
{ctx.slots.default?.()}
</div>
{(feedbackStatus || ctx.slots.suffixTemplate?.()) && (
<span class="devui-feedback-status">
{ctx.slots.suffixTemplate?.() ? (
ctx.slots.suffixTemplate?.()
) : (
<Icon name={iconData.value.name} color={iconData.value.color}></Icon>
)}
</span>
)}
</div>
{extraInfo && <div class="devui-form-control-extra-info">{extraInfo}</div>}
</div>
);
};
},
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { defineComponent, reactive, inject, onMounted, onBeforeUnmount, provide, ref} from 'vue';
import { defineComponent, reactive, inject, onMounted, onBeforeUnmount, provide, ref } from 'vue';
import AsyncValidator, { Rules } from 'async-validator';
import mitt from 'mitt';
import { dFormEvents, dFormItemEvents, IForm, formItemProps, formInjectionKey, formItemInjectionKey } from '../form-types';
import { dFormEvents, dFormItemEvents, IForm, formItemProps, formInjectionKey, formItemInjectionKey } from '../../form-types';
import './form-item.scss';


export default defineComponent({
name: 'DFormItem',
props: formItemProps,
Expand All @@ -18,9 +17,9 @@ export default defineComponent({
const rules = reactive(dForm.rules);

const resetField = () => {
if(Array.isArray(initFormItemData)) {
if (Array.isArray(initFormItemData)) {
formData[props.prop] = [...initFormItemData];
}else {
} else {
formData[props.prop] = initFormItemData;
}
};
Expand All @@ -29,7 +28,7 @@ export default defineComponent({
dHasFeedback: props.dHasFeedback,
prop: props.prop,
formItemMitt,
resetField
resetField,
});
provide(formItemInjectionKey, formItem);

Expand All @@ -48,39 +47,42 @@ export default defineComponent({

const validator = new AsyncValidator(descriptor);

validator.validate({[ruleKey]: formData[ruleKey]}).then(() => {
showMessage.value = false;
tipMessage.value = '';
}).catch(({ errors }) => {
showMessage.value = true;
tipMessage.value = errors[0].message;
});
validator
.validate({ [ruleKey]: formData[ruleKey] })
.then(() => {
showMessage.value = false;
tipMessage.value = '';
})
.catch(({ errors }) => {
showMessage.value = true;
tipMessage.value = errors[0].message;
});
};
const validateEvents = [];

const addValidateEvents = () => {
if(rules && rules[props.prop]) {
if (rules && rules[props.prop]) {
const ruleItem = rules[props.prop];
let eventName = ruleItem['trigger'];

if(Array.isArray(ruleItem)) {
if (Array.isArray(ruleItem)) {
ruleItem.forEach((item) => {
eventName = item['trigger'];
const cb = () => validate(eventName);
validateEvents.push({eventName: cb});
validateEvents.push({ eventName: cb });
formItem.formItemMitt.on(dFormItemEvents[eventName], cb);
});
}else {
} else {
const cb = () => validate(eventName);
validateEvents.push({eventName: cb});
validateEvents.push({ eventName: cb });
ruleItem && formItem.formItemMitt.on(dFormItemEvents[eventName], cb);
}
}
};

const removeValidateEvents = () => {
if(rules && rules[props.prop] && validateEvents.length > 0) {
validateEvents.forEach(item => {
if (rules && rules[props.prop] && validateEvents.length > 0) {
validateEvents.forEach((item) => {
formItem.formItemMitt.off(item.eventName, item.cb);
});
}
Expand All @@ -97,9 +99,14 @@ export default defineComponent({
});
return () => {
return (
<div class={`devui-form-item${isHorizontal ? '' : (isVertical ? ' devui-form-item-vertical' : ' devui-form-item-columns')}${isColumns ? ' devui-column-item ' + columnsClass.value : ''}`}>
<div
class={`devui-form-item${isHorizontal ? '' : isVertical ? ' devui-form-item-vertical' : ' devui-form-item-columns'}${
isColumns ? ' devui-column-item ' + columnsClass.value : ''
}`}>
{ctx.slots.default?.()}
<div class={`devui-validate-tip${isHorizontal ? ' devui-validate-tip-horizontal' : ''}`}>{showMessage.value && tipMessage.value}</div>
<div class={`devui-validate-tip${isHorizontal ? ' devui-validate-tip-horizontal' : ''}`}>
{showMessage.value && tipMessage.value}
</div>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { defineComponent, inject, reactive, computed } from 'vue';
import { IForm, formLabelProps, FormLabelProps, formInjectionKey } from '../../form-types';
import Icon from '../../../../icon/src/icon';
import Popover from '../../../../popover/src/popover';
import './form-label.scss';

export default defineComponent({
name: 'DFormLabel',
props: formLabelProps,
setup(props: FormLabelProps, ctx) {
const dForm = reactive(inject(formInjectionKey, {} as IForm));
const labelData = reactive(dForm.labelData);

const isHorizontal = computed(() => labelData.layout === 'horizontal').value;
const isLg = computed(() => labelData.labelSize === 'lg').value;
const isSm = computed(() => labelData.labelSize === 'sm').value;
const isCenter = computed(() => labelData.labelAlign === 'center').value;
const isEnd = computed(() => labelData.labelAlign === 'end').value;

const wrapperCls = `devui-form-label${
isHorizontal ? (isSm ? ' devui-form-label_sm' : isLg ? ' devui-form-label_lg' : ' devui-form-label_sd') : ''
}${isCenter ? ' devui-form-label_center' : isEnd ? ' devui-form-label_end' : ''}`;
const className = `${props.required ? ' devui-required' : ''}`;
const style = { display: isHorizontal ? 'inline' : 'inline-block' };

return () => {
return (
<span class={wrapperCls} style={style}>
<span class={className}>
{ctx.slots.default?.()}
{props.hasHelp && props.helpTips && (
<Popover
content={props.helpTips}
showAnimation={false}
position={'top'}
trigger={'hover'}
v-slots={{
reference: () => (
<span class="devui-form-label-help">
<Icon name="helping" color="#252b3a"></Icon>
</span>
),
}}></Popover>
)}
</span>
</span>
);
};
},
});
Loading