用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~

相关推荐
银色的白3 小时前
工作记录:人物对话功能开发与集成
vue.js·学习·前端框架
萌萌哒草头将军3 小时前
🚀🚀🚀什么?浏览器也能修改项目源文件了?Chrome 团队开源的超强 Vite 插件!🚀🚀🚀
vue.js·react.js·vite
Shimeng_19897 小时前
前端如何通过(手机)扫描二维码下载app
前端·javascript·vue.js·二维码·扫描二维码下载软件app
MZWeiei8 小时前
MVVM 模式,以及 Angular、React、Vue 和 jQuery 的区别与关系
vue.js·react.js·angular.js
述雾学java8 小时前
Spring Boot + Vue 前后端分离项目解决跨域问题详解
vue.js·spring boot·后端
空城机9 小时前
从零打造前沿Web聊天室:消息系统
前端·vue.js
燕燕燕燕燕9 小时前
vue2基础-vue基础知识和原理
vue.js
用户3802258598249 小时前
vue中结合vue3-sfc-loader加载远程组件
vue.js
Jinxiansen02119 小时前
Nuxt + Pinia + Element Plus 后台管理系统搭建教程(含源码)
前端·javascript·vue.js
奇舞精选10 小时前
从零实现 Vue 响应式机制:带你吃透依赖收集与更新原理
vue.js