gaoluyang
3 天以前 92230c9a97dc9ce9df3313d11d26999c04bb6b26
src/uni_modules/uni-forms/components/uni-forms/uni-forms.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,397 @@
<template>
   <view class="uni-forms">
      <form>
         <slot></slot>
      </form>
   </view>
</template>
<script>
   import Validator from './validate.js';
   import {
      deepCopy,
      getValue,
      isRequiredField,
      setDataValue,
      getDataValue,
      realName,
      isRealName,
      rawData,
      isEqual
   } from './utils.js'
   // #ifndef VUE3
   // åŽç»­ä¼šæ…¢æ…¢åºŸå¼ƒè¿™ä¸ªæ–¹æ³•
   import Vue from 'vue';
   Vue.prototype.binddata = function(name, value, formName) {
      if (formName) {
         this.$refs[formName].setValue(name, value);
      } else {
         let formVm;
         for (let i in this.$refs) {
            const vm = this.$refs[i];
            if (vm && vm.$options && vm.$options.name === 'uniForms') {
               formVm = vm;
               break;
            }
         }
         if (!formVm) return console.error('当前 uni-froms ç»„件缺少 ref å±žæ€§');
         formVm.setValue(name, value);
      }
   };
   // #endif
   /**
    * Forms è¡¨å•
    * @description ç”±è¾“入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据
    * @tutorial https://ext.dcloud.net.cn/plugin?id=2773
    * @property {Object} rules   è¡¨å•校验规则
    * @property {String} validateTrigger = [bind|submit|blur]   æ ¡éªŒè§¦å‘器方式 é»˜è®¤ submit
    * @value bind      å‘生变化时触发
    * @value submit   æäº¤æ—¶è§¦å‘
    * @value blur     å¤±åŽ»ç„¦ç‚¹æ—¶è§¦å‘
    * @property {String} labelPosition = [top|left]   label ä½ç½® é»˜è®¤ left
    * @value top      é¡¶éƒ¨æ˜¾ç¤º label
    * @value left   å·¦ä¾§æ˜¾ç¤º label
    * @property {String} labelWidth   label å®½åº¦ï¼Œé»˜è®¤ 65px
    * @property {String} labelAlign = [left|center|right]   label å±…中方式  é»˜è®¤ left
    * @value left      label å·¦ä¾§æ˜¾ç¤º
    * @value center   label å±…中
    * @value right      label å³ä¾§å¯¹é½
    * @property {String} errShowType = [undertext|toast|modal]   æ ¡éªŒé”™è¯¯ä¿¡æ¯æç¤ºæ–¹å¼
    * @value undertext   é”™è¯¯ä¿¡æ¯åœ¨åº•部显示
    * @value toast         é”™è¯¯ä¿¡æ¯toast显示
    * @value modal         é”™è¯¯ä¿¡æ¯modal显示
    * @event {Function} submit   æäº¤æ—¶è§¦å‘
    * @event {Function} validate   æ ¡éªŒç»“果发生变化触发
    */
   export default {
      name: 'uniForms',
      emits: ['validate', 'submit'],
      options: {
         virtualHost: true
      },
      props: {
         // å³å°†å¼ƒç”¨
         value: {
            type: Object,
            default () {
               return null;
            }
         },
         // vue3 æ›¿æ¢ value å±žæ€§
         modelValue: {
            type: Object,
            default () {
               return null;
            }
         },
         // 1.4.0 å¼€å§‹å°†ä¸æ”¯æŒ v-model ï¼Œä¸”废弃 value å’Œ modelValue
         model: {
            type: Object,
            default () {
               return null;
            }
         },
         // è¡¨å•校验规则
         rules: {
            type: Object,
            default () {
               return {};
            }
         },
         //校验错误信息提示方式 é»˜è®¤ undertext å–值 [undertext|toast|modal]
         errShowType: {
            type: String,
            default: 'undertext'
         },
         // æ ¡éªŒè§¦å‘器方式 é»˜è®¤ bind å–值 [bind|submit]
         validateTrigger: {
            type: String,
            default: 'submit'
         },
         // label ä½ç½®ï¼Œé»˜è®¤ left å–值  top/left
         labelPosition: {
            type: String,
            default: 'left'
         },
         // label å®½åº¦
         labelWidth: {
            type: [String, Number],
            default: ''
         },
         // label å±…中方式,默认 left å–值 left/center/right
         labelAlign: {
            type: String,
            default: 'left'
         },
         border: {
            type: Boolean,
            default: false
         }
      },
      provide() {
         return {
            uniForm: this
         }
      },
      data() {
         return {
            // è¡¨å•本地值的记录,不应该与传如的值进行关联
            formData: {},
            formRules: {}
         };
      },
      computed: {
         // è®¡ç®—数据源变化的
         localData() {
            const localVal = this.model || this.modelValue || this.value
            if (localVal) {
               return deepCopy(localVal)
            }
            return {}
         }
      },
      watch: {
         // ç›‘听数据变化 ,暂时不使用,需要单独赋值
         // localData: {},
         // ç›‘听规则变化
         rules: {
            handler: function(val, oldVal) {
               this.setRules(val)
            },
            deep: true,
            immediate: true
         }
      },
      created() {
         // #ifdef VUE3
         let getbinddata = getApp().$vm.$.appContext.config.globalProperties.binddata
         if (!getbinddata) {
            getApp().$vm.$.appContext.config.globalProperties.binddata = function(name, value, formName) {
               if (formName) {
                  this.$refs[formName].setValue(name, value);
               } else {
                  let formVm;
                  for (let i in this.$refs) {
                     const vm = this.$refs[i];
                     if (vm && vm.$options && vm.$options.name === 'uniForms') {
                        formVm = vm;
                        break;
                     }
                  }
                  if (!formVm) return console.error('当前 uni-froms ç»„件缺少 ref å±žæ€§');
                  formVm.setValue(name, value);
               }
            }
         }
         // #endif
         // å­ç»„件实例数组
         this.childrens = []
         // TODO å…¼å®¹æ—§ç‰ˆ uni-data-picker ,新版本中无效,只是避免报错
         this.inputChildrens = []
         this.setRules(this.rules)
      },
      methods: {
         /**
          * å¤–部调用方法
          * è®¾ç½®è§„则 ï¼Œä¸»è¦ç”¨äºŽå°ç¨‹åºè‡ªå®šä¹‰æ£€éªŒè§„则
          * @param {Array} rules è§„则源数据
          */
         setRules(rules) {
            // TODO æœ‰å¯èƒ½å­ç»„件合并规则的时机比这个要早,所以需要合并对象 ï¼Œè€Œä¸æ˜¯ç›´æŽ¥èµ‹å€¼ï¼Œå¯èƒ½ä¼šè¢«è¦†ç›–
            this.formRules = Object.assign({}, this.formRules, rules)
            // åˆå§‹åŒ–校验函数
            this.validator = new Validator(rules);
         },
         /**
          * å¤–部调用方法
          * è®¾ç½®æ•°æ®ï¼Œç”¨äºŽè®¾ç½®è¡¨å•数据,公开给用户使用 ï¼Œ ä¸æ”¯æŒåœ¨åŠ¨æ€è¡¨å•ä¸­ä½¿ç”¨
          * @param {Object} key
          * @param {Object} value
          */
         setValue(key, value) {
            let example = this.childrens.find(child => child.name === key);
            if (!example) return null;
            this.formData[key] = getValue(key, value, (this.formRules[key] && this.formRules[key].rules) || [])
            return example.onFieldChange(this.formData[key]);
         },
         /**
          * å¤–部调用方法
          * æ‰‹åŠ¨æäº¤æ ¡éªŒè¡¨å•
          * å¯¹æ•´ä¸ªè¡¨å•进行校验的方法,参数为一个回调函数。
          * @param {Array} keepitem ä¿ç•™ä¸å‚与校验的字段
          * @param {type} callback æ–¹æ³•回调
          */
         validate(keepitem, callback) {
            return this.checkAll(this.formData, keepitem, callback);
         },
         /**
          * å¤–部调用方法
          * éƒ¨åˆ†è¡¨å•校验
          * @param {Array|String} props éœ€è¦æ ¡éªŒçš„字段
          * @param {Function} å›žè°ƒå‡½æ•°
          */
         validateField(props = [], callback) {
            props = [].concat(props);
            let invalidFields = {};
            this.childrens.forEach(item => {
               const name = realName(item.name)
               if (props.indexOf(name) !== -1) {
                  invalidFields = Object.assign({}, invalidFields, {
                     [name]: this.formData[name]
                  });
               }
            });
            return this.checkAll(invalidFields, [], callback);
         },
         /**
          * å¤–部调用方法
          * ç§»é™¤è¡¨å•项的校验结果。传入待移除的表单项的 prop å±žæ€§æˆ–者 prop ç»„成的数组,如不传则移除整个表单的校验结果
          * @param {Array|String} props éœ€è¦ç§»é™¤æ ¡éªŒçš„字段 ï¼Œä¸å¡«ä¸ºæ‰€æœ‰
          */
         clearValidate(props = []) {
            props = [].concat(props);
            this.childrens.forEach(item => {
               if (props.length === 0) {
                  item.errMsg = '';
               } else {
                  const name = realName(item.name)
                  if (props.indexOf(name) !== -1) {
                     item.errMsg = '';
                  }
               }
            });
         },
         /**
          * å¤–部调用方法 ï¼Œå³å°†åºŸå¼ƒ
          * æ‰‹åŠ¨æäº¤æ ¡éªŒè¡¨å•
          * å¯¹æ•´ä¸ªè¡¨å•进行校验的方法,参数为一个回调函数。
          * @param {Array} keepitem ä¿ç•™ä¸å‚与校验的字段
          * @param {type} callback æ–¹æ³•回调
          */
         submit(keepitem, callback, type) {
            for (let i in this.dataValue) {
               const itemData = this.childrens.find(v => v.name === i);
               if (itemData) {
                  if (this.formData[i] === undefined) {
                     this.formData[i] = this._getValue(i, this.dataValue[i]);
                  }
               }
            }
            if (!type) {
               console.warn('submit æ–¹æ³•即将废弃,请使用validate方法代替!');
            }
            return this.checkAll(this.formData, keepitem, callback, 'submit');
         },
         // æ ¡éªŒæ‰€æœ‰
         async checkAll(invalidFields, keepitem, callback, type) {
            // ä¸å­˜åœ¨æ ¡éªŒè§„则 ï¼Œåˆ™åœæ­¢æ ¡éªŒæµç¨‹
            if (!this.validator) return
            let childrens = []
            // å¤„理参与校验的item实例
            for (let i in invalidFields) {
               const item = this.childrens.find(v => realName(v.name) === i)
               if (item) {
                  childrens.push(item)
               }
            }
            // å¦‚æžœvalidate第一个参数是funciont ,那就走回调
            if (!callback && typeof keepitem === 'function') {
               callback = keepitem;
            }
            let promise;
            // å¦‚果不存在回调,那么使用 Promise æ–¹å¼è¿”回
            if (!callback && typeof callback !== 'function' && Promise) {
               promise = new Promise((resolve, reject) => {
                  callback = function(valid, invalidFields) {
                     !valid ? resolve(invalidFields) : reject(valid);
                  };
               });
            }
            let results = [];
            // é¿å…å¼•用错乱 ï¼Œå»ºè®®æ‹·è´å¯¹è±¡å¤„理
            let tempFormData = JSON.parse(JSON.stringify(invalidFields))
            // æ‰€æœ‰å­ç»„件参与校验,使用 for å¯ä»¥ä½¿ç”¨  awiat
            for (let i in childrens) {
               const child = childrens[i]
               let name = realName(child.name);
               const result = await child.onFieldChange(tempFormData[name]);
               if (result) {
                  results.push(result);
                  // toast ,modal åªéœ€è¦æ‰§è¡Œç¬¬ä¸€æ¬¡å°±å¯ä»¥
                  if (this.errShowType === 'toast' || this.errShowType === 'modal') break;
               }
            }
            if (Array.isArray(results)) {
               if (results.length === 0) results = null;
            }
            if (Array.isArray(keepitem)) {
               keepitem.forEach(v => {
                  let vName = realName(v);
                  let value = getDataValue(v, this.localData)
                  if (value !== undefined) {
                     tempFormData[vName] = value
                  }
               });
            }
            // TODO submit å³å°†åºŸå¼ƒ
            if (type === 'submit') {
               this.$emit('submit', {
                  detail: {
                     value: tempFormData,
                     errors: results
                  }
               });
            } else {
               this.$emit('validate', results);
            }
            // const resetFormData = rawData(tempFormData, this.localData, this.name)
            let resetFormData = {}
            resetFormData = rawData(tempFormData, this.name)
            callback && typeof callback === 'function' && callback(results, resetFormData);
            if (promise && callback) {
               return promise;
            } else {
               return null;
            }
         },
         /**
          * è¿”回validate事件
          * @param {Object} result
          */
         validateCheck(result) {
            this.$emit('validate', result);
         },
         _getValue: getValue,
         _isRequiredField: isRequiredField,
         _setDataValue: setDataValue,
         _getDataValue: getDataValue,
         _realName: realName,
         _isRealName: isRealName,
         _isEqual: isEqual
      }
   };
</script>
<style lang="scss">
   .uni-forms {}
</style>