Skip to content

Form

表单包含 输入框, 单选框, 下拉选择, 多选框 等用户输入的组件。 使用表单,您可以收集、验证和提交数据。

TIP

Form组件为Flex 布局。

基础表单

name
desc
count
color
#
最基础的表单包括各种输入表单项,比如input、select、radio、checkbox等。
在每一个 form 组件中,你需要一个 form-item 字段作为输入项的容器,用于获取值与验证值。
<template>
  <cl-form :model="form" label-width="50px">
    <cl-form-item label="name">
      <cl-input v-model="form.name" />
    </cl-form-item>
    <cl-form-item label="desc">
      <cl-input v-model="form.desc" type="textarea" />
    </cl-form-item>
    <cl-form-item label="count">
      <cl-input-number v-model="form.count" />
    </cl-form-item>
    <cl-form-item label="color">
      <cl-color-picker v-model="form.color" />
    </cl-form-item>
    <cl-form-item label="">
      <cl-button type="primary" @click="onSubmit">Create</cl-button>
      <cl-button>Cancel</cl-button>
    </cl-form-item>
  </cl-form>
</template>

<script lang="ts" setup>
import { reactive } from "vue";

// do not use same name with ref
const form = reactive({
  name: "",
  count: "",
  color: "",
  desc: ""
});

const onSubmit = () => {
  console.log("submit!");
};
</script>

TIP

W3C 标准定义:

当一个表单中只有一个单行文本输入字段时, 浏览器应当将在此字段中按下 Enter (回车键)的行为视为提交表单的请求。 如果希望阻止这一默认行为,可以在 <cl-form> 标签上添加 @submit.prevent。

对齐方式

Name
Activity zone
Activity form
根据设计情况,来选择最佳的标签对齐方式。
通过设置 label-position 属性可以改变表单域标签的位置,可选值为 top、left, 当设为 top 时标签会置于表单域的顶部
<template>
  <cl-button-group label="label position">
    <cl-button @click="labelPosition='left'">Left</cl-button>
    <cl-button @click="labelPosition='right'">Right</cl-button>
    <cl-button @click="labelPosition='top'">Top</cl-button>
  </cl-button-group>
  <div style="margin: 20px" />
  <cl-form
    :label-position="labelPosition"
    label-width="100px"
    :model="formLabelAlign"
    style="max-width: 460px"
  >
    <cl-form-item label="Name">
      <cl-input v-model="formLabelAlign.name" />
    </cl-form-item>
    <cl-form-item label="Activity zone">
      <cl-input v-model="formLabelAlign.region" />
    </cl-form-item>
    <cl-form-item label="Activity form">
      <cl-input v-model="formLabelAlign.type" />
    </cl-form-item>
  </cl-form>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue'

const labelPosition = ref('right')

const formLabelAlign = reactive({
  name: '',
  region: '',
  type: '',
})
</script>

表单校验

TIP

使用scroll-to-error 参数控制是否在验证失败后滚动至第一个错误表单 使用scroll-into-view-options 进行scrollIntoView 的配置

Activity name
Activity form
Form 组件允许你验证用户的输入是否符合规范,来帮助你找到和纠正错误。
Form 组件提供了表单验证的功能,只需为 rules 属性传入约定的验证规则,并将 form-Item 的 prop 属性设置为需要验证的特殊键值即可。 更多高级用法可参考 async-validator。
<template>
  <cl-form
    ref="ruleFormRef"
    :model="ruleForm"
    :rules="rules"
    label-width="120px"
    class="demo-ruleForm"
    :size="formSize"
    status-icon
    scroll-to-error
    :scroll-into-view-options="{
      block:'center'
    }"
  >
    <cl-form-item label="Activity name" prop="name">
      <cl-input v-model="ruleForm.name" />
    </cl-form-item>
    <cl-form-item label="Activity form" prop="desc">
      <cl-input v-model="ruleForm.desc" type="textarea" />
    </cl-form-item>
    <cl-form-item>
      <cl-button type="primary" @click="submitForm(ruleFormRef)"> Create </cl-button>
      <cl-button @click="resetForm(ruleFormRef)">Reset</cl-button>
    </cl-form-item>
  </cl-form>
</template>

<script lang="ts" setup>
import { FormInstance, FormRules } from "@kirkw/carol-ui";
import { reactive, ref } from "vue";
interface RuleForm {
  name: string;
  region: string;
  count: string;
  date1: string;
  date2: string;
  delivery: boolean;
  type: string[];
  resource: string;
  desc: string;
}

const formSize = ref("medium");
const ruleFormRef = ref<FormInstance>();
const ruleForm = reactive<RuleForm>({
  name: "Hello",
  region: "",
  count: "",
  date1: "",
  date2: "",
  delivery: false,
  type: [],
  resource: "",
  desc: ""
});

const rules = reactive<FormRules<RuleForm>>({
  name: [
    { required: true, message: "Please input Activity name", trigger: "blur" },
    { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" }
  ],
  region: [
    {
      required: true,
      message: "Please select Activity zone",
      trigger: "change"
    }
  ],
  count: [
    {
      required: true,
      message: "Please select Activity count",
      trigger: "change"
    }
  ],
  date1: [
    {
      type: "date",
      required: true,
      message: "Please pick a date",
      trigger: "change"
    }
  ],
  date2: [
    {
      type: "date",
      required: true,
      message: "Please pick a time",
      trigger: "change"
    }
  ],
  type: [
    {
      type: "array",
      required: true,
      message: "Please select at least one activity type",
      trigger: "change"
    }
  ],
  resource: [
    {
      required: true,
      message: "Please select activity resource",
      trigger: "change"
    }
  ],
  desc: [{ required: true, message: "Please input activity form", trigger: "blur" }]
});

const submitForm = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate((valid, fields) => {
    if (valid) {
      console.log("submit!");
    } else {
      console.log("error submit!", fields);
    }
  });
};

const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.resetFields();
};

const options = Array.from({ length: 10000 }).map((_, idx) => ({
  value: `${idx + 1}`,
  label: `${idx + 1}`
}));
</script>

自定义校验规则

Password
Confirm
Age
这个例子中展示了如何使用自定义验证规则来完成密码的二次验证。
Form 组件提供了表单验证的功能,只需为 rules 属性传入约定的验证规则,并将 form-Item 的 prop 属性设置为需要验证的特殊键值即可。 更多高级用法可参考 async-validator。
<template>
  <cl-form ref="ruleFormRef" :model="ruleForm" status-icon :rules="rules" label-width="80" class="demo-ruleForm">
    <cl-form-item label="Password" prop="pass">
      <cl-input v-model="ruleForm.pass" type="password" autocomplete="off" />
    </cl-form-item>
    <cl-form-item label="Confirm" prop="checkPass">
      <cl-input v-model="ruleForm.checkPass" type="password" autocomplete="off" />
    </cl-form-item>
    <cl-form-item label="Age" prop="age">
      <cl-input v-model.number="ruleForm.age" />
    </cl-form-item>
    <cl-form-item>
      <cl-button type="primary" @click="submitForm(ruleFormRef)">Submit</cl-button>
      <cl-button @click="resetForm(ruleFormRef)">Reset</cl-button>
    </cl-form-item>
  </cl-form>
</template>

<script lang="ts" setup>
import type { FormInstance, FormRules } from "@kirkw/carol-ui";
import { reactive, ref } from "vue";

const ruleFormRef = ref<FormInstance>();

const checkAge = (rule: any, value: any, callback: any) => {
  if (!value) {
    return callback(new Error("Please input the age"));
  }
  setTimeout(() => {
    if (!Number.isInteger(value)) {
      callback(new Error("Please input digits"));
    } else {
      if (value < 18) {
        callback(new Error("Age must be greater than 18"));
      } else {
        callback();
      }
    }
  }, 1000);
};

const validatePass = (rule: any, value: any, callback: any) => {
  if (value === "") {
    callback(new Error("Please input the password"));
  } else {
    if (ruleForm.checkPass !== "") {
      if (!ruleFormRef.value) return;
      ruleFormRef.value.validateField("checkPass", () => null);
    }
    callback();
  }
};
const validatePass2 = (rule: any, value: any, callback: any) => {
  if (value === "") {
    callback(new Error("Please input the password again"));
  } else if (value !== ruleForm.pass) {
    callback(new Error("Two inputs don't match!"));
  } else {
    callback();
  }
};

const ruleForm = reactive({
  pass: "",
  checkPass: "",
  age: ""
});

const rules = reactive<FormRules<typeof ruleForm>>({
  pass: [{ validator: validatePass, trigger: "blur" }],
  checkPass: [{ validator: validatePass2, trigger: "blur" }],
  age: [{ validator: checkAge, trigger: "blur" }]
});

const submitForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.validate(valid => {
    if (valid) {
      console.log("submit!");
    } else {
      console.log("error submit!");
      return false;
    }
  });
};

const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.resetFields();
};
</script>

TIP

自定义的校验回调函数必须被调用。 更多高级用法可参考 async-validator。

添加/删除表单项

Email
Domain0
除了一次通过表单组件上的所有验证规则外. 您也可以动态地通过验证规则或删除单个表单字段的规则。
<template>
  <cl-form
    ref="formRef"
    :model="dynamicValidateForm"
    label-width="80px"
    class="demo-dynamic"
  >
    <cl-form-item
      prop="email"
      label="Email"
      :rules="[
        {
          required: true,
          message: 'Please input email address',
          trigger: 'blur',
        },
        {
          type: 'email',
          message: 'Please input correct email address',
          trigger: ['blur', 'change'],
        },
      ]"
    >
      <cl-input v-model="dynamicValidateForm.email" />
    </cl-form-item>
    <cl-form-item
      v-for="(domain, index) in dynamicValidateForm.domains"
      :key="domain.key"
      :label="'Domain' + index"
      :prop="'domains.' + index + '.value'"
      :rules="{
        required: true,
        message: 'domain can not be null',
        trigger: 'blur',
      }"
    >
      <cl-input v-model="domain.value" />
      <cl-button class="mt-2" @click.prevent="removeDomain(domain)"
        >Delete</cl-button
      >
    </cl-form-item>
    <cl-form-item>
      <cl-button type="primary" @click="submitForm(formRef)">Submit</cl-button>
      <cl-button @click="addDomain">New domain</cl-button>
      <cl-button @click="resetForm(formRef)">Reset</cl-button>
    </cl-form-item>
  </cl-form>
</template>

<script lang="ts" setup>
import { FormInstance } from '@kirkw/carol-ui';
import { reactive, ref } from 'vue'
const formRef = ref<FormInstance>()
const dynamicValidateForm = reactive<{
  domains: DomainItem[]
  email: string
}>({
  domains: [
    {
      key: 1,
      value: '',
    },
  ],
  email: '',
})

interface DomainItem {
  key: number
  value: string
}

const removeDomain = (item: DomainItem) => {
  const index = dynamicValidateForm.domains.indexOf(item)
  if (index !== -1) {
    dynamicValidateForm.domains.splice(index, 1)
  }
}

const addDomain = () => {
  dynamicValidateForm.domains.push({
    key: Date.now(),
    value: '',
  })
}

const submitForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.validate((valid) => {
    if (valid) {
      console.log('submit!')
    } else {
      console.log('error submit!')
      return false
    }
  })
}

const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.resetFields()
}
</script>

数字类型验证

age
数字类型的验证需要在 v-model 处加上 .number 的修饰符,这是 Vue 自身提供的用于将绑定值转化为 number 类型的修饰符。
<template>
  <cl-form
    ref="formRef"
    :model="numberValidateForm"
    label-width="auto"
    class="demo-ruleForm"
  >
    <cl-form-item
      label="age"
      prop="age"
      :rules="[
        { required: true, message: 'age is required' },
        { type: 'number', message: 'age must be a number' },
      ]"
    >
      <cl-input
        v-model.number="numberValidateForm.age"
        type="text"
        autocomplete="off"
      />
    </cl-form-item>
    <cl-form-item>
      <cl-button type="primary" @click="submitForm(formRef)">Submit</cl-button>
      <cl-button @click="resetForm(formRef)">Reset</cl-button>
    </cl-form-item>
  </cl-form>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue'
import type { FormInstance } from '@kirkw/carol-ui'

const formRef = ref<FormInstance>()

const numberValidateForm = reactive({
  age: '',
})

const submitForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.validate((valid) => {
    if (valid) {
      console.log('submit!')
    } else {
      console.log('error submit!')
      return false
    }
  })
}

const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.resetFields()
}
</script>

TIP

When an cl-form-item is nested in another cl-form-item, its label width will be 0. You can set label-width on that cl-form-item if needed.

尺寸控制


Activity name
表单中的所有子组件都继承了该表单的 size 属性。 同样,form-item 也有一个 size 属性。
如果希望某个表单项或某个表单组件的尺寸不同于 Form 上的 size 属性,直接为这个表单项或表单组件设置自己的 size 属性即可。
<template>
  <div>
    <cl-button-group v-model="size" label="size control">
      <cl-button label="large" @click="size = 'large'">large</cl-button>
      <cl-button label="medium" @click="size = 'medium'">medium</cl-button>
      <cl-button label="small" @click="size = 'small'">small</cl-button>
    </cl-button-group>
    <cl-button-group style="margin-left: 10px" v-model="labelPosition" label="position control">
      <cl-button label="left" @click="labelPosition = 'left'">Left</cl-button>
      <cl-button label="right" @click="labelPosition = 'right'">Right</cl-button>
      <cl-button label="top" @click="labelPosition = 'top'">Top</cl-button>
    </cl-button-group>
  </div>
  <br />
  <cl-form ref="form" :model="sizeForm" label-width="140px" :label-position="labelPosition" :size="size">
    <cl-form-item label="Activity name">
      <cl-input v-model="sizeForm.name" />
    </cl-form-item>
    <cl-form-item>
      <cl-button type="primary" @click="onSubmit">Create</cl-button>
      <cl-button>Cancel</cl-button>
    </cl-form-item>
  </cl-form>
</template>

<script lang="ts" setup>
import { reactive, ref } from "vue";

const size = ref("medium");
const labelPosition = ref("right");

const sizeForm = reactive({
  name: "",
  region: "",
  date1: "",
  date2: "",
  delivery: false,
  type: [],
  resource: "",
  desc: ""
});

function onSubmit() {
  console.log("submit!");
}
</script>

<style>
.cl-radio-group {
  margin-right: 12px;
}
</style>

:::

Form API

Form Attributes

属性描述类型默认值
model表单数据对象object Record<string, any>
rules表单验证规则.object FormRules
label-position表单域标签的位置, 当设置为 left 或 right 时,则也需要设置 label-width 属性enum 'left' | 'right' | 'top'right
label-width标签的长度,例如 '50px'。 作为 Form 直接子元素的 form-item 会继承该值。 可以使用 auto。string / number''
label-suffix表单域标签的后缀string''
hide-required-asterisk是否隐藏必填字段标签旁边的红色星号。booleanfalse
require-asterisk-position星号的位置。enum 'left' | 'right'left
show-message是否显示校验错误信息booleantrue
validate-on-rule-change是否在 rules 属性改变后立即触发一次验证booleantrue
size用于控制该表单内组件的尺寸form.enum '' | 'large' | 'default' | 'small'
disabled是否禁用该表单内的所有组件。 如果设置为 true, 它将覆盖内部组件的 disabled 属性booleanfalse
scroll-to-error当校验失败时,滚动到第一个错误表单项booleanfalse
scroll-into-view-options当校验有失败结果时,滚动到第一个失败的表单项目 可通过 scrollIntoView 配置object | boolean{block:'center'}

Form Events

属性描述类型
validate任一表单项被校验后触发Function (prop: FormItemProp, isValid: boolean, message: string) => void

Form Slots

属性描述Subtags
default自定义默认内容FormItem

Form Exposes

属性描述类型
validate对整个表单的内容进行验证。 接收一个回调函数,或返回 Promise。Function (callback?: FormValidateCallback) => Promise<void>
validateField验证具体的某个字段。Function (props?: Arrayable<FormItemProp> | undefined, callback?: FormValidateCallback | undefined) => FormValidationResult
resetFields重置该表单项,将其值重置为初始值,并移除校验结果
Function (props?: Arrayable<FormItemProp> | undefined) => void
clearValidate清理某个字段的表单验证信息。Function (props?: Arrayable<FormItemProp> | undefined) => void

FormItem API

FormItem Attributes

| 属性 | 描述 | 类型 | 默认值 | | ------------ | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------- | --- | --- | | prop | model 的键名。 它可以是一个属性的值(如 a.b.0 或 ['a', 'b', '0'])。 在使用了 validate、resetFields 的方法时,该属性是必填的 | string / string[] | — | | label | 标签文本 | string | — | | label-width | 标签宽度,例如 '50px'。 可以使用 auto。 | string / number | '' | | required | 是否为必填项,如不设置,则会根据校验规则确认 | boolean | — | | rules | 表单验证规则, 具体配置见下表, 更多内容可以参考async-validator | object Arrayable<FormItemRule> | — | | error | 表单域验证错误时的提示信息。设置该值会导致表单验证状态变为 error,并显示该错误信息。 | string | — | | show-message | 是否显示校验错误信息 | boolean | true | | size | 用于控制该表单域下组件的默认尺寸 | enum '' \| 'large' \| 'default' \| 'small' | — | | |

FormItemRule

属性描述类型默认值
trigger验证逻辑的触发方式enum 'blur' | 'change'

TIP

如果您不想根据输入事件触发验证器, 在相应的输入类型组件上设置 validate-event 属性为 false (<cl-input>, <cl-radio>, <cl-select>, . ……).

FormItem Slots

属性描述类型
default表单的内容。
label标签位置显示的内容object { label: string }
error验证错误信息的显示内容object { error: string }

FormItem Exposes

属性描述类型
size表单项大小object ComputedRef<'' | 'large' | 'default' | 'small'>
validateMessage校验消息object Ref<string>
validateState校验状态object Ref<'' | 'error' | 'validating' | 'success'>
validate验证表单项Function (trigger: string, callback?: FormValidateCallback | undefined) => FormValidationResult
resetField对该表单项进行重置,将其值重置为初始值并移除校验结果Function () => void
clearValidate移除该表单项的校验结果Function () => void

类型声明

展示类型声明
ts
type Arrayable<T> = T | T[];

type FormValidationResult = Promise<boolean>;

// ValidateFieldsError: see [async-validator](https://github.com/yiminghe/async-validator/blob/master/src/interface.ts)
type FormValidateCallback = (isValid: boolean, invalidFields?: ValidateFieldsError) => void;

// RuleItem: see [async-validator](https://github.com/yiminghe/async-validator/blob/master/src/interface.ts)
interface FormItemRule extends RuleItem {
  trigger?: Arrayable<string>;
}
type FormRules = Partial<Record<string, Arrayable<FormItemRule>>>;