Form 表单

示例

基础样式

使用 actions 插槽来提供表单操作按钮。

状态1
~
<template>
<article>
  <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-search-box
        v-model="formData.query"
        placeholder="请输入搜索内容"
      />
    </veui-field>

    <template #actions>
      <veui-button
        ui="primary"
        type="submit"
      >
        提交
      </veui-button>
      <veui-button>取消</veui-button>
    </template>
  </veui-form>
</article>
</template>

<script>
import moment from 'moment'
import { Form, Field, Button, Select, DatePicker, SearchBox } from 'veui'

export default {
  components: {
    'veui-form': Form,
    'veui-field': Field,
    'veui-button': Button,
    'veui-select': Select,
    'veui-datepicker': DatePicker,
    'veui-search-box': 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>

只读状态

设置 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>
  <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>

API

属性

名称类型默认值描述
readonlyboolean=false内部输入组件是否为只读状态。
disabledboolean=false内部输入组件是否为禁用状态。
dataObject-

表单绑定的数据,和表单中的输入组件通过 v-model 绑定,也是表单校验时的数据源。

validatorsArray<Object>=-

表单联合校验、异步校验器。项目类型为 {fields, validate, triggers}

名称类型描述
fieldsArray对应 Fieldfield 描述的集合。事件会绑定到对应 Field 中的输入组件上。
validatefunction自定义校验函数,传入参数为 (data[fields[0]], data[fields[1]], ...)data 为表单 data 属性值的引用。返回 undefined / true 代表校验成功,返回 {[field]: message, ...} 表示校验失败信息,详见表单 › 表单校验逻辑
triggersstring | Array<string>事件名称集合。
fieldstriggers绑定事件情况
['a']['change', 'blur,input,xxx', 'submit']a(change)
['a','b','c']['change', 'blur,input,xxx', 'submit']a(change), b(blur,input,xxx), c(submit)
['a','b','c']'blur'a(blur), b(submit), c(submit)
['a','b','c']'blur,input'a(blur,input), b(blur,input), c(blur,input)
before-validatefunction=-表单进入提交流程后,进行校验之前的 hook,传入参数为 (data)data 为表单 data 属性值的副本。支持返回 Promise,返回值或 Promise.resolve 的值为 trueundefined 表示流程继续,其它返回值表示中断流程并触发 invalid 事件。
after-validatefunction=-表单校验成功后,触发 submit 事件之前的 hook,传入参数为 (data),与 beforeValidate 的入参是同一个引用。支持返回 Promise,返回值或 Promise.resolve 的值为 trueundefined 表示流程继续,其它返回值表示中断流程并触发 invalid 事件。

插槽

名称描述
default可直接内联 FieldsetField 组件。无默认内容。
actions表单操作内容,如“提交”、“取消”按钮等。无默认内容。

事件

名称描述
submit

在原生 submit 事件之后触发,回调参数为 (data, event)。具体提交流程请参考表单 › 表单提交流程

名称类型描述
dataObject表单 data 属性值的引用。
eventEvent原生事件对象。
invalid

beforeValidatevalidateafterValidate 流程中某一项返回中断时触发,回调参数为流程 function 的返回值,参数为 (result),表示流程中断的信息,具体返回值类型由流程返回决定。具体提交流程请参考表单 › 表单提交流程validate 逻辑见表单 › 表单校验逻辑

表单提交流程

表单校验逻辑

表单校验内部分为 Fieldrule 校验和 validators 的校验。

  1. Fieldrule 是单值、同步校验。详见表单项
  2. 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']
  }
]

Fieldrulevalidators 的优先级

校验失败的信息会添加到对应的 Fieldvalidities 信息中。由于同个操作触发的校验,validators 的校验结果优先级大于 Fieldrule,不同操作触发的校验,展现最后一个结果。Fieldrule 内部的优先级,请参考其 rules 属性。

交互过程的校验

提交过程的校验

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