Form 表单
示例
排列模式
可选的 ui
属性值:inline
。
Default
Inline
<template>
<article>
<section>
<h3>Default</h3>
<veui-form :data="formData">
<veui-field label="状态:">
<veui-select
v-model="formData.statusSelected"
:options="statusOptions"
/>
</veui-field>
<veui-field label="时间:">
<veui-datepicker
v-model="formData.range"
range
/>
</veui-field>
<veui-field>
<veui-searchbox
v-model="formData.query"
placeholder="请输入搜索内容"
/>
</veui-field>
</veui-form>
</section>
<section>
<h3>Inline</h3>
<veui-form
:data="formData"
ui="inline"
>
<veui-fieldset
ui="alt"
class="left"
>
<veui-field label="状态:">
<veui-select
v-model="formData.statusSelected"
ui="alt"
:options="statusOptions"
/>
</veui-field>
<veui-field label="时间:">
<veui-datepicker
v-model="formData.range"
ui="alt"
range
/>
</veui-field>
</veui-fieldset>
<veui-fieldset class="right">
<veui-field>
<veui-searchbox
v-model="formData.query"
placeholder="请输入搜索内容"
/>
</veui-field>
</veui-fieldset>
</veui-form>
</section>
</article>
</template>
<script>
import moment from 'moment'
import { Form, Field, Fieldset, Select, DatePicker, Searchbox } from 'veui'
export default {
components: {
'veui-form': Form,
'veui-fieldset': Fieldset,
'veui-field': Field,
'veui-datepicker': DatePicker,
'veui-select': Select,
'veui-searchbox': Searchbox
},
data () {
return {
statusOptions: [
{
label: '状态1',
value: 1
},
{
label: '状态2',
value: 2
},
{
label: '状态3',
value: 3
},
{
label: '状态4',
value: 4
}
],
formData: {
statusSelected: 1,
query: '',
range: [
moment().toDate(),
moment()
.add(3, 'month')
.toDate()
]
}
}
}
}
</script>
<style lang="less" scoped>
.left {
float: left;
}
.right {
float: right;
}
</style>
只读状态
设置 readonly
来使内部表单项处于只读状态。
<template>
<article>
<section>
<veui-checkbox v-model="readonly">
只读
</veui-checkbox>
</section>
<veui-form
:data="formData"
:readonly="readonly"
>
<veui-field label="姓名:">
<veui-input v-model="formData.name"/>
</veui-field>
</veui-form>
</article>
</template>
<script>
import { Form, Field, Input, Checkbox } from 'veui'
export default {
components: {
'veui-checkbox': Checkbox,
'veui-form': Form,
'veui-field': Field,
'veui-input': Input
},
data () {
return {
readonly: true,
formData: {
name: ''
}
}
}
}
</script>
禁用状态
设置 disabled
来使内部表单项处于禁用状态。
<template>
<article>
<section>
<veui-checkbox v-model="disabled">
禁用
</veui-checkbox>
</section>
<veui-form
:data="formData"
:disabled="disabled"
>
<veui-field label="姓名:">
<veui-input v-model="formData.name"/>
</veui-field>
</veui-form>
</article>
</template>
<script>
import { Form, Field, Input, Checkbox } from 'veui'
export default {
components: {
'veui-checkbox': Checkbox,
'veui-form': Form,
'veui-field': Field,
'veui-input': Input
},
data () {
return {
disabled: true,
formData: {
name: ''
}
}
}
}
</script>
校验
<template>
<article>
<veui-form
ref="form"
:before-validate="beforeValidate"
:after-validate="afterValidate"
:readonly="isValidating"
:data="formData"
:validators="validators"
@invalid="handleInvalid"
>
<veui-field
disabled
field="name"
name="name1"
label="姓名:"
tip="disabled 值提交时会过滤"
>
<veui-input v-model="formData.name"/>
</veui-field>
<veui-field
field="name1"
name="name2"
label="姓名1:"
tip="在 field 上边 disabled,提交时才会过滤掉,该项在 input 上 disalbed"
>
<veui-input
v-model="formData.name1"
disabled
placeholder="长度不能短于2"
/>
</veui-field>
<veui-field
field="age"
name="age1"
:rules="ageRule"
label="年龄:"
>
<veui-input
v-model="formData.age"
placeholder="错误提示优先出在右侧"
/>
</veui-field>
<veui-field
field="desc"
name="desc"
rules="required"
label="介绍:"
>
<veui-textarea
v-model="formData.desc"
rows="3"
/>
</veui-field>
<veui-fieldset
name="phoneSet"
label="电话:"
:required="true"
>
<veui-field
field="phoneType"
name="phoneType"
>
<veui-select
v-model="formData.phoneType"
:options="phoneTypeOptions"
/>
</veui-field>
<veui-field
field="phone"
name="phone"
:rules="numRequiredRule"
>
<veui-input v-model="formData.phone"/>
</veui-field>
</veui-fieldset>
<veui-field
field="hobby"
name="hobby"
:rules="hobbyRule"
label="爱好:"
tip="选择则至少选三个"
>
<veui-checkboxgroup
v-model="formData.hobby"
type="checkbox"
:items="hobbyItems"
/>
</veui-field>
<veui-fieldset
label="预期收入:"
class="salary"
tip="联合校验,下限必须小于上限"
:required="true"
>
<veui-field
field="start"
name="start"
:rules="numRequiredRule"
class="start-field"
>
<veui-input v-model="formData.start"/>
</veui-field>
<veui-span>-</veui-span>
<veui-field
field="end"
name="end"
:rules="numRequiredRule"
>
<veui-input v-model="formData.end"/>
</veui-field>
<veui-span>万</veui-span>
</veui-fieldset>
<veui-field
label="收入下限:"
field="floor"
name="floor"
:rules="[
{name: 'required', value: true},
{name: 'min', value: 3500, message: '最低收入不小于 3500'}
]"
>
<veui-number-input v-model="formData.floor"/>
</veui-field>
<veui-field
field="protocol"
name="protocol"
:rules="protocolRequiredRule"
label="协议:"
>
<veui-checkbox
v-model="formData.protocol"
:true-value="true"
:false-value="null"
>
我已阅读并同意工作协议
</veui-checkbox>
</veui-field>
<div class="operation">
<veui-button
ui="primary"
:loading="isValidating"
type="submit"
>
提交
</veui-button>
<veui-button
:loading="isValidating"
@click="() => this.$refs.form.reset()"
>
重置
</veui-button>
</div>
</veui-form>
</article>
</template>
<script>
import {
Form,
Fieldset,
Field,
Span,
Input,
Button,
Select,
Textarea,
Checkbox,
CheckboxGroup,
NumberInput
} from 'veui'
export default {
components: {
'veui-span': Span,
'veui-input': Input,
'veui-number-input': NumberInput,
'veui-button': Button,
'veui-form': Form,
'veui-fieldset': Fieldset,
'veui-field': Field,
'veui-select': Select,
'veui-checkbox': Checkbox,
'veui-checkboxgroup': CheckboxGroup,
'veui-textarea': Textarea
},
data () {
return {
formData: {
name: 'liyunteng1',
name1: 'liyunteng2',
age: null,
desc: '',
hobby: ['🏸'],
phone: '18888888888',
phoneType: 'mobile',
start: null,
end: null,
protocol: null,
floor: 3501
},
hobbyItems: [
{
value: '⚽️',
label: '足球'
},
{
value: '🏀',
label: '篮球'
},
{
value: '🏸',
label: '羽毛球'
},
{
value: '🎾',
label: '网球'
}
],
phoneTypeOptions: [
{
label: '座机',
value: 'phone'
},
{
label: '手机',
value: 'mobile'
}
],
requiredRule: [
{
name: 'required',
value: true,
triggers: 'blur,input'
}
],
numRequiredRule: [
{
name: 'numeric',
value: true,
triggers: 'blur,input'
},
{
name: 'required',
value: true,
triggers: 'blur,input'
}
],
protocolRequiredRule: [
{
name: 'required',
value: true,
message: '请勾选阅读协议',
triggers: 'change'
}
],
dynamicNameRule: [
{
name: 'required',
value: true,
triggers: 'blur,input'
},
{
name: 'minLength',
value: 2
}
],
ageRule: [
{
name: 'required',
value: true,
triggers: 'input'
},
{
name: 'numeric',
value: true,
triggers: 'input'
},
{
name: 'maxLength',
value: 3,
triggers: 'change'
}
],
hobbyRule: [
{
name: 'minLength',
value: 3,
message: '至少选择三个爱好',
triggers: 'change'
}
],
isValidating: false,
validators: [
{
fields: ['start', 'end'],
handler (start, end) {
if (start == null || end == null) {
return true
}
if (parseInt(start, 10) >= parseInt(end, 10)) {
return {
start: '下限必须小于上限'
}
}
return true
},
triggers: ['change', 'submit,input']
},
{
fields: ['phone'],
validate (phone) {
return new Promise(function (resolve) {
setTimeout(function () {
let res
if (phone === '18888888888') {
res = {
phone: '该手机已被注册'
}
}
return resolve(res)
}, 3000)
})
},
triggers: ['input']
}
]
}
},
methods: {
beforeValidate () {
this.isValidating = true
},
afterValidate () {
this.isValidating = false
},
handleInvalid () {
this.isValidating = false
}
}
}
</script>
API
属性
名称 | 类型 | 默认值 | 描述 | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ui | string | - |
| |||||||||||||||||||||||||||
readonly | boolean | false | 内部输入组件是否为只读状态。 | |||||||||||||||||||||||||||
disabled | boolean | false | 内部输入组件是否为禁用状态。 | |||||||||||||||||||||||||||
data | Object | - | 表单绑定的数据,和表单中的输入组件通过
| |||||||||||||||||||||||||||
validators | Array<Object> | - | 表单联合校验、异步校验器。项目类型为
| |||||||||||||||||||||||||||
before-validate | function | - | 表单进入提交流程后,进行校验之前的 hook,传入参数为 (data) ,data 为表单 data 属性值的副本。支持返回 Promise ,返回值或 Promise.resolve 的值为 true 或 undefined 表示流程继续,其它返回值表示中断流程并触发 invalid 事件。 | |||||||||||||||||||||||||||
after-validate | function | - | 表单校验成功后,触发 submit 事件之前的 hook,传入参数为 (data) ,与 beforeValidate 的入参是同一个引用。支持返回 Promise ,返回值或 Promise.resolve 的值为 true 或 undefined 表示流程继续,其它返回值表示中断流程并触发 invalid 事件。 |
插槽
名称 | 描述 |
---|---|
default | 可直接内联 Fieldset 或 Field 组件。无默认内容。 |
事件
名称 | 描述 | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
submit | 在原生
| |||||||||
invalid |
|
表单提交流程
beforeValidate
和 afterValidate
以及 sumbit
事件操作的 data
均为 props data
的同一个副本。考虑到校验信息和 UI 中数据的一致性,validate
的目标数据是 props data
的源数据。因此在 beforeValidate
中不建议修改 data
副本。
表单校验逻辑
表单校验内部分为 Field
的 rule
校验和 validators
的校验。
Field
的rule
是单值、同步校验。详见表单项。validators
可以是多值、异步的校验。
validators: [
{
fields: ['start', 'end'],
validate (start, end) {
if (start == null || end == null) {
return true
}
if (parseInt(start, 10) >= parseInt(end, 10)) {
return {
start: '下限必须小于上限'
}
}
return true
},
triggers: ['change', 'submit,input']
},
{
fields: ['phone'],
validate (phone) {
return new Promise(function (resolve) {
setTimeout(function () {
let res
if (phone === '18888888888') {
res = {
phone: '该手机已被注册'
}
}
return resolve(res)
}, 3000)
})
},
triggers: ['input']
}
]
Field
的 rule
和 validators
的优先级
校验失败的信息会添加到对应的 Field
的 validities
信息中。由于同个操作触发的校验,validators
的校验结果优先级大于 Field
的 rule
,不同操作触发的校验,展现最后一个结果。Field
的 rule
内部的优先级,参考表单项 › 属性。
交互过程的校验
提交过程的校验
提交时,其中一个过程的校验失败不会导致整个校验终止,校验信息将在两个过程执行完毕后进行整合,并传递到 invalid
事件中去。