Form 是什么

Vant 的 Form 组件是一个 表单容器,用于管理一组 Field 的校验和提交。真正的输入框是 van-field,Form 负责:

  • 统一管理所有字段的校验规则
  • 一键触发校验 / 提交
  • 自动显示错误提示

最小可用示例

<template>
  <van-form @submit="onSubmit">
    <van-field
      v-model="username"
      name="username"
      label="用户名"
      placeholder="请输入用户名"
      :rules="[{ required: true, message: '请填写用户名' }]"
    />
    <van-field
      v-model="password"
      name="password"
      type="password"
      label="密码"
      placeholder="请输入密码"
      :rules="[
        { required: true, message: '请填写密码' },
        { min: 6, message: '密码至少 6 位' },
      ]"
    />
    <div style="margin: 16px">
      <van-button round block type="primary" native-type="submit">提交</van-button>
    </div>
  </van-form>
</template>

<script setup>
import { ref } from "vue";
import { showToast } from "vant";

const username = ref("");
const password = ref("");

const onSubmit = (values) => {
  console.log("表单数据:", values);
  showToast("提交成功");
};
</script>

校验规则 rules

每个 Field 可以通过 :rules 传入一个规则数组,常用规则:

// 必填
{ required: true, message: '此项必填' }

// 最小长度
{ min: 6, message: '至少 6 个字符' }

// 最大长度
{ max: 20, message: '最多 20 个字符' }

// 正则
{ pattern: /^1\d{10}$/, message: '请输入正确手机号' }

// 自定义校验函数
{
  validator: (value) => value.length >= 6,
  message: '密码至少 6 位',
}

// 异步校验
{
  validator: async (value) => {
    await new Promise((r) => setTimeout(r, 500));
    return value !== 'admin';
  },
  message: '用户名已存在',
}

完整的登录表单示例

<template>
  <van-form @submit="onSubmit">
    <van-cell-group inset title="账号登录">
      <van-field
        v-model="form.phone"
        name="phone"
        label="手机号"
        type="tel"
        maxlength="11"
        placeholder="请输入 11 位手机号"
        :rules="[
          { required: true, message: '手机号不能为空' },
          { pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' },
        ]"
      />
      <van-field
        v-model="form.code"
        name="code"
        label="验证码"
        type="tel"
        maxlength="6"
        placeholder="请输入 6 位验证码"
        :rules="[
          { required: true, message: '验证码不能为空' },
          { pattern: /^\d{6}$/, message: '验证码格式不正确' },
        ]"
      >
        <template #button>
          <van-button
            size="small"
            type="primary"
            :disabled="countdown > 0"
            @click="sendCode"
          >{{ countdown > 0 ? `${countdown}s 后重发` : '获取验证码' }}
          </van-button>
        </template>
      </van-field>
    </van-cell-group>
    <div style="margin: 16px">
      <van-button round block type="primary" native-type="submit" :loading="loading">
        登录
      </van-button>
    </div>
  </van-form>
</template>

<script setup>
import { reactive, ref } from "vue";
import { showToast } from "vant";

const form = reactive({ phone: "", code: "" });
const loading = ref(false);
const countdown = ref(0);

const sendCode = () => {
  if (countdown.value > 0) return;
  countdown.value = 60;
  const timer = setInterval(() => {
    countdown.value--;
    if (countdown.value <= 0) clearInterval(timer);
  }, 1000);
  showToast("验证码已发送");
};

const onSubmit = async (values) => {
  loading.value = true;
  try {
    await new Promise((r) => setTimeout(r, 1000));
    showToast("登录成功");
  } catch (e) {
    showToast("登录失败");
  } finally {
    loading.value = false;
  }
};
</script>

常见踩坑

  1. Field 必须有 name 属性,否则 Form 无法识别和校验它
  2. rules 里的自定义 validator 返回值:true 表示通过,false 表示不通过
  3. Form 的 submit 通过 native-type="submit" 触发,不要自己在按钮上加 @click
  4. 异步 validator 要在 async 函数里 return,Vant 会自动 await