imfe-xform-m
v1.1.0
Published
基于vant开发的配置化表单组件
Downloads
4
Readme
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-config | schema 表单配置,详细配置字段使用见下 | 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 | 【必填】表单项 Label | string | | key | 【必填】表单项 key,获取表单会取所有表单项的 key 字段集合 | string | | defaultValue | 【必填】表单项默认值,按照当前类型置空'',{},[]等 | any | | placeholder | 占位值 | string | | options | 静态选项值,只针对 select 、checkbox 、 radios 有效且【必填】,选项值依赖联动事件,可预传空数组 | FieldOptions | ((value: any) => FieldOptions) | | rules | 校验规则, 参考vant 表单项的 rules,可自定义 validator、pattern | Array<Partial> | | style | 自定义 style 样式 | Record<string, string> | | meta | 自定义数据 | any | | suffix | 表单项后缀单位,只针对 input 有效 | string | | broadcast | 设置为 true 后会广播当前表单项 change 事件,监听组件(被联动组件)可接受当前表单项的 change 事件和值,用于联动组件场景默认 false | boolean | | withInputValue | 若有值,表示当用户选择该值的选项会出现输入框(通常用于其他选项,用户输入其他值),只针对 checkbox、radios 有效。默认为空即不含输入框。 | any | | breakWrap | label 和表单内容主题是否换行显示。默认 false | boolean | | 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
如果是radios
或checkbox
有group
包裹的,passThroughProps
会使用两个字段分别透传,group
字段会透传到组,item
会透传到表单项。
例如,checkbox 默认使用 checkboxgroup 包裹,checkbox 和 checkboxgroup 都有 vant 属性,透传分别使用 group 和 item 透传。
passThroughProps:{
group: {
// 透传到checkboxgroup
},
item:{
// 透传到checkbox
}
}
options
只针对多选项表单组件有效,如select
、checkbox
、 radios
且必填。支持数组类型或者返回 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
schema
中field
使用自定义组件
// 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: [],
},
},