因为vant提供的组件比较分散,为了高效开发,对form表单封装进行二次封装,将用到的form组件集合成一个文件,统一调用,方便后期项目迭代与维护
功能说明:
-
提供input框输入并验证码发送模块(自定义规则校验-- 纯数字输入,密码输入,文本域,纯文本)
-
提供时间选框(支持日期选择-年月选择-月日选择,年月日选择)
-
提供下拉选框
-
提供头像上传功能(多图片上传,大图预览,上传图片数量限制,默认提示图片)
-
手机评分模块
-
单选框组(默认选中,排列方向--横向/纵向)
-
复选框组(默认选中,排列方向--横向/纵向,复选框形状)
-
步进器(购物车数量增加减少的模块)
-
支持多个form表单的展示
-
支持展示每一个form表单的title标题(可选位置,form表单顶部和底部,左对齐,居中,右对齐)
-
支持展示提交按钮
form表单案例
<template>
<div class="home">
<SeForm :searchData="registData" :formList="formList" ></SeForm>
</div>
</template>
<script>
import SeForm from '@/views/components/SeForm.vue'
export default {
name: 'Home',
components: {
SeForm
},
data () {
return {
registData: {
accountName: '',
accountCardNo: '',
accountSubBank: '',
email: ''
},
formList:[
{
id: '1',
title: '',
searchForm: [
{
prop: 'accountName',
placeholder: '请填写开户姓名',
name: '开户姓名',
type: 'text',
rules: [
{ required: true, message: '', trigger: 'onBlur' },
]
},
{
prop: 'accountCardNo',
placeholder: '请填写开户账号',
name: '开户账号',
type: 'text',
rules: [
// { required: true, message: '', trigger: 'onBlur' },
{
validator: this.vaildAccountCard,
message: '请填写开户账号',
trigger: 'onBlur'
}
]
},
{
prop: 'accountSubBank',
placeholder: '请填写开户银行',
type: 'text',
name: '开户银行',
rules: [
{ required: true, message: '', trigger: 'onBlur' },
]
},
{
prop: 'email',
placeholder: '请填写邮箱',
type: 'text',
name: '邮箱',
rules: [
{
validator: this.vaildEmail,
message: '请填写邮箱',
trigger: 'onBlur'
}
]
},
]
}
]
}
},
methods:{
vaildEmail(val) {
if(!val) {
Toast('请输入邮箱')
return false
}
const rule =/^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/
let result = rule.test(val)
if(result) {
return result
}else {
Toast('请输入正确的邮箱')
return result
}
},
vaildAccountCard(val) {
if(!val) {
Toast('请输入开户账号')
return false
}
const rule =/^\d+$/
const str = val.replace(/\s+/g,'')
let result = rule.test(str)
if(result) {
return result
}else {
Toast('请输入正确的开户账号')
return result
}
},
}
}
</script>
<style scoped lang="less"></style>
form表单的内部封装
<template>
<van-form ref="vantForm" validate-first @submit="onSubmit" @failed="onFailed">
<!-- list结构的form表单start -->
<template v-for="val in formList">
<div :key="val.id" class="vant_form">
<div v-if="isClass === '1' " class="form_list_title">
<span>{{ val.title }}</span>
</div>
<template v-for="item in val.searchForm">
<!-- input文本框部分 -->
<div class="filed_box" :key="item.prop">
<template
v-if="
[
'text',
'tel',
'digit',
'number',
'textarea',
'password'
].indexOf(item.type) !== -1
"
>
<van-field
v-model.trim="searchData[item.prop]"
:name="item.name"
:type="item.type"
:placeholder="item.placeholder"
:disabled="item.disabled ? item.disabled : false"
:rules="item.rules"
:label="item.name"
/>
</template>
<template v-if="['verCode'].indexOf(item.type) !== -1">
<van-field
v-model="searchData[item.prop]"
center
clearable
:label="item.name"
:rules="item.rules"
:name="item.name"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder"
>
<template #button>
<van-button
size="small"
type="primary"
:disabled="codeShow"
@click.stop.prevent="getCode"
>{{ codeText }}</van-button
>
</template>
</van-field>
</template>
<!-- 时间选择 -->
<template
v-if="
[
'date',
'year-month',
'month-day',
'time',
'datetime',
'datehour'
].indexOf(item.type) !== -1
"
>
<van-field
readonly
clickable
:name="item.type"
:value="searchData[item.prop]"
:label="item.name"
:placeholder="item.placeholder"
@click="showPicker = true"
/>
<van-popup v-model="showPicker" position="bottom">
<van-datetime-picker
:type="item.type"
@confirm="onConfirm(item, $event)"
@cancel="showPicker = false"
/>
</van-popup>
</template>
<!-- 选择器 -->
<template v-if="['picker'].indexOf(item.type) !== -1">
<van-field
readonly
clickable
:name="item.type"
:value="searchData[item.prop]"
:label="item.name"
:placeholder="item.placeholder"
@click="showSelectPicker = true"
/>
<van-popup v-model="showSelectPicker" position="bottom">
<van-picker
show-toolbar
:columns="item.columns"
@confirm="SelectConfirm(item, $event)"
@cancel="showSelectPicker = false"
/>
</van-popup>
</template>
<!-- 头像上传 -->
<template v-if="['uploaderImg'].indexOf(item.type) !== -1">
<van-field :name="item.type" :label="item.name">
<template #input>
<van-uploader
v-model="searchData[item.prop]"
:max-count="item.maxCount"
:before-read="beforeRead"
:after-read="afterRead"
@click-preview="clickPreview"
@click.native.stop="click(item)"
>
<div class="van-uploader__upload">
<!-- 是否显示自定义上传的提示图片 -->
<span v-if="item.modelImg" class="van-icon">
<img :src="item.modelImg" alt="" />
</span>
<span v-else></span>
<input
multiple="multiple"
type="file"
accept="image/*"
class="van-uploader__input"
/>
</div>
</van-uploader>
</template>
</van-field>
</template>
<!-- 评分模块 -->
<template v-if="['rate'].indexOf(item.type) !== -1">
<van-field :name="item.type" :label="item.name">
<template #input>
<van-rate v-model="searchData[item.prop]" />
</template>
</van-field>
</template>
<!-- 单选框组 -->
<template v-if="['radioGroup'].indexOf(item.type) !== -1">
<van-field :name="item.type" :label="item.name">
<template #input>
<van-radio-group
v-model="searchData[item.prop]"
:direction="item.direction"
>
<van-radio
v-for="(ele, indx) in item.options"
:key="indx"
:name="ele.value"
>
{{ ele.label }}
</van-radio>
</van-radio-group>
</template>
</van-field>
</template>
<!-- 复选框组 -->
<template v-if="['checkboxGroup'].indexOf(item.type) !== -1">
<van-field :name="item.type" :label="item.name">
<template #input>
<van-checkbox-group
v-model="searchData[item.prop]"
:direction="item.direction"
>
<van-checkbox
v-for="(ele, indx) in item.options"
:key="indx"
:name="ele.value"
:shape="item.shape"
>{{ ele.label }}</van-checkbox
>
</van-checkbox-group>
</template>
</van-field>
</template>
<template v-if="['stepper'].indexOf(item.type) !== -1">
<van-field :name="item.type" :label="item.name">
<template #input>
<van-stepper v-model="searchData[item.prop]" />
</template>
</van-field>
</template>
</div>
</template>
<div v-if="isClass === '2' " :class="['form_list_title',val.className]">
<span>{{ val.title }}</span>
</div>
</div>
</template>
<div style="margin-top: 0.16rem;" v-if="submitShow">
<van-button round block type="info" native-type="submit">
{{ btnName }}
</van-button>
</div>
<slot></slot>
</van-form>
</template>
<script>
import Moment from 'moment'
import { ImagePreview } from 'vant'
export default {
name: 'SeFormModel',
props: {
searchData: {
type: Object,
default: () => {}
},
formList: {
type: Array,
default: () => []
},
submitShow: {
type: Boolean,
default: () => false
},
btnName: {
type: String,
default: () => '登录'
},
codeShow: {
type: Boolean,
default: () => false
},
isClass: {
type: String,
default: () => '2'
},
codeText:{
type: String,
default:()=>'获取验证码'
}
},
data () {
return {
showPicker: false, //时间组件的显示隐藏
showSelectPicker: false, // 选择器的显示隐藏
propType: ''
}
},
methods: {
/**
* @function onConfirm
* @description 时间选择组件的确定赋值
*/
onConfirm (item, time) {
this.searchData[item.prop] = Moment(time).format('YYYY-MM-DD')
this.showPicker = false
},
/**
* @function SelectConfirm
* @description select选择器确定赋值
*/
SelectConfirm (item, value) {
this.searchData[item.prop] = value
this.showSelectPicker = false
},
/**
* @function onOversize
* @description 上传头像,显示图片的大小
*/
onOversize (file) {
console.log(file)
this.$toast('文件大小超过限制500kb')
},
/**
* @function beforeRead
* @description 上传前,显示图片格式
*/
beforeRead (file) {
console.log(1234)
// accept=".png,.PNG,.jpeg,.jpg,.JPG"
if (
file.type !== 'image/jpeg' &&
file.type !== 'image/png' &&
file.type !== 'image/jpg' &&
file.type !== 'image/gif'
) {
this.$toast('请上传 jpg,png 格式图片')
return false
}
return true
},
/**
* @function afterRead
* @description 文件上传完完毕之后的回调
*/
afterRead (file) {
file.status = 'uploading'
file.message = '加载...'
this.$emit('uploadImg', [file, this.propType])
},
click (item) {
this.propType = item.prop
},
clickPreview (val) {
ImagePreview({
images: [val.url],
closeable: true
})
},
onSubmit (values) {
this.$emit('submit', this.searchData)
},
onFailed (errorInfo) {
console.log('failed', errorInfo)
},
getCode () {
this.$emit('getCode')
}
}
}
</script>
<style scoped lang="less">
.vant_form {
border-radius: 0.1rem;
overflow: hidden;
}
.van-uploader__upload {
position: relative;
width: 100%;
> span {
display: inline-block;
width: 100%;
height: 100%;
> img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30%;
height: 50%;
}
}
}
::v-deep .van-uploader__input-wrapper {
width: 100%;
}
</style>
点击看看为青年律师报薪:https://fhui.qingcongai.com/activity?hmsr=%E5%8D%9A%E5%AE%A2%E5%9B%AD&hmpl=&hmcu=&hmkw=&hmci=
