ant-design-vue/components/form-model/Form.jsx

264 lines
8.1 KiB
Vue
Raw Normal View History

2020-07-06 14:31:07 +00:00
import { inject, provide } from 'vue';
2020-07-12 08:13:59 +00:00
// import scrollIntoView from 'dom-scroll-into-view';
import PropTypes from '../_util/vue-types';
import classNames from 'classnames';
import { ColProps } from '../grid/Col';
import isRegExp from 'lodash/isRegExp';
import warning from '../_util/warning';
import FormItem from './FormItem';
2020-07-06 14:31:07 +00:00
import { initDefaultProps, getListeners, getSlot } from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider';
2020-07-10 10:26:35 +00:00
import { getParams } from './utils';
export const FormProps = {
layout: PropTypes.oneOf(['horizontal', 'inline', 'vertical']),
labelCol: PropTypes.shape(ColProps).loose,
wrapperCol: PropTypes.shape(ColProps).loose,
colon: PropTypes.bool,
labelAlign: PropTypes.oneOf(['left', 'right']),
prefixCls: PropTypes.string,
hideRequiredMark: PropTypes.bool,
model: PropTypes.object,
rules: PropTypes.object,
validateMessages: PropTypes.any,
validateOnRuleChange: PropTypes.bool,
};
export const ValidationRule = {
/** validation error message */
message: PropTypes.string,
/** built-in validation type, available options: https://github.com/yiminghe/async-validator#type */
type: PropTypes.string,
/** indicates whether field is required */
required: PropTypes.boolean,
/** treat required fields that only contain whitespace as errors */
whitespace: PropTypes.boolean,
/** validate the exact length of a field */
len: PropTypes.number,
/** validate the min length of a field */
min: PropTypes.number,
/** validate the max length of a field */
max: PropTypes.number,
/** validate the value from a list of possible values */
enum: PropTypes.oneOfType([String, PropTypes.arrayOf(String)]),
/** validate from a regular expression */
pattern: PropTypes.custom(isRegExp),
/** transform a value before validation */
transform: PropTypes.func,
/** custom validate function (Note: callback must be called) */
validator: PropTypes.func,
2020-07-10 10:26:35 +00:00
// 提交失败自动滚动到第一个错误字段
scrollToFirstError: PropTypes.bool,
};
const Form = {
2020-03-16 04:06:55 +00:00
name: 'AFormModel',
2020-07-06 14:31:07 +00:00
inheritAttrs: false,
props: initDefaultProps(FormProps, {
layout: 'horizontal',
hideRequiredMark: false,
colon: true,
}),
Item: FormItem,
created() {
this.fields = [];
2020-07-06 14:31:07 +00:00
this.form = undefined;
provide('FormContext', this);
},
2020-07-06 14:31:07 +00:00
setup() {
return {
2020-07-06 14:31:07 +00:00
configProvider: inject('configProvider', ConfigConsumerProps),
};
},
watch: {
rules() {
if (this.validateOnRuleChange) {
this.validate(() => {});
}
},
},
computed: {
vertical() {
return this.layout === 'vertical';
},
},
methods: {
addField(field) {
if (field) {
this.fields.push(field);
}
},
removeField(field) {
if (field.prop) {
this.fields.splice(this.fields.indexOf(field), 1);
}
},
onSubmit(e) {
if (!getListeners(this).submit) {
e.preventDefault();
} else {
this.$emit('submit', e);
}
},
2020-07-12 08:13:59 +00:00
resetFields(props = []) {
if (!this.model) {
2020-03-16 04:06:55 +00:00
warning(false, 'FormModel', 'model is required for resetFields to work.');
return;
}
2020-07-10 10:26:35 +00:00
const fields = props.length
? typeof props === 'string'
? this.fields.filter(field => props === field.prop)
: this.fields.filter(field => props.indexOf(field.prop) > -1)
: this.fields;
fields.forEach(field => {
field.resetField();
});
},
clearValidate(props = []) {
const fields = props.length
? typeof props === 'string'
? this.fields.filter(field => props === field.prop)
: this.fields.filter(field => props.indexOf(field.prop) > -1)
: this.fields;
fields.forEach(field => {
field.clearValidate();
});
},
2020-07-12 08:13:59 +00:00
validate() {
return this.validateField(...arguments);
// if (!this.model) {
// warning(false, 'FormModel', 'model is required for resetFields to work.');
// return;
// }
// let promise;
// // if no callback, return promise
// if (typeof callback !== 'function' && window.Promise) {
// promise = new window.Promise((resolve, reject) => {
// callback = function(valid) {
// valid ? resolve(valid) : reject(valid);
// };
// });
// }
// let valid = true;
// let count = 0;
// // 如果需要验证的fields为空调用验证时立刻返回callback
// if (this.fields.length === 0 && callback) {
// callback(true);
// }
// let invalidFields = {};
// this.fields.forEach(field => {
// field.validate('', (message, field) => {
// if (message) {
// valid = false;
// }
// invalidFields = Object.assign({}, invalidFields, field);
// if (typeof callback === 'function' && ++count === this.fields.length) {
// callback(valid, invalidFields);
// }
// });
// });
// if (promise) {
// return promise;
// }
},
2020-07-10 10:26:35 +00:00
scrollToField() {},
getFieldsValue(allFields) {
2020-07-12 08:13:59 +00:00
const values = {};
allFields.forEach(({ prop, fieldValue }) => {
values[prop] = fieldValue;
2020-07-10 10:26:35 +00:00
});
2020-07-12 08:13:59 +00:00
return values;
2020-07-10 10:26:35 +00:00
},
validateFields() {
2020-07-12 08:13:59 +00:00
return this.validateField(...arguments);
2020-07-10 10:26:35 +00:00
},
validateField(ns, opt, cb) {
const pending = new Promise((resolve, reject) => {
const params = getParams(ns, opt, cb);
const { names, options } = params;
let { callback } = params;
if (!callback || typeof callback === 'function') {
const oldCb = callback;
callback = (errors, values) => {
if (oldCb) {
oldCb(errors, values);
} else if (errors) {
reject({ errors, values });
} else {
resolve(values);
}
};
}
const allFields = names
? this.fields.filter(field => names.indexOf(field.prop) !== -1)
: this.fields;
const fields = allFields.filter(field => {
const rules = field.getFilteredRule('');
return rules && rules.length;
});
if (!fields.length) {
callback(null, this.getFieldsValue(allFields));
return;
}
if (!('firstFields' in options)) {
options.firstFields = allFields.filter(field => {
return !!field.validateFirst;
});
}
2020-07-12 08:13:59 +00:00
let fieldsErrors = {};
2020-07-10 10:26:35 +00:00
let valid = true;
let count = 0;
fields.forEach(field => {
2020-07-12 08:13:59 +00:00
field.validate('', errors => {
if (errors) {
2020-07-10 10:26:35 +00:00
valid = false;
2020-07-12 08:13:59 +00:00
fieldsErrors[field.prop] = errors;
2020-07-10 10:26:35 +00:00
}
2020-07-12 08:13:59 +00:00
if (++count === fields.length) {
callback(valid ? null : fieldsErrors, this.getFieldsValue(fields));
2020-07-10 10:26:35 +00:00
}
});
});
});
pending.catch(e => {
if (console.error && process.env.NODE_ENV !== 'production') {
console.error(e);
}
return e;
});
2020-07-10 10:26:35 +00:00
return pending;
// names = [].concat(names);
// const fields = this.fields.filter(field => names.indexOf(field.prop) !== -1);
// if (!fields.length) {
// warning(false, 'FormModel', 'please pass correct props!');
// return;
// }
// fields.forEach(field => {
// field.validate('', cb);
// });
},
},
render() {
2020-07-06 14:31:07 +00:00
const { prefixCls: customizePrefixCls, hideRequiredMark, layout, onSubmit } = this;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('form', customizePrefixCls);
2020-07-06 14:31:07 +00:00
const { class: className, onSubmit: originSubmit, ...restProps } = this.$attrs;
2020-07-06 14:31:07 +00:00
const formClassName = classNames(prefixCls, className, {
[`${prefixCls}-horizontal`]: layout === 'horizontal',
[`${prefixCls}-vertical`]: layout === 'vertical',
[`${prefixCls}-inline`]: layout === 'inline',
[`${prefixCls}-hide-required-mark`]: hideRequiredMark,
});
return (
2020-07-06 14:31:07 +00:00
<form onSubmit={onSubmit} class={formClassName} {...restProps}>
{getSlot(this)}
</form>
);
},
};
export default Form;