vant-ui是属于vue开发移动端中用的比较多的一个组件库了,网上基于它的一些组件的二次封装也数不胜数,但是却都是零零散散,不成体系。总不能用一个就去找类似的封装吧,这样拼凑起来的也不是我们想要的。尤其,涉及众多表单业务的时候。
1.场景需求
假如你遇到这样一个场景,需要做一个表单收集。涉及到输入框,单选框,多选框,下拉框,时间选择等等七八个表单。基于vant-ui的情况下,去官网单个的去复制粘贴。就这样一个vue页面是不是起码就几百行了。这还不算假如是在一个tabs标签页下,每个标签页下都有七八个表单呢?
这个时候,阁下又该如何自处?组件化,那就多少个标签多少个组件。不利于维护。改起来也麻烦。写一个页面?哪起码上千行代码了。
先看下效果图,有个最直观的感受:
所以,当涉及多个表单时,我们就迫切的需要一个成体系化的二次封装表单。
2.vform使用说明
v-form: 🎉 基于Vant-UI进行二次封装动态生成表单,也可完全自定义控件,通过 JSON 的形式配置,内部集成繁琐的校验规则,可扩展的校验
文档有详细说明。照猫画虎对于一些动手能力强的人来说那就是手拿把掐。这里我也就不多加叙述了。
3.从无到有
文档上说的很清楚,也不得不承认它的封装的却是很好。不多相比于大多数业务场景而言,也用不到那么复制。就一个最常见的场景来说。生成表单,收集信息 这八个字就是我们的核心思想,迫切所需要的。
闲话少叙,跟随lz的脚步,我们一起实操下:
1.引入依赖,全局注册
man.js
// 注册封装表单:https://gitee.com/cgb-lowcode/v-form
import VForm from '@xuanmo/v-form'
import '@xuanmo/v-form/packages/style/index.less'
Vue.use(VForm);
// 设置防抖时间,默认200ms
Vue.use(VForm, {
debounceTime: 200
})
tips:项目技术栈是vue+vant。记得提前引入vant。
2.模拟动态表单数据
formModel.js
const formdata = {
family:[
{ rules: { type: "VCell" } },
{
key:'text1',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra',
}
},
{
key:'text2',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text3',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text4',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text5',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text6',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text7',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{ rules: { type: "VCell" } },
{
key:'text8',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text9',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text10',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{ rules: { type: "VCell" } },
{
key:'text11',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text12',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text13',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text14',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text15',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
}
],
health:[
{ rules: { type: "VCell" } },
{
key:'text1',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra',
}
},
{
key:'text2',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text3',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text4',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
],
children:[
{ rules: { type: "VCell" } },
{
key:'text1',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra',
}
},
{
key:'text2',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text3',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text4',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{ rules: { type: "VCell" } },
{
key:'text5',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra',
}
},
{
key:'text6',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
],
retire:[
{ rules: { type: "VCell" } },
{
key:'text1',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra',
}
},
{
key:'text2',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text3',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text4',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text5',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text6',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{ rules: { type: "VCell" } },
{
key:'text7',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text8',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text9',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
],
riches:[
{ rules: { type: "VCell" } },
{
key:'text1',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra',
}
},
{
key:'text2',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text3',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text4',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
},
{
key:'text5',
value: '',
rules: {
label: '文本',
type: 'VInput',
vRules: 'required',
placeholder: '请输入',
errorMsg: '请输入',
extra: 'extra'
}
}
]
}
export default formdata;
3.vue页面使用
<template>
<div class='dataedit'>
<van-nav-bar
title='数据收集'
left-arrow
@click-left='$router.back()'
></van-nav-bar>
<div class="main">
<van-tabs @disabled="onClickDisabled" @change="changeTab" v-model.number="active" sticky color="#FF6837" title-active-color="#FF6837" line-width="0.5rem">
<van-tab disabled>
<template #title><img src="@/assets/menu/11.png" alt="" style="width:0.8rem;height:0.8rem;margin-top:0.2rem"></template>
</van-tab>
<van-tab title="表单1">
<v-form
ref="vform"
v-model="formValue.family"
:model="model.family"
:disabled="disabled"
label-width="80%"
:label-color="labelColor"
@change="change"
>
</v-form>
</van-tab>
<van-tab title="表单2">
<v-form
ref="vform"
v-model="formValue.health"
:model="model.health"
:disabled="disabled"
label-width="80%"
:label-color="labelColor"
@change="change"
>
</v-form>
</van-tab>
<van-tab title="表单3">
<v-form
ref="vform"
v-model="formValue.children"
:model="model.children"
:disabled="disabled"
label-width="80%"
:label-color="labelColor"
@change="change"
>
</v-form>
</van-tab>
<van-tab title="表单4">
<v-form
ref="vform"
v-model="formValue.retire"
:model="model.retire"
:disabled="disabled"
label-width="80%"
:label-color="labelColor"
@change="change"
>
</v-form>
</van-tab>
<van-tab title="表单5">
<v-form
ref="vform"
v-model="formValue.riches"
:model="model.riches"
:disabled="disabled"
label-width="80%"
:label-color="labelColor"
@change="change"
>
</v-form>
</van-tab>
</van-tabs>
</div>
<div class="btn">
<van-button round block type="info" color="#FF6837" to="/sys/data/addinfo">保存</van-button>
</div>
</div>
</template>
<script>
import formModel from "./formModel";
export default {
name: 'dataedit',
data () {
return {
active:1,
model:formModel,
disabled: false, // 是否禁用表单
labelColor: "inherit", // label颜色
formValue: { // 表单值
family:{},
health:{},
retire:{},
children:{},
riches:{}
},
}
},
mounted(){
console.log("参数:",this.$route.query.id);
},
methods: {
onClickDisabled(){
console.log("点击头像");
},
change({ value, errorMsg, isValid }) {
console.log("表单值:",value);
this.formData = value;
this.formError = errorMsg;
this.isValid = isValid;
},
changeTab(val){
console.log("当前标签===》",val);
},
}
}
</script>
<style lang='less' scoped>
::v-deep .v-form-container .v-form-row{
min-height: 1.5rem;
padding-top: 0.25rem;
}
.main{
::v-deep .van-tab__pane{
height: 80vh;
overflow-y: auto;
padding-bottom: 1rem;
}
}
.btn{
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 1.6rem;
display: flex;
justify-content: center;
align-items: center;
background: #ffffff;
padding: 0 5%;
}
</style>
4.最终效果图
数据是实时获取的。formModel.js定义的key值就是键。type为表单类型,如VInput,VRadio,VSelect等常用表单,label就是label文本,vRules是验证规则,placeholder是原生的placeholder,errorMsg就是验证失败提示。
其他的请自行查看文档。