From a4563d338b4df06d3fe6ca9123dcd1ec9c23a81b Mon Sep 17 00:00:00 2001 From: Tevin <tingquanren@163.com> Date: Fri, 17 Nov 2023 11:40:37 +0800 Subject: [PATCH] 表单体验升级,支持设定错误集,支持错误自动滚动 --- forms/form/CFormItem.vue | 91 ++++++++++++++-------- forms/form/cForm.scss | 7 + forms/form/CForm.vue | 84 ++++++++++++++++++--- 3 files changed, 137 insertions(+), 45 deletions(-) diff --git a/forms/form/CForm.vue b/forms/form/CForm.vue index 541c3b0..8789128 100644 --- a/forms/form/CForm.vue +++ b/forms/form/CForm.vue @@ -4,12 +4,30 @@ */ <template> - <form - class="c-form" - @submit="evt=>handleSubmit()" - > - <slot :formRes="formRes" /> - </form> + <view :class="'c-form-outer ' + (autoScrollToError==='on'?'auto-scroll':'')"> + <form + class="c-form" + v-if="autoScrollToError==='off'" + @submit="evt=>handleSubmit()" + > + <slot :formRes="formRes" /> + </form> + <scroll-view + class="c-form-scroller" + v-else-if="autoScrollToError==='on'" + :scrollY="true" + :scroll-top="scrollTop" + :scroll-with-animation="true" + @scroll="evt => handleScroll(evt)" + > + <form + class="c-form" + @submit="evt=>handleSubmit()" + > + <slot :formRes="formRes" /> + </form> + </scroll-view> + </view> </template> <script> @@ -21,6 +39,11 @@ props: { // 表单数据主体 formData: Object, + // 是否自动滚动到错误位置,on/off + autoScrollToError: { + type: String, + default: 'off', + }, // 表单项变化的回调 // 回调传参只有单项数据 onChange: Function, @@ -35,9 +58,13 @@ formData: this.formData, }, validators: {}, + scrollTop: '', }; }, methods: { + handleScroll(evt) { + this.scrollTop = evt.target.scrollTop; + }, handleSubmit() { const checklist = []; // 所有项验证 @@ -51,6 +78,9 @@ for (let validation of validations) { // 第一个不通过项提示 if (!validation.passed) { + if (this.autoScrollToError === 'on') { + this.scrollTop = validation.offset.top; + } Taro.showToast({ title: validation.msg, icon: 'none', @@ -64,7 +94,12 @@ this.onFinish && this.onFinish(); }); }, - preCheckValidators(keys, callback) { + // 手工提交 + $submit() { + this.handleSubmit(); + }, + // 提前验证指定项 + $preVerify(keys, callback) { const checklist = []; // 从选定的项中验证 keys.forEach(key => { @@ -77,6 +112,9 @@ for (let validation of validations) { // 第一个不通过项提示 if (!validation.passed) { + if (this.autoScrollToError === 'on') { + this.scrollTop = validation.offset.top; + } Taro.showToast({ title: validation.msg, icon: 'none', @@ -92,11 +130,33 @@ callback && callback(true); }); }, - $submit() { - this.handleSubmit(); - }, - $preVerify(keys, callback) { - this.preCheckValidators(keys, callback); + // 直接设定错误 + $setErrors(errors) { + const checklist = []; + errors.forEach(errorItem => { + if (typeof this.validators[errorItem.key] !== 'undefined') { + checklist.push( + this.validators[errorItem.key]('setError', errorItem.msg) + ); + } + }); + Promise.all(checklist).then(validations => { + for (let validation of validations) { + // 第一个不通过项提示 + if (!validation.passed) { + if (this.autoScrollToError === 'on') { + this.scrollTop = validation.offset.top; + } + Taro.showToast({ + title: validation.msg, + icon: 'none', + mask: false, + duration: 2000, + }); + return; + } + } + }); }, }, mounted() { diff --git a/forms/form/CFormItem.vue b/forms/form/CFormItem.vue index 1187f85..3cb342b 100644 --- a/forms/form/CFormItem.vue +++ b/forms/form/CFormItem.vue @@ -5,13 +5,17 @@ */ <template> - <view class="c-form-item"> + <view + class="c-form-item" + ref="formItem" + > <slot :itemRes="itemRes" /> </view> </template> <script> import Schema from 'async-validator'; +import { $ } from '@tarojs/extend'; import { validateMsgs } from './validateMsgs.js'; export default { @@ -109,15 +113,33 @@ mounted() { this.$nextTick(() => { // 注册验证 - this.formRes.$regItemValidator(this.name, validateType => { + this.formRes.$regItemValidator(this.name, (validateType, errMsg) => { + // 直接设定错误 + if (validateType === 'setError') { + this.$setError(); + return new Promise((resolve, reject) => { + $(this.$refs.formItem) + .offset() + .then(offset => { + resolve({ + name: this.name, + passed: false, + msg: errMsg, + offset, + }); + }); + }); + } // 未设置验证 - if (!this.required && !this.rules) { + else if (!this.required && !this.rules) { this.error = false; return Promise.resolve({ name: this.name, passed: true, }); - } else { + } + // 正常验证 + else { // 验证规则 const descriptor = this.rules || []; if (this.required) { @@ -130,36 +152,39 @@ }); // 汉化通用验证消息 validator.messages(validateMsgs); - return validator - .validate({ - [this.name]: this.formRes.formData[this.name], - }) - .then( - res => { - this.error = false; - return { - name: this.name, - passed: true, - }; - }, - ({ errors, fields }) => { - if (validateType !== 'msgOnly') { - this.error = true; - clearTimeout(this.errTimer); - this.errTimer = setTimeout(() => { - this.error = false; - }, 5000); + return new Promise((resolve, reject) => { + validator + .validate({ + [this.name]: this.formRes.formData[this.name], + }) + .then( + res => { + this.error = false; + resolve({ + name: this.name, + passed: true, + }); + }, + ({ errors, fields }) => { + if (validateType !== 'msgOnly') { + this.$setError(); + } + $(this.$refs.formItem) + .offset() + .then(offset => { + resolve({ + name: this.name, + passed: false, + msg: errors[0].message.replace( + this.name, + this.label || this.name + ), + offset, + }); + }); } - return { - name: this.name, - passed: false, - msg: errors[0].message.replace( - this.name, - this.label || this.name - ), - }; - } - ); + ); + }); } }); }); diff --git a/forms/form/cForm.scss b/forms/form/cForm.scss index 94c760e..23d53af 100644 --- a/forms/form/cForm.scss +++ b/forms/form/cForm.scss @@ -5,6 +5,13 @@ @import "../../common/sassMixin"; +.c-form-outer.auto-scroll { + height: 100%; + .c-form-scroller { + height: 100%; + } +} + .c-form { display: block; width: 100%; -- Gitblit v1.9.1