@hxyfe/vs-form

1.0.0 • Public • Published

Form json 配置表单

介绍

基于 Vue 的使用 js 对象 进行配置生成的表单,主要适用于移动端

该表单的特点如下:

  1. 可以支持任意的表单组件库,例如:vant、vux甚至自建组件库等
  2. 表单内部未内置任何表单项

由于和表单项松耦合,所以表单项必须实现 v-model ,如果没有实现,则无法正常管理该表单项

作者本人使用 VsForm 的原因:

  1. 提供表单项的联动,只需简单配置就能实现
  2. 配置直接生成表单,不用写一堆臃肿的模板
  3. 配置支持设置默认属性,使得能够统一表单项的默认设置
  4. 支持配置校验规则,不用写一堆 if else 来手动校验
  5. 数据驱动视图,只需要修改配置对象,则会自动刷新UI
  6. 支持在指定位置渲染 slot
  7. 通过 v-model 直接拿到/设置 表单的值
  8. 作者还在回忆...

由于内部没有内置表单项,下面演示代码均是配合组件库 vant 编写。(vant 不会告我侵权吧。。。)

使用

安装

NPM

npm i --save vs-form

YARN

yarn add vs-form

简单使用

在最简单的情况下,无需其他配置就可以使用

// main.js
import VsForm from 'vs-form';
// Vant 可以换成任意的组件库,或者压根就不引入组件库而使用自定义组件
import Vant, { Toast } from 'vant';
Vue.use(Vant);

// 设置校验不通过提示方法
VsForm.showError = (errors) => {
  // 弹框提示未通过原因
  Toast(errors[0].message)
}

Vue.use(VsForm);
<vs-form v-model="form" ref="form" :fieldsConfig="fieldsConfig" class="gc-mb60">
  <template slot="introduction">
    <div>我是 slot introduction</div>
  </template>
</vs-form>
// 由于没有做其他配置,所以配置中 type 就是表单项组件名  
// 其中 slot 是特殊的,代表这里需要渲染一个 具名slot, slot 名是配置项中的 key  
export default {
  data() {
    return {
      // 如果需要绑定事件,则需要传 this 给配置数组
      fieldsConfig: [
        {
          label: '你的名字',
          key: 'name',
          type: 'van-field',
          props: {
            rules: [{ required: true, message: '请输入姓名' }],
          },
        },
        {
          label: '费用科目',
          key: 'costAccount',
          type: 'van-picker',
          props: {
            required: true,
            columns: ['杭州', '宁波', '温州', '绍兴', '湖州', '嘉兴', '金华', '衢州'],
          },
        },
        {
          key: 'introduction',
          type: 'slot',
          // 只有 vip 才配拥有这个 slot
          ifRender(form, config) {
            let render = false;
            if (form.name === 'vip') {
              render = true;
            }

            return render;
          },
        },
      ],
      form: {},
    };
  },
  methods: {
    // 校验表单
    validate() {
      return this.$refs.form.validate();
    },
  },
};

自定义 typeMap

VsForm 还有一个静态属性 typeMap ,这个映射表可以指定配置中的 type 指向的是什么组件。
同时还可以对该组件进行默认配置,例如给所有的 textarea 设置一个最大长度

// main.js
import VsForm from 'vs-form';
import typeMap from '[你的自定义 typeMap 存放位置]';

Vue.use(VsForm, typeMap);
// typeMap.js

// 表单项映射表
export default {
  // 普通输入框
  input: {
    // 指定实际的组件名
    // 使用懒加载的方式加载
    // 此处直接加载了组件,也可以指定一个已经注册了的组件名
    // 例如:component: 'van-field'
    component: () => {
      import('vant/lib/field/style');
      return import('vant/lib/field');
    },
    props: {},
  },

  // 进度条
  progress: {
    // 指定实际的组件名
    component: () => {
      import('vant/lib/progress/style');
      return import('vant/lib/progress');
    },
    props: {},
  },

  // 开关
  switch: {
    // 指定实际的组件名
    component: () => {
      import('vant/lib/switch-cell/style');
      return import('vant/lib/switch-cell');
    },
    props: {},
  },

  // 多行输入
  textArea: {
    // 指定实际的组件名
    component: () => {
      import('vant/lib/field/style');
      return import('vant/lib/field');
    },
    props: {
      rows: '2',
      autosize: true,
      type: 'textarea',
      maxlength: '50',
      'show-word-limit': true,
    },
  },

  // 数字输入
  inputNumber: {
    // 指定实际的组件名
    component: () => {
      import('vant/lib/field/style');
      return import('vant/lib/field');
    },
    props: {
      type: 'number',
    },
  },

  // 图片选择
  imgPic: {
    // 指定实际的组件名
    component: () => {
      import('vant/lib/uploader/style');
      return import('vant/lib/uploader');
    },
    props: {},
  },

  // 纯文本展示
  cell: {
    component: () => {
      import('vant/lib/cell/style');
      return import('vant/lib/cell');
    },
    // 动态 props
    // props 取值顺序是:typeMap.props -> item.props -> typeMap.getProps() -> item.getProps()
    // 后面对象的同名属性会覆盖前面的对象的同名属性,使用的Object.assign实现
    getProps(form, config) {
      return {
        title: config.label || config.key,
      };
    },
  },
};

对表单进行校验

可以通过在配置参数中的props参数里面传入rules进行表单表单项的规则配置,表单校验采用了 async-validator 作为底层,规则与之一致。

表单支持使用插件注册处理校验结果的方法,此处未做演示。

<vs-form ref="form" v-model="form" :fields-config="fieldsConfig" />
<div>
  <p v-if="errors.length > 0">我是校验不通过的提示</p>
  <p style="{ color: red }" v-for="(err, i) in errors" :key="err + i">
    {{ err.message }}
  </p>
</div>

<div @click="submit" type="default">提交</div>
export default {
  data() {
    return {
      fieldsConfig: [
        {
          label: '请假事由',
          key: 'leaveReason',
          type: 'input',
          props: {
            required: true,
            rules: [
              { type: 'string', required: true, message: '请假事由必填' },
              {
                type: 'string',
                message: '请假事由不能小于6个字符',
                pattern: /\w{6,}/,
              },
            ],
          },
          // 事件绑定
          on: {
            input() {
              console.log('input');
            },
            change() {
              console.log('change');
            },
          },
        },
      ],
      form: {},

      errors: [],
    };
  },

  methods: {
    submit() {
      this.$refs.form.validate().then(
        () => {
          console.log('校验通过');
          this.errors = [];
        },
        err => {
          this.errors = err;
          Toast({ message: err[0].message });
        }
      );
    },
  },
};

API

Props

参数 说明 类型 默认值
v-model (value) 当前表单的值 object -
fieldsConfig 当前表单的配置参数 config[] -

表单赋值采用的合并操作,并不是替换操作,类似 Object.assign ,所以不会删除 val 中没有的键名的值
例如表单值为: { a: 2, b: 1 }, val 值为 { a: 1 },则赋值后的结果为 { a: 1, b: 1 }
如果需要清空值,请使用 reset()

config

参数 说明 类型 默认值
type 表单项的类型/表单项的名称 string -
key 当前表单项绑定的属性名/slot的具名名称 string -
on 当前表单项绑定的事件(需要自行处理 this 指向) object: {event} -
nativeOn .native 绑定的事件 object: {event} -
component 表单项的标签名/表单项(直接引入) VNode | string -
label 表单项展示的 label 名(如果必填项未提供校验message则会使用这个 label 进行提示) string -
required 表单项是否必填(也可以写在 rules 里面)(ui部分需要自行处理) boolean -
others 其他支持 jsx 的一些属性,其中 on / nativeOn / props 被移除 object -
props 表单项的配置参数,直接透传给对应表单项 object -
getProps 表单项的动态参数计算方法,计算出结果后和上面的 props 合并 function -
ifRender 表单项的是否渲染计算方法,根据计算出的结果决定是否渲染表单项,表单联动的实现机制 function -
$$ifRender 表单项是否渲由上面方法计算出的结果 function -

getProps({...form}, config) 的入参为:form 的当前值 和 当前表单项当时的配置值(可能前面已经被动态修改了)

ifRender({...form}, config) 的入参为:form 的当前值 和 当前表单项当时的配置值(可能会由于其他表单项的改变而改变)

props 取值顺序是:typeMap.props -> item.props -> typeMap.getProps() -> item.getProps()
后面对象的同名属性会覆盖前面的对象的同名属性,使用的 Object.assign 实现

type 为 slot 类型的表单项,其实是一个 slot 占位,slot 名与 key 或者 name 属性的值相同

Events

VsForm 使用 v-model 进行值管理,所以会冒泡 input 事件
其他表单项事件由各个表单项确定,监听事件需要自行定义在配置项中

事件 说明 回调参数
input 表单内容变化时触发 value: object (当前表单的值)

方法

通过 ref 可以获取到 Form 实例并调用实例方法

方法名 说明 参数 返回值
validate 校验整个表单 noTips: Boolean(是否需要弹窗提示错误信息) promise: Promise
reset 重置表单值为指定的值 val: Object -
refreshUI 强制重新计算配置项,进行UI刷新 force: boolean (true:无论是否有更新,强制刷新) -

理论上你永远都不需要使用 refreshUI 方法,如果你遇到了需要使用的场景,请联系我

Q&A

  1. Q:为什么没有处理 CSS?A:移动端一般都是自定义样式,使用者自行定义一套全局样式即可。
  2. Q:为什么使用 v-model 来实现数据绑定?A:符合 vue 使用者的习惯,而且可以做到和表单项解耦。
  3. Q:表单项的值是复杂对象怎么办?A:复杂对象会被深度监听,使用者需要自己注意不要死循环,其他和单值没区别。
  4. ...

Readme

Keywords

Package Sidebar

Install

npm i @hxyfe/vs-form

Weekly Downloads

1

Version

1.0.0

License

MIT

Unpacked Size

4.49 MB

Total Files

31

Last publish

Collaborators

  • yxy-hxy