Form.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  2. import _typeof from "@babel/runtime/helpers/esm/typeof";
  3. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  4. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  5. import { createVNode as _createVNode } from "vue";
  6. import { defineComponent, computed, watch, ref } from 'vue';
  7. import PropTypes from '../_util/vue-types';
  8. import classNames from '../_util/classNames';
  9. import warning from '../_util/warning';
  10. import FormItem from './FormItem';
  11. import { getNamePath, containsNamePath, cloneByNamePathList } from './utils/valueUtil';
  12. import { defaultValidateMessages } from './utils/messages';
  13. import { allPromiseFinish } from './utils/asyncUtil';
  14. import { toArray } from './utils/typeUtil';
  15. import isEqual from 'lodash-es/isEqual';
  16. import scrollIntoView from 'scroll-into-view-if-needed';
  17. import initDefaultProps from '../_util/props-util/initDefaultProps';
  18. import { tuple } from '../_util/type';
  19. import { useInjectSize } from '../_util/hooks/useSize';
  20. import useConfigInject from '../_util/hooks/useConfigInject';
  21. import { useProvideForm } from './context';
  22. import useForm from './useForm';
  23. import { useInjectGlobalForm } from '../config-provider/context';
  24. export var formProps = function formProps() {
  25. return {
  26. layout: PropTypes.oneOf(tuple('horizontal', 'inline', 'vertical')),
  27. labelCol: {
  28. type: Object
  29. },
  30. wrapperCol: {
  31. type: Object
  32. },
  33. colon: {
  34. type: Boolean,
  35. default: undefined
  36. },
  37. labelAlign: PropTypes.oneOf(tuple('left', 'right')),
  38. labelWrap: {
  39. type: Boolean,
  40. default: undefined
  41. },
  42. prefixCls: String,
  43. requiredMark: {
  44. type: [String, Boolean],
  45. default: undefined
  46. },
  47. /** @deprecated Will warning in future branch. Pls use `requiredMark` instead. */
  48. hideRequiredMark: {
  49. type: Boolean,
  50. default: undefined
  51. },
  52. model: PropTypes.object,
  53. rules: {
  54. type: Object
  55. },
  56. validateMessages: {
  57. type: Object,
  58. default: undefined
  59. },
  60. validateOnRuleChange: {
  61. type: Boolean,
  62. default: undefined
  63. },
  64. // 提交失败自动滚动到第一个错误字段
  65. scrollToFirstError: {
  66. type: [Boolean, Object]
  67. },
  68. onSubmit: Function,
  69. name: String,
  70. validateTrigger: {
  71. type: [String, Array]
  72. },
  73. size: {
  74. type: String
  75. },
  76. onValuesChange: {
  77. type: Function
  78. },
  79. onFieldsChange: {
  80. type: Function
  81. },
  82. onFinish: {
  83. type: Function
  84. },
  85. onFinishFailed: {
  86. type: Function
  87. },
  88. onValidate: {
  89. type: Function
  90. }
  91. };
  92. };
  93. function isEqualName(name1, name2) {
  94. return isEqual(toArray(name1), toArray(name2));
  95. }
  96. var Form = defineComponent({
  97. compatConfig: {
  98. MODE: 3
  99. },
  100. name: 'AForm',
  101. inheritAttrs: false,
  102. props: initDefaultProps(formProps(), {
  103. layout: 'horizontal',
  104. hideRequiredMark: false,
  105. colon: true
  106. }),
  107. Item: FormItem,
  108. useForm: useForm,
  109. // emits: ['finishFailed', 'submit', 'finish', 'validate'],
  110. setup: function setup(props, _ref) {
  111. var emit = _ref.emit,
  112. slots = _ref.slots,
  113. expose = _ref.expose,
  114. attrs = _ref.attrs;
  115. var size = useInjectSize(props);
  116. var _useConfigInject = useConfigInject('form', props),
  117. prefixCls = _useConfigInject.prefixCls,
  118. direction = _useConfigInject.direction,
  119. contextForm = _useConfigInject.form;
  120. var requiredMark = computed(function () {
  121. return props.requiredMark === '' || props.requiredMark;
  122. });
  123. var mergedRequiredMark = computed(function () {
  124. var _contextForm$value;
  125. if (requiredMark.value !== undefined) {
  126. return requiredMark.value;
  127. }
  128. if (contextForm && ((_contextForm$value = contextForm.value) === null || _contextForm$value === void 0 ? void 0 : _contextForm$value.requiredMark) !== undefined) {
  129. return contextForm.value.requiredMark;
  130. }
  131. if (props.hideRequiredMark) {
  132. return false;
  133. }
  134. return true;
  135. });
  136. var mergedColon = computed(function () {
  137. var _props$colon, _contextForm$value2;
  138. return (_props$colon = props.colon) !== null && _props$colon !== void 0 ? _props$colon : (_contextForm$value2 = contextForm.value) === null || _contextForm$value2 === void 0 ? void 0 : _contextForm$value2.colon;
  139. });
  140. var _useInjectGlobalForm = useInjectGlobalForm(),
  141. globalValidateMessages = _useInjectGlobalForm.validateMessages;
  142. var validateMessages = computed(function () {
  143. return _objectSpread(_objectSpread(_objectSpread({}, defaultValidateMessages), globalValidateMessages.value), props.validateMessages);
  144. });
  145. var formClassName = computed(function () {
  146. var _classNames;
  147. return classNames(prefixCls.value, (_classNames = {}, _defineProperty(_classNames, "".concat(prefixCls.value, "-").concat(props.layout), true), _defineProperty(_classNames, "".concat(prefixCls.value, "-hide-required-mark"), mergedRequiredMark.value === false), _defineProperty(_classNames, "".concat(prefixCls.value, "-rtl"), direction.value === 'rtl'), _defineProperty(_classNames, "".concat(prefixCls.value, "-").concat(size.value), size.value), _classNames));
  148. });
  149. var lastValidatePromise = ref();
  150. var fields = {};
  151. var addField = function addField(eventKey, field) {
  152. fields[eventKey] = field;
  153. };
  154. var removeField = function removeField(eventKey) {
  155. delete fields[eventKey];
  156. };
  157. var getFieldsByNameList = function getFieldsByNameList(nameList) {
  158. var provideNameList = !!nameList;
  159. var namePathList = provideNameList ? toArray(nameList).map(getNamePath) : [];
  160. if (!provideNameList) {
  161. return Object.values(fields);
  162. } else {
  163. return Object.values(fields).filter(function (field) {
  164. return namePathList.findIndex(function (namePath) {
  165. return isEqualName(namePath, field.fieldName.value);
  166. }) > -1;
  167. });
  168. }
  169. };
  170. var resetFields = function resetFields(name) {
  171. if (!props.model) {
  172. warning(false, 'Form', 'model is required for resetFields to work.');
  173. return;
  174. }
  175. getFieldsByNameList(name).forEach(function (field) {
  176. field.resetField();
  177. });
  178. };
  179. var clearValidate = function clearValidate(name) {
  180. getFieldsByNameList(name).forEach(function (field) {
  181. field.clearValidate();
  182. });
  183. };
  184. var handleFinishFailed = function handleFinishFailed(errorInfo) {
  185. var scrollToFirstError = props.scrollToFirstError;
  186. emit('finishFailed', errorInfo);
  187. if (scrollToFirstError && errorInfo.errorFields.length) {
  188. var scrollToFieldOptions = {};
  189. if (_typeof(scrollToFirstError) === 'object') {
  190. scrollToFieldOptions = scrollToFirstError;
  191. }
  192. scrollToField(errorInfo.errorFields[0].name, scrollToFieldOptions);
  193. }
  194. };
  195. var validate = function validate() {
  196. return validateField.apply(void 0, arguments);
  197. };
  198. var scrollToField = function scrollToField(name) {
  199. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  200. var fields = getFieldsByNameList(name ? [name] : undefined);
  201. if (fields.length) {
  202. var fieldId = fields[0].fieldId.value;
  203. var node = fieldId ? document.getElementById(fieldId) : null;
  204. if (node) {
  205. scrollIntoView(node, _objectSpread({
  206. scrollMode: 'if-needed',
  207. block: 'nearest'
  208. }, options));
  209. }
  210. }
  211. };
  212. // eslint-disable-next-line no-unused-vars
  213. var getFieldsValue = function getFieldsValue() {
  214. var nameList = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
  215. if (nameList === true) {
  216. var allNameList = [];
  217. Object.values(fields).forEach(function (_ref2) {
  218. var namePath = _ref2.namePath;
  219. allNameList.push(namePath.value);
  220. });
  221. return cloneByNamePathList(props.model, allNameList);
  222. } else {
  223. return cloneByNamePathList(props.model, nameList);
  224. }
  225. };
  226. var validateFields = function validateFields(nameList, options) {
  227. warning(!(nameList instanceof Function), 'Form', 'validateFields/validateField/validate not support callback, please use promise instead');
  228. if (!props.model) {
  229. warning(false, 'Form', 'model is required for validateFields to work.');
  230. return Promise.reject('Form `model` is required for validateFields to work.');
  231. }
  232. var provideNameList = !!nameList;
  233. var namePathList = provideNameList ? toArray(nameList).map(getNamePath) : [];
  234. // Collect result in promise list
  235. var promiseList = [];
  236. Object.values(fields).forEach(function (field) {
  237. var _field$rules;
  238. // Add field if not provide `nameList`
  239. if (!provideNameList) {
  240. namePathList.push(field.namePath.value);
  241. }
  242. // Skip if without rule
  243. if (!((_field$rules = field.rules) !== null && _field$rules !== void 0 && _field$rules.value.length)) {
  244. return;
  245. }
  246. var fieldNamePath = field.namePath.value;
  247. // Add field validate rule in to promise list
  248. if (!provideNameList || containsNamePath(namePathList, fieldNamePath)) {
  249. var promise = field.validateRules(_objectSpread({
  250. validateMessages: validateMessages.value
  251. }, options));
  252. // Wrap promise with field
  253. promiseList.push(promise.then(function () {
  254. return {
  255. name: fieldNamePath,
  256. errors: [],
  257. warnings: []
  258. };
  259. }).catch(function (ruleErrors) {
  260. var mergedErrors = [];
  261. var mergedWarnings = [];
  262. ruleErrors.forEach(function (_ref3) {
  263. var warningOnly = _ref3.rule.warningOnly,
  264. errors = _ref3.errors;
  265. if (warningOnly) {
  266. mergedWarnings.push.apply(mergedWarnings, _toConsumableArray(errors));
  267. } else {
  268. mergedErrors.push.apply(mergedErrors, _toConsumableArray(errors));
  269. }
  270. });
  271. if (mergedErrors.length) {
  272. return Promise.reject({
  273. name: fieldNamePath,
  274. errors: mergedErrors,
  275. warnings: mergedWarnings
  276. });
  277. }
  278. return {
  279. name: fieldNamePath,
  280. errors: mergedErrors,
  281. warnings: mergedWarnings
  282. };
  283. }));
  284. }
  285. });
  286. var summaryPromise = allPromiseFinish(promiseList);
  287. lastValidatePromise.value = summaryPromise;
  288. var returnPromise = summaryPromise.then(function () {
  289. if (lastValidatePromise.value === summaryPromise) {
  290. return Promise.resolve(getFieldsValue(namePathList));
  291. }
  292. return Promise.reject([]);
  293. }).catch(function (results) {
  294. var errorList = results.filter(function (result) {
  295. return result && result.errors.length;
  296. });
  297. return Promise.reject({
  298. values: getFieldsValue(namePathList),
  299. errorFields: errorList,
  300. outOfDate: lastValidatePromise.value !== summaryPromise
  301. });
  302. });
  303. // Do not throw in console
  304. returnPromise.catch(function (e) {
  305. return e;
  306. });
  307. return returnPromise;
  308. };
  309. var validateField = function validateField() {
  310. return validateFields.apply(void 0, arguments);
  311. };
  312. var handleSubmit = function handleSubmit(e) {
  313. e.preventDefault();
  314. e.stopPropagation();
  315. emit('submit', e);
  316. if (props.model) {
  317. var res = validateFields();
  318. res.then(function (values) {
  319. emit('finish', values);
  320. }).catch(function (errors) {
  321. handleFinishFailed(errors);
  322. });
  323. }
  324. };
  325. expose({
  326. resetFields: resetFields,
  327. clearValidate: clearValidate,
  328. validateFields: validateFields,
  329. getFieldsValue: getFieldsValue,
  330. validate: validate,
  331. scrollToField: scrollToField
  332. });
  333. useProvideForm({
  334. model: computed(function () {
  335. return props.model;
  336. }),
  337. name: computed(function () {
  338. return props.name;
  339. }),
  340. labelAlign: computed(function () {
  341. return props.labelAlign;
  342. }),
  343. labelCol: computed(function () {
  344. return props.labelCol;
  345. }),
  346. labelWrap: computed(function () {
  347. return props.labelWrap;
  348. }),
  349. wrapperCol: computed(function () {
  350. return props.wrapperCol;
  351. }),
  352. vertical: computed(function () {
  353. return props.layout === 'vertical';
  354. }),
  355. colon: mergedColon,
  356. requiredMark: mergedRequiredMark,
  357. validateTrigger: computed(function () {
  358. return props.validateTrigger;
  359. }),
  360. rules: computed(function () {
  361. return props.rules;
  362. }),
  363. addField: addField,
  364. removeField: removeField,
  365. onValidate: function onValidate(name, status, errors) {
  366. emit('validate', name, status, errors);
  367. },
  368. validateMessages: validateMessages
  369. });
  370. watch(function () {
  371. return props.rules;
  372. }, function () {
  373. if (props.validateOnRuleChange) {
  374. validateFields();
  375. }
  376. });
  377. return function () {
  378. var _slots$default;
  379. return _createVNode("form", _objectSpread(_objectSpread({}, attrs), {}, {
  380. "onSubmit": handleSubmit,
  381. "class": [formClassName.value, attrs.class]
  382. }), [(_slots$default = slots.default) === null || _slots$default === void 0 ? void 0 : _slots$default.call(slots)]);
  383. };
  384. }
  385. });
  386. export default Form;