@zdwh/vue-json-schema-form v1.0.19
@zdwh/vue-json-schema-form
表单从来没有这么简单,通过一份 json-schema,你就拥有了一套交互完整,校验完善的表单。
USAGE
npm i @zdwh/vue-json-schema-form -S
然后
import JsonSchemaForm from '@zdwh/vue-json-schema-form'
import JsonSchemaFormThemeElement from '@zdwh/vue-json-schema-form/dist/theme-element/index.common.js'
vue.use(JsonSchemaForm)
vue.use(JsonSchemaFormThemeElement)
<JsonSchemaForm :schema="schema" v-model="value" :formProps="...props pass to form" :plugins="plugins" locale="" />
theme 是必须的,真正的表单组件都是由 theme 提供的,将 theme 拆分出来的目的很明显,未来对于不同的使用场景,可以无缝迁移,同时可以在移动端使用。
props
schema
json schema 对象
v-model
绑定结果值
formProps
传递给表单组件的props
,这里面的值根据表单的最终实现来定,比如theme-element
的formProps
可以是任意 element-ui 中的 form 组件的props
plugins
插件
uiSchema
具体参考下面的vjsf
locale
默认zh
中文,支持值参考ajv-i18n
ajvInstanceOptions
vjsf
vjsf
是我们用来在 json schema 基础上帮助我们更好得渲染表单的工具参数,我们可以通过在每个 schema 节点上带上来传递:
const schema = {
type: 'string',
vjsf: {
component: 'your-custom-component',
additionProps: {
...props, // 对于表单组件你想传递的其他参数
},
title: '名称',
description: '描述',
withFormItem: true, // 对于自定义组件,是否使用formItem,默认为true
},
}
如果你不想把这些属性放在schema
上面,那么你可以通过给JsonSchemaForm
传递uiSchema
参数来进行定制,只要保持uiSchema
的结构和schema
一致就行
比如:
const schema = {
type: 'object',
properties: {
name: {
type: string,
},
pets: {
type: 'array',
items: {
type: 'string',
},
},
},
}
const uiSchema = {
title: '我和我的宠物',
properties: {
name: {
title: '我的名字',
component: 'input',
},
pets: {
title: '我的宠物们',
items: {
title: '名字',
},
},
},
}
custom event
如果你有自定义组件,并且你的自定义组件想要跟JsonSchemaForm
的 owner 进行沟通,你的组件可以inject: ['fireEvent']
,然后通过this.fireEvent(name, data)
来向外触发一个事件,在 owner 处,我们可以进行监听:
<json-schema-form @youEventName="yourHandler" />
propertiesOrder
对象 key 的排序,尤其在有dependencies
的时候非常有用,在声明 schema 的时候,在type
为object
的 schema 中可以增加propertiesOrder
属性,
他的值是一个数组,properties
里面的变量会根据这里声明的顺序进行渲染,没有出现在这个数组里面的变量名则放在最后面
插件
插件能够帮助我们非常方便地扩展功能,目前支持情况如下:
- customFormat
- customKeywords
如何创建一个自定义 format 插件
接口定义如下:
interface AjvFormat {
name: string
definition: FormatValidator | FormatDefinition
}
interface CustomFormat extends AjvFormat {
component: String // jsf-text-input || your-custom-component
}
interface JsonSchemFormPlugin {
customFormats: CustomFormat[]
}
比如我们增加一个图片上传的插件,这个组件返回的结果最终的图片链接,我们需要校验的自然也是这个链接,所以我们的 format 定义如下:
const format = {
name: 'image',
definition: /reg-to-valid-image-url/,
}
对于这个 format 我们需要使用特定的组件来进行渲染,毕竟他要实现上传图片并返回图片路径的功能,这并不是标准的 json schema 功能,并且上传的操作是业务相关操作直接写死在 vjsf 库内肯定不合适。
这时候我们可以创建一个ImageUploader
组件,并且注册到 vue 上,这时候我们的插件就成型了:
const plugin = {
customFormats: [
{
name: 'image',
definition: /reg-to-valid-image-url/,
component: 'image-uploader',
},
],
}
在我们定义如下 schema 之后:
{
type: 'string',
format: 'image'
}
我们的表单就是如下:
<image-uploader v-model="value" />
快速替换 format 使用的组件
默认的 format 组件映射关系如下:
export const stringFormatComponentMap: StringMap = {
default: 'jsf-text-input',
color: 'jsf-color-picker',
date: 'jsf-date-picker',
'date-time': 'jsf-date-time-picker',
time: 'jsf-time-picker',
}
export const numberFormatComponentMap: StringMap = {
default: 'jsf-number-input',
date: 'jsf-date-picker',
'date-time': 'jsf-date-time-picker',
time: 'jsf-time-picker',
}
我们可以通过formatMaps
来定义 format 默认使用的组件,比如:
const formats = {
string: {
date: 'jsf-my-date-picker'
}
}
<JsonSchemaForm :formatMaps="formats" />
之后字符串类型的日期format
就会使用jsf-my-date-picker
作为表单组件
重要:该方式建议只对 json schema 默认支持的 format 使用,对于你自定义的 format,你仍然需要使用插件的方式,因为你需要制定该 format 的校验方式。
如何自定义关键字插件
接口如下:
interface CustomKeyword {
name: string
definition: KeywordDefinition
transformSchema?: (originSchema: Schema) => Schema
}
关于自定义 Ajv 关键字,请看Ajv 文档,此处的definition
就是这个作用,而name
则是你的关键字的名字。
自定义关键字和customFormats
最大的区别是,我们不需要指定组件(毕竟我们不可能到每个类型里面判断关键字该怎么渲染)。
在这里我们通过transformSchema
来转换 schema,也就是我们真正渲染的 schema 是通过transformSchema
转换的结果,
这个方法会收到原始的 schema,你需要返回一个新的 schema,注意:不要 originSchema 上做改动
表单渲染会根据你返回的新的 schema 来进行。
示例
const plugin: JsonSchemFormPlugin = {
customKeywords: [
{
name: 'test',
definition: {
// validate(schema: any, data: any) {
// return typeof data === 'object' && data.x === 1
// },
macro(schema: any) {
return {
...schema,
type: 'object',
properties: {
x: {
type: 'number',
minimum: 5,
},
},
}
},
errors: true,
},
transformSchema(schema: any) {
return {
...schema,
type: 'object',
properties: {
x: {
type: 'number',
vjsf: {
title: '测试数字',
},
},
},
}
},
},
],
}
在使用关键字的时候,我们只需要:
const schema = {
test: true,
}
实际等于的效果如下:
const schema = {
type: 'object',
properties: {
x: {
type: 'number',
vjsf: {
title: '测试数字',
},
},
},
}
我们建议通过macro
来声明该关键字的校验,因为这能够完全契合 vjsf 的错误显示。如果你通过validation
来进行校验,最终校验结果只针对于当前路径,而并不会有针对 transform 之后的 schema 的校验,可能就需要你自行显示错误信息了。
校验
你可以通过给JsonSchemaForm
组件指定一个ref
,然后通过ref.doValidate()
来进行校验,会返回{ errors, valid }
,其中errors
是错误信息的对象。组件会把错误信息显示到每个表单项,你可以根据自己的需求以另外的方式提醒错误。
TODO:
- 输入时对每个表单项独立进行校验
错误信息
我们通过ajv-errors
来提供错误信息的定制,在你的每一项 schema 里面你可以:
const schema = {
type: 'string',
pattern: '/^abc$/',
errorMessage: {
pattern: '请填写正确的内容',
},
}
如果你不写errorMessage
,那么在用户输入的内容不匹配正则的时候,显示的错误信息是:应当匹配模式 "/^abc&/",这对于非技术人员显然不够友好,在增加了errorMessage
之后,对于关键字pattern
的错误信息就会显示你指定的错误信息。
Note:你可以对每个关键字设定错误信息
errorMessage: {
pattern: '请填写正确的内容',
maxLength: '最长不超过xxx',
//...others
}
主题
主题就是一组规范命名的组件,这一组组件都注册到全局 vue 上之后,vjsf 渲染表单就会使用这些组件,组件列表:
{
JsfColorPicker: '颜色选择器' // 非必须,会回滚到text-input
JsfDatePicker: '日期选择'
JsfDateTimePicker: '日期时间选择'
JsfDateTimeRangePicker: '日期时间区间选择'
JsfForm: '表单组件'
JsfFormItem: '表单项组件'
JsfNumberInput: '数字输入'
JsfSelection: '下拉选择'
JsfSwitch: 'boolean开关'
JsfTextInput: '字符串输入'
JsfTimePicker: '时间选择'
JsfSingleTypeArrayWrapper: '单类型数组区块控制器'
JsfAlert: '提示框'
}
你要实现一个主题则需要实现这些组件并逐一注册到vue
使用默认主题
vjsf 的默认主题现在打包在一起,需要通过:
import JsonSchemaFormThemeElement from '@zdwh/vue-json-schema-form/dist/theme-element/index.common.js'
import JsonSchemaFormThemeElement from '@zdwh/vue-json-schema-form/dist/theme-element/index.css'
来引入。
在自定义组件的时候,你需要给你的表单组件套上FormItem
来进行布局和错误显示:
<FormItem v-bind="formItemProps">
<YourContent>
</FormItem>
import { FormItem, CommonBase } from '@zdwh/vue-json-schema-form/dist/theme-element/index.common.js'
export default class YourComponent extends CommonBase
为了方便获取formItemProps
,我们提供了CommonBase
作为你的组件可继承的基类
当然如果你只需要一个 mixin,你可以import { FormItemPropsMixin } from '@zdwh/vue-json-schema-form/dist/theme-element/index.common.js'
帮助方法
import { ThemeBaseClass, ThemeBaseMixin } from '@zdwh/vue-json-schema-form'
如果你用class
开发可以继承前者,或者你可以使用后面的 mixin
表单依赖
demo
export default {
name: '依赖关系',
schema: {
type: 'object',
properties: {
selected: {
type: 'number',
title: '是否选中',
enum: [1, 2, 3],
},
},
dependencies: {
selected: {
oneOf: [
{
properties: {
selected: {
// const: 1,
const: 1,
},
name1: {
type: 'string',
title: '名字1',
},
},
required: ['name1'],
},
{
properties: {
selected: {
const: 2,
},
name2: {
type: 'string',
title: '名字2',
},
},
required: ['name2'],
},
{
properties: {
selected: {
const: 3,
},
name3: {
type: 'string',
title: '名字3',
},
},
required: ['name3'],
},
],
},
},
},
}
解释
通过dependencies
声明依赖关系,dependencies
的key
是依赖选项,比如在这里selected
是依赖项,在selected
有值的情况下才会展示和执行他包含的内容。
selected
的值是一个oneOf
则对应我们对于selected
不同的结果会现实其中某个结果,比如在这里如果:
selected
是1
,则我们必须填写name1
selected
是2
,则我们必须填写name2
selected
是3
,则我们必须填写name3
注意这里我们在每一项中都声明了一个selected
,他的类型是const
也就是固定值,以此我们来强制区分不同的结果,符合selected
为1
的结果必定不会符合其他的选项,就完全符合oneOf
的逻辑。
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago