# Form

# 优先阅读

# 介绍

  • form是一个动态表单组件,可以根据传入的config配置动态的增加减少表单项,并且反应到我们绑定的v-model中。
  • 支持v-model传入我们需要操作的表单对象。表单项的数据以v-model传入的object为主。
  • 为什么需要动态表单组件?有某些需求:相同入口 --> 相同页面,不同类型展示不同表单结构。有了动态表单,不需要根据不同的类型建立多个component页面,或者是不需要痛苦的v-if、v-show进行区分。
  • 动态表单的优势在于相同的表单项可以进行复用,随意的组合。

# 示例

# 基本使用

<template>
  <div>
    <lx-form
        :config="config"
        v-model="formData"
        @submit="onsubmit"
        @reset="onReset"
    >
    </lx-form>
  </div>
</template>

<script>
export default {
  data () {
    return {
      config: [
        { label: '名称',  filed: 'name', type: 'input'}
      ],
      formData: {
        name: '我是form'
      }
    }
  },
  methods: {
    onsubmit (form) {
      console.log('onsubmit', form)
    },
    onReset (form) {
      console.log('onReset', form)
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Expand Copy

# 使用控件

<template>
  <div>
    <lx-form
        :config="config"
        :form-attr="formAttr"
        v-model="formData"
        @submit="onsubmit"
        @reset="onReset"
    >
    </lx-form>
  </div>
</template>

<script>
export default {
  data () {
    return {
      formAttr: {
        labelWidth: '120px'
      },
      config: [
        { label: '名称',  filed: 'name', type: 'input'},
        { label: '生日',  filed: 'birthday', type: 'date'},
        {
          label: '性别',
          filed: 'sex',
          type: 'enum', // 下拉选择框
          options: [
            { label: '男', value: 1 },
            { label: '女', value: 0 },
          ]
        },
        {
          label: '是否开启',
          filed: 'status',
          type: 'radio', // 下拉选择框
          options: [
            { label: '是', value: 1 },
            { label: '否', value: 0 },
          ]
        },
        {
          label: '兴趣爱好',
          filed: 'hobby',
          type: 'checkbox', // 下拉选择框
          options: [
            { label: '唱歌', value: 1 },
            { label: '跳舞', value: 2 },
            { label: '游泳', value: 3 },
            { label: '篮球', value: 4 },
          ]
        },
      ],
      formData: {
        name: '我是formData'
      }
    }
  },
  methods: {
    onsubmit (form) {
      console.log('onsubmit', form)
    },
    onReset (form) {
      console.log('onReset', form)
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Expand Copy

# head和折叠

<template>
  <div>
    <lx-form
        :config="config"
        :form-attr="formAttr"
        v-model="formData"
        @submit="onsubmit"
        @reset="onReset"
    >
    </lx-form>
  </div>
</template>

<script>
export default {
  data () {
    return {
      formAttr: {
        labelWidth: '120px'
      },
      config: [
        {
          headShow: true, title: '个人信息'
        },
        { label: '名称',  filed: 'name', type: 'input'},
        { label: '生日',  filed: 'birthday', type: 'date'},
        {
          label: '性别',
          filed: 'sex',
          type: 'enum', // 下拉选择框
          options: [
            { label: '男', value: 1 },
            { label: '女', value: 0 },
          ]
        },
        {
          headShow: true, title: '其他信息(可折叠)',
          type: 'collapse',
          name: 'otherInfo',
          componentProps: {
            on: {
              change: (val) => {
                console.log('val', val)
              }
            }
          },
          children: [
            {
              label: '是否开启',
              filed: 'status',
              type: 'radio', // 下拉选择框
              options: [
                { label: '是', value: 1 },
                { label: '否', value: 0 },
              ]
            },
            {
              label: '兴趣爱好',
              filed: 'hobby',
              type: 'checkbox', // 下拉选择框
              options: [
                { label: '唱歌', value: 1 },
                { label: '跳舞', value: 2 },
                { label: '游泳', value: 3 },
                { label: '篮球', value: 4 },
              ]
            },
          ]
        },
      ],
      formData: {
        name: '我是formData'
      }
    }
  },
  methods: {
    onsubmit (form) {
      console.log('onsubmit', form)
    },
    onReset (form) {
      console.log('onReset', form)
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Expand Copy

# 编辑/只读

<template>
  <div>
    <lx-form
        :config="config"
        :form-attr="formAttr"
        v-model="formData"
        :default-btn-show="false"
        :is-edit="isEdit"
    >
    </lx-form>
    <div>
      <el-button :type="isEdit ? 'primary' : ''" @click="isEdit = true">编辑模式</el-button>
      <el-button :type="!isEdit ? 'primary' : ''" @click="isEdit = false">只读模式</el-button>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      isEdit: true,
      formAttr: {
        labelWidth: '120px'
      },
      config: [
        { label: '名称',  filed: 'name', type: 'input'},
        { label: '生日',  filed: 'birthday', type: 'date'},
        {
          label: '性别',
          filed: 'sex',
          type: 'enum', // 下拉选择框
          options: [
            { label: '男', value: 1 },
            { label: '女', value: 0 },
          ]
        },
        {
          label: '是否开启',
          filed: 'status',
          type: 'radio', // 下拉选择框
          options: [
            { label: '是', value: 1 },
            { label: '否', value: 0 },
          ]
        },
        {
          label: '兴趣爱好',
          filed: 'hobby',
          type: 'checkbox', // 下拉选择框
          options: [
            { label: '唱歌', value: 1 },
            { label: '跳舞', value: 2 },
            { label: '游泳', value: 3 },
            { label: '篮球', value: 4 },
          ]
        },
      ],
      formData: {
        name: '我是formData',
        birthday: '2000-10-17',
        sex: 1,
        status: 1,
        hobby: [1, 2, 3]
      }
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
Expand Copy

# 校验

  • 使用默认按钮的时候
<template>
  <lx-form :valid="true" :rules="rules"></lx-form>
</template>
1
2
3

配置rules

<script>
export default {
  data () {
    return {
      rules: {
        name: [
          {required: true, message: '请填写密码', trigger: ['change']}
        ]
      }
    }
  }
}
</script>

// 或者是内联配置 formItemProps 属性
<script>
const config = [
  {
    label: '名称',
    filed: 'name',
    type: 'input',
    formItemProps: {
      rules: [
        {required: true, message: '请填写用户名', trigger: ['change']}
      ]
    },
    ...
  }
]

</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
  <div>
    <lx-form
        ref="ruleForm"
        :config="config"
        v-model="formData"
        :valid="true"
        @submit="onsubmit"
        @reset="onReset"
    >
    </lx-form>
  </div>
</template>

<script>
export default {
  data () {
    return {
      config: [
        {
          label: '用户名',
          filed: 'username',
          type: 'input',
          formItemProps: {
            rules: [
              {required: true, message: '请填写用户名', trigger: ['change']}
            ]
          }
        },
        {
          label: '密码',
          filed: 'password',
          type: 'input',
          formItemProps: {
            rules: [
              {required: true, message: '请填写密码', trigger: ['change']}
            ]
          }
        },
      ],
      formData: {
        name: ''
      }
    }
  },
  methods: {
    onsubmit (form) {
      console.log('onsubmit', form)
    },
    onReset (form) {
      console.log('onReset', form)
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Expand Copy
  • 非默认按钮的时候,想使用校验,可以获取组件的实例方法来操作,使用方法和ElementUI的form表单使用方式一样。
<template>
  <lx-form ref="ruleForm"></lx-form>
</template>
<script >
export default {
  methods: {
    validate () {
      this.$refs.ruleForm.validate(callback)
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
<template>
  <div>
    <lx-form
        ref="ruleForm"
        :config="config"
        v-model="formData"
        :default-btn-show="false"
    >
    </lx-form>
    <div>
      <el-button type="primary" @click="login">登录</el-button>
      <el-button @click="reset">重置</el-button>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      config: [
        {
          label: '用户名',
          filed: 'username',
          type: 'input',
          formItemProps: {
            rules: [
              {required: true, message: '请填写用户名', trigger: ['change']}
            ]
          }
        },
        {
          label: '密码',
          filed: 'password',
          type: 'input',
          formItemProps: {
            rules: [
              {required: true, message: '请填写密码', trigger: ['change']}
            ]
          }
        },
      ],
      formData: {
        name: ''
      }
    }
  },
  methods: {
    login () {
      this.$refs.ruleForm.validate((valid) => {
        console.log('valid', valid)
      })
    },
    reset () {
      this.$refs.ruleForm.reset()
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Expand Copy

# 插槽

提供了两种作用域插槽属性:slotName(整行插槽)、formItemSlotName(内容插槽),
具体请看下面例子的 lx-form 里的元素和 config里的配置slotName、formItemSlotName属性的关系

<template>
  <div>
    <lx-form
        ref="ruleForm"
        :config="config"
        v-model="formData"
        :form-attr="{ labelWidth: '120px' }"
        :default-btn-show="false"
    >
      <template #mobileSlot="scope">
        <div>{{scope}}</div>
        <el-input placeholder="请填写手机号码" v-model="formData.mobile"></el-input>
      </template>
      <template #imgCodeSlot="scope">
        <div class="img-code">
          <div>{{scope}}</div>
          <img src="" alt="验证码图片,没放图片">
        </div>
      </template>
    </lx-form>
    <div>
      <el-button type="primary" @click="login">登录</el-button>
      <el-button @click="reset">重置</el-button>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      config: [
        {
          label: '用户名',
          filed: 'username',
          type: 'input',
          formItemProps: {
            rules: [
              {required: true, message: '请填写用户名', trigger: ['change']}
            ]
          }
        },
        {
          label: '手机号码',
          formItemSlotName: 'mobileSlot'
        },
        {
          label: '密码',
          filed: 'password',
          type: 'input',
          formItemProps: {
            rules: [
              {required: true, message: '请填写密码', trigger: ['change']}
            ]
          }
        },
        {
          slotName: 'imgCodeSlot'
        }
      ],
      formData: {
        name: ''
      }
    }
  },
  methods: {
    login () {
      this.$refs.ruleForm.validate((valid) => {
        console.log('valid', valid)
      })
    },
    reset () {
      this.$refs.ruleForm.reset()
    }
  }
}
</script>
<style>
.img-code {
  padding: 10px;
  box-sizing: border-box;
  border: 1px solid orange;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
Expand Copy

# Form Attributes

参数 是否必填 说明 类型 可选值 默认值
v-model form表单的绑定值 Object - 如果没有v-model,仅有config的配置的对象
config form表单的配置 Array - -
form-attr el-form的配置,具体配置请阅读:Element-ui -> Form Object - -
ignoreTypeWar 是否忽略类型提示,例如 config: { type: 'xxx' }, xxx是不存在的一个类型,发出警告 Boolean - false
is-edit 是否可编辑模式 Boolean - true
size 所有form里的控件的尺寸 String medium / small / mini -
rules 校验规则 Array - -
all-formItem-style 所有表单行的样式 Object - -
all-control-style 所有表单项内容容器(左边的label是标题,右边的部分就是内容容器)的样式 Object - -
default-btn-show 是否显示默认按钮(提交、重置) Boolean - true
valid 是否点击提交(默认按钮)的时候进行rules规则校验 Boolean - false
submit-btn-text 默认按钮"提交"的文字 String - '提交'
reset-btn-text 默认按钮"重置"的文字 Boolean - '重置'

# Config

属性 是否必填 说明 类型 可选值 默认值
headShow 该层是否为分隔层 Boolean true / false false
name 名称,可以作为 type="collapse" 时 组件的value string null
title 分隔层标题,headShow为true时有效 String - -
label 表单项标题 String - -
filed 字段名 String - -
type 表单项控件类型 String 下方详细列举 -
value 默认filed的值,会被v-model传入的数据覆盖,当v-model属性没有被设置,或者是空对象的时候起作用。 - - -
component 组件,可以赋值一个组件或者一个全局组件的名称成为一个表单项控件,优先级高于type属性 component / componentName - -
componentProps 表单项控件的属性配置 Object - -
tips 注释,可以是一段字符串,也可以是一个对象{ text: 'xxxx', style: {} } String / Object - -
render 自定义整行 Function Function(h, { form, row, searchConfig }) -
formItemRender 自定义内容行 Function Function(h, { form, row, searchConfig }) -
slotName 行插槽,定义一个插槽 String scope{form, row, slotName} -
formItemSlotName 内容插槽,定义一个插槽 String scope{form, row, slotName} -
options - 列表项数据,当type等于enum、checkbox、radio等类型时,会使用该项属性作为数据,默认取值,[{ label: '展示文字', value: '值' }] Array[Object] - -
optionsProps - 自定义options中的字段名称 Object { label, value, disabled ... } -
tag - 自定义元素 String el-form-item
inline - 设置该项为内联元素 Boolean false
span - 元素宽度,1~10 = 10%~100% Integer 1~10 -

# config 的配置示例

config接收一个JSON类型的数组数据

遵循使用常用的字段名称习惯,具体config属性

import { Input } from 'element-ui'
{
   label: 'xxx', // form-item 的 label
   filed: 'xxxx', // 必填项 表单数据对象的字段名
   type: 'input', // 必填项 表单输入控件的类型,如:文本框:input、下拉选择框:select
   props: 'xxx', //  表单输入控件的 props
   component: Input, // 直接赋值组件,formItem内容被替换成组件
   component: 'el-input', // 使用已经被注册的组件名称,,formItem内容被替换成组件
   componentProps: {
    style: { // 样式
      width: '200px',
      marginLeft: '40px'
    },
    classList: ['default', 'text-center'], // 样式类名 
    attrs: { // dom的原生属性
      placeholder: '请输入姓名'
    },
    on: { // 表单暴露的API方法
      change: (val) => {}
    }
   },
   options: [ // 当 type 为 select之类的需要配置数据的类型时的数据
     { label: '已选', value: 1 },
     { label: '未选', value: 0 }
   ],
   tips: String | Object // Object: { text:'我是注释', style: {} }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

hidden

注意:
有关于config赋值的问题: 在template中使用的时候应用变量进行赋值,不可直接把值写在template模板中,
如果直接写在模板中,每一次模板更新的时候,该值都是一个全新的值,导致不断触发更新机制,使组件内部的更新逻辑形成死循环。

正确

<template>
  <div>
    <lx-form :config="config"></lx-form>
  </div>
</template>
<script>
export default {
  data () {
    return {
      config: [{ label: '名称', type: 'input', filed: 'name' }]
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

错误

<template>
  <div>
    <lx-form :config="[{ label: '名称', type: 'input', filed: 'name' }]"></lx-form>
  </div>
</template>

1
2
3
4
5
6

# componentProps

  • 介绍:
    componentProps属性 等于正常使用组件时传入的props、on(Event)、directive等属性。
属性名 说明 类型 可选值 默认值
props 表单项控件的props Object - -
attrs 表单项控件的attrs Object - -
on 表单项控件的on Object - -
classList 表单项控件的class Array - -
style 表单项控件的style Array - -

componentProps的基本使用可以到VUE官网中查看 createElement函数的传参方式。
props 是配置组件原来提供的Attr、Event。
Vue2 createElement
https://v2.cn.vuejs.org/v2/guide/render-function.html#深入数据对象 (opens new window)

# Type

参数 说明 数值类型 数据示例 特殊说明
collapse 折叠,此层是一个折叠层,需要设置headShow = true,内容config写在 children 属性中 - 理解为当表单数据太多的时候,我们希望某一块区域可以折叠起来,而children就是折叠区域里的内容
input 文本输入框 any
enum 枚举(下拉框),需要配置options String / Number
time-select Element 的 el-time-select -
date-picker Element 的 el-date-picker -
date 日期 -
date-range 日期范围 Array
date-time 日期时间,年月日 - 时分秒 -
date-time-range 日期时间范围 Array
input-number 数字输入框 - 可以使用 leftText和rightText属性
cascader 级联选择器 Array
checkbox 复选框 String / Number
checkboxButton 复选框组 Array
radio 单选框 Array
radioButton 单选按钮组 Array
uploadImg 上传图片,图片列表 Array
digit-select 数值选择器 Array

# type: 'uploadImg'

图片,列表、上传。实例

{
    label: '各类证件',
    type: 'uploadImg',
    filed: 'papers',
    componentProps: {
      props: {
        value: [], // Array(多张图片) | String(单张图片)
        isMulti: true, // 是否多选
        delConfirm: false, // 点击右上角删除按钮时是否显示确认
        limit: 6, // 可上传图片数量上限
        limitMessage: '', // 超过上限消息
        width: '155px', // 图片框宽度
        height: '255px', // 图片框高度
        limitWidth: 48, // 上传图片指定宽度
        limitHeight: 48, // 上传图片指定高度
        limitHeightRange: [48, 96], // 上传图片宽度指定范围
        limitWidthRange: [48, 96], // 上传图片高度指定范围
        restrictMessage: '', // 不符合限制条件的消息
        tips: { // 组件注释
          text: '测试拖拽'
        },
        accept: 'jpg,png', // 限制上传类型后缀
        acceptMessage: '', // 不符合限制上传类型的消息
        showIndex: true, // 是否显示序列号
        filedPrimaryKey: 'file' // 当数据是一个对象数组的时候,这个属性对应对象的字段
      },
      on: {
        'file-change': (file, fileList, options) {
          
        }
      }
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# Event

on事件监听

{
    on: {...}
}
1
2
3

也是在原有的on基础上进行处理,本form组件为了业务方便,提供了一些新的event事件,根据type的不同,调用不同的on事件。

# type: enum

原来的change或者v-model只提供了value的值。
在某些需求下,需要选择的那一项的所有数据,所以提供了一个新的 Event:select。

    on: {
        select: (item) => {
            // item 选中的 option 数据
        }
    }
1
2
3
4
5

# 自定义

自定义分两种,

  • render,完全覆盖一整行。
  • formItemRender,保留标题和其他属性,只覆盖control内容。

数据类型:Function 类型,参数 h(createElement),参数2 form({form, row, searchConfig})

# 示例:render 和 formItemRender

<template>
  <div>
    <lx-form :config="config" :form-attr="{labelWidth: '120px'}"></lx-form>
  </div>
</template>
<script>
export default {
  data () {
    return {
      config: [
        {
          label: 'ID',
          filed: 'id',
          type: 'input'
        },
        {
          label: '名称', // 由于是render,所以label会被无视
          filed: 'name', // 由于是render,所以filed会被无视
          type: 'input', // 由于是render,所以type会被无视
          render: (h, { form, row, searchConfig }) => {
            return <div style="margin-bottom: 40px;">
              <span>render</span>
              <el-input v-model={form.name}></el-input>
            </div>
          }
        },
        {
          label: 'formItemRender',
          filed: 'addr',
          type: 'input', // 由于是render,所以type会被无视
          formItemRender: (h, { form, row, searchConfig }) => {
            return <el-input v-model={form.addr}></el-input>
          }
        }
      ]
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Expand Copy

# Form Event

属性名 说明 参数 返回值
keyup 按钮事件 - {event, formData}
submit 默认查询按钮 - {formData}
reset 默认重置按钮 - {formData}

# Form Methods

事件名称 说明 参数 返回值
getForm 获取formData数据,获取form组件的实例 - {form, formEle}
validate 表单校验 Function(valid: Boolean){} -
reset 重置表单 - -
validateField 校验表单 - -
clearValidate 清除校验结果 - -
clear 清除表单数据 - -
searchConfig 获取配置项 - -

# searchConfig 查询config里的某个配置

查询config的配置,可以用于修改我们想要修改配置的属性值,例如disabled = true,改为 false

可以通过 ref 的方式调用组件内部的 searchConfig 函数 searchConfig({ key: 'filed', value: 'name' })

就可以找到 属性名:filed,值:name 的这一项配置,直接修改想要修改的属性即可