common.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { getPropByPath, isObject, isPromise } from '@/packages/utils/util';
  2. import { computed, PropType, 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 as PropType<import('./types').FormRules>,
  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 = () => {
  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. () => {
  32. clearErrorTips();
  33. },
  34. { immediate: true }
  35. );
  36. const findFormItem = (vnodes: VNode[]) => {
  37. let task: FormRule[] = [];
  38. vnodes.forEach((vnode: VNode) => {
  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. try {
  69. tipMessage(errorMsg);
  70. resolve(errorMsg);
  71. } catch (error) {
  72. reject(error);
  73. }
  74. });
  75. };
  76. if (!prop) {
  77. console.warn('[NutUI] <FormItem> 使用 rules 校验规则时 , 必须设置 prop 参数');
  78. }
  79. const value = getPropByPath(props.modelValue, prop || '');
  80. // clear tips
  81. tipMessage({ prop, message: '' });
  82. const formRules: FormRules = props.rules || {};
  83. const _rules = [...(formRules?.[prop] || []), ...rules];
  84. while (_rules.length) {
  85. const rule = _rules.shift() as FormItemRule;
  86. const { validator, ...ruleWithoutValidator } = rule;
  87. const { required, regex, message } = ruleWithoutValidator;
  88. const errorMsg = { prop, message };
  89. if (required) {
  90. if (!value && value !== 0) {
  91. return _Promise(errorMsg);
  92. }
  93. }
  94. if (regex && !regex.test(String(value))) {
  95. return _Promise(errorMsg);
  96. }
  97. if (validator) {
  98. const result = validator(value, ruleWithoutValidator);
  99. if (isPromise(result)) {
  100. return new Promise((r, j) => {
  101. result
  102. .then((res) => {
  103. if (!res) {
  104. tipMessage(errorMsg);
  105. r(errorMsg);
  106. } else {
  107. r(true);
  108. }
  109. })
  110. .catch((e) => j(e));
  111. });
  112. } else {
  113. if (!result) {
  114. return _Promise(errorMsg);
  115. }
  116. }
  117. }
  118. }
  119. return Promise.resolve(true);
  120. };
  121. /**
  122. * 校验
  123. * @param customProp 指定校验,用于用户自定义场景时触发,例如 blur、change 事件
  124. * @returns
  125. */
  126. const validate = (customProp = '') => {
  127. return new Promise((resolve, reject) => {
  128. try {
  129. const task = findFormItem(slots.default());
  130. const errors = task.map((item) => {
  131. if (customProp) {
  132. if (customProp == item.prop) {
  133. return checkRule(item);
  134. } else {
  135. return Promise.resolve(true);
  136. }
  137. } else {
  138. return checkRule(item);
  139. }
  140. });
  141. Promise.all(errors).then((errorRes) => {
  142. errorRes = errorRes.filter((item) => item != true);
  143. const res = { valid: true, errors: [] };
  144. if (errorRes.length) {
  145. res.valid = false;
  146. res.errors = errorRes as any;
  147. }
  148. resolve(res);
  149. });
  150. } catch (error) {
  151. reject(error);
  152. }
  153. });
  154. };
  155. const submit = () => {
  156. validate();
  157. return false;
  158. };
  159. return { validate, reset, submit, formErrorTip };
  160. }
  161. };