用JSON schema配置生成Vue页面表单

用 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 中的showPasswordplaceholder等和对应的 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~

相关推荐
啊~哈24 分钟前
vue3+elementplus表格表头加图标及文字提示
前端·javascript·vue.js
xiaogg367831 分钟前
vue+elementui 网站首页顶部菜单上下布局
javascript·vue.js·elementui
HelloWord~1 小时前
SpringSecurity+vue通用权限系统
vue.js·spring boot
神探小白牙2 小时前
vue-video-player视频保活成功确无法推送问题
前端·vue.js·音视频
Angel_girl3192 小时前
vue项目使用svg图标
前端·vue.js
難釋懷3 小时前
vue 项目中常用的 2 个 Ajax 库
前端·vue.js·ajax
爱生活的苏苏3 小时前
vue生成二维码图片+文字说明
前端·vue.js
前端百草阁3 小时前
从npm库 Vue 组件到独立SDK:打包与 CDN 引入的最佳实践
前端·vue.js·npm
且白4 小时前
vsCode使用本地低版本node启动配置文件
前端·vue.js·vscode·编辑器
疯狂的沙粒4 小时前
在uni-app中如何从Options API迁移到Composition API?
javascript·vue.js·uni-app