antd-vue 2.x 表单验证的坑,你遇到过么

好多年没有写博客了,突然写一篇。

最近学习AntdVue2.x+Vue3.x ,据说antdv 特别优秀,更新也及时还适配了vue3,所以,选择了antdv,而不是elementui,虽然elementui也有新版本了,但是总觉得那不是同一套体系了

在学习到Form表单验证的时候,发现一个问题,来这里吐个槽,看看大家有没有遇到这样的问题。

问题的起因是,Antdv表单项间距特别高,我想他这是为了可以放错误提示信息,不仅官方文档这样写,提供的pro框架也是这样的,参考了很多的第三方框架也是遵循官方的提示样式,可能大家都觉得这样挺好的,表单项下放置错误提示信息。

凡事总有个但是,但是我觉得这样不好,间距太高了,提示信息太占位置了,页面比较紧凑时,更是让人难以接受,于是,我想改一下这个显示方式,使用 鼠标悬停气泡显示错误提示信息。

表单项验证错误时,显示红框,并在后面显示红色的X,再辅助鼠标悬停气泡提示,这样就能够适应绝大多数场景。

毕竟是学习框架,看着文档发现了这个form事件validate,在每个表单项被验证后会触发事件,我就真的写了,但是他竟然不生效

我本地不生效,然后还特地写了最小验证环境  CodeSandBox  ,提交到了官方github

不过,总不能这个事件不生效,就放弃了,如果这是项目要求,总得想办法实现的。经过一天的奋战,总算解决了。

解决思路如下:

1、借用验证规则中的自定义验证,所有的规则,不管是用户自定义的验证规则,还是使用已定义的,我都再进行一层包装,包装内,自己调用验证

2、css调整表单项间距

3、悬浮气泡提示信息,借用第三方提供的(tippy.js),由于antdv并没有提供方法调用方式的tooltip

最终实现效果如下:

 

 核心代码如下:

  1 <template>
  2   <a-form
  3     name="custom-validation"
  4     class="lan-form"
  5     ref="formRef"
  6     :labelCol="{ span: 24 }"
  7     :model="user"
  8     :rules="rules"
  9     :layout="formlayout"
 10     @submit="mySubmit"
 11     @validate="myValidateForm"
 12     @validateField="myValidateFormFields"
 13     @finish="myFinish"
 14     @finishFailed="myFinishFailed"
 15   >
 16     <a-form-item
 17       has-feedback
 18       label="姓名"
 19       name="name"
 20       :labelCol="{ span: 2 }"
 21       :validateFirst="true"
 22       class="name"
 23     >
 24       <a-input v-model:value="user.name" type="text" autocomplete="off" />
 25     </a-form-item>
 26     <a-form-item
 27       has-feedback
 28       label="年龄"
 29       name="age"
 30       :labelCol="{ span: 2 }"
 31       :validateFirst="true"
 32     >
 33       <!-- <a-input-number v-model:value="user.age" class="nohandler"></a-input-number> -->
 34       <a-input v-model:value.number="user.age"></a-input>
 35     </a-form-item>
 36     <a-form-item
 37       has-feedback
 38       label="定量"
 39       name="num"
 40       :labelCol="{ span: 2 }"
 41       :validateFirst="true"
 42     >
 43       <!-- <a-input-number v-model:value="user.age" class="nohandler"></a-input-number> -->
 44       <a-input v-model:value.number="user.num"></a-input>
 45     </a-form-item>
 46     <a-form-item
 47       has-feedback
 48       label="远程"
 49       name="remote"
 50       :labelCol="{ span: 2 }"
 51       :validateFirst="true"
 52     >
 53       <a-input v-model:value.number="user.remote"></a-input>
 54     </a-form-item>
 55     <a-form-item>
 56       <a-button type="primary" html-type="submit" @click="submitForm"
 57         >提交</a-button
 58       >
 59       <a-button type="default" @click="resetForm">重置</a-button>
 60 
 61       <a-button type="default" @click="loadRules">重置</a-button>
 62       <a-button type="default" @click="validate2">重置</a-button>
 63       <a-button type="default" @click="validate3">设置验证</a-button>
 64     </a-form-item>
 65   </a-form>
 66 </template>
 67 <script>
 68 import Schema from "async-validator";
 69 import tippy from "tippy.js";
 70 export default {
 71   name: "App",
 72   data: function () {
 73     return {
 74       user: {
 75         name: "",
 76         age: 0,
 77         num: "",
 78         remote: "",
 79       },
 80       rules: {
 81         name: [
 82           {
 83             required: true,
 84             min: 3,
 85             max: 5,
 86             message: "请输入3——5个字符",
 87           },
 88         ],
 89         age: [
 90           {
 91             type: "number",
 92             min: 3,
 93             max: 5,
 94             message: "只能输入大于3小于5的数字",
 95           },
 96         ],
 97         num: [
 98           {
 99             required: true,
100             type: "enum",
101             enum: [5, 6],
102             message: "输入数字5或者6",
103           },
104           {
105             validator: function (rule, value, callback) {
106               debugger;
107               if (value == 5) {
108                 return callback();
109               }
110               return callback("请输入数字5(同步验证)");
111             },
112           },
113         ],
114         remote: [
115           {
116             type: "number",
117             //异步验证
118             min: 3,
119             max: 9,
120             message: "只能输入大于3小于9的数字",
121           },
122           {
123             type: "number",
124             asyncValidator: function (rule, value, callback) {
125               setTimeout(function () {
126                 if (value == 5) {
127                   return callback();
128                 }
129                 return callback("只能输入数字5(异步验证)");
130               }, 1000);
131             },
132           },
133         ],
134       },
135     };
136   },
137   methods: {
138     validateField: function (
139       model,
140       rules,
141       isFirst,
142       successCallback,
143       failCallback,
144       preCallback
145     ) {
146       let validator = new Schema(rules);
147       let option = isFirst ? { firstFields: true } : {};
148       return validator
149         .validate(model, option)
150         .then(() => {
151           // 校验通过
152           successCallback(model);
153           preCallback();
154         })
155         .catch(({ fields, errors }) => {
156           failCallback(model, errors);
157           preCallback(errors);
158         });
159     },
160     validateSucessCallback: function (argmodel) {
161       try {
162         for (let field in argmodel) {
163           let formElement = document.getElementById(
164             "custom-validation_" + field
165           );
166           let formitemWrapper =
167             formElement.parentElement.parentElement.parentElement;
168           let lanTippy2 = formitemWrapper.lanTippy;
169           if (lanTippy2) {
170             lanTippy2.hide();
171             lanTippy2.disable();
172           }
173         }
174       } catch (e) {
175         console.error(e);
176       }
177     },
178     validateFailCallback: function (model, errors) {
179       let errorMessages = "";
180       for (let { field, message } of errors) {
181         errorMessages += message + "<br/>";
182       }
183       let formElement = document.getElementById(
184         "custom-validation_" + Object.keys(model)[0]
185       );
186       let formitemWrapper =
187         formElement.parentElement.parentElement.parentElement;
188       let lanTippy2 = formitemWrapper.lanTippy;
189       if (lanTippy2) {
190         lanTippy2.setContent(errorMessages);
191         lanTippy2.enable();
192         lanTippy2.show();
193       } else {
194         let lanTippy = tippy(formitemWrapper, {
195           content: errorMessages,
196           allowHTML: true,
197         });
198         lanTippy.show();
199         formitemWrapper.lanTippy = lanTippy;
200       }
201     },
202     setFormFieldValidate: function (formRef, fields) {
203       let self = this;
204       let formModel = formRef.model;
205       let formRules = formRef.rules;
206       if (!formModel) {
207         throw "表单未设置model";
208       }
209       if (!formRules) {
210         throw "表单未设置rules";
211       }
212       if (formRef.$el.className.indexOf("lan-form") == -1) {
213         formRef.$el.className = formRef.$el.className + " lan-form";
214       }
215 
216       let model = {};
217       if (fields && fields instanceof Array) {
218         for (let i = 0; i < fields.length; i++) {
219           let f = fields[i];
220           model[f] = formModel[f];
221         }
222       } else if (fields && fields instanceof String) {
223         model[fields] = formModel[fields];
224       } else {
225         model = formModel;
226       }
227       for (let key in model) {
228         let ruleItemArr = formRules[key];
229         if (!ruleItemArr) {
230           continue;
231         }
232         let miniRuleArr = [];
233         for (let i = 0; i < ruleItemArr.length; i++) {
234           let orginalRuleInfo = ruleItemArr[i];
235           let ruleInfo = JSON.parse(JSON.stringify(orginalRuleInfo));
236 
237           let miniValidateField = function (rule, value, callback) {
238             // if(rule&&(rule.field=='validator'||rule.field=='asyncValidator')){debugger;
239             //     return;
240             // }
241             let ruleObj = {};
242             ruleObj[this.key] = this.rule;
243             let modelObj = {};
244             modelObj[this.key] = value;
246             self.validateField(
247               modelObj,
248               ruleObj,
249               false,
250               self.validateSucessCallback,
251               self.validateFailCallback,
252               callback
253             );
254           };
255           if (orginalRuleInfo.validator) {
256             ruleInfo.validator = orginalRuleInfo.validator;
257             orginalRuleInfo.validator = miniValidateField.bind({
258               key: key,
259               rule: ruleInfo,
260             });
261           } else if (orginalRuleInfo.asyncValidator) {
262             ruleInfo.asyncValidator = orginalRuleInfo.asyncValidator;
263             orginalRuleInfo.asyncValidator = miniValidateField.bind({
264               key: key,
265               rule: ruleInfo,
266             });
267           } else {
268             orginalRuleInfo.validator = miniValidateField.bind({
269               key: key,
270               rule: ruleInfo,
271             });
272           }
273         }
274       }
275     },
276   },
277 
278   mounted: function () {
279     this.setFormFieldValidate(this.$refs.formRef);
280   },
281 };
282 </script>
283 
284 <style lang="less">
285 .lan-form {
286   .ant-form-item-control {
287     border-radius: 5px;
288     .ant-input,
289     .ant-input-number {
290       border-radius: 5px;
291     }
292   }
293   .ant-form-item {
294     margin-bottom: 5px;
295   }
296   .ant-form-explain {
297     display: none;
298   }
299   .ant-input-number {
300     width: 100%;
301     .ant-input-number-handler-wrap {
302       display: none;
303     }
304   }
305 }
306 </style>

 

 

最小环境校验

 

 

 

官方文档Form有这么一个事件,validate,任一表单项被校验后触发

 

posted @ 2021-04-16 22:39  郑延杰  阅读(2155)  评论(0编辑  收藏  举报