@zdwh/vue-json-schema-form

1.0.19 • Public • Published

@zdwh/vue-json-schema-form

表单从来没有这么简单,通过一份 json-schema,你就拥有了一套交互完整,校验完善的表单。

Version License Coverage Build

demo 演示

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-elementformProps可以是任意 element-ui 中的 form 组件的props

plugins

插件

uiSchema

具体参考下面的vjsf

locale

默认zh中文,支持值参考ajv-i18n

ajvInstanceOptions

参考ajv options

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 的时候,在typeobject的 schema 中可以增加propertiesOrder属性, 他的值是一个数组,properties里面的变量会根据这里声明的顺序进行渲染,没有出现在这个数组里面的变量名则放在最后面

插件

插件能够帮助我们非常方便地扩展功能,目前支持情况如下:

  • [x] customFormat
  • [x] 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声明依赖关系,dependencieskey是依赖选项,比如在这里selected是依赖项,在selected有值的情况下才会展示和执行他包含的内容。

selected的值是一个oneOf则对应我们对于selected不同的结果会现实其中某个结果,比如在这里如果:

  • selected1,则我们必须填写name1
  • selected2,则我们必须填写name2
  • selected3,则我们必须填写name3

注意这里我们在每一项中都声明了一个selected,他的类型是const也就是固定值,以此我们来强制区分不同的结果,符合selected1的结果必定不会符合其他的选项,就完全符合oneOf的逻辑。

Readme

Keywords

none

Package Sidebar

Install

npm i @zdwh/vue-json-schema-form

Weekly Downloads

19

Version

1.0.19

License

MIT

Unpacked Size

2.65 MB

Total Files

17

Last publish

Collaborators

  • jokcy
  • zdwh