FormItem.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.formItemProps = exports.default = void 0;
  7. var _vue = require("vue");
  8. var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
  9. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  10. var _LoadingOutlined = _interopRequireDefault(require("@ant-design/icons-vue/lib/icons/LoadingOutlined"));
  11. var _CloseCircleFilled = _interopRequireDefault(require("@ant-design/icons-vue/lib/icons/CloseCircleFilled"));
  12. var _CheckCircleFilled = _interopRequireDefault(require("@ant-design/icons-vue/lib/icons/CheckCircleFilled"));
  13. var _ExclamationCircleFilled = _interopRequireDefault(require("@ant-design/icons-vue/lib/icons/ExclamationCircleFilled"));
  14. var _cloneDeep = _interopRequireDefault(require("lodash/cloneDeep"));
  15. var _vueTypes = _interopRequireDefault(require("../_util/vue-types"));
  16. var _Row = _interopRequireDefault(require("../grid/Row"));
  17. var _propsUtil = require("../_util/props-util");
  18. var _validateUtil = require("./utils/validateUtil");
  19. var _valueUtil = require("./utils/valueUtil");
  20. var _typeUtil = require("./utils/typeUtil");
  21. var _warning = require("../vc-util/warning");
  22. var _find = _interopRequireDefault(require("lodash/find"));
  23. var _type = require("../_util/type");
  24. var _useConfigInject = _interopRequireDefault(require("../config-provider/hooks/useConfigInject"));
  25. var _context = require("./context");
  26. var _FormItemLabel = _interopRequireDefault(require("./FormItemLabel"));
  27. var _FormItemInput = _interopRequireDefault(require("./FormItemInput"));
  28. var _FormItemContext = require("./FormItemContext");
  29. var _useDebounce = _interopRequireDefault(require("./utils/useDebounce"));
  30. var _classNames = _interopRequireDefault(require("../_util/classNames"));
  31. var _style = _interopRequireDefault(require("./style"));
  32. const ValidateStatuses = (0, _type.tuple)('success', 'warning', 'error', 'validating', '');
  33. const iconMap = {
  34. success: _CheckCircleFilled.default,
  35. warning: _ExclamationCircleFilled.default,
  36. error: _CloseCircleFilled.default,
  37. validating: _LoadingOutlined.default
  38. };
  39. function getPropByPath(obj, namePathList, strict) {
  40. let tempObj = obj;
  41. const keyArr = namePathList;
  42. let i = 0;
  43. try {
  44. for (let len = keyArr.length; i < len - 1; ++i) {
  45. if (!tempObj && !strict) break;
  46. const key = keyArr[i];
  47. if (key in tempObj) {
  48. tempObj = tempObj[key];
  49. } else {
  50. if (strict) {
  51. throw Error('please transfer a valid name path to form item!');
  52. }
  53. break;
  54. }
  55. }
  56. if (strict && !tempObj) {
  57. throw Error('please transfer a valid name path to form item!');
  58. }
  59. } catch (error) {
  60. console.error('please transfer a valid name path to form item!');
  61. }
  62. return {
  63. o: tempObj,
  64. k: keyArr[i],
  65. v: tempObj ? tempObj[keyArr[i]] : undefined
  66. };
  67. }
  68. const formItemProps = () => ({
  69. htmlFor: String,
  70. prefixCls: String,
  71. label: _vueTypes.default.any,
  72. help: _vueTypes.default.any,
  73. extra: _vueTypes.default.any,
  74. labelCol: {
  75. type: Object
  76. },
  77. wrapperCol: {
  78. type: Object
  79. },
  80. hasFeedback: {
  81. type: Boolean,
  82. default: false
  83. },
  84. colon: {
  85. type: Boolean,
  86. default: undefined
  87. },
  88. labelAlign: String,
  89. prop: {
  90. type: [String, Number, Array]
  91. },
  92. name: {
  93. type: [String, Number, Array]
  94. },
  95. rules: [Array, Object],
  96. autoLink: {
  97. type: Boolean,
  98. default: true
  99. },
  100. required: {
  101. type: Boolean,
  102. default: undefined
  103. },
  104. validateFirst: {
  105. type: Boolean,
  106. default: undefined
  107. },
  108. validateStatus: _vueTypes.default.oneOf((0, _type.tuple)('', 'success', 'warning', 'error', 'validating')),
  109. validateTrigger: {
  110. type: [String, Array]
  111. },
  112. messageVariables: {
  113. type: Object
  114. },
  115. hidden: Boolean,
  116. noStyle: Boolean,
  117. tooltip: String
  118. });
  119. exports.formItemProps = formItemProps;
  120. let indexGuid = 0;
  121. // default form item id prefix.
  122. const defaultItemNamePrefixCls = 'form_item';
  123. var _default = exports.default = (0, _vue.defineComponent)({
  124. compatConfig: {
  125. MODE: 3
  126. },
  127. name: 'AFormItem',
  128. inheritAttrs: false,
  129. __ANT_NEW_FORM_ITEM: true,
  130. props: formItemProps(),
  131. slots: Object,
  132. setup(props, _ref) {
  133. let {
  134. slots,
  135. attrs,
  136. expose
  137. } = _ref;
  138. (0, _warning.warning)(props.prop === undefined, `\`prop\` is deprecated. Please use \`name\` instead.`);
  139. const eventKey = `form-item-${++indexGuid}`;
  140. const {
  141. prefixCls
  142. } = (0, _useConfigInject.default)('form', props);
  143. const [wrapSSR, hashId] = (0, _style.default)(prefixCls);
  144. const itemRef = (0, _vue.shallowRef)();
  145. const formContext = (0, _context.useInjectForm)();
  146. const fieldName = (0, _vue.computed)(() => props.name || props.prop);
  147. const errors = (0, _vue.shallowRef)([]);
  148. const validateDisabled = (0, _vue.shallowRef)(false);
  149. const inputRef = (0, _vue.shallowRef)();
  150. const namePath = (0, _vue.computed)(() => {
  151. const val = fieldName.value;
  152. return (0, _valueUtil.getNamePath)(val);
  153. });
  154. const fieldId = (0, _vue.computed)(() => {
  155. if (!namePath.value.length) {
  156. return undefined;
  157. } else {
  158. const formName = formContext.name.value;
  159. const mergedId = namePath.value.join('_');
  160. return formName ? `${formName}_${mergedId}` : `${defaultItemNamePrefixCls}_${mergedId}`;
  161. }
  162. });
  163. const getNewFieldValue = () => {
  164. const model = formContext.model.value;
  165. if (!model || !fieldName.value) {
  166. return;
  167. } else {
  168. return getPropByPath(model, namePath.value, true).v;
  169. }
  170. };
  171. const fieldValue = (0, _vue.computed)(() => getNewFieldValue());
  172. const initialValue = (0, _vue.shallowRef)((0, _cloneDeep.default)(fieldValue.value));
  173. const mergedValidateTrigger = (0, _vue.computed)(() => {
  174. let validateTrigger = props.validateTrigger !== undefined ? props.validateTrigger : formContext.validateTrigger.value;
  175. validateTrigger = validateTrigger === undefined ? 'change' : validateTrigger;
  176. return (0, _typeUtil.toArray)(validateTrigger);
  177. });
  178. const rulesRef = (0, _vue.computed)(() => {
  179. let formRules = formContext.rules.value;
  180. const selfRules = props.rules;
  181. const requiredRule = props.required !== undefined ? {
  182. required: !!props.required,
  183. trigger: mergedValidateTrigger.value
  184. } : [];
  185. const prop = getPropByPath(formRules, namePath.value);
  186. formRules = formRules ? prop.o[prop.k] || prop.v : [];
  187. const rules = [].concat(selfRules || formRules || []);
  188. if ((0, _find.default)(rules, rule => rule.required)) {
  189. return rules;
  190. } else {
  191. return rules.concat(requiredRule);
  192. }
  193. });
  194. const isRequired = (0, _vue.computed)(() => {
  195. const rules = rulesRef.value;
  196. let isRequired = false;
  197. if (rules && rules.length) {
  198. rules.every(rule => {
  199. if (rule.required) {
  200. isRequired = true;
  201. return false;
  202. }
  203. return true;
  204. });
  205. }
  206. return isRequired || props.required;
  207. });
  208. const validateState = (0, _vue.shallowRef)();
  209. (0, _vue.watchEffect)(() => {
  210. validateState.value = props.validateStatus;
  211. });
  212. const messageVariables = (0, _vue.computed)(() => {
  213. let variables = {};
  214. if (typeof props.label === 'string') {
  215. variables.label = props.label;
  216. } else if (props.name) {
  217. variables.label = String(props.name);
  218. }
  219. if (props.messageVariables) {
  220. variables = (0, _extends2.default)((0, _extends2.default)({}, variables), props.messageVariables);
  221. }
  222. return variables;
  223. });
  224. const validateRules = options => {
  225. // no name, no value, so the validate result is incorrect
  226. if (namePath.value.length === 0) {
  227. return;
  228. }
  229. const {
  230. validateFirst = false
  231. } = props;
  232. const {
  233. triggerName
  234. } = options || {};
  235. let filteredRules = rulesRef.value;
  236. if (triggerName) {
  237. filteredRules = filteredRules.filter(rule => {
  238. const {
  239. trigger
  240. } = rule;
  241. if (!trigger && !mergedValidateTrigger.value.length) {
  242. return true;
  243. }
  244. const triggerList = (0, _typeUtil.toArray)(trigger || mergedValidateTrigger.value);
  245. return triggerList.includes(triggerName);
  246. });
  247. }
  248. if (!filteredRules.length) {
  249. return Promise.resolve();
  250. }
  251. const promise = (0, _validateUtil.validateRules)(namePath.value, fieldValue.value, filteredRules, (0, _extends2.default)({
  252. validateMessages: formContext.validateMessages.value
  253. }, options), validateFirst, messageVariables.value);
  254. validateState.value = 'validating';
  255. errors.value = [];
  256. promise.catch(e => e).then(function () {
  257. let results = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  258. if (validateState.value === 'validating') {
  259. const res = results.filter(result => result && result.errors.length);
  260. validateState.value = res.length ? 'error' : 'success';
  261. errors.value = res.map(r => r.errors);
  262. formContext.onValidate(fieldName.value, !errors.value.length, errors.value.length ? (0, _vue.toRaw)(errors.value[0]) : null);
  263. }
  264. });
  265. return promise;
  266. };
  267. const onFieldBlur = () => {
  268. validateRules({
  269. triggerName: 'blur'
  270. });
  271. };
  272. const onFieldChange = () => {
  273. if (validateDisabled.value) {
  274. validateDisabled.value = false;
  275. return;
  276. }
  277. validateRules({
  278. triggerName: 'change'
  279. });
  280. };
  281. const clearValidate = () => {
  282. validateState.value = props.validateStatus;
  283. validateDisabled.value = false;
  284. errors.value = [];
  285. };
  286. const resetField = () => {
  287. var _a;
  288. validateState.value = props.validateStatus;
  289. validateDisabled.value = true;
  290. errors.value = [];
  291. const model = formContext.model.value || {};
  292. const value = fieldValue.value;
  293. const prop = getPropByPath(model, namePath.value, true);
  294. if (Array.isArray(value)) {
  295. prop.o[prop.k] = [].concat((_a = initialValue.value) !== null && _a !== void 0 ? _a : []);
  296. } else {
  297. prop.o[prop.k] = initialValue.value;
  298. }
  299. // reset validateDisabled after onFieldChange triggered
  300. (0, _vue.nextTick)(() => {
  301. validateDisabled.value = false;
  302. });
  303. };
  304. const htmlFor = (0, _vue.computed)(() => {
  305. return props.htmlFor === undefined ? fieldId.value : props.htmlFor;
  306. });
  307. const onLabelClick = () => {
  308. const id = htmlFor.value;
  309. if (!id || !inputRef.value) {
  310. return;
  311. }
  312. const control = inputRef.value.$el.querySelector(`[id="${id}"]`);
  313. if (control && control.focus) {
  314. control.focus();
  315. }
  316. };
  317. expose({
  318. onFieldBlur,
  319. onFieldChange,
  320. clearValidate,
  321. resetField
  322. });
  323. (0, _FormItemContext.useProvideFormItemContext)({
  324. id: fieldId,
  325. onFieldBlur: () => {
  326. if (props.autoLink) {
  327. onFieldBlur();
  328. }
  329. },
  330. onFieldChange: () => {
  331. if (props.autoLink) {
  332. onFieldChange();
  333. }
  334. },
  335. clearValidate
  336. }, (0, _vue.computed)(() => {
  337. return !!(props.autoLink && formContext.model.value && fieldName.value);
  338. }));
  339. let registered = false;
  340. (0, _vue.watch)(fieldName, val => {
  341. if (val) {
  342. if (!registered) {
  343. registered = true;
  344. formContext.addField(eventKey, {
  345. fieldValue,
  346. fieldId,
  347. fieldName,
  348. resetField,
  349. clearValidate,
  350. namePath,
  351. validateRules,
  352. rules: rulesRef
  353. });
  354. }
  355. } else {
  356. registered = false;
  357. formContext.removeField(eventKey);
  358. }
  359. }, {
  360. immediate: true
  361. });
  362. (0, _vue.onBeforeUnmount)(() => {
  363. formContext.removeField(eventKey);
  364. });
  365. const debounceErrors = (0, _useDebounce.default)(errors);
  366. const mergedValidateStatus = (0, _vue.computed)(() => {
  367. if (props.validateStatus !== undefined) {
  368. return props.validateStatus;
  369. } else if (debounceErrors.value.length) {
  370. return 'error';
  371. }
  372. return validateState.value;
  373. });
  374. const itemClassName = (0, _vue.computed)(() => ({
  375. [`${prefixCls.value}-item`]: true,
  376. [hashId.value]: true,
  377. // Status
  378. [`${prefixCls.value}-item-has-feedback`]: mergedValidateStatus.value && props.hasFeedback,
  379. [`${prefixCls.value}-item-has-success`]: mergedValidateStatus.value === 'success',
  380. [`${prefixCls.value}-item-has-warning`]: mergedValidateStatus.value === 'warning',
  381. [`${prefixCls.value}-item-has-error`]: mergedValidateStatus.value === 'error',
  382. [`${prefixCls.value}-item-is-validating`]: mergedValidateStatus.value === 'validating',
  383. [`${prefixCls.value}-item-hidden`]: props.hidden
  384. }));
  385. const formItemInputContext = (0, _vue.reactive)({});
  386. _FormItemContext.FormItemInputContext.useProvide(formItemInputContext);
  387. (0, _vue.watchEffect)(() => {
  388. let feedbackIcon;
  389. if (props.hasFeedback) {
  390. const IconNode = mergedValidateStatus.value && iconMap[mergedValidateStatus.value];
  391. feedbackIcon = IconNode ? (0, _vue.createVNode)("span", {
  392. "class": (0, _classNames.default)(`${prefixCls.value}-item-feedback-icon`, `${prefixCls.value}-item-feedback-icon-${mergedValidateStatus.value}`)
  393. }, [(0, _vue.createVNode)(IconNode, null, null)]) : null;
  394. }
  395. (0, _extends2.default)(formItemInputContext, {
  396. status: mergedValidateStatus.value,
  397. hasFeedback: props.hasFeedback,
  398. feedbackIcon,
  399. isFormItemInput: true
  400. });
  401. });
  402. const marginBottom = (0, _vue.shallowRef)(null);
  403. const showMarginOffset = (0, _vue.shallowRef)(false);
  404. const updateMarginBottom = () => {
  405. if (itemRef.value) {
  406. const itemStyle = getComputedStyle(itemRef.value);
  407. marginBottom.value = parseInt(itemStyle.marginBottom, 10);
  408. }
  409. };
  410. (0, _vue.onMounted)(() => {
  411. (0, _vue.watch)(showMarginOffset, () => {
  412. if (showMarginOffset.value) {
  413. updateMarginBottom();
  414. }
  415. }, {
  416. flush: 'post',
  417. immediate: true
  418. });
  419. });
  420. const onErrorVisibleChanged = nextVisible => {
  421. if (!nextVisible) {
  422. marginBottom.value = null;
  423. }
  424. };
  425. return () => {
  426. var _a, _b;
  427. if (props.noStyle) return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
  428. const help = (_b = props.help) !== null && _b !== void 0 ? _b : slots.help ? (0, _propsUtil.filterEmpty)(slots.help()) : null;
  429. const withHelp = !!(help !== undefined && help !== null && Array.isArray(help) && help.length || debounceErrors.value.length);
  430. showMarginOffset.value = withHelp;
  431. return wrapSSR((0, _vue.createVNode)("div", {
  432. "class": [itemClassName.value, withHelp ? `${prefixCls.value}-item-with-help` : '', attrs.class],
  433. "ref": itemRef
  434. }, [(0, _vue.createVNode)(_Row.default, (0, _objectSpread2.default)((0, _objectSpread2.default)({}, attrs), {}, {
  435. "class": `${prefixCls.value}-item-row`,
  436. "key": "row"
  437. }), {
  438. default: () => {
  439. var _a, _b;
  440. return (0, _vue.createVNode)(_vue.Fragment, null, [(0, _vue.createVNode)(_FormItemLabel.default, (0, _objectSpread2.default)((0, _objectSpread2.default)({}, props), {}, {
  441. "htmlFor": htmlFor.value,
  442. "required": isRequired.value,
  443. "requiredMark": formContext.requiredMark.value,
  444. "prefixCls": prefixCls.value,
  445. "onClick": onLabelClick,
  446. "label": props.label
  447. }), {
  448. label: slots.label,
  449. tooltip: slots.tooltip
  450. }), (0, _vue.createVNode)(_FormItemInput.default, (0, _objectSpread2.default)((0, _objectSpread2.default)({}, props), {}, {
  451. "errors": help !== undefined && help !== null ? (0, _typeUtil.toArray)(help) : debounceErrors.value,
  452. "marginBottom": marginBottom.value,
  453. "prefixCls": prefixCls.value,
  454. "status": mergedValidateStatus.value,
  455. "ref": inputRef,
  456. "help": help,
  457. "extra": (_a = props.extra) !== null && _a !== void 0 ? _a : (_b = slots.extra) === null || _b === void 0 ? void 0 : _b.call(slots),
  458. "onErrorVisibleChanged": onErrorVisibleChanged
  459. }), {
  460. default: slots.default
  461. })]);
  462. }
  463. }), !!marginBottom.value && (0, _vue.createVNode)("div", {
  464. "class": `${prefixCls.value}-margin-offset`,
  465. "style": {
  466. marginBottom: `-${marginBottom.value}px`
  467. }
  468. }, null)]));
  469. };
  470. }
  471. });