前言
在使用Vue写移动端开发的时候,难免会遇到需要写很多的表单,所以我在现在的项目里面集成了有赞的Vant-ui组件库,但是感觉Vant-ui对于表单组件的调用方式有点繁琐,实在不愿意去干这么一样的事情,就封装了一个基于Vant-ui的数据驱动式表单自动生成组件。
具体怎么玩,我们下面一起来看看吧~
正文
组件现在的状态
-
目前已经集成的组件如下:
Address、Checkbox、DatePicker、Input、Radio、Select、Text、Switch、Upload
其它组件还在完善中...
- 组件的调用方式采取json配置的形式,具体参数见model数据说明
- 校验规则已经集成VeeValidate插件,也可以自定义扩展规则,更多资料: https://logaretm.github.io/vee-validate
在线演示地址:
https://codesandbox.io/s/v-formshili-3hs2c?file=/src/main.js:53-383
安装
# yarn
yarn add @xuanmo/v-form
# npm
npm install @xuanmo/v-form -S
复制
使用
配置
vue.config.js配置组件编译(注:如果组件引入采取的后编译需要配置这一项)
module.exports = {
transpileDependencies: [
'@xuanmo/v-form'
]
}
复制
Props
字段名 | 说明 | 类型 | 默认值 |
---|---|---|---|
v-model(value) | 获取组件处理完成的数据 | object | {} |
model | 数据模型(具体类型参考后续文档) | object | {} |
disabled | 是否禁用表单 | boolean | false |
label-width | label宽度 | string | 20% |
label-position | label对齐方式,可选:left/right | string | left |
label-color | label文字颜色 | string | - |
show-label | 是否显示label | boolean | true |
event
事件名 | 说明 | 回调参数 |
---|---|---|
change | 数据更改时触发 | object{value,errorMsg,isValid} |
event | 数据发生改变所发送的事件 | object{event,formModel} |
slots
组件可接受多个slot,用于替换当前行的表单组件,会为该slot传入该组件的原始数据,每个slot的name为当前行的key
注:该slot不继承所有校验规则
<v-form :model="model">
<template v-slot:text="{ data }">
<van-field v-model="data.value"></van-field>
</template>
<!-- 行扩展字段slot,格式{key}-extra -->
<template #text-extra>
extra
</template>
</v-form>
复制
示例
main.js中全局注册
import "@vant/touch-emulator";
// 如果没有安装vant-ui可以采取这种方式引入组件
import VForm from "@xuanmo/v-form/dist/v-form.umd.js";
import "@xuanmo/v-form/dist/v-form.css";
// 项目已经引入vant-ui推荐使用这种方式引入,后编译
// import VForm from '@xuanmo/v-form'
Vue.use(VForm);
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
复制
页面中使用
<template>
<div id="app">
<div style="margin-bottom: 30px">
<span style="vertical-align: super;">切换表单禁用状态</span>
<van-switch v-model="disabled" size="20px" />
</div>
<v-form
ref="vform"
v-model="formValue"
:model="model"
:disabled="disabled"
label-width="100px"
@change="_change"
@event="_event"
>
<template #text1-label>
自定义label
</template>
<template #text1-extra>
<!-- <van-field v-model="data.value"></van-field> -->
extra
</template>
</v-form>
<div style="margin: 20px 0;text-align: center;">
<van-button type="primary" @click="_submit">提交数据</van-button>
</div>
</div>
</template>
<script>
import { Switch, Button } from 'vant'
export default {
name: 'App',
components: {
// 'van-field': Field,
'van-switch': Switch,
'van-button': Button
},
data () {
return {
formValue: {
text: 1
},
formData: {},
formError: [],
isValid: false,
disabled: false,
model: {
numberKeyboard: {
value: '',
rules: {
label: '数字键盘',
type: 'VNumberKeyboard',
placeholder: '点击输入',
// theme: 'custom',
extraKey: '.',
// closeButtonText: '完成'
}
},
file: {
value: [{ path: 'https://www.xuanmo.xin/wp-content/uploads/2019/10/xuanmo_avatar.JPG' }],
rules: {
label: '文件上传',
type: 'VUpload',
action: 'xxx',
accept: 'image/png',
multiple: true,
name: 'file',
data: {
dir: 'test'
},
props: {
url: 'path'
}
}
},
switch: {
value: true,
rules: {
label: '是否启用编辑',
type: 'VSwitch'
}
},
text: {
value: '',
rules: {
label: '文字',
type: 'VInput',
vRules: 'required|max:2',
placeholder: '请输入文字',
errorMsg: '请输入文字',
extra: 'extra'
}
},
text1: {
value: '文字内容',
rules: {
label: '文字1',
type: 'VText'
}
},
checkbox: {
value: ['a'],
rules: {
label: '复选框',
type: 'VCheckbox',
vRules: 'required',
placeholder: '请输入复选框',
errorMsg: '请输入复选框',
direction: 'horizontal',
options: [
{ label: '复选框 a', value: 'a' },
{ label: '复选框 b', value: 'b' },
{ label: '复选框 c', value: 'c' }
]
}
},
radio: {
value: 'b',
rules: {
label: '单选框',
type: 'VRadio',
vRules: 'required',
disabled: true,
placeholder: '请输入单选框',
errorMsg: '请输入单选框',
direction: 'horizontal',
options: [
{ label: '复选框 a', value: 'a' },
{ label: '复选框 b', value: 'b' },
{ label: '复选框 c', value: 'c' }
]
}
},
date: {
value: Date.now(),
rules: {
label: '时间',
type: 'VDatePicker|datetime',
valueFormat: 'timestamp'
}
},
dateRange: {
value: [Date.now(), Date.now()],
rules: {
label: '时间',
type: 'VDatePickerRange|time',
valueFormat: 'timestamp',
rangeSeparator: '至'
}
},
number: {
value: '',
rules: {
label: '数字',
type: 'VInput|digit',
vRules: 'required',
placeholder: '请输入数字',
errorMsg: '请输入数字'
}
},
address: {
value: '110000,110100,110114',
rules: {
label: '地址选择',
type: 'VAddress',
vRules: 'required',
placeholder: '请输入地址',
errorMsg: '请输入地址'
}
},
textarea: {
value: '',
rules: {
label: '文本域',
type: 'VInput|textarea',
vRules: 'required',
placeholder: '文本域',
errorMsg: '文本域'
}
},
select: {
value: '4',
rules: {
label: '选择器',
type: 'VSelect',
placeholder: 'picker选择器',
errorMsg: 'picker选择器',
vRules: 'required',
options: [
{ text: '杭州', value: 1 },
{ text: '宁波', value: 2 },
{ text: '温州', value: 3 },
{ text: '嘉兴', value: 4 },
{ text: '湖州', value: 5 }
]
}
},
selectMultiple: {
value: '4,2',
rules: {
label: '多列选择器',
type: 'VSelect',
placeholder: 'picker选择器',
errorMsg: 'picker选择器',
options: [
[
{ text: '杭州', value: '1' },
{ text: '宁波', value: '2' },
{ text: '温州', value: '3' },
{ text: '嘉兴', value: '4' },
{ text: '湖州', value: '5' }
],
[
{ text: '杭州', value: '1' },
{ text: '宁波', value: '2' },
{ text: '温州', value: '3' },
{ text: '嘉兴', value: '4' },
{ text: '湖州', value: '5' }
]
]
}
}
}
}
},
methods: {
_change ({ value, errorMsg, isValid }) {
this.formData = value
this.formError = errorMsg
this.isValid = isValid
},
_event ({ type, value }) {
console.log(type, value)
},
_submit () {
if (!this.isValid) {
this.$toast(this.formError[0].errorMsg)
return
}
this.$toast('提交成功')
}
}
}
</script>
<style>
#app {
font-size: 14px;
}
</style>
复制
最终效果
结语
说实话,自从封了这个组件就感觉对于表单的处理就只是一串数据而已,其它的基本不用管,最后直接取填完表单后的数据就可以了,减少了不少繁琐,不用到处复制粘贴,还可以自己去更好的扩展自己想要的功能。如果各位同学表示认同,可以去我的Github帮点个Star,表示支持吧~
仓库地址:
https://github.com/xuanmos/v-form