| | |
| | | /** |
| | | * 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: false, |
| | | duration: 2000, |
| | | duration: validation.msg.length < 16 ? 2000 : 3000, |
| | | }); |
| | | return; |
| | | } |
| | |
| | | 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 = []) => { |
| | | Object.keys(evt).forEach((key) => { |
| | | if (typeof this.formData[key] === 'undefined') { |
| | | this.$set(this.formData, key, evt[key]); |
| | | // 当表单项变化时 |
| | | 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.formData[key] = evt[key]; |
| | | 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 = {}; |