开发过程中常常遇到各种各样的表单,有的重复度很高,就产生了自己封装一个动态form表单组件的想法
灵感来源ncform和阿里巴巴的formilyjs地址为https://formilyjs.org/#/0yTeT0/VEt5tQHbh2
代码写的比较烂,希望大家多提提意见和建议
留下您的表单使用场景,我们把他考虑进去
核心
递归
目前只在渲染组件提供了 props为value @input事件
例如
<el-input value="xxx" @input="xxxx">
自定义组件必须提供v-model
为什么用template而不是render
使用render时, formData改变会导致整个表单重绘。有些时候我们只是向去更新对应的组件而已。
依赖
vue element-ui lodash(get, set, debounce)
能做什么
通过书写json schema来控制表单生成
接下来的考虑
- 接收大家的意见和建议
- 如何更加自由的
UI布局
示例

Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| value/v-model | 绑定值 | object | - | {} |
| schema | schema用来渲染表单 | object | - | {} |
Events
| 事件名称 | 说明 | 回调参数 |
|---|---|---|
| input | 当绑定值变化时触发的事件 | (value) |
| onFormMount | 当表单初始化完毕时出发 | (formData) |
| onFieldChange | 表单的某个字段发生变化 | (idxChain, value, schema) schema当前组件的schema |
schema
未来计划支持表达式(disabled, hidden)
{
type: 'object|string|number|array|array-card|array-tabs|array-table',
// 目前array只支持 array和array-table
//当type为 object时 properties必须
properties: {},
//当type为 array时 items必须
items: {},
ui: {
// 同element
column: 'default - 24',
default: '默认值',
labelWidth: 'el-form-item label-width',
label: 'el-form-item label',
widget: 'el-input等等,接受一切v-model的组件',
render: (h, context) => { // h同vue, context 同vue functional }
hidden: 'boolean|function', // (formData) => {}
rules: 'array|function', // (formData) => {}
disabled: 'boolean|function' // (formData) => {}
}
}
如果要设置formData,请使用this.$refs.debbyForm.setFormData
setFormData(idxChain, value) {
utils.set(this.dataForm, idxChain, value)
}
Examples
一个比较复杂点的场景
vue template
<debby-form
v-model="dataForm"
label-width="140px"
label-position="top"
:schema="formSchema"
/>
schema
{
type: 'object',
'ui:label': '班级',
'ui:showLabel': true,
properties: {
grade: {
type: 'string',
default: '3',
ui: {
label: '年级',
column: 12
},
rules: [
{
required: true, message: 'custom rule', trigger: 'blur'
}
]
},
class: {
type: 'string',
ui: {
label: '班级',
column: 12,
disabled: (formData) => {
return formData.grade === '3'
}
},
/**
* 验证器同el-form的验证器 方法里面还支持this哦
* @param formData
* @returns {[{validator: validGrade, trigger: string, required: boolean}]}
*/
rules: function(formData) {
const validGrade = (rule, value, callback) => {
if (formData.grade < 10) {
callback(new Error('年级不能大于10'))
}
}
return [
{
required: true, validator: validGrade, trigger: 'blur'
}
]
}
},
other: {
type: 'object',
ui: {
label: '属性'
},
properties: {
stuNum: {
type: 'string',
ui: {
label: '学生数量'
}
},
teaNum: {
type: 'string',
ui: {
label: '教师数量'
}
}
}
},
students: {
type: 'array',
ui: {
label: '学生信息'
},
items: {
name: {
type: 'string',
ui: {
label: '姓名'
},
rules: function() {
const validName = (rule, value, callback) => {
if (!isNaN(value)) {
callback(new Error('不能为空并且不能输入数字哦'))
}
}
return [
{
required: true, validator: validName, trigger: 'blur'
}
]
}
},
sex: {
type: 'boolean',
default: true,
ui: {
label: '性别',
widget: 'el-checkbox'
}
}
}
}
}
}
一个支持 vue render的场景
schema
{
type: 'object',
'ui:labelWidth': 0,
properties: {
name: {
'ui:label': '步骤名称'
},
filename: {
ui: {
column: 12,
label: '文件名称'
}
},
test: {
'ui:label': 'render by jsx',
default: true,
ui: {
column: 12,
labelWidth: '100px',
render: (h, context) => {
const props = context.props || {}
const listeners = context.listeners || {}
return (
<el-checkbox value={props.value} vOn:input={value => listeners.input(value)} />
)
}
}
}
}
}
}