1.1.0 • Published 2 years ago

imfe-xform-m v1.1.0

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

imfe-xform-m

基于vant2移动端配置化表单组件,通过内置表单组件和规则以及自定义组件,快速完成较复杂表单开发。

安装

$ npm install -S imfe-xform-m
#or
$ yarn add imfe-xform-m

该组件的peerDependencies

{
  "peerDependencies": {
    "vue": "^2.7.0",
    "vant": "^2.12.48",
    "@vant/area-data": "^1.3.1"
  }
}

使用

组件注册

// main.[ts|js]
import imfexform from "imfe-xform-m";
import "imfe-xform-m/lib/imfe-m-xform.css";
Vue.use(imfexform);

组件使用

<imfe-xform ref="xform" :form-data="formData" :form-config="formSchema" />

组件属性

属性名说明类型
form-data注入表单中表单项值,会和配置中默认值合并Record<string, any>
form-configschema 表单配置,详细配置字段使用见下xformSchema

form-data

用户新增编辑表单,该字段不传或传空即可,二次编辑需要从接口获取该表单数据入参。即接口获取的表单数据,来自于用户编辑保存。

所有的字段来自于 schema 中表单项的 key,平铺展示。

export default {
  name: "张三", //  schema中表单项key为name
  sex: "0",
  tumorTime: 1660188452878,
  birthday: "1992-10-20",
  idCard: "340111199210200637",
};

form-config

From props

字段说明类型
formKey【必填】表单唯一标识string
title表单标题string
description表单描述string
style表单自定义 style 样式object/ Record<string, string>
passThroughProps透传vant-form props,已有相同字段属性会覆盖透传字段object/ Record<string, any>
children【必填】表单组array/ Array

Group props

字段说明类型
title分组标题string
style组模块自定义 style 样式Record<string, string>
passThroughProps透传vant-cell-group props,已有相同字段属性会覆盖透传字段Record<string, any>
children【必填】表单项Array

Field props

字段说明类型
ui:type【必填】当前表单项类型,自定义组件使用 custom-*开头,内建组件见下string
label【必填】表单项 Labelstring
key【必填】表单项 key,获取表单会取所有表单项的 key 字段集合string
defaultValue【必填】表单项默认值,按照当前类型置空'',{},[]等any
placeholder占位值string
options静态选项值,只针对 select 、checkbox 、 radios 有效且【必填】,选项值依赖联动事件,可预传空数组FieldOptions | ((value: any) => FieldOptions)
rules校验规则, 参考vant 表单项的 rules,可自定义 validator、patternArray<Partial>
style自定义 style 样式Record<string, string>
meta自定义数据any
suffix表单项后缀单位,只针对 input 有效string
broadcast设置为 true 后会广播当前表单项 change 事件,监听组件(被联动组件)可接受当前表单项的 change 事件和值,用于联动组件场景默认 falseboolean
withInputValue若有值,表示当用户选择该值的选项会出现输入框(通常用于其他选项,用户输入其他值),只针对 checkbox、radios 有效。默认为空即不含输入框。any
breakWraplabel 和表单内容主题是否换行显示。默认 falseboolean
passThroughProps透传 vant 表单项 props,已有相同字段属性会覆盖透传字段 详细见下核心属性说明Record<string | 'group' | 'item', string>
linkEvents联动事件行为注册,配合 broadcast 属性,联动用法见下案例Array<{key:string; action: (payload: any, vm: any) => void;}>
visiable该表单项是否可见,主要用于联动初始化boolean
disabled该表单项是否禁用boolean

表单项配置属性(field prop)核心字段说明

passThroughProps

如果是radioscheckboxgroup包裹的,passThroughProps会使用两个字段分别透传,group字段会透传到组,item会透传到表单项。

例如,checkbox 默认使用 checkboxgroup 包裹,checkbox 和 checkboxgroup 都有 vant 属性,透传分别使用 group 和 item 透传。

passThroughProps:{
	group: {
		// 透传到checkboxgroup
	},
	item:{
		// 透传到checkbox
	}
}

options

只针对多选项表单组件有效,如selectcheckboxradios 且必填。支持数组类型或者返回 promise 的函数

如何从接口数据获取动态 options?

函数可以更好的调用动态options数据,调用数据中心模块或接口获取,promise resolve数据类型为数组。

{
  "ui:type": "checkbox",
   label: "兴趣爱好",
   key: "hobby",
   rules: [{ required: true, trigger: "onBlur" }],
   defaultValue: [1, 2],
   options: () => { //接口数据获取动态options
     return Promise.resolve([
       {
         label: "麻将",
         value: "1",
       },
       {
         label: "骑马",
         value: "2",
       },
       {
         label: "射箭",
         value: "3",
       },
     ]);
 }

组件事件

事件名说明回调参数
change当表单中表单项值发生变化触发事件payload: Record<"key"|"value", any>
failed提交表单且验证不通过后触发errorInfo: { values: object, errors: object[] }

组件配置示例

export const formOptions: xformSchema = {
  formKey: 'xform-sample',
  title: 'xform配置化表单样例',
  description: '根据schema描述配置自动化生成表单,支持自定义校验',
  passThroughProps: {
    colon: true
  },
  children: [
    {
      title: '基本信息',
      children:[
        {
          'ui:type': 'input',
          label: '姓名',
          key: 'name',
          defaultValue: '',
          rules: [
            {
              required: true,
              trigger: 'onChange',
              validator: (value: any, rule: any): boolean | Promise<boolean> => {
                if (value.length < 2) {
                  rule.message = '请输入正确的姓名';
                  return false;
                }
                return true;
              }
            }
          ],
          passThroughProps: {
            readonly: true
          },
          style: {
            backgroundColor: #77e8cc,
          }
        }
      ]
    }
  ]
}

表单组件联动示例

疫苗接种,当选择已接种,需要联动新冠疫苗厂家的显隐。

1、疫苗接种的表单项需要增加广播选项broadcast: true,把当前表单项变动广播出去,让需要的的组件接收。

{
'ui:type': 'radio',
label: '疫苗接种',
key: 'vaccine-COVID-19',
broadcast: true,
}

2、疫苗厂家表单项,这时候就可以接受到疫苗接种的广播事件了,使用linkEvents注册对所有接收事件的处理。

linkEvents可以配置多个广播事件监听,key为监听的表单项 key 值(内建组件需要设置 broadcast 为 true,自定义组件也需要按照广播事件规范进行),action为行为注册,入参为监听表单项的值和监听表单组件的实例。

{
'ui:type': 'select',
label: '新冠疫苗厂家',
key: 'vaccine-COVID-19-factory',
options: ['科兴', '智飞'],
linkEvents: [
  {
    key: 'vaccine-COVID-19',
    action: (payload: any, vm: any): void => {
      if (payload.value === '-1') {
        vm.visible = false;
      } else {
        vm.visible = true;
      }
   }
  }
]
}

LinkEvents 联动事件注册

类型 Array

LinkItem

字段说明类型
key监听表单项的 key,自定义组件需要提前注册到事件总线,注册方式见下string
action行为注册,入参为监听的表单项的荷载值 payload 和监听表单组件的实例 vm(payload: any, vm: any) => void

自定义组件的使用

表单支持研发增加自定义复杂业务表单组件,其简单原理为注册用户自定义表单组件到 vue 全局组件即可。

1、在components/目录下新增自定义组件目录xform-custom(可按业务和场景自定义任意目录名)

2、在入口文件 main.js,注册该目录下的组件到全局或利用 webpack 按路径批量注册(推荐)

// main.js
const xformCustom = require.context(
  "src/components/xform-custom",
  true,
  /\.vue$/
);
[xformCustom].forEach((formFiles) => {
  formFiles.keys().forEach((filePath) => {
    const fileConfig = formFiles(filePath);
    const fileName = fileConfig.default.name;
    Vue.component(fileName, fileConfig.default || fileConfig);
  });
});

3、自定义组件请使用imfe-custom-*开头命名组件

4、schema文件中配置该组件省略掉imfe,直接使用custom-*即可

增加自定义组件目录,项目简化结构示意如下

src/
├── App.vue
├── assets
│   └── logo.png
├── components
│   └── xform-custom
│       └── imfe-custom-faceid.vue
├── main.js

schemafield使用自定义组件

// schema
export default {
	...
  {
    "ui:type": "custom-faceid",
    label: "人脸识别",
    key: "faceId",
    defaultValue: "",
    broadcast: true, // 是否广播change值。可选。
    LinkEvents: [], // 如何接受其他表单项change值。可选。
    meta: {
    	//自定义组件数据
    },
   }
   ...
}

xform 自定义 vue 组件开发模板(重要)

核心是要模板注入表单组件提供的 Mixin customMixin,其提供内建自定义组件的值和方法。

<template>
  <div class="imfe-xform-custom-test">
    <van-field
      v-model="fieldValue"
      :label="formItem.label"
      @input="emitFormChange"
    />
    <div class="custom-test-msg">{{ customMeta.msg }}</div>
  </div>
</template>

<script>
import { defineComponent } from "vue";
import { customMixin } from "imfe-xform-m";
export default defineComponent({
  name: "imfe-custom-test",
  mixins: [customMixin],
});
</script>

customMixin内建资源

自定义组件 Mixin
this.formItem约定为 schema 中当前表单项配置
this.customMeta约定为配置项中 meta 值
this.fieldValue约定为当前表单项值
this.emitFormChange约定为表单项 change 或 input 等事件监听函数
export const customMixin = {
  props: {
    // formItem,schema中当前表单项配置
    formItem: {
      type: Object,
      required: true,
    },
  },
  computed: {
    // customMeta,配置项中meta值
    customMeta() {
      return this.formItem.meta || {};
    },
  },
  watch: {
    // fieldValue,当前表单项值
    "$attrs.formModel"(newFormModel) {
      this.fieldValue = newFormModel[this.formItem.key];
      this.emitFormChange(this.fieldValue);
    },
  },
  mounted() {
    // 联动配置使用linkevents源码,研发无需关心
    this.formItem.linkEvents &&
      this.formItem.linkEvents.forEach((item) => {
        EventBus.$on(item.key, (payload) => {
          item.action(payload, this);
        });
      });
  },
  beforeDestroy() {
    // 自动销毁eventbus
    EventBus.$off();
  },
  methods: {
    // 表单项change或input事件监听函数
    emitFormChange(value) {
      const { key, broadcast } = this.formItem;
      const payload = { key, value };
      // 表单项联动,广播事件
      broadcast && EventBus.$emit(key, payload);
      // 表单变动,触发表单收集事件
      fieldChangeHandler(payload);
    },
  },
};

TypeScript Types

export type FormDefValue = string | number | any[];
export type FieldOptions = Array<{ label: string; value: any }> | Array<string>;
export type FieldDynamicOptions = FieldOptions | (() => Promise<FieldOptions>);
interface FieldRules {
  required: boolean;
  message: string | ((value: any, rule: any) => string);
  pattern: RegExp;
  trigger: "onSubmit" | "onBlur" | "onChange";
  validator: (value: any, rule: any) => boolean | Promise<boolean>;
  formatter: (value: any, rule: any) => any;
}
interface LinkItem {
  key: string | string[];
  action: (payload: any, vm: any) => void;
}
interface FormDef {
  "ui:type": string; // 当前表单项名称
  label: string;
  key: string; // 表单项唯一标识,自动会加上表单key作为namespace安全隔离
  defaultValue?: any; // 默认值
  visiable?: boolean; // 是否可见
  disabled?: boolean; // 是否禁用
  placeholder?: string;
  options?: FieldDynamicOptions; // 选项
  rules?: Array<Partial<FieldRules>>; // 校验规则, 参考vant表单项的rules
  style?: Record<string, string | number>;
  meta?: any; // 自定义表单项的数据携带,非内建表单项使用
  suffix?: string; // 后缀单位
  withInputValue?: any; // 带输入框信息
  breakWrap?: boolean; // 是否label和表单强制换行显示
  passThroughProps?: Record<string, any>; // vant表单项透传属性,透传的vant属性如果和表单项的属性重名,透传属性将不会生效,如label、placeholder等
  broadcast?: boolean; // 广播当前表单项change事件,事件名为当前表单项key值,用于联动组件场景
  linkEvents?: Array<LinkItem>; // 联动事件行为注册
}
export interface FieldProps extends FormDef {
  siblings?: Array<FormDef>; // 默认一行一个表单,如果siblings有值,则表示同行多个表单项
}
export interface GroupProps {
  title?: string; // 组标题
  style?: Record<string, string | number>; // 组样式
  children: Array<FieldProps>; // 子表单项
  passThroughProps?: Record<string, any>;
}
export interface xformSchema {
  formKey: string; // 表单唯一标识,
  title?: string; // 表单标题
  description?: string; // 表单描述
  style?: Record<string, string>; // 表单样式
  passThroughProps?: Record<string, any>;
  children: Array<GroupProps>;
}

内建组件

input

输入框

{
  'ui:type': 'input',
  label: '',
  key: '',
  defaultValue: '',
  placeholder:'',
  rules: [{}],
  suffix: '',
},

radio

单选

{
  'ui:type': 'radio',
  label: '',
  key: '',
  defaultValue: '',
  withInput: false,
  options:[{ label: '', value: '' }],
  rules: [{}],
  passThroughProps:{
  group: {
      // 透传到radiogroup
    },
    item:{
      // 透传到radio
    }
  }
},

checkbox

复选框

{
  'ui:type': 'checkbox',
  label: '',
  key: '',
  defaultValue: '',
  withInput: false,
  options:[{ label: '', value: '' }],
  rules: [{}],
  passThroughProps:{
    group: {
      // 透传到checkboxgroup
    },
    item:{
      // 透传到checkbox
    }
  }
},

select

下拉滑动选择器

{
  'ui:type': 'select',
  label: '',
  key: '',
  defaultValue: '',
  options:[''],
  rules: [{}],
},

date

日期选择

{
  'ui:type': 'date',
  label: '',
  key: '',
  defaultValue: '',
  rules: [{}],
},

area

城市选择,带详细地址填写。

{
  'ui:type': 'area',
  label: '',
  key: '',
  defaultValue: '',
  rules: [{}],
},

rate

评分

{
  'ui:type': 'rate',
  label: '',
  key: '',
  defaultValue: '',
  rules: [{}],
},

slider

滑动选择器

{
  'ui:type': 'slider',
  label: '',
  key: '',
  defaultValue: '',
  rules: [{}],
  passThroughProps:{}
},

stepper

步进器

{
  'ui:type': 'stepper',
  label: '',
  key: '',
  defaultValue: '',
  rules: [{}],
  passThroughProps: {}
},

switch

开关

{
  'ui:type': 'switch',
  label: '',
  key: '',
  defaultValue: '',
  rules: [{}]
},

uploader

文件上传

{
  'ui:type': 'uploader',
  label: '',
  key: '',
  defaultValue: '',
  rules: [{}],
  passThroughProps: {}
},

title

标题组件

{
  'ui:type': 'title',
  label: '',
  key: '',
  defaultValue: '',
},

业务标准组件

std-checkbox

复选框

{
  'ui:type': 'checkbox',
  label: '',
  key: '',
  defaultValue: '',
  meta: {
    span: 8,
    options: [{ label: '', value: '' }],
    withInput: false
  }
},

std-radios-checkbox

二值单选+复选框

{
    'ui:type': 'custom-radios-checkbox',
    label: '二值单选-复选框',
    key: '',
    rules: [{ required: true, trigger: 'onBlur' }],
    defaultValue: {
      radios: '-1',
      checkbox: {}
    },
    meta:{
      radios: {
        options: [
          {
            label: '无',
            value: '-1'
          },
          {
            label: '有',
            value: '1'
          }
        ]
      },
      checkbox: {
        span: 12,
        options: [{ label: '', value: '' }],
        withInput: true
    }
}

std-radios-textarea

二值单选+文本录入

{
          "ui:type": "std-radios-textarea",
          label: "二值单选-文本录入",
          key: "",
          defaultValue: {
            radios: "",
            input: "",
          },
          meta: {
            // 业务组件自定义数据字段
            radios: {
              options: [
                {
                  label: "无",
                  value: "-1",
                },
                {
                  label: "有",
                  value: "1",
                },
              ],
            },
            textarea: {
              placeholder: "请输入",
            },
          },
        },

std-dynamic-fields-type1

动态新增表单项组(input-date)

{
          "ui:type": "std-dynamic-fields-type1",
          label: "动态新增表单项组",
          key: "",
          rules: [{ required: true, trigger: "onBlur", message: "必填" }],
          meta: {
            // 业务组件自定义数据字段
            radiosOptions: [
              {
                label: "无",
                value: "-1",
              },
              {
                label: "有",
                value: "1",
              },
            ],
            addText: "添加选项",
            formItems: {
              input: {
                label: "名称",
                placeholder: "请输入名称",
                passThroughProps: {
                  maxlength: 10,
                },
              },
              date: {
                label: "时间",
                placeholder: "请选择时间",
                passThroughProps: {
                  "min-date": new Date("1950-01-01"),
                },
              },
            },
          },
          defaultValue: {
            exsist: "-1",
            value: [],
          },
        },