用 JSON schema 配置的方式,生成页面的表单及表格等,基于Vue及其它UI框架,例如Element-UI。 本文主要介绍生成表单,因为表单是Vue后台中最常用的。
sc-form 是一个支持JSON schema配置式生成页面表单的组件(基于element-ui),可以认为它是Element所有form组件及其它组件的JSON配置版本,它是目前JSON构建Element表单的最简单方法,它的特性有:
- 纯JSON配置化构建整个表单
- 接近 0 自定义api字段,上手成本低
- 拥有Element 表单及表单控件的全部特性(支持全部api及事件)
- 支持绑定多个复杂model
- 支持字段标签、文本、自定义组件、插槽等
- 加强定制了某些内容,支持全局配置
- 为复杂的 UI 提供col span布局支持
- 拥有 Javascript 完全编程能力
一个简单双向绑定表单:
js
<template>
<sc-form
v-model="model"
:schema="schema"
/>
</template>
<script>
export default {
data() {
return {
model: {},
schema: {
inline: true,
formItems: [
{
type: 'input',
label: '名字',
field: 'name',
placeholder: '请输入名字'
},
{
type: 'input',
label: '密码',
field: 'password',
showPassword: true,
placeholder: '请输入密码'
},
{
type: 'button',
content: '提交',
on: {
click() {
console.log('submit!');
}
}
}
]
}
}
}
}
</script>

上面完成了一个简单的双向绑定例子,schema 中的showPassword
,placeholder
等和对应的 Element 表单控件属性一一对应,其中可以省略模板中的v-model="model"
定义,统一在schema中定义model,例如:
js
<template>
<sc-form :schema="schema" />
</template>
<script>
export default {
data() {
return {
model: {} // 定义表单model
}
},
computed: {
schema() {
return {
inline: true,
model: this.model, // 这里绑定表单model
formItems: [
...
]
}
}
}
}
</script>
上面2种写法实现了同样的效果,注意后面一种写法的 schema 需要在 computed 中定义,因为它依赖了组件中的this.model
。
实际项目中建议 schema 全部在 computed 中定义,因为表单中的控件往往需要依赖后端的数据来渲染,例如 select options、cascader等。
例子2 绑定多个model并验证表单某个字段:
js
<template>
<div>
<sc-form :schema="schema" />
<p>model: {{ JSON.stringify(model) }}</p>
<p>likes: {{ JSON.stringify(likes) }}</p>
</div>
</template>
<script>
export default {
data() {
return {
model: {},
likes: [],
}
},
computed: {
schema() {
return {
bind: this, // 多个model需要绑定当前组件this
inline: true,
model: this.model, // 整个表单的model
formItems: [
{
type: 'input',
label: '名字',
field: 'name',
placeholder: '请输入名字'
},
{
type: 'input.number',
label: "年龄",
field: "age",
rules: [ // 字段验证规则,api与element form一致
{ required: true, message: '年龄不能为空' },
{ type: 'number', message: '年龄必须为数字值' }
]
},
{
type: 'checkbox',
label: '喜好',
model: this.likes, // 此字段独立绑定一个model
field: 'likes', // 此field名字跟model名字保持一致
options: [{
label: '足球',
value: 1
}, {
label: '篮球',
value: 2
}, {
label: '爬山',
value: 3
}, {
label: '旅游',
value: 4
}]
},
]
}
}
}
}
</script>

例子3 同一份schema根据不同model生成独立表单:
js
<template>
<div>
<sc-form :schema="schema1" />
<sc-form :schema="schema2" />
<p>model1: {{ JSON.stringify(model1) }}</p>
<p>model2: {{ JSON.stringify(model2) }}</p>
</div>
</template>
<script>
import { cloneDeep } from 'lodash-es';
// 表单schema,可以独立储存在其它地方作为公用
const schema = {
formItems: [
{
type: 'input',
label: '名字',
field: 'name',
placeholder: '请输入名字'
},
{
type: 'input.number',
label: "年龄",
field: "age",
rules: [
{ required: true, message: '年龄不能为空' },
{ type: 'number', message: '年龄必须为数字值' }
]
}
]
};
export default {
data() {
return {
model1: {},
model2: {},
}
},
computed: {
schema1() {
return {
inline: true,
model: this.model1, // 这里绑定表单model1
...cloneDeep(schema)
}
},
schema2() {
return {
inline: true,
model: this.model2, // 这里绑定表单model2
...cloneDeep(schema)
}
}
}
}
</script>

注意:需要引入lodash库的cloneDeep方法来深度复制schema,如果只是简单assign复制或解构复制可能会影响另外的表单,除非schema仅供一个表单使用。
例子4 绑定多个事件并使用title、slot等扩展功能:
js
<template>
<div>
<sc-form ref="form" :schema="schema">
<template #age-slot>
<el-input v-model.number="model.age" />
</template>
</sc-form>
<p>model: {{ JSON.stringify(model) }}</p>
</div>
</template>
<script>
export default {
data() {
return {
model: {},
}
},
computed: {
schema() {
return {
bind: this, // 绑定当前组件this以使事件中能访问组件的数据及方法
inline: true,
model: this.model, // 这里绑定表单model
formItems: [
{
type: 'title', // 使用表单title
content: '示例表单'
},
{
type: 'input.trim', // trim去掉空格,同vue官方v-model.trim
label: '名字',
field: 'name',
placeholder: '请输入名字',
on: {
'keyup.enter.native'() { // enter回车触发,同vue官方@keyup.enter.native
console.log('keyup enter!');
},
change() { // change事件 同elment官方
console.log('change!');
}
}
},
{
type: 'slot', // 使用表单slot
name: 'age-slot', // slot名字
label: "年龄",
field: 'age',
rules: [
{ required: true, message: '年龄不能为空' },
{ type: 'number', message: '年龄必须为数字值' }
],
},
{
type: 'button',
content: '重置',
on: {
click() {
this.$refs.form.elForm.resetFields(); // elForm属性获得el-form组件实例,并使用它的方法resetFields
}
}
},
]
}
}
}
}
</script>

更多例子和玩法,欢迎参观 Github sc-form
P.S. 构思制作这个sc-form之前,其实也有在社区去看下有没有相关的轮子实现,发现有不少不错的库例如这个form-create,其中不少理念巧合相同也借鉴了它的部分api,但本人目标是JSON shema复用和模块化保存,及多模型model支持,及更少的API学习成本甚至0 API,像平常用Element组件一样去写对应的JSON,等等。
可以认为 sc-form 没有任何的魔法,你平常怎么用 Element 组件就怎么写它的 JSON shema,它不过是Element-ui 的 JSON schema 配置版本,就这么简单。
enjoy~