Form 表单

示例

基础样式

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

请选择
请选择日期
在 GitHub 上编辑此示例编辑
<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 来使内部表单项处于只读状态。

在 GitHub 上编辑此示例编辑
<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 来使内部表单项处于禁用状态。

在 GitHub 上编辑此示例编辑
<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>

提示与辅助文本

辅助信息位置:
至少 2 个字符
精确到门牌号
在 GitHub 上编辑此示例编辑
<template>
<article>
  <section>
    <span class="label-text">辅助信息位置:</span>
    <veui-radio-button-group
      v-model="helpPosition"
      ui="s"
      :items="helpPositions"
    />
    <veui-checkbox
      v-model="labelPosition"
      true-value="top"
      false-value="side"
      ui="s"
      style="margin-left: 8px"
    >
      上下布局
    </veui-checkbox>
  </section>
  <veui-form
    :data="formData"
    :label-position="labelPosition"
  >
    <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, RadioButtonGroup } from 'veui'

export default {
  components: {
    'veui-form': Form,
    'veui-field': Field,
    'veui-input': Input,
    'veui-radio-button-group': RadioButtonGroup,
    'veui-checkbox': Checkbox
  },
  data () {
    return {
      formData: {
        name: '',
        address: ''
      },
      helpPositions: [
        { label: 'top', value: 'top' },
        { label: 'side', value: 'side' },
        { label: 'bottom', value: 'bottom' }
      ],
      helpPosition: 'side',
      labelPosition: 'side'
    }
  }
}
</script>

<style lang="less" scoped>
section {
  margin-bottom: 20px;
}

.label-text {
   font-size: 12px;
   margin-right: 8px;
 }
</style>

规则校验

在 GitHub 上编辑此示例编辑
<template>
<article class="veui-form-demo">
  <veui-form
    ref="form"
    :data="data"
  >
    <veui-field
      field="phone"
      name="phone"
      :rules="phoneRule"
      label="手机"
    >
      <veui-input
        v-model="data.phone"
        name="phone"
        autocomplete="off"
      />
    </veui-field>

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

<script>
import {
  Form,
  Field,
  Input,
  Button
} from 'veui'

export default {
  name: 'demo-form',
  components: {
    'veui-input': Input,
    'veui-button': Button,
    'veui-form': Form,
    'veui-field': Field
  },
  data () {
    return {
      data: {
        phone: '1888888888a'
      },
      phoneRule: [
        {
          name: 'pattern',
          value: /^1\d{10}$/,
          message: '{value} 不是正确的手机号',
          triggers: 'blur'
        }
      ]
    }
  }
}
</script>

内联规则校验

在 GitHub 上编辑此示例编辑
<template>
<article class="veui-form-demo">
  <veui-form
    ref="form"
    :data="data"
  >
    <veui-field
      label="密码"
      name="password"
      :rules="[
        { name: 'required', triggers: 'input,blur' },
        { name: 'minLength', value: '6', triggers: 'blur' }
      ]"
    >
      <veui-input
        v-model="data.password"
        type="password"
      />
    </veui-field>

    <veui-field
      label="确认密码"
      name="password2"
      :rules="p2Rules"
    >
      <veui-input
        v-model="data.password2"
        type="password"
      />
    </veui-field>
  </veui-form>
</article>
</template>

<script>
import {
  Form,
  Field,
  Input
} from 'veui'

export default {
  name: 'demo-form',
  components: {
    'veui-input': Input,
    'veui-form': Form,
    'veui-field': Field
  },
  data () {
    return {
      data: {
        password: '',
        password2: ''
      }
    }
  },
  computed: {
    p2Rules () {
      return [
        { name: 'required', triggers: 'input,blur' },
        {
          name: 'prefix',
          value: this.data.password,
          triggers: 'input',
          message: '两次输入的密码不一致',
          validate (val, ruleValue) {
            return (ruleValue || '').indexOf(val || '') === 0
          }
        },
        {
          name: 'same',
          value: this.data.password,
          triggers: 'change,password:input',
          message: '两次输入的密码不一致',
          validate (val, ruleValue) {
            return !val || (ruleValue || '') === val
          }
        }
      ]
    }
  }
}
</script>

异步联合校验

-
在 GitHub 上编辑此示例编辑
<template>
<article class="veui-form-demo">
  <veui-form
    ref="form"
    :data="data"
    :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="data.start"/>
      </veui-field>
      <veui-span style="margin: 0 4px">-</veui-span>
      <veui-field field="end" name="end" :rules="numRequiredRule">
        <veui-input v-model="data.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,
  Input,
  Button,
  Span
} from 'veui'

export default {
  name: 'demo-form',
  components: {
    'veui-span': Span,
    'veui-input': Input,
    'veui-button': Button,
    'veui-form': Form,
    'veui-field': Field,
    'veui-fieldset': Fieldset
  },
  data () {
    return {
      data: {
        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 () {
                let res = {}
                if (parseInt(start, 10) < 4000) {
                  res.start = {
                    status: 'warning',
                    message: '请提高下限'
                  }
                }
                if (parseInt(start, 10) >= parseInt(end, 10)) {
                  res.end = '上限必须大于下限'
                }
                resolve(Object.keys(res).length ? res : true)
              }, 2000)
            })
          },
          triggers: ['change', 'submit,input']
        }
      ]
    }
  }
}
</script>

<style lang="less" scoped>
.veui-form-demo {
  .salary {
  .veui-input {
      width: 80px;
    }
  }
}
</style>

前置、后置校验

在 GitHub 上编辑此示例编辑
<template>
<article class="veui-form-demo">
  <veui-form
    ref="form"
    :data="data"
    :before-validate="beforeValidate"
    :after-validate="afterValidate"
  >
    <veui-field
      field="name"
      name="name"
      label="姓名"
    >
      <veui-input v-model="data.name"/>
    </veui-field>

    <veui-field
      field="phone"
      name="phone"
      label="手机"
    >
      <veui-input
        v-model="data.phone"
        name="phone"
        autocomplete="off"
      />
    </veui-field>

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

<script>
import {
  Form,
  Field,
  Input,
  Button
} 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 {
      data: {
        name: '曹达华',
        phone: '18888888888'
      }
    }
  },
  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>

抽象表单项

待选项

  • 门店1
  • 门店2
  • 门店3
  • 门店4

已选项

请选择
在 GitHub 上编辑此示例编辑
<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>

原生输入框与表单项整合

在 GitHub 上编辑此示例编辑
<template>
<article>
  <veui-form
    :data="formData"
  >
    <veui-field
      label="名称"
      name="name"
      withhold-validity
      :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

属性

名称类型默认值描述
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>事件名称集合, 支持 <fieldName>:<eventName> 表示当 fieldName 字段发生 eventName 事件时触发校验。
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)
['a']'blur,b:input'a(blur,b: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 组件。无默认内容。

名称类型描述
submit() => void触发表单提交。
validatingboolean是否正在执行表单校验。
actions表单操作内容,如“提交”、“取消”按钮等。无默认内容。作用域参数与 default 插槽相同。

事件

名称描述
submit

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

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

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

方法

方法描述
submit

手动提交表单。

function submit(): void
validate

手动校验表单。

function validate(fieldNames?: Array<string> | null): Promise<true | Record<string, Object>>

可选参数 fieldNames 可以指定进行校验的字段。

返回值是 Promise,校验成功解析为 true,校验失败则解析为 Record<string, Object>,其中 key 是错误字段名称。

clearValidities

手动清除表单校验信息。

function clearValidities(fieldNames?: Array<string> | null): void

可选参数 fieldNames 可以指定清除的字段。

setValidities

手动设置表单校验信息。

type InputValidity = {
  status: 'success' | 'warning' | 'error'
  message: string
}

function setValidities(validities: Record<string, string | InputValidity>): void

// 示例:将表单提交返回错误添加到表单中
this.$refs.form.setValidities({
  name: 'name error',
  email: 'email error'
})

表单提交流程

表单校验逻辑

表单校验内部分为 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']
  }
]

交互过程的校验

提交过程的校验

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

在 GitHub 上编辑此页面编辑