Form 表单
示例
基础样式
使用 actions
插槽来提供表单操作按钮。
<template>
<article>
<veui-form
:data="formData"
@submit="handleSubmit"
>
<veui-field label="型号">
<veui-select
v-model="formData.model"
:options="models"
/>
</veui-field>
<veui-field label="日期">
<veui-date-picker
v-model="formData.date"
/>
</veui-field>
<template #actions>
<veui-button
ui="primary"
type="submit"
>
提交
</veui-button>
<veui-button>取消</veui-button>
</template>
</veui-form>
</article>
</template>
<script>
import { Form, Field, Button, Select, DatePicker } from 'veui'
export default {
components: {
'veui-form': Form,
'veui-field': Field,
'veui-button': Button,
'veui-select': Select,
'veui-date-picker': DatePicker
},
data () {
return {
models: [
{
label: 'APTX-4867',
value: 'aptx-4867'
},
{
label: 'APTX-4868',
value: 'aptx-4868'
},
{
label: 'APTX-4869',
value: 'aptx-4869'
},
{
label: 'APTX-4870',
value: 'aptx-4870'
}
],
formData: {
model: null,
date: null
}
}
},
methods: {
handleSubmit (data) {
this.$toast(JSON.stringify(data, null, 2))
}
}
}
</script>
只读状态
设置 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>
<style lang="less" scoped>
section {
margin-bottom: 20px;
}
</style>
禁用状态
设置 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>
<style lang="less" scoped>
section {
margin-bottom: 20px;
}
</style>
提示与辅助文本
<template>
<article>
<section>
<veui-checkbox
v-model="helpPosition"
true-value="bottom"
false-value="side"
>
辅助信息显示在底部
</veui-checkbox>
</section>
<veui-form
:data="formData"
>
<veui-field
name="name"
label="姓名"
tip="你的全名"
help="至少 2 个字符"
:help-position="helpPosition"
>
<veui-input v-model="formData.name"/>
</veui-field>
<veui-field
name="address"
label="地址"
tip="居住地的详细地址"
help="精确到门牌号"
:help-position="helpPosition"
>
<veui-input v-model="formData.address"/>
</veui-field>
</veui-form>
</article>
</template>
<script>
import { Form, Field, Input, Checkbox } from 'veui'
export default {
components: {
'veui-form': Form,
'veui-field': Field,
'veui-input': Input,
'veui-checkbox': Checkbox
},
data () {
return {
formData: {
name: '',
address: ''
},
helpPosition: 'side'
}
}
}
</script>
<style lang="less" scoped>
section {
margin-bottom: 20px;
}
</style>
校验
<template>
<article>
<veui-form
ref="form"
:before-validate="beforeValidate"
:after-validate="afterValidate"
:readonly="isValidating"
:data="formData"
:validators="validators"
@invalid="handleInvalid"
>
<veui-field
name="name"
label="姓名"
rules="required"
>
<veui-input v-model="formData.name"/>
</veui-field>
<veui-field
name="age"
:rules="ageRule"
label="年龄"
>
<veui-input v-model="formData.age"/>
</veui-field>
<veui-field
name="desc"
rules="required"
label="介绍"
>
<veui-textarea
v-model="formData.desc"
rows="3"
/>
</veui-field>
<veui-fieldset
name="phoneSet"
label="电话"
required
>
<veui-field
name="phoneType"
:rules="numRequiredRule"
>
<veui-select
v-model="formData.phoneType"
:options="phoneTypeOptions"
/>
</veui-field>
<veui-field
name="phone"
:rules="numRequiredRule"
>
<veui-input v-model="formData.phone"/>
</veui-field>
</veui-fieldset>
<veui-field
name="hobby"
:rules="hobbyRule"
label="爱好"
tip="选择则至少选三个"
>
<veui-checkbox-group
v-model="formData.hobby"
:items="hobbyItems"
/>
</veui-field>
<veui-fieldset
label="预期收入"
class="salary"
tip="联合校验,下限必须小于上限"
required
>
<veui-field
name="start"
:rules="numRequiredRule"
>
<veui-input v-model="formData.start"/>
</veui-field>
<veui-span>-</veui-span>
<veui-field
name="end"
:rules="numRequiredRule"
>
<veui-input v-model="formData.end"/>
</veui-field>
<veui-span>万</veui-span>
</veui-fieldset>
<veui-field
label="收入下限"
name="floor"
:rules="[
{name: 'required', value: true},
{name: 'min', value: 3500, message: '最低收入不小于 3500'}
]"
>
<veui-number-input v-model="formData.floor"/>
</veui-field>
<veui-field
name="term"
:rules="termRequiredRule"
label="协议"
>
<veui-checkbox
v-model="formData.term"
>
我已阅读并同意工作协议
</veui-checkbox>
</veui-field>
<template #actions>
<veui-button
ui="primary"
:loading="isValidating"
type="submit"
>
提交
</veui-button>
<veui-button
:loading="isValidating"
@click="resetForm"
>
重置
</veui-button>
</template>
</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-checkbox-group': CheckboxGroup,
'veui-textarea': Textarea
},
data () {
return {
formData: {
name: 'liyunteng1',
name1: 'liyunteng2',
age: null,
desc: '',
hobby: ['🏸'],
phone: '18888888888',
phoneType: 'mobile',
start: null,
end: null,
term: 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'
}
],
termRequiredRule: [
{
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
},
resetForm () {
this.$refs.form.reset()
}
}
}
</script>
规则校验
<template>
<article class="veui-form-demo">
<veui-form
ref="form"
:data="storeData4"
>
<veui-field
field="name"
name="name"
label="姓名"
:rules="requiredRule"
>
<veui-input v-model="storeData4.name"/>
</veui-field>
<veui-field
field="phone"
name="phone"
:rules="phoneRule"
label="手机"
>
<veui-input
v-model="storeData4.phone"
name="phone"
autocomplete="off"
/>
</veui-field>
<template #actions="{ validating }">
<veui-button
ui="primary"
:loading="validating"
type="submit"
>提交</veui-button>
</template>
</veui-form>
</article>
</template>
<script>
import {
Form,
Fieldset,
Field,
Span,
Input,
Button,
Select,
Checkbox,
CheckboxGroup,
NumberInput,
Transfer,
ConfigProvider
} from 'veui'
export default {
name: 'demo-form',
components: {
'veui-input': Input,
'veui-button': Button,
'veui-form': Form,
'veui-field': Field
},
data () {
return {
storeData4: {
name: '',
phone: '1888888888a'
},
requiredRule: [
{
name: 'required',
value: true,
triggers: 'blur,input'
}
],
phoneRule: [
{ name: 'required', triggers: 'change,input,blur' },
{
name: 'pattern',
value: /^1\d{10}$/,
message: '请输入正确的手机号',
triggers: 'blur'
}
]
}
}
}
</script>
<style lang="less" scoped>
@import "~veui-theme-dls/lib.less";
.veui-form-demo {
.operation {
margin-top: 60px;
margin-left: 120px;
[class*="veui"] {
margin-left: 10px;
}
[class*="veui"]:first-child {
margin-left: 0;
}
}
}
</style>
异步联合校验
<template>
<article class="veui-form-demo">
<veui-form
ref="form"
:data="storeData4"
:validators="validators"
>
<veui-fieldset
label="预期收入"
class="salary"
tip="下限必须小于上限"
:required="true"
>
<veui-field
field="start"
name="start"
:rules="numRequiredRule"
class="start-field"
>
<veui-input v-model="storeData4.start"/>
</veui-field>
<veui-span style="margin: 0 4px">-</veui-span>
<veui-field field="end" name="end" :rules="numRequiredRule">
<veui-input v-model="storeData4.end"/>
</veui-field>
<veui-span>万</veui-span>
</veui-fieldset>
<template #actions="{ validating }">
<veui-button
ui="primary"
:loading="validating"
type="submit"
>提交</veui-button>
</template>
</veui-form>
</article>
</template>
<script>
import {
Form,
Fieldset,
Field,
Span,
Input,
Button,
Select,
Checkbox,
CheckboxGroup,
NumberInput,
Transfer,
ConfigProvider
} from 'veui'
export default {
name: 'demo-form',
components: {
'veui-input': Input,
'veui-button': Button,
'veui-form': Form,
'veui-field': Field,
'veui-fieldset': Fieldset
},
data () {
return {
storeData4: {
start: 20000,
end: 10000
},
numRequiredRule: [
{
name: 'numeric',
value: true,
triggers: 'blur,input'
},
{
name: 'required',
value: true,
triggers: 'blur,input'
}
],
validators: [
{
fields: ['start', 'end'],
handler (start, end) {
if (start == null || end == null) {
return true
}
return new Promise(function (resolve) {
setTimeout(function () {
if (parseInt(start, 10) >= parseInt(end, 10)) {
return resolve({
start: '下限必须小于上限'
})
}
return resolve(true)
}, 2000)
})
},
triggers: ['change', 'submit,input']
}
]
}
}
}
</script>
<style lang="less" scoped>
@import "~veui-theme-dls/lib.less";
.veui-form-demo {
.salary {
.veui-input {
width: 80px;
}
}
.operation {
margin-top: 60px;
margin-left: 120px;
[class*="veui"] {
margin-left: 10px;
}
[class*="veui"]:first-child {
margin-left: 0;
}
}
}
</style>
前置、后置校验
<template>
<article class="veui-form-demo">
<veui-form
ref="form"
:data="storeData4"
:before-validate="beforeValidate"
:after-validate="afterValidate"
>
<veui-field
field="name"
name="name"
label="姓名"
:rules="requiredRule"
>
<veui-input v-model="storeData4.name"/>
</veui-field>
<veui-field
field="phone"
name="phone"
:rules="phoneRule"
label="手机"
>
<veui-input
v-model="storeData4.phone"
name="phone"
autocomplete="off"
/>
</veui-field>
<template #actions="{ validating }">
<veui-button
ui="primary"
:loading="validating"
type="submit"
>提交</veui-button>
</template>
</veui-form>
</article>
</template>
<script>
import {
Form,
Fieldset,
Field,
Span,
Input,
Button,
Select,
Checkbox,
CheckboxGroup,
NumberInput,
Transfer,
ConfigProvider
} from 'veui'
import confirmManager from 'veui/managers/confirm'
export default {
name: 'demo-form',
components: {
'veui-input': Input,
'veui-button': Button,
'veui-form': Form,
'veui-field': Field
},
data () {
return {
storeData4: {
name: '曹达华',
phone: '18888888888'
},
requiredRule: [
{
name: 'required',
value: true,
triggers: 'blur,input'
}
],
phoneRule: [
{ name: 'required', triggers: 'change,input,blur' },
{
name: 'pattern',
value: /^1\d{10}$/,
message: '请输入正确的手机号',
triggers: 'blur'
}
]
}
},
methods: {
beforeValidate () {
return new Promise((resolve) => {
confirmManager
.warn('前置校验通过吗?', '确认', {
ok: () => {}
})
.then((ok) => {
resolve(ok)
})
})
},
afterValidate () {
return new Promise((resolve) => {
confirmManager
.warn('后置校验通过吗?', '确认', {
ok: () => {}
})
.then((ok) => {
resolve(ok)
})
})
}
}
}
</script>
<style lang="less" scoped>
@import "~veui-theme-dls/lib.less";
.veui-form-demo {
.operation {
margin-top: 60px;
margin-left: 120px;
[class*="veui"] {
margin-left: 10px;
}
[class*="veui"]:first-child {
margin-left: 0;
}
}
}
</style>
警告信息
<template>
<article class="veui-form-demo">
<veui-form
ref="form"
:data="storeData4"
:validators="validators"
>
<veui-field
label="收入下限"
field="floor"
name="floor"
help-position="bottom"
:rules="[
{ name: 'required', value: true, triggers: 'change'},
{ name: 'min', value: 3500, message: '最低收入不小于 3500',triggers: 'change' }
]"
>
<veui-number-input v-model="storeData4.floor"/>
<template #help>
<p>低于 4000 将会得到警告</p>
</template>
</veui-field>
<template #actions="{ validating }">
<veui-button
ui="primary"
:loading="validating"
type="submit"
>提交</veui-button>
</template>
</veui-form>
</article>
</template>
<script>
import {
Form,
Fieldset,
Field,
Span,
Input,
Button,
Select,
Checkbox,
CheckboxGroup,
NumberInput,
Transfer,
ConfigProvider
} from 'veui'
export default {
name: 'demo-form',
components: {
'veui-number-input': NumberInput,
'veui-button': Button,
'veui-form': Form,
'veui-field': Field
},
data () {
return {
storeData4: {
floor: null
},
validators: [
{
fields: ['floor'],
validate (floor) {
if (floor == null) {
return true
}
return new Promise(function (resolve) {
setTimeout(function () {
let res
if (floor <= 4000) {
res = {
floor: {
status: 'warning',
message: '请提高预期收入下限'
}
}
}
return resolve(res)
}, 3000)
})
},
triggers: ['change']
}
]
}
}
}
</script>
<style lang="less" scoped>
@import "~veui-theme-dls/lib.less";
.veui-form-demo {
p {
margin: 0;
}
.operation {
margin-top: 60px;
margin-left: 120px;
[class*="veui"] {
margin-left: 10px;
}
[class*="veui"]:first-child {
margin-left: 0;
}
}
}
</style>
抽象表单项
<template>
<article>
<veui-form
:data="formData"
>
<veui-field
label="门店"
name="store"
:rules="[{
name: 'required', message: '请选择门店', triggers: 'select'
}]"
>
<veui-transfer
v-model="formData.store"
:datasource="storeList"
>
<template #selected-item-label="{ label, value }">
<div class="selected-store">
<span class="store-label">{{ label }}</span>
<veui-field
:key="`storeCounts.${value}`"
:name="`storeCounts.${value}`"
:rules="[
{ name: 'required', message: `请填写${label}的数量`, triggers: 'change,blur' }
]"
abstract
>
<veui-number-input
v-model="formData.storeCounts[value]"
class="store-number"
ui="s"
:min="1"
/>
</veui-field>
</div>
</template>
</veui-transfer>
</veui-field>
</veui-form>
</article>
</template>
<script>
import { Form, Field, NumberInput, Transfer } from 'veui'
export default {
components: {
'veui-form': Form,
'veui-field': Field,
'veui-number-input': NumberInput,
'veui-transfer': Transfer
},
data () {
return {
disabled: true,
formData: {
store: [],
storeCounts: {}
},
storeList: [
{ label: '门店1', value: '1' },
{ label: '门店2', value: '2' },
{ label: '门店3', value: '3' },
{ label: '门店4', value: '4' }
]
}
}
}
</script>
<style lang="less" scoped>
.selected-store {
display: flex;
align-items: center;
.store-label {
min-width: 60px;
}
}
</style>
原生输入框与表单项整合
<template>
<article>
<veui-form
:data="formData"
>
<veui-field
label="名称:"
name="name"
:rules="[{ name: 'required', triggers: 'input,blur' }]"
>
<template #default="{ invalid, listeners }">
<input
v-model="formData.name"
:class="{
'demo-invalid': invalid
}"
v-on="listeners"
>
</template>
</veui-field>
</veui-form>
</article>
</template>
<script>
import { Form, Field } from 'veui'
export default {
components: {
'veui-form': Form,
'veui-field': Field
},
data () {
return {
formData: {
name: ''
}
}
}
}
</script>
<style lang="less" scoped>
.demo-invalid {
border: 1px solid #cc1800;
}
</style>
API
属性
名称 | 类型 | 默认值 | 描述 | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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 | 可直接内联
| |||||||||
actions | 表单操作内容,如“提交”、“取消”按钮等。无默认内容。作用域参数与 default 插槽相同。 |
事件
名称 | 描述 | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
submit | 在原生
| |||||||||
invalid |
|
方法
方法 | 描述 |
---|---|
submit | 手动提交表单。
|
validate | 手动校验表单。
可选参数 返回值是 |
clearValidities | 手动清除表单校验信息。
可选参数 |
setValidities | 手动设置表单校验信息。
|
表单提交流程

表单校验逻辑
表单校验内部分为 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']
}
]
交互过程的校验

提交过程的校验

提交时,其中一个过程的校验失败不会导致整个校验终止,校验信息将在两个过程执行完毕后进行整合,并传递到 invalid
事件中去。