系统表单输入总是会有一些后端验证某些字段的值是否已经被占用了,比如账户的账号,如果已经被别的用户申请占用了,得提示用户被占用了
方案一:使用的是 formValidation
其实 formValidation 效果挺好的,就是为了搞个验证,js得写一大坨,见如下代码,果断放弃!
addValid: function () { var that = this; this.signin_form = KTUtil.getById("kt_login_signin_form"); // console.log(this.signin_form); this.fv = formValidation(this.signin_form, { fields: { name: { validators: { // verbose: false, // 代表验证按顺序验证 验证成功才会发最后一个remote远程验证 notEmpty: { message: "账户名称不能为空" }, remote: { message: '账户名称已经被注册', method: 'POST', data: function() { return { id: that.currAccount.id, name: that.currAccount.name }; }, validKey: "success", url: "/api/account/validate-name" } } }, password: { validators: { notEmpty: { message: "密码不能为空" }, stringLength: { min: 6, max: 20, message: '长度至少6位,最多20位' } } }, password2: { validators: { notEmpty: { message: "密码不能为空" }, callback: { message: '两次密码输入不一致', callback: function(input) { return input.value == that.currAccount.password; } } } } }, plugins: { trigger: new Trigger({ event: { name: "blur" } }), submitButton: new SubmitButton(), bootstrap: new Bootstrap() } }); this.fv.on("core.form.valid", () => { this.account2save(); return false; }); this.fv.on("core.form.invalid", () => { Swal.fire({ title: "提示", text: "请正确填写表单!", icon: "error", confirmButtonClass: "btn btn-secondary", heightAuto: false }); }); },
方案二:采用的是 vuetify 的验证
vuetify 的验证可以写的很简洁,但是后端验证是一个异步方法,得做些不同的处理,比如加两个变量,在异步方法里改变这两个变量的状态,达到异步校验的效果,代码如下
<v-text-field type="input" name="name" v-model="currAccount.name" autocomplete="off" :error-messages="myRules.remoteNameMsg" :rules="[remoteName, myRules.remoteNameValid]" validate-on-blur append-icon="mdi-account" outlined dense ></v-text-field>
问题1: 下面第一句黄色代码,如果不加上,如果去后端验证,结果为false,等到第二句黄色代码再赋值false,这个过程中,如果用户去点击了保存按钮,整体的验证是通过的(在异步方法还没有返回并执行第二句黄色代码时),那么相同的值就被存到了数据库,不过这个异步校验都是比较快的,我在后端睡一分钟才测试出的这个问题,但即使没有这个问题,也还是存在另一个问题
问题2:加上下面第一句黄色的代码,想像一下,验证方法一开始执行,我就认为你时不通过的,知道异步方法结束,不管结果是验证通过还是不通过,第二句黄色的代码都会纠正这个值,但想的总是美好的,测试结果确是,如果加上了,就会在验证通过的时候,无限制的去执行这个验证方法,具体为什么,我也没有去细究,因为这个方式还有一个问题
问题3:每次点击保存方法,都会去访问后端验证,每次鼠标离开,也都会去访问后端验证,当然前端可以保存上一次验证的值及验证的结果,下次再近验证方法的时候,判断一下是否和上次去后端验证的值一致,如果一致,直接返回上一次的验证结果,这样做又得增加代码的复杂度,于是相中了第三种方案
remoteName: function (v) { var that = this; // that.myRules.remoteNameValid = false; that.myRules.remoteNameMsg = ''; if (!v) { return '不能为空'; } that.myRules.remoteNameMsg = '等待校验'; Account.validateName(that.currAccount).then(({ data }) => { that.myRules.remoteNameValid = data; that.myRules.remoteNameMsg = data ? '' : v + " 已经经被占用,不能重复!"; }); return true; },
@PostMapping("validate-name") public JsonResult validateName(@RequestBody Account account) { List<Account> list = accountService.selectByName(account.getName(), account.getId()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return new JsonResult(list.size() <= 0); }
方案三: 利用change事件去执行验证方法
<v-text-field type="input" name="name" v-model="currAccount.name" autocomplete="off" @change="remoteName(currAccount.name)" :rules="[myRules.remoteNameValid]" append-icon="mdi-account" outlined dense ></v-text-field>
remoteName: function (v) { var that = this; that.myRules.remoteNameValid = false; if (!v) { that.myRules.remoteNameValid = '不能为空'; return } that.myRules.remoteNameValid = '等待校验'; Account.validateName(that.currAccount).then(({ data }) => { that.myRules.remoteNameValid = data ? true : v + " 已经经被占用,不能重复!"; }); },
这个方案由于是change事件,点击保存和离开输入框时,都是不会触发验证方法去访问后端的,但也还是存在一个小问题,那就是点开表单后,我什么都不填,什么都不改,直接点保存,这个change事件也不会被触发,点击保存验证表单的时候,该输入框没能做验证,因此,在表单打开之前,就必须得执行一下这个change事件上绑定的js方法,作为验证的初始化状态,代码如下,在 resetValidation 之前执行,这样打开页面后,不会显示红色错误提示,而是在你点击保存后,才会提示是否验证通过,或者在编辑该输入框之后提示是否验证通过
account2initAdd: function () { var that = this; that.resetAccount(); Account.selectRoleList().then(({ data }) => { // 模式窗口 - 新增初始化 that.accountDialog.roleList = data; that.accountDialog.dialog = true; that.accountDialog.title = "新增账户"; that.accountDialog.isAdd = true; if (that.accountDialog.orgzList.length > 0) { that.currAccount.orgzId = that.accountDialog.orgzList[0].id; } that.remoteName(that.currAccount.name); if (this.$refs.form && this.$refs.form.resetValidation) { this.$refs.form.resetValidation(); } }); }, account2initEdit: function (id) { var that = this; Account.findById(id).then(({ data }) => { // 模式窗口 - 修改初始化 that.currAccount = data.account; that.accountDialog.roleList = data.roleList; that.accountDialog.dialog = true; that.accountDialog.title = "修改账户"; that.accountDialog.isAdd = false; that.remoteName(that.currAccount.name); if (this.$refs.form && this.$refs.form.resetValidation) { this.$refs.form.resetValidation(); } }); },
案例:ip port 连个字段关联验证
<v-text-field dense v-model="videodeviceInfo.ip" outlined label="*内网ip地址" :rules="[ruleInfo.ipmsg]" @change="remoteIpPort()" ></v-text-field> <v-text-field v-model="videodeviceInfo.defaultPort" outlined dense label="*TCP端口" :rules="[ruleInfo.portmsg]" @change="remoteIpPort()" ></v-text-field>
两个字段分别处理自己前端的值校验
ipRule: function(v) { this.ruleInfo.ipmsg = true; if (!v || !this.ruleInfo.reg.test(v)) { this.ruleInfo.ipmsg = "(必输项)必须合法的IP地址。如:192.168.1.25"; return false; } return true; }, portRule: function(v) { this.ruleInfo.portmsg = true; if (!v) { this.ruleInfo.portmsg = "TCP端口是必输项"; return false; } if (v < 1 || v > 65535) { this.ruleInfo.portmsg = "端口的取值范围是:0-65535"; return false; } return true; },
后端校验要在前端校验通过的基础上进行,注意这里不能直接在 if 里写 if (!this.ipRule(ip) || !this.portRule(port)) { ,因为 || 在左边ip验证为true后就不会去执行port的校验了,或语句只要一个为true则为true,if (!(this.ipRule(ip) && this.portRule(port))) { 这样子也是不行的,与是只要一个为false 则为false
remoteIpPort: function () { var ip = this.videodeviceInfo.ip; var port = this.videodeviceInfo.defaultPort; var ipRule = this.ipRule(ip); var portRule = this.portRule(port); if (!ipRule || !portRule) { return; } this.ruleInfo.ipmsg = '等待校验'; this.ruleInfo.portmsg = '等待校验'; var param = { ip: ip, defaultPort: port, typeid: this.videodeviceInfo.proxyServiceId, id: this.videodeviceInfo.id }; window.ApiService.post( "/api/videodevice/RulesValiDate", param ).then(data => { if (data.data) { this.ruleInfo.ipmsg = true; this.ruleInfo.portmsg = true; } else { this.ruleInfo.ipmsg = data.msg; this.ruleInfo.portmsg = data.msg; } }); }
当输入表单显示后,做一次初始化验证
this.remoteIpPort();