表单
背景
表单是客户端Weex开发常见的页面, 尤其特别在toB的项目中, 收集提交一些数据是非常高频且重要的业务. 相比于其他页面表单本身有一定的特性, 比如一定存在的提交按钮 竖向排列的UI来区分不同的数据字段 必要数据的必填项 输入错误的提示等等, 所以它本身是可以进行抽象封装的.
在此前的表单业务开发当中, 由于考虑范围只在当次那个业务当中, 所以整个表单方向代码复用率低, 扩展性低. 此次方案, 主要思路是从过往的表单业务中归纳分析, 抱着以统一处理所有相关业务的思想, 分类并逐步完善与表单相关的所有业务, 技术上追求在表单业务应有的规范下, 一套框架可以满足当前和以后所有的表单.
目标
落地客户端Weex表单框架, 和设计共同维护表单业务规范, 能在未来的迭代中不断完善和调优
做法
表单本身涉及技术点较多, 所以需要将相关联的技术点进行汇总统一, 大的方向上划分出分类, 每个分类单独进行调研和处理
分类
表单规范
规范表单实现, 规范表单数据处理
表单组件和布局
负责整体UI页面的搭建
表单验证和错误处理
负责统一处理必填项 输入项校验 错误提示等
常见业务的处理
比如关闭时的弹框提示 时间选择 日期选择组件等
表单规范
表单数据
对于表单内的数据, 例如输入的input
名字, 选择的性别男女sex等. 所有因为表单组件产生或修改的数据, 都统一包装到页面的一个对象内, 对象名字为formData
, 每一项对应的数据内容要有相应的注释, 不应该包含其他无关属性. 数据格式的设计应该按照表单页面结构或者顺序设计, 不应该按照后台接口结构设计.
表单数据的处理: 提交和回填
遵循表单数据规范后, 数据提交就是将formData数据转换成后台接口body结构, 数据回填就是将后台返回body转换成formData
, 所以统一命名方法为: formDataToBody()
,bodyToFormData(body)
//自定义组件, (通过v-model双向绑定来传递数据)
<fg-form-xxx v-model="form.data.selectAge"></fg-form-xxx>
//data, (所有数据放到formData中, 并且注释)
formData: {
name: '', //姓名
sex: 0, //性别,默认男
selectAge:0 //年龄
}
//method
formDataToBody(){
return {
upPhotos: this.fomrData.imageUrl
...
}
}
bodyToFormData(body){
this.formData.imageUrl = body.upPhotos
...
}
表单组件
表单组件要支持v-model, 并且尽可能的只使用v-mode来传递和配置数据
表单页面的组件应该尽可能使用已有组件, 避免重复造相似的轮子, 对于业务上的不同, 应该尽可能通过配置去扩展组件来达到需求效果
表单组件需要配备相应的文档说明, 且有demo
fg-form
属性
参数 | 说明 | 类型 | 作用范围 | 默认值 |
---|---|---|---|---|
data | 所有表单数据 | Object | form | |
rules | 所有表单数据的验证规则 | Array | form | |
containterStyle | scroller组件的style | Object | form | |
canGestureClose | 能否手势返回 | Boolaen | form | false |
以下属性作用于子元素fg-form-item, 非Form本身 | ||||
position | 容器在右使用:left,right 容器在下使用:top left和right区别是label对齐方式 |
String | 一级Item | 默认left. 可选left, right, top |
labelWidth | label域的宽度,label存在时有效 | String | 所有Item | left,right时:77wx top时: auto |
labelHeight | label域的高度,label存在时有效 | String | 所有Item | left,right默认:44wx top默认: auto |
height | 整个Item的高度, 非固定高度一般使用labelHeight代替 | String | 所有Item | auto |
required | label是否显示红色必填 | Boolean | 所有Item | false |
marginTop | 上间距 | String | 一级Item | 0wx |
marginRight | 右间距 | String | 一级Item | 0wx |
marginBottom | 下间距 | String | 一级Item | 0wx |
marginLeft | 左间距 | String | 一级Item | 16wx |
paddingTop | 上内间距 | String | 一级Item | 0wx |
paddingRight | 右内间距 | String | 一级Item | 16wx |
paddingBottom | 下内间距 | String | 一级Item | 0wx |
paddingLeft | 左内间距 | String | 一级Item | 0wx |
disabled | 整个元素的disabled状态 | Boolean | 所有Item | false |
slotDisabled | 元素插槽的disabled状态 | Boolean | 所有Item | false |
disabledOpacity | disbled时的透明度 | Number | 所有Item | 0.4 |
backgroundColor | 除上下desc部分,剩余部分背景颜色 | String | 所有Item | white |
showLine | 是否显示下划线 | Boolean | 最深Item | false |
lineLeftRightMargin | 下划线左右边距 | Array | 最深Item | [‘16wx’,’0wx’] |
rightSlotWidth | 右侧插槽宽度 | String | 最深Item | 0wx |
fg-form-item
属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
labelWidth | label域的宽度, label存在时才有效 | String | left,right时:77wx top时: auto |
labelHeight | label域的高度, label存在时才有效 | String | 44wx |
rightSlotWidth | 右侧插槽宽度 | String | 0wx |
position | 容器在右使用:left,right 容器在下使用:top left和right区别是label对齐方式 |
String | 默认left. 可选left, right, top |
inline | 行内模式(插槽row,并且内部的item会被自动均分) | Boolean | false |
height | 整个容器的高度, 非必要建议使用labelHeight撑开 | String | auto |
label | label的标题内容 | String | |
labelRichConfig | label富文本属性 | Array | |
subLabel | label的副标题内容 | String | |
subLabelRichConfig | label的副标题富文本属性 | Array | |
required | label是否显示必填 | Boolean | false |
requiredString | 必填label的文本内容 | String | * |
topDesc | 顶部描述 | String | |
topDescRichConfig | 顶部描述富文本属性 | Array | |
bottomDesc | 底部描述 | String | |
bottomDescRichConfig | 底部描述富文本属性 | Array | |
disabled | 整个元素的disabled状态 | Boolean | false |
slotDisabled | 元素插槽的disabled状态 | Boolean | false |
disabledOpacity | disbled时的透明度 | Number | 0.4 |
showLine | 是否显示下划线 | Boolean | false |
lineLeftRightMargin | 下划线左右边距 | Array | [‘16wx’,’0wx’] |
backgroundColor | 除上下desc部分,剩余部分背景颜色 | String | white |
marginTop | 上间距 | String | 0wx |
marginRight | 右间距 | String | 0wx |
marginBottom | 下间距 | String | 0wx |
marginLeft | 左间距 | String | 16wx |
paddingTop | 上内间距 | String | 0wx |
paddingRight | 右内间距 | String | 16wx |
paddingBotton | 下内间距 | String | 0wx |
paddingLeft | 左内间距 | String | 0wx |
containterStyle | 自定义容器的style | Object | |
contentStyle | 自定义容器的style | Object | |
labelContainterStyle | 自定义容器的style | Object | |
labelStyle | 自定义容器的style | Object | |
subLabelStyle | 自定义容器的style | Object | |
requiredStyle | 自定义容器的style | Object | |
slotContainterStyle | 自定义容器的style | Object | |
lineStyle | 自定义容器的style | Object | |
topDescContainter | 自定义容器的style | Object | |
topDescLabelStyle | 自定义容器的style | Object | |
bottomDescContainter | 自定义容器的style | Object | |
bottomDescLabelStyle | 自定义容器的style | Object | |
baseContentContainterStyle | 自定义容器的style | Object | |
baseContentStyle | 自定义容器的style | Object | |
contentRightStyle | 自定义容器的style | Object | |
labelSuffixStyle | 自定义容器的style | Object | |
labelPrefixStyle | 自定义容器的style | Object | |
labelContentStyle | 自定义容器的style | Object |
插槽
slot名字 | 说明 |
---|---|
default | 主要内容插槽 |
topDesc | 顶部描述插槽 |
bottomDesc | 底部描述插槽 |
labelPrefix | 文本域前插槽 |
labelSuffix | 文本域后插槽 |
方法
事件名 | 参数 | 说明 |
---|---|---|
_fatherFormItemClickEvent | 插槽里的子组件使用,当Item被点击时,传递给一级children的事件 | |
getDefaultSlotWidth | 获取当前插槽的预估宽度, 当插槽内部组件需要知道当前宽度时使用, 宽度计算内部根据padding,margin等, 当自定义了style后不保证一定正确 |
注意事项
- label域的label默认不支持换行, 如需换行, 请手动设置width
- Item和Form支持的属性和事件内部使用$parent等来实现通讯, 所以当自己对组件进行包装时, 可能会影响通讯导致出现一些不可预知问题
- 当Item插槽内部容器需要换行时, 例如flexwrap或者Text. 若使用的是top模式,且内部没有row容器嵌套则不需要给宽度,就可以正常换行. 若使用的是left/right模式, 需要手动通过getDefaultSlotWidth方法获取宽度, 并显示的给予对应组件,才可以正常换行
demo:
fg-form-layoutdemo(布局相关), fg-form-formdemo(属性相关)
fg-form-input
属性
名字 | 类型 | 默认值 | 说明 |
---|---|---|---|
value | String | 文本内容, 外部v-model绑定值 | |
placeholder | String | 占位文本 | |
placeholderColor | String | #A2ABB3 | 占位文本颜色 |
autofocus | Boolean | false | 是否焦点 |
fatherItemClickAutofocus | Boolean | true | 当父组件是item时, 点击item自动让input获取焦点 |
height | String | 父级为Item时, 默认labelHeight或height,或form的height 父级非Item时, 默认auto |
Input组件高度 |
maxLength | Number | 1000 | 最大文本长度 |
returnKeyType | String | 键盘return键类型 | |
type | String | text | 键盘类型 |
textAlign | String | right | 文本对齐方式 |
fontSize | String | 字体大小 | |
formatRule | String | 正则校验 | |
recoverRule | String | 正则校验 | |
containterStyle | Object | 容器Style | |
containterInputStyle | Object | 容器Style | |
inputStyle | Object | InputStyle | |
containterPrefix | Object | 前插槽Style | |
border | Boolean | false | 是否显示Border |
radius | String | 圆角 | |
paddingLeft | String | 左内边距 |
插槽
名字 | 说明 |
---|---|
prefix | 整体前插槽, 在边框外部 |
inputPrefix | input前插槽, 在边框内部 |
inputSuffix | input后插槽, 在边框内部 |
suffix | 整体后插槽, 在边框外部 |
表单验证和错误处理
使用方法
给form的data属性赋值formData
, 给form的rules属性赋值formRules
示例
//template
<fg-form
:data="formData"
:rules="formRules"
@validateChange="validateChange"
>
//...
<fg-form>
//script data
formData: {
name: '',
age: undefined,
sex: '',
}
formRules: [
{name: {required: true, msg: '请输入姓名'}}, //表示姓名必填
{age: [
{required: true, msg: '请输入年龄'}, //表示年龄必填,且在1~200范围内
{min:1,max:200, msg:'年龄范围应在1岁到200岁之间'}
]},
{sex: [
{required: false}, //表示性别非必填, 若填写必须为男或女
{enum:['男','女'], msg:'性别只能是男或女'}
]}
]
//script methods
// 校验结果回调
validateChange(data){
//当formData数据或者formRules变化时, 自动进行校验并回调结果
//data结构 {validate: 校验结果, msg: 第一个失败的msg, info: 详细校验结果, channleId:当前channleId}
}
注意事项
- fomRules中的属性必须是formData的子集, 且校验顺序内部是按formRules的集合顺序
- formRules的格式为: [ {属性1: Object或集合 } , {属性2: Object或集合 } ], 当只有一个校验规则时使用Object, 多个使用集合包裹起来. 每个Object只能有一种规则
所有规则
| key | value类型 | 说明 | | —- | —- | —- | | required | Boolaen | 是否必填.
必填时: 有值才校验
非必填时: 无值不校验, 有值才校验
是否有值规则: undefine,null, 空集合, 空字符都是无值的 | | min, max | Number | min: 最小, max:最大. 可以只填其中一项
注:
当值类型为Number时, 校验的是值本身大小
当值类型为String/Array时, 校验的是值长度 | | len | Number | 长度, 校验String/Array的固定长度 | | enum | Array | 限制值范围 | | whitespace | Boolean | 限制非空格 | | pattern | RegExp/String | 自定义正则校验 | | validator | function | 自定义方法校验, 带一个参数为当前值, 需要一个返回值Boolean |
规则提示
key | value类型 | 说明 |
---|---|---|
msg | String | 当校验失败时传递出来的错误提示文本 |
fg-form-button
包装自fg-button, 属性和方法同fg-button. 当它和fg-form同时存在时, 此button的disabled状态会自动关联校验结果, 即校验成功时高亮, 校验失败时不高亮, 且效验失败时点击button会自动提示第一个失败的msg信息.
当提交按钮在导航栏时, 可以直接将导航栏的type设为form, 右侧按钮即自动为fg-form-button.
demo
fg-form-validateform