Appearance
Form 表单
高性能表单控件,自带数据域管理。包含数据录入、校验以及对应样式。
何时使用
- 用于创建一个实体或收集信息。
- 需要对输入的数据类型进行校验时。
表单
我们为 form 提供了以下三种排列方式:
- 水平排列:标签和表单控件水平排列;(默认)
- 垂直排列:标签和表单控件上下垂直排列;
- 行内排列:表单项水平行内排列。
表单域
表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等。
代码演示
基本使用
基本的表单数据域控制展示,包含布局、初始化、验证、提交。
vue
<template>
<a-form
:model="formState"
name="basic"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="Username"
name="username"
:rules="[{ required: true, message: 'Please input your username!' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="Password"
name="password"
:rules="[{ required: true, message: 'Please input your password!' }]"
>
<a-input-password v-model:value="formState.password" />
</a-form-item>
<a-form-item name="remember" :wrapper-col="{ offset: 8, span: 16 }">
<a-checkbox v-model:checked="formState.remember">Remember me</a-checkbox>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
<a-button type="primary" html-type="submit">Submit</a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
interface FormState {
username: string
password: string
remember: boolean
}
const formState: FormState = reactive({
username: '',
password: '',
remember: true
})
const onFinish = (values: any) => {
console.log('Success:', values)
}
const onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo)
}
</script>固定 Label 宽度
通过 labelCol.style 设置固定宽度
vue
<template>
<a-form :model="formState" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="Activity name">
<a-input v-model:value="formState.name" />
</a-form-item>
<a-form-item label="Instant delivery">
<a-switch v-model:checked="formState.delivery" />
</a-form-item>
<a-form-item label="Activity type">
<a-checkbox-group v-model:value="formState.type">
<a-checkbox value="1" name="type">Online</a-checkbox>
<a-checkbox value="2" name="type">Promotion</a-checkbox>
<a-checkbox value="3" name="type">Offline</a-checkbox>
</a-checkbox-group>
</a-form-item>
<a-form-item label="Resources">
<a-radio-group v-model:value="formState.resource">
<a-radio value="1">Sponsor</a-radio>
<a-radio value="2">Venue</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="Activity form">
<a-textarea v-model:value="formState.desc" />
</a-form-item>
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
<a-button type="primary" @click="onSubmit">Create</a-button>
<a-button style="margin-left: 10px">Cancel</a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { reactive, toRaw } from 'vue';
import type { UnwrapRef } from 'vue';
interface FormState {
name: string;
delivery: boolean;
type: string[];
resource: string;
desc: string;
}
const formState: UnwrapRef<FormState> = reactive({
name: '',
delivery: false,
type: [],
resource: '',
desc: '',
});
const onSubmit = () => {
console.log('submit!', toRaw(formState));
};
const labelCol = { style: { width: '150px' } };
const wrapperCol = { span: 14 };
</script>内联登录栏
水平登录栏,常用在顶部导航栏中。
vue
<template>
<a-form
layout="inline"
:model="formState"
@finish="handleFinish"
@finishFailed="handleFinishFailed"
>
<a-form-item>
<a-input v-model:value="formState.user" placeholder="Username">
<template #prefix>
<UserOutlined style="color: rgba(0, 0, 0, 0.25)" />
</template>
</a-input>
</a-form-item>
<a-form-item>
<a-input v-model:value="formState.password" type="password" placeholder="Password">
<template #prefix>
<LockOutlined style="color: rgba(0, 0, 0, 0.25)" />
</template>
</a-input>
</a-form-item>
<a-form-item>
<a-button
type="primary"
html-type="submit"
:disabled="formState.user === '' || formState.password === ''"
>
Log in
</a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
interface FormState {
user: string
password: string
}
const formState: FormState = reactive({
user: '',
password: ''
})
const handleFinish = (values: any) => {
console.log(values, formState)
}
const handleFinishFailed = (errors: any) => {
console.log(errors)
}
</script>表单禁用
设置表单组件禁用,仅对 antd 组件有效。
vue
<template>
<a-checkbox :checked="componentDisabled" @change="(e) => (componentDisabled = e.target.checked)">
Form disabled
</a-checkbox>
<a-form
:label-col="labelCol"
:wrapper-col="wrapperCol"
layout="horizontal"
:disabled="componentDisabled"
style="max-width: 600px"
>
<a-form-item label="Checkbox">
<a-checkbox>checkbox</a-checkbox>
</a-form-item>
<a-form-item label="Radio">
<a-radio-group v-model:value="radioValue">
<a-radio value="apple">Apple</a-radio>
<a-radio value="pear">Pear</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="Input">
<a-input />
</a-form-item>
<a-form-item label="Select">
<a-select>
<a-select-option value="demo">Demo</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="TreeSelect">
<a-tree-select :tree-data="treeData" />
</a-form-item>
<a-form-item label="Cascader">
<a-cascader :options="options" />
</a-form-item>
<a-form-item label="DatePicker">
<a-date-picker />
</a-form-item>
<a-form-item label="RangePicker">
<a-range-picker />
</a-form-item>
<a-form-item label="InputNumber">
<a-input-number />
</a-form-item>
<a-form-item label="TextArea">
<a-textarea :rows="4" />
</a-form-item>
<a-form-item label="Switch">
<a-switch v-model:checked="checked" />
</a-form-item>
<a-form-item label="Upload">
<a-upload action="/upload.do" list-type="picture-card">
<div>
<PlusOutlined />
<div style="margin-top: 8px">Upload</div>
</div>
</a-upload>
</a-form-item>
<a-form-item label="Button">
<a-button>Button</a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { PlusOutlined } from '@ant-design/icons-vue'
import type { TreeSelectProps, CascaderProps } from '@design-middle-center/fuse-design-vue'
const componentDisabled = ref(true)
const labelCol = { style: { width: '150px' } }
const wrapperCol = { span: 14 }
const radioValue = ref('apple')
const treeData = reactive<TreeSelectProps['treeData']>([
{
title: 'Light',
value: 'light',
children: [{ title: 'Bamboo', value: 'bamboo' }]
}
])
const options = reactive<CascaderProps['options']>([
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou'
}
]
}
])
const checked = ref(false)
</script>登录框
普通的登录框,可以容纳更多的元素。
vue
<template>
<a-form
:model="formState"
name="normal_login"
class="login-form"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="Username"
name="username"
:rules="[{ required: true, message: 'Please input your username!' }]"
>
<a-input v-model:value="formState.username">
<template #prefix>
<UserOutlined class="site-form-item-icon" />
</template>
</a-input>
</a-form-item>
<a-form-item
label="Password"
name="password"
:rules="[{ required: true, message: 'Please input your password!' }]"
>
<a-input-password v-model:value="formState.password">
<template #prefix>
<LockOutlined class="site-form-item-icon" />
</template>
</a-input-password>
</a-form-item>
<a-form-item name="remember" no-style>
<a-checkbox v-model:checked="formState.remember">Remember me</a-checkbox>
</a-form-item>
<a class="login-form-forgot" href="">Forgot password</a>
<a-form-item>
<a-button :disabled="disabled" type="primary" html-type="submit" class="login-form-button">
Log in
</a-button>
Or <a href="">register now!</a>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { reactive, computed } from 'vue'
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
interface FormState {
username: string
password: string
remember: boolean
}
const formState: FormState = reactive({
username: '',
password: '',
remember: true
})
const onFinish = (values: any) => {
console.log('Success:', values)
}
const onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo)
}
const disabled = computed(() => {
return !(formState.username && formState.password)
})
</script>
<style scoped>
#components-form-demo-normal-login .login-form {
max-width: 300px;
}
#components-form-demo-normal-login .login-form-forgot {
float: right;
}
#components-form-demo-normal-login .login-form-button {
width: 100%;
}
</style>表单验证
Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 FormItem 的 name 属性设置为需校验的字段名即可。校验规则参见 async-validator
vue
<template>
<a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item ref="name" label="Activity name" name="name">
<a-input v-model:value="formState.name" />
</a-form-item>
<a-form-item label="Activity zone" name="region">
<a-select v-model:value="formState.region" placeholder="please select your zone">
<a-select-option value="shanghai">Zone one</a-select-option>
<a-select-option value="beijing">Zone two</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="Activity time" required name="date1">
<a-date-picker
v-model:value="formState.date1"
show-time
type="date"
placeholder="Pick a date"
style="width: 100%"
/>
</a-form-item>
<a-form-item label="Instant delivery" name="delivery">
<a-switch v-model:checked="formState.delivery" />
</a-form-item>
<a-form-item label="Activity type" name="type">
<a-checkbox-group v-model:value="formState.type">
<a-checkbox value="1" name="type">Online</a-checkbox>
<a-checkbox value="2" name="type">Promotion</a-checkbox>
<a-checkbox value="3" name="type">Offline</a-checkbox>
</a-checkbox-group>
</a-form-item>
<a-form-item label="Resources" name="resource">
<a-radio-group v-model:value="formState.resource">
<a-radio value="1">Sponsor</a-radio>
<a-radio value="2">Venue</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="Activity form" name="desc">
<a-textarea v-model:value="formState.desc" />
</a-form-item>
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
<a-button type="primary" @click="onSubmit">Create</a-button>
<a-button style="margin-left: 10px" @click="resetForm">Reset</a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { Dayjs } from 'dayjs'
import { reactive, ref, toRaw } from 'vue'
import type { UnwrapRef } from 'vue'
import type { Rule } from '@design-middle-center/fuse-design-vue/es/form'
interface FormState {
name: string
region: string | undefined
date1: Dayjs | undefined
delivery: boolean
type: string[]
resource: string
desc: string
}
const formRef = ref()
const labelCol = { span: 5 }
const wrapperCol = { span: 13 }
const formState: UnwrapRef<FormState> = reactive({
name: '',
region: undefined,
date1: undefined,
delivery: false,
type: [],
resource: '',
desc: ''
})
const rules: Record<string, Rule[]> = {
name: [
{ required: true, message: 'Please input Activity name', trigger: 'change' },
{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' }
],
region: [{ required: true, message: 'Please select Activity zone', trigger: 'change' }],
date1: [{ required: true, message: 'Please pick a date', trigger: 'change', type: 'object' }],
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 onSubmit = () => {
formRef.value
.validate()
.then(() => {
console.log('values', formState, toRaw(formState))
})
.catch((error) => {
console.log('error', error)
})
}
const resetForm = () => {
formRef.value.resetFields()
}
</script>动态增减表单项
动态增加、减少表单项。
vue
<template>
<a-form
ref="formRef"
name="dynamic_form_item"
:model="dynamicValidateForm"
v-bind="formItemLayoutWithOutLabel"
>
<a-form-item
v-for="(domain, index) in dynamicValidateForm.domains"
:key="domain.key"
v-bind="index === 0 ? formItemLayout : {}"
:label="index === 0 ? 'Domains' : ''"
:name="['domains', index, 'value']"
:rules="{
required: true,
message: 'domain can not be null',
trigger: 'change'
}"
>
<a-input
v-model:value="domain.value"
placeholder="please input domain"
style="width: 60%; margin-right: 8px"
/>
<MinusCircleOutlined
v-if="dynamicValidateForm.domains.length > 1"
class="dynamic-delete-button"
@click="removeDomain(domain)"
/>
</a-form-item>
<a-form-item v-bind="formItemLayoutWithOutLabel">
<a-button type="dashed" style="width: 60%" @click="addDomain">
<PlusOutlined />
Add field
</a-button>
</a-form-item>
<a-form-item v-bind="formItemLayoutWithOutLabel">
<a-button type="primary" html-type="submit" @click="submitForm"> Submit </a-button>
<a-button style="margin-left: 10px" @click="resetForm"> Reset </a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons-vue'
import type { FormInstance } from '@design-middle-center/fuse-design-vue'
interface Domain {
value: string
key: number
}
const formRef = ref<FormInstance>()
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 4 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 }
}
}
const formItemLayoutWithOutLabel = {
wrapperCol: {
xs: { span: 24, offset: 0 },
sm: { span: 20, offset: 4 }
}
}
const dynamicValidateForm = reactive({
domains: []
})
const submitForm = () => {
formRef.value
?.validate()
.then(() => {
console.log('values', dynamicValidateForm.domains)
})
.catch((error) => {
console.log('error', error)
})
}
const addDomain = () => {
const newDomain = {
value: '',
key: Date.now()
}
dynamicValidateForm.domains.push(newDomain)
}
const removeDomain = (domain: Domain) => {
const index = dynamicValidateForm.domains.indexOf(domain)
if (index !== -1) {
dynamicValidateForm.domains.splice(index, 1)
}
}
const resetForm = () => {
formRef.value?.resetFields()
}
</script>
