最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 让el-form更好用,通过配置的方式

    正文概述 掘金(颜酱)   2020-12-30   602

    element-ui虽然有el-form组件,但是仍然需要手动写el-form-item

    让el-form更好用,通过配置的方式

    这里希望进一步抽离配置,在el-form的基础上封装个enhanced-el-form组件。

    使用的时候希望这样,不再需要手动写里面的el-form-item

    <enhanced-el-form :model="model" :schema="schema" ></enhanced-el-form>
    

    这边借鉴cube-ui的form属性

    • model属性,表单数据对象,{name:'颜酱',age:18}
    • schema属性,每个表单项的配置数组,如下

    让el-form更好用,通过配置的方式

    本文代码后期可能较复杂,需要的话,可以去看github代码

    当然文末也附上了,每个小节的具体代码,有需要也可以看看

    TL;DR

    • 就是写了个enhanced-el-form组件,充当原先的el-form组件
    • 唯一不一样的是,rules换成schema,其他属性、事件、方法同el-form组件
    • 哦,如果表单项不足以用schema描述的话,这边提供了slot
    • 按钮部分也不需要用schema,这里提供slot#footer
    • 想直接看enhanced-el-form组件怎么用的,包括复杂情况,直接跳到本文的演示实例的优化

    组件的概况

    综上,enhanced-el-form组件的大概就出来了。

    注意,rules属性是el-form有的,这里通过schema得到

    让el-form更好用,通过配置的方式

    el-form-item的处理

    现在有一个很简单的表单,需要填写姓名年龄

    App.vue里可以简单写下:

    让el-form更好用,通过配置的方式

    enhanced-el-form内部,没有schema的情况下,长这样

    el-form(:model="model" :rules="rules")
      el-form-item(label="姓名" prop="name")
        el-input(v-model="model.name" maxlength="20")
      el-form-item(label="年龄" prop="age")
        el-input(v-model="model.age" maxlength="20")
    

    当然,有了schema,直接就循环了

    el-form(:model="model" :rules="rules")
      el-form-item(:label="config.label" :prop="config.modelKey" v-for="config in schema" :key="config.modelKey")
        el-input(v-model="model[config.modelKey]" v-bind="config.props")
    

    匹配除了input之外的元素

    表单组件当然不止input,查看官网的侧边栏发现有以下组件,其中需要配合子组件使用的放在末尾,其他的都可以单独使用。

    • radio
    • checkbox
    • input
    • input-number
    • cascader
    • switch
    • slider
    • time-select
    • date-picker
    • rate
    • color-picker
    • transfer
    • radio-group,需要子组件
    • checkbox-group,需要子组件
    • select,需要子组件
    • upload,需要子组件

    enhanced-el-form内部,可以使用component匹配不同组件,需要子组件的,单独匹配。 让el-form更好用,通过配置的方式

    option项里没有设定labelvalue的时候,默认两者一致。

    演示实例 - 添加表单项

    接下来,通过改写schema来显示表单。

    将最开始的官方的例子,用schema改写下:

    让el-form更好用,通过配置的方式

    这是大概效果,嗯,还差点,没关系,后面还有优化~

    让el-form更好用,通过配置的方式

    优化

    • el-form自身也有很多属性,这里通过简单的v-bind="$attrs",将enhanced-el-from上面的属性自动到el-form
    • 同理,v-on="$listeners"
    • el-form上面的方法,稍微麻烦点,通过手动赋值
    • 一般提交按钮不需要配置项,直接插入即可,这里增加slot#footer
    • 同理,表单的开始有可能有别的描述,这里增加slot#header
    • 部分表单项,需要定制,通过slotName属性,表示不参与内部循环,需要自己写逻辑
    • date系列的表单,可能需要多个组件拼接,一般有children,这种时候再需要自己定制的基础上,还需要处理childrenrules
    // el-form(ref="elForm" :model="model" :rules="rules" v-bind="$attrs" v-on="$listeners")
      mounted() {
        const methods = [ "validate", "validateField", "resetFields", "clearValidate" ];
        methods.forEach(method => (this[method] = this.$refs.elForm[method]));
      }
    

    以上逻辑将在下面的部分展示全部代码。

    演示实例的优化

    实例的效果:

    让el-form更好用,通过配置的方式

    EnhancedElForm的代码如下

    <template lang="pug">
    el-form(ref="elForm" :model="model" :rules="rules" v-bind="$attrs" v-on="$listeners")
      slot(name="header")
      
      template(v-for="config in schema" )
        slot(v-if="config.slotName" :name="config.slotName" v-bind="config")
     
        el-form-item(v-else :label="config.label" :prop="config.modelKey" :key="config.modelKey")
          el-radio-group(v-if="config.type==='radio-group'"   v-model="model[config.modelKey]" v-bind="config.props")
            el-radio(v-for="(item,index) in config.props.options" :key="index" :label="typeof item==='object'?item.value:item") {{ typeof item==='object'?item.label:item }}
          el-checkbox-group(v-else-if="config.type==='checkbox-group'"   v-model="model[config.modelKey]" v-bind="config.props")
            el-checkbox(v-for="(item,index) in config.props.options" :key="index" :label="typeof item==='object'?item.value:item") {{ typeof item==='object'?item.label:item }}
          el-select(v-else-if="config.type==='select'"   v-model="model[config.modelKey]" v-bind="config.props")
            el-option(v-for="(item,index) in config.props.options" :key="index" :value="typeof item==='object'?item.value:item" :label="typeof item==='object'?item.label:item")
    
          component(v-else :is="'el-'+config.type" v-model="model[config.modelKey]" v-bind="config.props") {{config.text}}
    
      slot(name="footer")
    </template>
    <script>
    export default {
      name: "enhanced-el-form",
      props: {
        model: {
          type: Object,
          default() {
            return {};
          }
        },
        schema: {
          type: Array,
          default() {
            return {};
          }
        }
      },
      computed: {
        rules() {
          return this.schema.reduce((acc, cur) => {
            acc[cur.modelKey] = cur.rules;
            // 日期组件可能有children
            const hasChildren = cur.children && cur.children.length;
            hasChildren &&
              cur.children.forEach(child => (acc[child.modelKey] = child.rules));
            return acc;
          }, {});
        }
      },
      mounted() {
        // el-form上面的方法继承过来
        const methods = [
          "validate",
          "validateField",
          "resetFields",
          "clearValidate"
        ];
        methods.forEach(method => (this[method] = this.$refs.elForm[method]));
      }
    };
    </script>
    
    

    App.vue的代码如下

    <template lang="pug">
    div#app
      enhanced-el-form(ref='ruleForm' :model="model" :schema="schema"  label-width="100px" @validate="validate")
        template(#date="config")
          el-form-item(:label="config.label")
            el-col(:span="11")
              el-form-item(:prop="config.children[0].modelKey")
                el-date-picker(v-model="model[config.children[0].modelKey]" v-bind="config.children[0].props")
            el-col(:span="2") --
            el-col(:span="11")
              el-form-item(:prop="config.children[1].modelKey")
                el-time-picker(v-model="model[config.children[1].modelKey]" v-bind="config.children[1].props")
        template(#footer)
          el-form-item
            el-button(type="primary" @click="submitForm('ruleForm')") 立即创建
            el-button(@click="resetForm('ruleForm')") 重置
      div {{model}}
    </template>
    
    <script>
    import EnhancedElForm from "./components/EnhancedElForm.vue";
    export default {
      name: "App",
      components: { EnhancedElForm },
      data() {
        return {
          model: { type: [] },
          schema: [
            {
              type: "input",
              modelKey: "name",
              label: "活动名称",
              rules: [
                { required: true, message: "请输入活动名称", trigger: "blur" },
                { min: 3, max: 5, message: "长度在 3 到 5 个字符", trigger: "blur" }
              ]
            },
            {
              type: "select",
              modelKey: "region",
              label: "活动区域",
              props: {
                placeholder: "请选择活动区域",
                options: [
                  { label: "区域一", value: "shanghai" },
                  { label: "区域二", value: "beijing" }
                ]
              },
              rules: [
                { required: true, message: "请选择活动区域", trigger: "change" }
              ]
            },
            {
              slotName: "date",
              label: "活动时间",
              children: [
                {
                  modelKey: "date1",
                  props: {
                    type: "date",
                    placeholder: "选择日期",
                    style: "width:100%"
                  },
                  rules: [
                    {
                      type: "date",
                      required: true,
                      message: "请选择日期",
                      trigger: "change"
                    }
                  ]
                },
                {
                  modelKey: "date2",
                  props: {
                    placeholder: "选择时间",
                    style: "width:100%"
                  },
                  rules: [
                    {
                      type: "date",
                      required: true,
                      message: "请选择时间",
                      trigger: "change"
                    }
                  ]
                }
              ]
            },
            {
              type: "switch",
              modelKey: "delivery",
              label: "即时配送",
              props: {}
            },
    
            {
              type: "checkbox-group",
              modelKey: "type",
              label: "活动性质",
              props: {
                options: [
                  "美食/餐厅线上活动",
                  "地推活动",
                  "线下主题活动",
                  "单纯品牌曝光"
                ]
              },
              rules: [
                {
                  type: "array",
                  required: true,
                  message: "请至少选择一个活动性质",
                  trigger: "change"
                }
              ]
            },
    
            {
              type: "radio-group",
              modelKey: "resource",
              label: "特殊资源",
              props: {
                options: [
                  { label: "线上品牌商赞助", value: "xianshang" },
                  { label: "线下场地免费", value: "xianxia" }
                ]
              },
              rules: [
                { required: true, message: "请选择活动资源", trigger: "change" }
              ]
            },
            {
              type: "input",
              modelKey: "desc",
              label: "活动形式",
              props: {
                type: "textarea"
              },
              rules: [
                { required: true, message: "请填写活动形式", trigger: "blur" }
              ]
            }
          ]
        };
      },
      methods: {
        validate(...args) {
          console.log(...args);
        },
        submitForm(formName) {
          this.$refs[formName].validate(valid => {
            if (valid) {
              alert("submit!");
            } else {
              console.log("error submit!!");
              return false;
            }
          });
        },
        resetForm(formName) {
          this.$refs[formName].resetFields();
        }
      }
    };
    </script>
    
    <style>
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      color: #2c3e50;
    }
    </style>
    
    

    不足

    为了方便,这里的组件EnhancedElForm,直接修改了父组件的model

    但这是违反规范的,回头有空再研究下,读者有思路也可以指导下~

    表单组件可能有少量遗漏,实际使用中如果发现el-undefined未注册之类的错误信息,可以自己匹配下

    代码

    代码:组件概况

    <template lang="pug">
      el-form(:model="model" :rules="rules")
    </template>
    <script>
    export default {
      name: "enhanced-el-form",
      props: {
        model: {
          type: Object, default() { return {}; }
        },
        schema: {
          type: Array, default() { return {}; }
        }
      },
      computed: {
        rules() {
          return this.schema.reduce((acc, cur) => {
            acc[cur.modelKey] = cur.rules;
            return acc;
          }, {});
        }
      }
    };
    </script>
    
    

    代码:el-form-item的处理

    App.vue

    
    <template lang="pug">
    div#app
      enhanced-el-form(:model="model" :schema="schema")
      div {{model}}
    </template>
    
    <script>
    import EnhancedElForm from "./components/EnhancedElForm.vue";
    export default {
      name: "App",
      components: { EnhancedElForm },
      data() {
        return {
          model: { name: "", age: "" },
          schema: [
            {
              type: "input", modelKey: "name", label: "姓名",
              props: { maxlength: 20 },
              rules: [{ required: true, message: "请输入姓名", trigger: "blur" }]
            },
            {
              type: "input", modelKey: "age", label: "年龄",
              props: { maxlength: 5 },
              rules: [{ required: true, message: "请输入年龄", trigger: "blur" }]
            }
          ]
        };
      }
    };
    </script>
    
    <style>
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      color: #2c3e50;
    }
    </style>
    
    

    代码:除input之外的元素

    EnhancedElForm.vue

    <template lang="pug">
    el-form(:model="model" :rules="rules")
      el-form-item(v-for="config in schema" :label="config.label" :prop="config.modelKey" :key="config.modelKey")
      
        el-radio-group(v-if="config.type==='radio-group'"   v-model="model[config.modelKey]" v-bind="config.props")
          el-radio(v-for="(item,index) in config.props.options" :key="index" :label="typeof item==='object'?item.value:item") {{ typeof item==='object'?item.label:item }}
          
        el-checkbox-group(v-else-if="config.type==='checkbox-group'"   v-model="model[config.modelKey]" v-bind="config.props")
          el-checkbox(v-for="(item,index) in config.props.options" :key="index" :label="typeof item==='object'?item.value:item") {{ typeof item==='object'?item.label:item }}
          
        el-select(v-else-if="config.type==='select'"   v-model="model[config.modelKey]" v-bind="config.props")
          el-option(v-for="(item,index) in config.props.options" :key="index" :value="typeof item==='object'?item.label:item" :label="typeof item==='object'?item.value:item")
    
        component(v-else :is="'el-'+config.type" v-model="model[config.modelKey]" v-bind="config.props") {{config.text}}
        
    </template>
    <script>
    export default {
      name: "enhanced-el-form",
      props: {
        model: {
          type: Object,
          default() {
            return {};
          }
        },
        schema: {
          type: Array,
          default() {
            return {};
          }
        }
      },
      computed: {
        rules() {
          return this.schema.reduce((acc, cur) => {
            acc[cur.modelKey] = cur.rules;
            return acc;
          }, {});
        }
      }
    };
    </script>
    
    </script>
    
    

    代码:演示实例 - 添加表单项


    起源地下载网 » 让el-form更好用,通过配置的方式

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元