feat: udpate formmodel

pull/2419/head^2
undefined 2020-07-06 22:31:07 +08:00
parent a3c8930b39
commit 6cad9c3cc0
6 changed files with 101 additions and 102 deletions

View File

@ -1,10 +1,10 @@
import { filterEmpty } from './props-util'; import { filterEmpty } from './props-util';
import { cloneVNode } from 'vue'; import { cloneVNode } from 'vue';
export function cloneElement(n, nodeProps = {}, override = true) { export function cloneElement(vnode, nodeProps = {}, override = true) {
let ele = n; let ele = vnode;
if (Array.isArray(n)) { if (Array.isArray(vnode)) {
ele = filterEmpty(n)[0]; ele = filterEmpty(vnode)[0];
} }
if (!ele) { if (!ele) {
return null; return null;
@ -15,3 +15,7 @@ export function cloneElement(n, nodeProps = {}, override = true) {
node.props = override ? { ...node.props, ...nodeProps } : node.props; node.props = override ? { ...node.props, ...nodeProps } : node.props;
return node; return node;
} }
export function cloneVNodes(vnodes, nodeProps = {}, override = true) {
return vnodes.map(vnode => cloneElement(vnode, nodeProps, override));
}

View File

@ -1,10 +1,11 @@
import { inject, provide } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { ColProps } from '../grid/Col'; import { ColProps } from '../grid/Col';
import isRegExp from 'lodash/isRegExp'; import isRegExp from 'lodash/isRegExp';
import warning from '../_util/warning'; import warning from '../_util/warning';
import FormItem from './FormItem'; import FormItem from './FormItem';
import { initDefaultProps, getListeners } from '../_util/props-util'; import { initDefaultProps, getListeners, getSlot } from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider'; import { ConfigConsumerProps } from '../config-provider';
export const FormProps = { export const FormProps = {
@ -48,6 +49,7 @@ export const ValidationRule = {
const Form = { const Form = {
name: 'AFormModel', name: 'AFormModel',
inheritAttrs: false,
props: initDefaultProps(FormProps, { props: initDefaultProps(FormProps, {
layout: 'horizontal', layout: 'horizontal',
hideRequiredMark: false, hideRequiredMark: false,
@ -56,15 +58,14 @@ const Form = {
Item: FormItem, Item: FormItem,
created() { created() {
this.fields = []; this.fields = [];
this.form = undefined;
provide('FormContext', this);
}, },
provide() { setup() {
return { return {
FormContext: this, configProvider: inject('configProvider', ConfigConsumerProps),
}; };
}, },
inject: {
configProvider: { default: () => ConfigConsumerProps },
},
watch: { watch: {
rules() { rules() {
if (this.validateOnRuleChange) { if (this.validateOnRuleChange) {
@ -164,19 +165,20 @@ const Form = {
}, },
render() { render() {
const { prefixCls: customizePrefixCls, hideRequiredMark, layout, onSubmit, $slots } = this; const { prefixCls: customizePrefixCls, hideRequiredMark, layout, onSubmit } = this;
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('form', customizePrefixCls); const prefixCls = getPrefixCls('form', customizePrefixCls);
const { class: className, onSubmit: originSubmit, ...restProps } = this.$attrs;
const formClassName = classNames(prefixCls, { const formClassName = classNames(prefixCls, className, {
[`${prefixCls}-horizontal`]: layout === 'horizontal', [`${prefixCls}-horizontal`]: layout === 'horizontal',
[`${prefixCls}-vertical`]: layout === 'vertical', [`${prefixCls}-vertical`]: layout === 'vertical',
[`${prefixCls}-inline`]: layout === 'inline', [`${prefixCls}-inline`]: layout === 'inline',
[`${prefixCls}-hide-required-mark`]: hideRequiredMark, [`${prefixCls}-hide-required-mark`]: hideRequiredMark,
}); });
return ( return (
<form onSubmit={onSubmit} class={formClassName}> <form onSubmit={onSubmit} class={formClassName} {...restProps}>
{$slots.default} {getSlot(this)}
</form> </form>
); );
}, },

View File

@ -1,14 +1,15 @@
import { inject } from 'vue';
import AsyncValidator from 'async-validator'; import AsyncValidator from 'async-validator';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { ColProps } from '../grid/Col'; import { ColProps } from '../grid/Col';
import { import {
initDefaultProps, initDefaultProps,
getComponentFromProp, getComponent,
getOptionProps, getOptionProps,
getEvents, getEvents,
filterEmpty,
isValidElement, isValidElement,
getSlot,
} from '../_util/props-util'; } from '../_util/props-util';
import BaseMixin from '../_util/BaseMixin'; import BaseMixin from '../_util/BaseMixin';
import { ConfigConsumerProps } from '../config-provider'; import { ConfigConsumerProps } from '../config-provider';
@ -63,15 +64,18 @@ export const FormItemProps = {
export default { export default {
name: 'AFormModelItem', name: 'AFormModelItem',
__ANT_NEW_FORM_ITEM: true,
mixins: [BaseMixin], mixins: [BaseMixin],
inheritAttrs: false,
__ANT_NEW_FORM_ITEM: true,
props: initDefaultProps(FormItemProps, { props: initDefaultProps(FormItemProps, {
hasFeedback: false, hasFeedback: false,
autoLink: true, autoLink: true,
}), }),
inject: { setup() {
configProvider: { default: () => ConfigConsumerProps }, return {
FormContext: { default: () => ({}) }, configProvider: inject('configProvider', ConfigConsumerProps),
FormContext: inject('FormContext', {}),
};
}, },
data() { data() {
return { return {
@ -216,34 +220,31 @@ export default {
}, },
}, },
render() { render() {
const { $slots, $scopedSlots } = this; const { autoLink, ...props } = getOptionProps(this);
const props = getOptionProps(this); const label = getComponent(this, 'label');
const label = getComponentFromProp(this, 'label'); const extra = getComponent(this, 'extra');
const extra = getComponentFromProp(this, 'extra'); const help = getComponent(this, 'help');
const help = getComponentFromProp(this, 'help');
const formProps = { const formProps = {
props: { ...this.$attrs,
...props, ...props,
label, label,
extra, extra,
validateStatus: this.validateState, validateStatus: this.validateState,
help: this.validateMessage || help, help: this.validateMessage || help,
required: this.isRequired || props.required, required: this.isRequired || props.required,
},
}; };
const children = filterEmpty($scopedSlots.default ? $scopedSlots.default() : $slots.default); const children = getSlot(this);
let firstChildren = children[0]; let firstChildren = children[0];
if (this.prop && this.autoLink && isValidElement(firstChildren)) { if (this.prop && autoLink && isValidElement(firstChildren)) {
const originalEvents = getEvents(firstChildren); const originalEvents = getEvents(firstChildren);
const originalBlur = originalEvents.blur; const originalBlur = originalEvents.onBlur;
const originalChange = originalEvents.change; const originalChange = originalEvents.onChange;
firstChildren = cloneElement(firstChildren, { firstChildren = cloneElement(firstChildren, {
on: { onBlur: (...args) => {
blur: (...args) => {
originalBlur && originalBlur(...args); originalBlur && originalBlur(...args);
this.onFieldBlur(); this.onFieldBlur();
}, },
change: (...args) => { onChange: (...args) => {
if (Array.isArray(originalChange)) { if (Array.isArray(originalChange)) {
for (let i = 0, l = originalChange.length; i < l; i++) { for (let i = 0, l = originalChange.length; i < l; i++) {
originalChange[i](...args); originalChange[i](...args);
@ -253,7 +254,6 @@ export default {
} }
this.onFieldChange(); this.onFieldChange();
}, },
},
}); });
} }
return ( return (

View File

@ -1,20 +1,12 @@
import Vue from 'vue';
import Form from './Form'; import Form from './Form';
import ref from 'vue-ref';
import FormDecoratorDirective from '../_util/FormDecoratorDirective';
import Base from '../base';
Vue.use(ref, { name: 'ant-ref' });
Vue.use(FormDecoratorDirective);
export { FormProps, ValidationRule } from './Form'; export { FormProps, ValidationRule } from './Form';
export { FormItemProps } from './FormItem'; export { FormItemProps } from './FormItem';
/* istanbul ignore next */ /* istanbul ignore next */
Form.install = function(Vue) { Form.install = function(app) {
Vue.use(Base); app.component(Form.name, Form);
Vue.component(Form.name, Form); app.component(Form.Item.name, Form.Item);
Vue.component(Form.Item.name, Form.Item);
}; };
export default Form; export default Form;

View File

@ -1,3 +1,4 @@
import { provide, inject, Transition } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import classNames from 'classnames'; import classNames from 'classnames';
import find from 'lodash/find'; import find from 'lodash/find';
@ -7,11 +8,12 @@ import warning from '../_util/warning';
import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants'; import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants';
import { import {
initDefaultProps, initDefaultProps,
getComponentFromProp, getComponent,
filterEmpty,
getSlotOptions, getSlotOptions,
isValidElement, isValidElement,
getAllChildren, getAllChildren,
findDOMNode,
getSlot,
} from '../_util/props-util'; } from '../_util/props-util';
import getTransitionProps from '../_util/getTransitionProps'; import getTransitionProps from '../_util/getTransitionProps';
import BaseMixin from '../_util/BaseMixin'; import BaseMixin from '../_util/BaseMixin';
@ -73,23 +75,21 @@ function comeFromSlot(vnodes = [], itemVnode) {
export default { export default {
name: 'AFormItem', name: 'AFormItem',
__ANT_FORM_ITEM: true,
mixins: [BaseMixin], mixins: [BaseMixin],
inheritAttrs: false,
__ANT_FORM_ITEM: true,
props: initDefaultProps(FormItemProps, { props: initDefaultProps(FormItemProps, {
hasFeedback: false, hasFeedback: false,
}), }),
provide() { setup() {
return { return {
isFormItemChildren: true, isFormItemChildren: inject('isFormItemChildren', false),
FormContext: inject('FormContext', {}),
decoratorFormProps: inject('decoratorFormProps', {}),
collectFormItemContext: inject('collectFormItemContext', noop),
configProvider: inject('configProvider', ConfigConsumerProps),
}; };
}, },
inject: {
isFormItemChildren: { default: false },
FormContext: { default: () => ({}) },
decoratorFormProps: { default: () => ({}) },
collectFormItemContext: { default: () => noop },
configProvider: { default: () => ConfigConsumerProps },
},
data() { data() {
return { helpShow: false }; return { helpShow: false };
}, },
@ -99,6 +99,7 @@ export default {
}, },
}, },
created() { created() {
provide('isFormItemChildren', true);
this.collectContext(); this.collectContext();
}, },
beforeUpdate() { beforeUpdate() {
@ -145,7 +146,7 @@ export default {
} }
}, },
getHelpMessage() { getHelpMessage() {
const help = getComponentFromProp(this, 'help'); const help = getComponent(this, 'help');
const onlyControl = this.getOnlyControl(); const onlyControl = this.getOnlyControl();
if (help === undefined && onlyControl) { if (help === undefined && onlyControl) {
const errors = this.getField().errors; const errors = this.getField().errors;
@ -177,15 +178,15 @@ export default {
} }
const child = childrenArray[i]; const child = childrenArray[i];
if (!child.tag && child.text.trim() === '') { // if (!child.tag && child.text.trim() === '') {
continue; // continue;
} // }
if (getSlotOptions(child).__ANT_FORM_ITEM) { if (typeof child.type === 'object' && child.type.__ANT_FORM_ITEM) {
continue; continue;
} }
const children = getAllChildren(child); const children = getAllChildren(child);
const attrs = (child.data && child.data.attrs) || {}; const attrs = child.props || {};
if (FIELD_META_PROP in attrs) { if (FIELD_META_PROP in attrs) {
// And means FIELD_DATA_PROP in child.props, too. // And means FIELD_DATA_PROP in child.props, too.
controls.push(child); controls.push(child);
@ -207,6 +208,7 @@ export default {
if (!child) { if (!child) {
return undefined; return undefined;
} }
debugger;
if (child.data) { if (child.data) {
data = child.data; data = child.data;
} else if (child.$vnode && child.$vnode.data) { } else if (child.$vnode && child.$vnode.data) {
@ -253,7 +255,7 @@ export default {
if (!id) { if (!id) {
return; return;
} }
const formItemNode = this.$el; const formItemNode = findDOMNode(this);
const control = formItemNode.querySelector(`[id="${id}"]`); const control = formItemNode.querySelector(`[id="${id}"]`);
if (control && control.focus) { if (control && control.focus) {
control.focus(); control.focus();
@ -296,18 +298,18 @@ export default {
this.helpShow = !!children; this.helpShow = !!children;
} }
const transitionProps = getTransitionProps('show-help', { const transitionProps = getTransitionProps('show-help', {
afterEnter: () => this.onHelpAnimEnd('help', true), onAfterEnter: () => this.onHelpAnimEnd('help', true),
afterLeave: () => this.onHelpAnimEnd('help', false), onAfterLeave: () => this.onHelpAnimEnd('help', false),
}); });
return ( return (
<transition {...transitionProps} key="help"> <Transition {...transitionProps} key="help">
{children} {children}
</transition> </Transition>
); );
}, },
renderExtra(prefixCls) { renderExtra(prefixCls) {
const extra = getComponentFromProp(this, 'extra'); const extra = getComponent(this, 'extra');
return extra ? <div class={`${prefixCls}-extra`}>{extra}</div> : null; return extra ? <div class={`${prefixCls}-extra`}>{extra}</div> : null;
}, },
@ -353,15 +355,14 @@ export default {
const { wrapperCol: contextWrapperCol } = this.isFormItemChildren ? {} : this.FormContext; const { wrapperCol: contextWrapperCol } = this.isFormItemChildren ? {} : this.FormContext;
const { wrapperCol } = this; const { wrapperCol } = this;
const mergedWrapperCol = wrapperCol || contextWrapperCol || {}; const mergedWrapperCol = wrapperCol || contextWrapperCol || {};
const { style, id, on, ...restProps } = mergedWrapperCol; const { style, id, ...restProps } = mergedWrapperCol;
const className = classNames(`${prefixCls}-item-control-wrapper`, mergedWrapperCol.class); const className = classNames(`${prefixCls}-item-control-wrapper`, mergedWrapperCol.class);
const colProps = { const colProps = {
props: restProps, ...restProps,
class: className, class: className,
key: 'wrapper', key: 'wrapper',
style, style,
id, id,
on,
}; };
return <Col {...colProps}>{children}</Col>; return <Col {...colProps}>{children}</Col>;
}, },
@ -374,7 +375,7 @@ export default {
colon: contextColon, colon: contextColon,
} = this.FormContext; } = this.FormContext;
const { labelAlign, labelCol, colon, id, htmlFor } = this; const { labelAlign, labelCol, colon, id, htmlFor } = this;
const label = getComponentFromProp(this, 'label'); const label = getComponent(this, 'label');
const required = this.isRequired(); const required = this.isRequired();
const mergedLabelCol = labelCol || contextLabelCol || {}; const mergedLabelCol = labelCol || contextLabelCol || {};
@ -389,7 +390,6 @@ export default {
class: labelColClass, class: labelColClass,
style: labelColStyle, style: labelColStyle,
id: labelColId, id: labelColId,
on,
...restProps ...restProps
} = mergedLabelCol; } = mergedLabelCol;
let labelChildren = label; let labelChildren = label;
@ -406,12 +406,11 @@ export default {
[`${prefixCls}-item-no-colon`]: !computedColon, [`${prefixCls}-item-no-colon`]: !computedColon,
}); });
const colProps = { const colProps = {
props: restProps, ...restProps,
class: labelColClassName, class: labelColClassName,
key: 'label', key: 'label',
style: labelColStyle, style: labelColStyle,
id: labelColId, id: labelColId,
on,
}; };
return label ? ( return label ? (
@ -443,16 +442,18 @@ export default {
}, },
renderFormItem() { renderFormItem() {
const { prefixCls: customizePrefixCls } = this.$props; const { prefixCls: customizePrefixCls } = this.$props;
const { class: className, ...restProps } = this.$attrs;
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('form', customizePrefixCls); const prefixCls = getPrefixCls('form', customizePrefixCls);
const children = this.renderChildren(prefixCls); const children = this.renderChildren(prefixCls);
const itemClassName = { const itemClassName = {
[className]: true,
[`${prefixCls}-item`]: true, [`${prefixCls}-item`]: true,
[`${prefixCls}-item-with-help`]: this.helpShow, [`${prefixCls}-item-with-help`]: this.helpShow,
}; };
return ( return (
<Row class={classNames(itemClassName)} key="row"> <Row class={classNames(itemClassName)} key="row" {...restProps}>
{children} {children}
</Row> </Row>
); );
@ -497,14 +498,8 @@ export default {
}, },
render() { render() {
const { const { decoratorFormProps, fieldDecoratorId, fieldDecoratorOptions = {}, FormContext } = this;
$slots, let child = getSlot(this);
decoratorFormProps,
fieldDecoratorId,
fieldDecoratorOptions = {},
FormContext,
} = this;
let child = filterEmpty($slots.default || []);
if (decoratorFormProps.form && fieldDecoratorId && child.length) { if (decoratorFormProps.form && fieldDecoratorId && child.length) {
const getFieldDecorator = decoratorFormProps.form.getFieldDecorator; const getFieldDecorator = decoratorFormProps.form.getFieldDecorator;
child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions, this)(child[0]); child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions, this)(child[0]);

View File

@ -17,6 +17,9 @@ import {
Tooltip, Tooltip,
Col, Col,
Row, Row,
FormModel,
Switch,
Checkbox,
notification, notification,
message, message,
} from 'ant-design-vue'; } from 'ant-design-vue';
@ -49,6 +52,9 @@ app
.use(Col) .use(Col)
.use(Row) .use(Row)
.use(Radio) .use(Radio)
.use(Switch)
.use(Checkbox)
.use(InputNumber) .use(InputNumber)
.use(AutoComplete) .use(AutoComplete)
.use(FormModel)
.mount('#app'); .mount('#app');