From 3b03f87a02458f719e2eb4bf112a13441b427d14 Mon Sep 17 00:00:00 2001 From: ‘chensiAb’ <‘chenchenco03@163.com’> Date: Tue, 25 Mar 2025 13:54:34 +0800 Subject: [PATCH] Merge branch 'master' of ssh://dev.zhiheiot.com:29418/mob-components --- forms/form/CForm.vue | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 167 insertions(+), 13 deletions(-) diff --git a/forms/form/CForm.vue b/forms/form/CForm.vue index db0608b..7245951 100644 --- a/forms/form/CForm.vue +++ b/forms/form/CForm.vue @@ -1,46 +1,99 @@ /** - * CForm + * CForm - 表单套装组件,套件的主体 + * 表单容器组件,用于管理表单数据、处理表单验证和提交 + * 支持自动滚动到错误位置,提供表单项变化回调和表单完成回调 + * 提供手动提交、预验证和错误设置等功能 * @author Tevin */ <template> - <form - class="c-form" - @submit="evt=>handleSubmit()" + <view + :class="'c-form-outer ' + (autoScrollToError==='on'?'auto-scroll':'')" + ref="formOuter" > - <slot :formData="formData" /> - </form> + <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> import Taro from '@tarojs/taro'; +import { $ } from '@tarojs/extend'; +import './cForm.scss'; export default { name: 'CForm', props: { + // 表单数据主体 formData: Object, + // 是否自动滚动到错误位置,on/off + autoScrollToError: { + type: String, + default: 'off', + }, + // 表单项变化的回调 + // 回调传参只有单项数据 onChange: Function, + // 表单完成的回调 + // 提交且通过表单验证后调用,不通过验证不调用 + // 回调传参包含整个表单数据 onFinish: Function, }, data() { return { + formRes: { + formData: this.formData, + }, validators: {}, + scrollTop: 0, + offsetTop: 0, }; }, methods: { + handleScroll(evt) { + this.scrollTop = evt.target.scrollTop; + }, handleSubmit() { const checklist = []; - Object.keys(this.validators).forEach((key) => { - checklist.push(this.validators[key]()); + // 所有项验证 + Object.keys(this.validators).forEach(key => { + // 仍有效的验证器 + if (this.validators[key]) { + checklist.push(this.validators[key]()); + } }); - Promise.all(checklist).then((validations) => { + Promise.all(checklist).then(validations => { for (let validation of validations) { + // 第一个不通过项提示 if (!validation.passed) { + if (this.autoScrollToError === 'on') { + this.scrollTop = validation.offset.top - this.offsetTop; + } Taro.showToast({ title: validation.msg, icon: 'none', - mask: true, - duration: 2000, + mask: false, + duration: validation.msg.length < 16 ? 2000 : 3000, }); return; } @@ -49,14 +102,115 @@ this.onFinish && this.onFinish(); }); }, + // 手工提交 + $submit() { + this.handleSubmit(); + }, + // 提前验证指定项 + $preVerify(keys, callback) { + const checklist = []; + // 从选定的项中验证 + keys.forEach(key => { + // 效的验证器 + if (this.validators[key]) { + checklist.push(this.validators[key]('msgOnly')); + } + }); + Promise.all(checklist).then(validations => { + for (let validation of validations) { + // 第一个不通过项提示 + if (!validation.passed) { + if (this.autoScrollToError === 'on') { + this.scrollTop = validation.offset.top - this.offsetTop; + } + Taro.showToast({ + title: validation.msg, + icon: 'none', + mask: false, + duration: validation.msg.length < 16 ? 2000 : 3000, + }); + // 检查失败 + callback && callback(false); + return; + } + } + // 所有检查通过 + callback && callback(true); + }); + }, + // 直接设定错误 + $setErrors(errors) { + const checklist = []; + const unchecks = []; + Object.keys(errors).forEach(errorKey => { + if (typeof this.validators[errorKey] !== 'undefined') { + checklist.push( + this.validators[errorKey]('setError', errors[errorKey]), + ); + } else { + unchecks.push(errors[errorKey]); + } + }); + if (checklist.length > 0) { + Promise.all(checklist).then(validations => { + for (let validation of validations) { + // 第一个不通过项提示 + if (!validation.passed) { + if (this.autoScrollToError === 'on') { + this.scrollTop = validation.offset.top - this.offsetTop; + } + Taro.showToast({ + title: validation.msg, + icon: 'none', + mask: false, + duration: validation.msg.length < 16 ? 2000 : 3000, + }); + return; + } + } + }); + } else if (unchecks.length > 0) { + Taro.showToast({ + title: unchecks[0], + icon: 'none', + mask: false, + duration: unchecks[0] < 16 ? 2000 : 3000, + }); + } + }, + $setScrollTop(top) { + if (this.autoScrollToError === 'on') { + this.scrollTop = top; + } + }, }, mounted() { - this.formData.$handleChange = (evt) => { + // 当表单项变化时 + this.formRes.$handleChange = (evt = []) => { + Object.keys(evt).forEach(key => { + // 直接改值 + if (typeof this.formRes.formData[key] === 'undefined') { + this.$set(this.formRes.formData, key, evt[key]); + } else { + this.formRes.formData[key] = evt[key]; + } + }); this.onChange && this.onChange(evt); }; - this.formData.$regItemValidator = (name, cb) => { + // 注册表单验证器 + this.formRes.$regItemValidator = (name, cb) => { this.validators[name] = cb; }; + // 表单滚动偏移 + if (this.autoScrollToError === 'on') { + setTimeout(() => { + $(this.$refs.formOuter) + .offset() + .then(offset => { + this.offsetTop = offset.top + 10; + }); + }, 10); + } }, beforeDestroy() { this.validators = {}; -- Gitblit v1.9.1