common.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import { getPropByPath, isObject, isPromise } from '@/packages/utils/util';
  2. import { computed, provide, reactive, VNode, watch } from 'vue';
  3. import { FormItemRule } from '../formitem/types';
  4. import { ErrorMessage, FormRule, FormRules } from './types';
  5. export const component = {
  6. props: {
  7. modelValue: {
  8. type: Object,
  9. default: {}
  10. },
  11. rules: {
  12. type: Object,
  13. default: {}
  14. }
  15. },
  16. components: {},
  17. emits: ['validate'],
  18. setup(props: any, { emit, slots }: any) {
  19. const formErrorTip = computed(() => reactive<any>({}));
  20. provide('formErrorTip', formErrorTip);
  21. const clearErrorTips = (value = props.modelValue) => {
  22. Object.keys(formErrorTip.value).forEach((item) => {
  23. formErrorTip.value[item] = '';
  24. });
  25. };
  26. const reset = () => {
  27. clearErrorTips();
  28. };
  29. watch(
  30. () => props.modelValue,
  31. (value: any) => {
  32. clearErrorTips(value);
  33. },
  34. { immediate: true }
  35. );
  36. const findFormItem = (vnodes: VNode[]) => {
  37. let task: FormRule[] = [];
  38. vnodes.forEach((vnode: VNode, index: number) => {
  39. let type = vnode.type;
  40. type = (type as any).name || type;
  41. if (type == 'nut-form-item' || type?.toString().endsWith('form-item')) {
  42. task.push({
  43. prop: vnode.props?.['prop'],
  44. rules: vnode.props?.['rules'] || []
  45. });
  46. } else if (Array.isArray(vnode.children) && vnode.children?.length) {
  47. task = task.concat(findFormItem(vnode.children as VNode[]));
  48. } else if (isObject(vnode.children) && Object.keys(vnode.children)) {
  49. // 异步节点获取
  50. if ((vnode.children as any)?.default) {
  51. vnode.children = (vnode.children as any).default();
  52. task = task.concat(findFormItem(vnode.children as VNode[]));
  53. }
  54. }
  55. });
  56. return task;
  57. };
  58. const tipMessage = (errorMsg: ErrorMessage) => {
  59. if (errorMsg.message) {
  60. emit('validate', errorMsg);
  61. }
  62. formErrorTip.value[errorMsg.prop] = errorMsg.message;
  63. };
  64. const checkRule = (item: FormRule): Promise<ErrorMessage | boolean> => {
  65. const { rules, prop } = item;
  66. const _Promise = (errorMsg: ErrorMessage): Promise<ErrorMessage> => {
  67. return new Promise((resolve, reject) => {
  68. tipMessage(errorMsg);
  69. resolve(errorMsg);
  70. });
  71. };
  72. if (!prop) {
  73. console.warn('[NutUI] <FormItem> 使用 rules 校验规则时 , 必须设置 prop 参数');
  74. }
  75. const value = getPropByPath(props.modelValue, prop || '');
  76. // clear tips
  77. tipMessage({ prop, message: '' });
  78. const formRules: FormRules = props.rules || {};
  79. const _rules = [...(formRules?.[prop] || []), ...rules];
  80. while (_rules.length) {
  81. const rule = _rules.shift() as FormItemRule;
  82. const { validator, ...ruleWithoutValidator } = rule;
  83. const { required, regex, message } = ruleWithoutValidator;
  84. const errorMsg = { prop, message };
  85. if (required) {
  86. if (value === '' || value === undefined || value === null) {
  87. return _Promise(errorMsg);
  88. }
  89. }
  90. if (regex && !regex.test(String(value))) {
  91. return _Promise(errorMsg);
  92. }
  93. if (validator) {
  94. const result = validator(value, ruleWithoutValidator);
  95. if (isPromise(result)) {
  96. return new Promise((r, j) => {
  97. result.then((res) => {
  98. if (!res) {
  99. tipMessage(errorMsg);
  100. r(errorMsg);
  101. } else {
  102. r(true);
  103. }
  104. });
  105. });
  106. } else {
  107. if (!result) {
  108. return _Promise(errorMsg);
  109. }
  110. }
  111. }
  112. }
  113. return Promise.resolve(true);
  114. };
  115. /**
  116. * 校验
  117. * @param customProp 指定校验,用于用户自定义场景时触发,例如 blur、change 事件
  118. * @returns
  119. */
  120. const validate = (customProp = '') => {
  121. return new Promise((resolve, reject) => {
  122. const task = findFormItem(slots.default());
  123. const errors = task.map((item) => {
  124. if (customProp) {
  125. if (customProp == item.prop) {
  126. return checkRule(item);
  127. } else {
  128. return Promise.resolve(true);
  129. }
  130. } else {
  131. return checkRule(item);
  132. }
  133. });
  134. Promise.all(errors).then((errorRes) => {
  135. errorRes = errorRes.filter((item) => item != true);
  136. const res = { valid: true, errors: [] };
  137. if (errorRes.length) {
  138. res.valid = false;
  139. res.errors = errorRes as any;
  140. }
  141. resolve(res);
  142. });
  143. });
  144. };
  145. const onSubmit = () => {
  146. validate();
  147. return false;
  148. };
  149. return { validate, reset, onSubmit, formErrorTip };
  150. }
  151. };