vue动态渲染表单配置项

vue动态渲染表单配置项

在项目中,我们经常会用到各种表单,但是数量一般都不多,那当我们编写大量表单时怎么办呢,难道我们还要一个标签一个标签的写吗?如果各个表单之间又出现了各种级联关系各种判断呢?所以这时候就出现了另外一种方式,我们可以通过对象的方式编写表单项来替换element本来的标签写法,以使我们能够方便清晰的最大化的控制每个表单。

当我们编写一个普通的input表单时,我们一般会直接在template里这样写:

<el-input v-model="inputValue" placeholder="请输入"></el-input>

用了表单配置项后,我们可以这样在data里写:

{
  type: 'input',
  key: 'inputValue',
  label: 'input框:',
  placeholder: '请输入'
}

我们先不讨论怎么就可以这样写了,我们先来明白为什么要这样用

  • 可以通过this.XXX来改变某个表单项
  • 不用再在标签里加各种判断
  • 标签写法太死板
  • render写法,更贴近vue底层

如何配置

input举栗,最后会附上其他的表单项

// 准备一个文件---itemRenders.js
export default {
  name: 'ItemRender',
  props: {
    // 类型,input or select or ...
    type: String,
    // 主要配置项
    config: Object
  },
  // 渲染函数
  render: function (h) {
    switch (this.type) {
      case 'input':
        return inputItem(h, this.config);
      default:
        return '';
    }
  }
};  

function inputItem(h, config) {
  let {
    key, // 绑定的值
    props, // 属性, 如disable等
    listeners, // 事件,如onblur等
    placeholder,
    areaProps // 额外属性如type等
  } = config;
  let on = listeners || {};
  let pps = props || {};
  let area = areaProps || {};
  return h('el-input', {
    props: {
      ...pps,
      value: key,
      disabled: pps.disabled
    },
    attrs: {
      placeholder: placeholder || pps.placeholder || '请输入',
      ...area
    },
    on: {
      ...on,
      // 实现双向绑定
      input: (val) => {
        key = val;
      }
    }
  });
}

如何使用

在.vue文件中使用itemRenders.js

<template>
    <div>
        <item-render
          :type="item.type"
          :config="{
            placeholder: item.placeholder || '',
            ...item.config,
            key: item.key
          }"
        >
        </item-render>
    </div>
</template>

<script>
    import ItemRender from './itemRenders';
    export default {
        components: { 'item-render': ItemRender },
        data() {
            return {
                item: {
                    type: 'input',
                     key: 'inputValue',
                     label: 'input框:',
                     placeholder: '请输入',
                     config: {
                        props: {
                          disabled: true
                        },
                        listeners: {
                          blur: _this.blur1
                        }
                     }
                }
            }
        }
    }
</script>

这只是渲染一个input的写法,但既然会用到这种写法,那就代表要渲染的表单数量可能有点大,像我们项目中有些页面新增时表单项多达100+,所以,如果页面只用到个位数的表单的情况,这里还是建议亲亲直接使用ele提供的标签写法。

渲染大量表单

那渲染大量表单时要怎么配置呢?

其实也很简单,套个form就行了(主要以提交form表单为主)

我们可以先来准备个form表单组件,用的时候只需引入该组件即可

// formRender.vue
<template>
  <el-form
    ref="formRender"
    :model="model"
    :rules="rules"
  >
    <div class="block">
      <el-form-item
        v-for="item in items"
        :key="item.key"
        :label="item.label"
        :prop="item.key"
      >
        <item-render
          :type="item.type"
          :config="{
            placeholder: item.placeholder || '',
            ...item.config,
            model,
            key: item.key
          }"
        >
        </item-render>
      </el-form-item>
    </div>
  </el-form>
</template>
<script>
    import ItemRender from './itemRenders';
    export default {
        components: { 'item-render': ItemRender },
        props: {
            model: {
              type: Object,
              default: () => {}
            },
            // 规则
            rules: {
              type: Object,
              default: () => {}
            },
            items: {
              type: Array,
              default: () => []
            }
        },
    }
</script>

项目中使用

<template>
    <FormRender
        ref="formRender"
        :model="form"
        :rules="basicRules"
        :items="items1"
      ></FormRender>
</template>
<script>
    import FormRender from './formRender';
    export default {
        components: {FormRender},
        data() {
            return {
                form: {
                    formItem1: '',
                    formItem2: [],
                    formItem3: ''
                },
                items1: [
                    // 输入框
                    {
                        type: 'input',
                        key: 'formItem1',
                        label: 'formItem1:',
                        placeholder: '请输入',
                    },
                    // 下拉框
                    {
                        type: 'select',
                        key: 'formItem2',
                        label: 'formItem2:',
                        placeholder: '请选择',
                        config: {
                            props: {
                              filterable: true,
                              clearable: true,
                              disabled: false
                            },
                            options: () => _this.formItem2s,
                            optionConfig: {
                              label: 'name',
                              value: 'code'
                            },
                            listeners: {
                              change: _this.handleChange
                            }
                      }
                    }
                ],
                basicRules: {
                    formItem1: [ { required: true, trigger: 'blur' }]
                },
                formItem2s: []
            }
         }
    }
</script>

其余表单配置项

这里只提供部分常用的给大家做参考,大家也可以从别的角度去考虑,适合我们的并不一定适合所有人

  • 下拉选择框

    function selectItem(h, config) {
    let {
      model,
      key,
      props,
      listeners,
      options,
      optionConfig,
      placeholder,
      syncConfig
    } = config;
    
    let opl = 'label';
    let opv = 'value';
    if (optionConfig) {
      if (optionConfig.label) {
        opl = optionConfig.label;
      }
      if (optionConfig.value) {
        opv = optionConfig.value;
      }
    }
    
    let opts = options();
    let ops = [];
    if (opts) {
      ops = opts.map((option) => {
        return h('el-option', {
          key: option[opv],
          props: {
            label: option[opl] || option['text'],
            value: option[opv],
            disabled: option.disabled
          }
        });
      });
    }
    
    let on = listeners || {};
    let pps = props || {};
    if (syncConfig) {
      pps = {
        ...pps,
        ...syncConfig()
      };
    }
    
    return h(
      'el-select', {
        props: {
          placeholder,
          ...pps,
          value: model[key]
        },
        on: {
          ...on,
          change: (val) => {
            model[key] = val;
            if (on.change) {
              on.change(val);
            }
          }
        }
      },
      ops
    );
    }
    
  • 日期选择器

    function dateItem(h, config) {
      let {
        model,
        key,
        props,
        listeners,
        placeholder,
        type
      } = config;
      let pps = props || {};
      let on = listeners || {};
      return h('el-date-picker', {
        props: {
          ...pps,
          value: model[key]
        },
        attrs: {
          placeholder: placeholder || pps.placeholder || '请输入',
          type: type || 'date'
        },
        on: {
          ...on,
          input: (val) => {
            model[key] = val;
          }
        }
      });
    }
    
  • 计数器

    function inputNumberItem(h, config) {
      let {
        model,
        key,
        props
      } = config;
      let pps = props || {};
      return h('el-input-number', {
        props: {
          ...pps,
          value: model[key]
        },
        on: {
          change: (val) => {
            model[key] = val;
          }
        }
      });
    }
    
  • 上传

    function uploadItem(h, config) {
      let {
        model,
        key,
        props,
        btnProps,
        listeners,
        iconProps
      } = config;
      let pps = props || {};
      let btnProp = btnProps || {};
      let iconProp = iconProps || {};
      let on = listeners || {};
      return h(
        'el-upload', {
          props: {
            ...pps,
            'file-list': model[key]
          },
          class: {
            upload_demo: true
          },
          on: {
            ...on
          }
        },
        [
          h(
            'el-button', {
              props: {
                ...btnProp,
                size: btnProp.size || 'small'
              }
            },
            [
              h('svg-icon', {
                style: iconProp.styles,
                props: {
                  iconClass: iconProp.iconClass
                }
              }),
              '上传'
            ]
          )
        ]
      );
    }
    
  • 级联选择器

    function cascaderItem(h, config) {
      let {
        model,
        key,
        props,
        listeners,
        options,
        optionProps
      } = config;
      let ops = options();
      let on = listeners || {};
      let pps = props || {};
      return h('el-cascader', {
        props: {
          value: model[key],
          ...pps,
          options: ops,
          props: optionProps
        },
        on: {
          change: (val) => {
            model[key] = val;
            if (on.change) {
              on.change(val);
            }
          }
        }
      });
    }
    
  • 时间选择器

    function timePicker(h, config) {
      let {
        model,
        key,
        props,
        listeners,
        placeholder,
        type,
        syncConfig
      } = config;
      let pps = props || {};
      if (syncConfig) {
        pps = {
          ...pps,
          ...syncConfig()
        };
      }
      let on = listeners || {};
      return h('el-time-picker', {
        props: {
          ...pps,
          value: model[key]
        },
        attrs: {
          placeholder: placeholder || pps.placeholder || '请输入',
          type: type || 'date'
        },
        on: {
          ...on,
          input: (val) => {
            model[key] = val;
          }
        }
      });
    }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,694评论 0 3
  • Vue 实例 属性和方法 每个 Vue 实例都会代理其 data 对象里所有的属性:var data = { a:...
    云之外阅读 2,204评论 0 6
  • 以下内容是我在学习和研究Vue时,对Vue的特性、重点和注意事项的提取、精练和总结,可以做为Vue特性的字典; 1...
    科研者阅读 14,059评论 3 24
  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些阅读 2,028评论 0 2
  • 看过很多相似的场景,一个孩子吃着饭突然从碗底翻出一个荷包蛋或者一个黄灿灿的太阳蛋,母亲在一旁慈爱地看着他,“海底捞...
    飞飞的碎碎念阅读 1,194评论 28 36