From b29ed998189c3de5e408f37d61802dfa30e4f9a8 Mon Sep 17 00:00:00 2001 From: Tevin <tingquanren@163.com> Date: Tue, 01 Dec 2020 22:01:50 +0800 Subject: [PATCH] 优化表单模块数据传递机制,不再污染数据源 --- forms/textarea/CTextArea.vue | 14 +- forms/form/CFormItem.vue | 32 ++++--- forms/imagePicker/CImagePicker.vue | 14 +- forms/switch/CSwitch.vue | 12 +- forms/input/CInputPhoneCode.vue | 44 ++++++---- forms/chinaArea/CChinaArea.vue | 16 ++-- forms/form/CForm.vue | 54 +++++++++++- forms/input/CInput.vue | 15 ++- forms/select/CSelect.vue | 16 ++-- forms/form/CFormAgreement.vue | 6 10 files changed, 138 insertions(+), 85 deletions(-) diff --git a/forms/chinaArea/CChinaArea.vue b/forms/chinaArea/CChinaArea.vue index 6a6d577..9efc4b7 100644 --- a/forms/chinaArea/CChinaArea.vue +++ b/forms/chinaArea/CChinaArea.vue @@ -16,10 +16,10 @@ <view @tap="evt=>handleOpen(evt)"> <AtInput ref="input" - :name="itemData.name" - :title="itemData.label" - :required="itemData.required" - :error="itemData.error" + :name="itemRes.name" + :title="itemRes.label" + :required="itemRes.required" + :error="itemRes.error" :placeholder="placeholder" :value="selected" > @@ -49,7 +49,7 @@ default: false, }, placeholder: String, - itemData: Object, + itemRes: Object, }, data() { return { @@ -59,7 +59,7 @@ }, computed: { selected() { - const curVal = this.itemData.formData[this.itemData.name]; + const curVal = this.itemRes.formData[this.itemRes.name]; if (curVal && curVal.length === 3) { return getRegionNames(curVal).join(' / '); } else { @@ -74,7 +74,7 @@ evt.preventDefault(); return; } - const curVal = this.itemData.formData[this.itemData.name]; + const curVal = this.itemRes.formData[this.itemRes.name]; const range = [ locationTree, locationTree[0].children, @@ -136,7 +136,7 @@ codes[1] = city.value; const area = city.children[detail[2]]; codes[2] = area.value; - this.itemData.onChange(codes); + this.itemRes.onChange(codes); }, }, mounted() {}, diff --git a/forms/form/CForm.vue b/forms/form/CForm.vue index a13df65..6008449 100644 --- a/forms/form/CForm.vue +++ b/forms/form/CForm.vue @@ -8,7 +8,7 @@ class="c-form" @submit="evt=>handleSubmit()" > - <slot :formData="formData" /> + <slot :formRes="formRes" /> </form> </template> @@ -25,6 +25,9 @@ }, data() { return { + formRes: { + formData: this.formData, + }, validators: {}, }; }, @@ -32,10 +35,14 @@ handleSubmit() { const checklist = []; Object.keys(this.validators).forEach((key) => { - checklist.push(this.validators[key]()); + // 仍有效的验证器 + if (this.validators[key]) { + checklist.push(this.validators[key]()); + } }); Promise.all(checklist).then((validations) => { for (let validation of validations) { + // 第一个不通过项提示 if (!validation.passed) { Taro.showToast({ title: validation.msg, @@ -50,19 +57,52 @@ this.onFinish && this.onFinish(); }); }, + preCheckValidators(callback) { + const checklist = []; + Object.keys(this.validators).forEach((key) => { + // 仍有效的验证器 + if (this.validators[key]) { + checklist.push(this.validators[key]('msgOnly')); + } + }); + Promise.all(checklist).then((validations) => { + for (let validation of validations) { + // 跳过同意协议 + if (validation.name === '$agreement') { + continue; + } + if (!validation.passed) { + Taro.showToast({ + title: validation.msg, + icon: 'none', + mask: false, + duration: 2000, + }); + // 检查失败 + callback && callback(false); + return; + } + } + // 检查通过 + callback && callback(true); + }); + }, }, mounted() { - this.formData.$handleChange = (evt = []) => { + // 当表单项变化时 + this.formRes.$handleChange = (evt = []) => { Object.keys(evt).forEach((key) => { - if (typeof this.formData[key] === 'undefined') { - this.$set(this.formData, key, evt[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; }; }, diff --git a/forms/form/CFormAgreement.vue b/forms/form/CFormAgreement.vue index 2fe2408..31d6392 100644 --- a/forms/form/CFormAgreement.vue +++ b/forms/form/CFormAgreement.vue @@ -38,12 +38,12 @@ AtFloatLayout, }, props: { - formData: Object, + formRes: Object, protocol: String, }, data() { return { - name: 'agreement', + name: '$agreement', protocolShow: false, checked: false, }; @@ -61,7 +61,7 @@ }, mounted() { this.$nextTick(() => { - this.formData.$regItemValidator(this.name, () => { + this.formRes.$regItemValidator(this.name, () => { if (this.checked) { return Promise.resolve({ name: this.name, diff --git a/forms/form/CFormItem.vue b/forms/form/CFormItem.vue index ffbf570..2d0ed74 100644 --- a/forms/form/CFormItem.vue +++ b/forms/form/CFormItem.vue @@ -6,7 +6,7 @@ <template> <view class="c-form-item"> - <slot :itemData="itemData" /> + <slot :itemRes="itemRes" /> </view> </template> @@ -21,7 +21,7 @@ label: String, required: Boolean, rules: Array, - formData: Object, + formRes: Object, }, data() { return { @@ -29,9 +29,9 @@ }; }, computed: { - itemData() { + itemRes() { return { - formData: this.formData, + formData: this.formRes.formData, name: this.name, label: this.label, required: this.isRequired, @@ -71,10 +71,10 @@ } } // 未改变值不触发 - if (this.formData[this.name] === evt) { + if (this.formRes.formData[this.name] === evt) { return; } - this.formData.$handleChange({ + this.formRes.$handleChange({ [this.name]: evt, }); }, @@ -83,7 +83,7 @@ this.$nextTick(() => { // 未设置验证 if (!this.required && !this.rules) { - this.formData.$regItemValidator(this.name, () => { + this.formRes.$regItemValidator(this.name, (validateType) => { this.error = false; return Promise.resolve({ name: this.name, @@ -105,10 +105,10 @@ validator.messages(validateMsgs); // 注册验证 let errTimer = null; - this.formData.$regItemValidator(this.name, () => { + this.formRes.$regItemValidator(this.name, (validateType) => { return validator .validate({ - [this.name]: this.formData[this.name], + [this.name]: this.formRes.formData[this.name], }) .then( (res) => { @@ -119,11 +119,13 @@ }; }, ({ errors, fields }) => { - this.error = true; - clearTimeout(errTimer); - errTimer = setTimeout(() => { - this.error = false; - }, 5000); + if (validateType !== 'msgOnly') { + this.error = true; + clearTimeout(errTimer); + errTimer = setTimeout(() => { + this.error = false; + }, 5000); + } return { name: this.name, passed: false, @@ -139,7 +141,7 @@ }); }, beforeDestroy() { - this.formData.$regItemValidator(this.name, null); + this.formRes.$regItemValidator(this.name, null); }, }; </script> \ No newline at end of file diff --git a/forms/imagePicker/CImagePicker.vue b/forms/imagePicker/CImagePicker.vue index 1ed467b..0e32565 100644 --- a/forms/imagePicker/CImagePicker.vue +++ b/forms/imagePicker/CImagePicker.vue @@ -7,10 +7,10 @@ <view class="c-image-picker"> <AtInput ref="input" - :name="itemData.name" - :title="itemData.label" - :required="itemData.required" - :error="itemData.error" + :name="itemRes.name" + :title="itemRes.label" + :required="itemRes.required" + :error="itemRes.error" /> <AtImagePicker ref="picker" @@ -51,7 +51,7 @@ AtCurtain, }, props: { - itemData: Object, + itemRes: Object, }, data() { return { @@ -61,7 +61,7 @@ }, computed: { files() { - const value = this.itemData.formData[this.itemData.name]; + const value = this.itemRes.formData[this.itemRes.name]; let files = []; if (Object.prototype.toString.call(value) === '[object String]') { files = value.split(',').map((url) => ({ url })); @@ -83,7 +83,7 @@ } value.push(file.url); }); - this.itemData.onChange(value); + this.itemRes.onChange(value); }, handleImgView(index, file) { this.curtainImg = file.url; diff --git a/forms/input/CInput.vue b/forms/input/CInput.vue index c09081c..86acc3e 100644 --- a/forms/input/CInput.vue +++ b/forms/input/CInput.vue @@ -5,14 +5,14 @@ <template> <AtInput - :name="itemData.name" - :title="itemData.label" + :name="itemRes.name" + :title="itemRes.label" :type="type" :placeholder="placeholder" - :required="itemData.required" - :error="itemData.error" - :value="itemData.formData[itemData.name]" - :onChange="evt=>itemData.onChange(evt)" + :required="itemRes.required" + :error="itemRes.error" + :value="itemRes.formData[itemRes.name]" + :onChange="evt=>itemRes.onChange(evt)" > <slot /> </AtInput> @@ -29,7 +29,8 @@ props: { type: String, placeholder: String, - itemData: Object, + itemRes: Object, }, + mounted() {}, }; </script> \ No newline at end of file diff --git a/forms/input/CInputPhoneCode.vue b/forms/input/CInputPhoneCode.vue index 161473e..9f00532 100644 --- a/forms/input/CInputPhoneCode.vue +++ b/forms/input/CInputPhoneCode.vue @@ -6,14 +6,14 @@ <template> <view class="c-input-phone-code"> <AtInput - :name="itemData.name" - :title="itemData.label" + :name="itemRes.name" + :title="itemRes.label" type="text" :placeholder="placeholder" - :required="itemData.required" - :error="itemData.error" - :value="itemData.formData[itemData.name]" - :onChange="evt=>itemData.onChange(evt)" + :required="itemRes.required" + :error="itemRes.error" + :value="itemRes.formData[itemRes.name]" + :onChange="evt=>itemRes.onChange(evt)" > <AtButton class="c-input-phone-btn" @@ -38,7 +38,8 @@ }, props: { placeholder: String, - itemData: Object, + itemRes: Object, + autoStart: { type: Boolean, default: true }, onCallPhoneCode: Function, }, data() { @@ -61,16 +62,25 @@ if (this.holding) { return; } - this.holding = true; - this.cd = 60; - this.onCallPhoneCode(); - const timer = setInterval(() => { - this.cd -= 1; - if (this.cd === 0) { - this.holding = false; - this.cd = 60; - } - }, 1000); + const startCD = () => { + this.holding = true; + this.cd = 60; + setInterval(() => { + this.cd -= 1; + if (this.cd === 0) { + this.holding = false; + this.cd = 60; + } + }, 1000); + }; + if (this.autoStart) { + startCD(); + this.onCallPhoneCode(() => null); + } else { + this.onCallPhoneCode(() => { + startCD(); + }); + } }, }, }; diff --git a/forms/select/CSelect.vue b/forms/select/CSelect.vue index c7f0381..eb3e4d2 100644 --- a/forms/select/CSelect.vue +++ b/forms/select/CSelect.vue @@ -14,10 +14,10 @@ > <AtInput ref="input" - :name="itemData.name" - :title="itemData.label" - :required="itemData.required" - :error="itemData.error" + :name="itemRes.name" + :title="itemRes.label" + :required="itemRes.required" + :error="itemRes.error" :placeholder="placeholder" :value="selected" > @@ -39,7 +39,7 @@ props: { options: Array, placeholder: String, - itemData: Object, + itemRes: Object, }, data() { return {}; @@ -49,7 +49,7 @@ return typeof (this.options[0] || {}).value === 'undefined' ? 'id' : 'value'; }, current() { - const curVal = this.itemData.formData[this.itemData.name]; + const curVal = this.itemRes.formData[this.itemRes.name]; for (let i = 0, item; (item = this.options[i]); i++) { if (curVal === item[this.optionKey]) { return i; @@ -58,7 +58,7 @@ return -1; }, selected() { - const curVal = this.itemData.formData[this.itemData.name]; + const curVal = this.itemRes.formData[this.itemRes.name]; for (let i = 0, item; (item = this.options[i]); i++) { if (curVal === item[this.optionKey]) { return item.name; @@ -70,7 +70,7 @@ methods: { handleChange(evt) { const item = this.options[evt.value]; - this.itemData.onChange(item[this.optionKey]); + this.itemRes.onChange(item[this.optionKey]); }, }, mounted() {}, diff --git a/forms/switch/CSwitch.vue b/forms/switch/CSwitch.vue index 6171b54..a5620c1 100644 --- a/forms/switch/CSwitch.vue +++ b/forms/switch/CSwitch.vue @@ -6,9 +6,9 @@ <template> <view :class="['c-switch', className]"> <AtSwitch - :title="itemData.label" - :checked="itemData.formData[itemData.name]" - :onChange="evt=>itemData.onChange(evt)" + :title="itemRes.label" + :checked="itemRes.formData[itemRes.name]" + :onChange="evt=>itemRes.onChange(evt)" /> </view> </template> @@ -21,7 +21,7 @@ name: 'CSwitch', components: { AtSwitch }, props: { - itemData: Object, + itemRes: Object, }, data() { return {}; @@ -29,10 +29,10 @@ computed: { className() { let className = ''; - if (this.itemData.required) { + if (this.itemRes.required) { className += 'c-switch-required '; } - if (this.itemData.error) { + if (this.itemRes.error) { className += 'c-switch-error '; } return className; diff --git a/forms/textarea/CTextArea.vue b/forms/textarea/CTextArea.vue index 3a592ba..69f97d8 100644 --- a/forms/textarea/CTextArea.vue +++ b/forms/textarea/CTextArea.vue @@ -7,19 +7,19 @@ <view class="c-textarea"> <AtInput ref="input" - :name="itemData.name" - :title="itemData.label" - :required="itemData.required" - :error="itemData.error" + :name="itemRes.name" + :title="itemRes.label" + :required="itemRes.required" + :error="itemRes.error" /> <textarea ref="textarea" class="textarea" :style="{height: height || '2rem'}" :placeholder="placeholder" - :value="itemData.formData[itemData.name]" + :value="itemRes.formData[itemRes.name]" :autoFocus="true" - @input="evt=>itemData.onChange(evt.detail.value)" + @input="evt=>itemRes.onChange(evt.detail.value)" /> </view> </template> @@ -37,7 +37,7 @@ props: { height: String, placeholder: String, - itemData: Object, + itemRes: Object, }, data() { return {}; -- Gitblit v1.9.1