| | |
| | | */ |
| | | |
| | | <template> |
| | | <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> |
| | |
| | | props: { |
| | | // 表单数据主体 |
| | | formData: Object, |
| | | // 是否自动滚动到错误位置,on/off |
| | | autoScrollToError: { |
| | | type: String, |
| | | default: 'off', |
| | | }, |
| | | // 表单项变化的回调 |
| | | // 回调传参只有单项数据 |
| | | onChange: Function, |
| | |
| | | formData: this.formData, |
| | | }, |
| | | validators: {}, |
| | | scrollTop: '', |
| | | }; |
| | | }, |
| | | methods: { |
| | | handleScroll(evt) { |
| | | this.scrollTop = evt.target.scrollTop; |
| | | }, |
| | | handleSubmit() { |
| | | const checklist = []; |
| | | // 所有项验证 |
| | |
| | | for (let validation of validations) { |
| | | // 第一个不通过项提示 |
| | | if (!validation.passed) { |
| | | if (this.autoScrollToError === 'on') { |
| | | this.scrollTop = validation.offset.top; |
| | | } |
| | | Taro.showToast({ |
| | | title: validation.msg, |
| | | icon: 'none', |
| | |
| | | this.onFinish && this.onFinish(); |
| | | }); |
| | | }, |
| | | preCheckValidators(keys, callback) { |
| | | // 手工提交 |
| | | $submit() { |
| | | this.handleSubmit(); |
| | | }, |
| | | // 提前验证指定项 |
| | | $preVerify(keys, callback) { |
| | | const checklist = []; |
| | | // 从选定的项中验证 |
| | | keys.forEach(key => { |
| | |
| | | for (let validation of validations) { |
| | | // 第一个不通过项提示 |
| | | if (!validation.passed) { |
| | | if (this.autoScrollToError === 'on') { |
| | | this.scrollTop = validation.offset.top; |
| | | } |
| | | Taro.showToast({ |
| | | title: validation.msg, |
| | | icon: 'none', |
| | |
| | | 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() { |
| | |
| | | */ |
| | | |
| | | <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 { |
| | |
| | | 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) { |
| | |
| | | }); |
| | | // 汉化通用验证消息 |
| | | validator.messages(validateMsgs); |
| | | return validator |
| | | return new Promise((resolve, reject) => { |
| | | validator |
| | | .validate({ |
| | | [this.name]: this.formRes.formData[this.name], |
| | | }) |
| | | .then( |
| | | res => { |
| | | this.error = false; |
| | | return { |
| | | resolve({ |
| | | name: this.name, |
| | | passed: true, |
| | | }; |
| | | }); |
| | | }, |
| | | ({ errors, fields }) => { |
| | | if (validateType !== 'msgOnly') { |
| | | this.error = true; |
| | | clearTimeout(this.errTimer); |
| | | this.errTimer = setTimeout(() => { |
| | | this.error = false; |
| | | }, 5000); |
| | | this.$setError(); |
| | | } |
| | | return { |
| | | $(this.$refs.formItem) |
| | | .offset() |
| | | .then(offset => { |
| | | resolve({ |
| | | name: this.name, |
| | | passed: false, |
| | | msg: errors[0].message.replace( |
| | | this.name, |
| | | this.label || this.name |
| | | ), |
| | | }; |
| | | offset, |
| | | }); |
| | | }); |
| | | } |
| | | ); |
| | | }); |
| | | } |
| | | }); |
| | | }); |
| | |
| | | |
| | | @import "../../common/sassMixin"; |
| | | |
| | | .c-form-outer.auto-scroll { |
| | | height: 100%; |
| | | .c-form-scroller { |
| | | height: 100%; |
| | | } |
| | | } |
| | | |
| | | .c-form { |
| | | display: block; |
| | | width: 100%; |