Form 表单
表单封装,通过配置生成表单。
TIP
如果表单项组件支持placeholder
、clearable
、filterable
等属性,会被默认配置
基础用法
- 传入
columns
定义表单,modelValue
为表单数据,options
为数据配置项,component
为表单项组件,field
为字段名,fieldProps
为组件属性配置,formItemProps
为表单项装饰组件属性配置 FormItem
组件属性(表单项装饰组件)属性配置在formItemProps
字段中,有些字段为了方便使用,直接配置在column
项中也可生效(如:label
、required
、message
、rule
等)- 事件使用
on
+事件名
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElInput } from 'element-plus'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
time: [],
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = [
{
component: ElInput,
field: 'name',
label: '姓名',
onInput: (val: string) => {
console.log(val, 'input event')
},
onChange: (val: string) => {
console.log(val, 'change event')
},
required: true,
},
{
component: 'select',
field: 'gender',
label: '性别',
onChange: (val: string) => {
console.log(val, 'change event')
},
onFocus: () => {
console.log('focus event')
},
},
{
component: 'el-date-picker',
field: 'time',
label: '出生日期',
fieldProps: {
type: 'daterange',
startPlaceholder: '开始日期',
endPlaceholder: '结束日期',
},
onChange: (val: string[]) => {
console.log(val, 'change event')
},
},
{
slot: 'button',
},
]
function reset() {
formRef.value.resetFields()
}
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid)
console.log(formData.value, 'config.formData')
else
console.log('error')
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:options="options"
:columns="columns"
label-width="80px"
size="small"
>
<template #button>
<div class="w-full flex">
<el-button class="w-full" @click="reset">
重置
</el-button>
<el-button type="primary" class="w-full" @click="submit">
提交
</el-button>
</div>
</template>
</z-form>
</template>
列布局
表单传入column
,支持1 ~ 3
列布局,默认为1
列布局。
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
age: '',
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = [
{
component: 'input',
field: 'name',
label: '姓名',
required: true,
},
{
component: 'select',
field: 'gender',
label: '性别',
},
{
component: 'input',
label: '年龄',
field: 'age',
},
{
slot: 'button',
},
]
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid) {
ElMessage.success('success')
console.log(formData.value, 'formData.value')
}
else {
console.log('error')
}
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:options="options"
:columns="columns"
label-width="90px"
size="small"
:column="2"
>
<template #button>
<el-button class="w-full" type="primary" @click="submit">
提交
</el-button>
</template>
</z-form>
</template>
自定义布局
如果列布局不满足要求,可以在column
项中配置span
、offset
、pull
、push
、xs
、sm
、md
、lg
、xl
实现自定义响应式布局
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
age: '',
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = [
{
component: 'input',
field: 'name',
label: '姓名',
required: true,
span: 24,
},
{
component: 'select',
field: 'gender',
label: '性别',
span: 12,
},
{
component: 'input',
label: '年龄',
field: 'age',
span: 12,
},
{
span: 24,
slot: 'button',
},
]
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid) {
ElMessage.success('success')
console.log(formData.value, 'formData.value')
}
else {
console.log('error')
}
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:options="options"
:columns="columns"
label-width="90px"
size="small"
>
<template #button>
<el-button class="w-full" type="primary" @click="submit">
提交
</el-button>
</template>
</z-form>
</template>
提示
column
传入tooltip
、extra
和colon
,可以配置提示、额外信息和冒号。tooltip
和extra
支持字符串和render
函数配置。 表单传入colon
,可以统一配置所有表单项冒号。
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import { ElMessage } from 'element-plus'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
age: '',
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = [
{
component: 'input',
field: 'name',
label: '姓名',
tooltip: '这是姓名',
extra: 'extraSlot',
required: true,
},
{
component: 'select',
field: 'gender',
label: '性别',
colon: false,
tooltip: () => h('span', {}, '性别提示'),
required: true,
},
{
component: 'input',
label: '年龄',
field: 'age',
formItemProps: {
tooltip: '这是年龄',
extra: () => h('span', {}, '这是额外信息'),
colon: false,
},
},
{
slot: 'button',
},
]
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid) {
ElMessage.success('success')
console.log(formData.value, 'formData.value')
}
else {
console.log('error')
}
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:options="options"
:columns="columns"
label-width="90px"
size="small"
:column="2"
colon
>
<template #extraSlot>
<span>提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示提示</span>
</template>
<template #button>
<el-button class="w-full" type="primary" @click="submit">
提交
</el-button>
</template>
</z-form>
</template>
tooltip
支持传入render函数
和插槽自定义。
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import { ElMessage } from 'element-plus'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
age: '',
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = [
{
component: 'input',
field: 'name',
label: '姓名',
tooltip: {
reference: () => h('span', {}, '?'),
content: '姓名提示',
},
extra: 'extraSlot',
required: true,
},
{
component: 'select',
field: 'gender',
label: '性别',
colon: false,
tooltip: () => h('span', {}, '性别提示'),
required: true,
},
{
component: 'input',
label: '年龄',
field: 'age',
formItemProps: {
tooltip: {
reference: 'tooltipSlot',
content: () => h('span', {}, '自定义年龄提示'),
},
colon: false,
},
},
{
slot: 'button',
},
]
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid) {
ElMessage.success('success')
console.log(formData.value, 'formData.value')
}
else {
console.log('error')
}
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:options="options"
:columns="columns"
label-width="90px"
size="small"
:column="2"
colon
>
<template #tooltipSlot>
<el-icon class="cursor-pointer">
<i-delete />
</el-icon>
</template>
<template #button>
<el-button class="w-full" type="primary" @click="submit">
提交
</el-button>
</template>
</z-form>
</template>
校验
columns
表单项中添加required
字段,或者formItemProps
中设置required
字段,即可设置必填,校验信息会根据label
自动生成也可自定义。form
传入rules
字段,可以定义表单校验规则。columns
表单项配置rules
,可以定义当前表单项校验规则。
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
age: '',
hobby: '',
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const rules = {
age: [{ required: true, message: '年龄必填' }],
}
const columns = [
{
component: 'input',
field: 'name',
label: '姓名',
required: true,
},
{
component: 'select',
field: 'gender',
formItemProps: {
required: true,
label: '性别',
},
},
{
component: 'input',
label: '年龄',
field: 'age',
},
{
component: 'input',
label: '爱好',
field: 'hobby',
rules: {
required: true,
message: '请填写爱好',
},
},
{
slot: 'button',
},
]
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid) {
ElMessage.success('success')
console.log(formData.value, 'formData.value')
}
else {
console.log('error')
}
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:options="options"
:columns="columns"
label-width="90px"
size="small"
:rules="rules"
:column="3"
>
<template #button>
<el-button class="w-full" type="primary" @click="submit">
提交
</el-button>
</template>
</z-form>
</template>
联动
column
表单项传入hide
(支持布尔值或函数类型),可以配置表单项联动隐藏。
某些业务中需要使用v-show
决定组件显隐,column
表单项传入字段show
(支持布尔值或函数类型)即可。
姓名输入框有值,会展示性别选择框,然后试试选中性别,年龄表单项会出现。
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
age: '',
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = [
{
component: 'input',
field: 'name',
label: '姓名',
required: true,
},
{
component: 'select',
field: 'gender',
label: '性别',
show: (formData: any) => formData.name,
},
{
component: 'input',
label: '年龄',
hide: formData => formData.gender === '1' || !formData.gender,
field: 'age',
},
{
slot: 'button',
},
]
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid) {
ElMessage.success('success')
console.log(formData.value, 'formData.value')
}
else {
console.log('error')
}
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:options="options"
:columns="columns"
label-width="90px"
size="small"
:column="2"
>
<template #button>
<el-button class="w-full" type="primary" @click="submit">
提交
</el-button>
</template>
</z-form>
</template>
表单项自定义
我们可以使用slot
或render
自定义表单项内容。请仍然传入field
字段,否则重置无法清空数据(field
字段不传,默认取slot
字段)。
<script lang="ts" setup>
import { h, ref } from 'vue'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
age: '',
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = [
{
component: 'input',
field: 'name',
label: '姓名',
required: true,
},
{
slot: 'gender',
field: 'gender',
label: '性别',
},
{
label: '年龄',
render: () => h('span', {}, '年龄内容'),
},
]
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:columns="columns"
label-width="90px"
size="small"
>
<template #gender>
<z-select v-model="formData.gender" :options="options.gender" clearable filterable placeholder="请选择性别" />
</template>
</z-form>
</template>
label、error自定义
label
和error
支持传入字符串、render
函数或拼接Slot的字符串
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import { ElMessage } from 'element-plus'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
age: '',
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = [
{
component: 'input',
field: 'name',
label: () => h('span', {}, '姓名'),
error: 'error message',
required: true,
},
{
component: 'select',
field: 'gender',
label: 'labelSlot',
required: true,
error: h('span', {}, 'errorSlot'),
},
{
component: 'input',
field: 'age',
label: '年龄',
required: true,
error: 'errorSlot',
},
{
slot: 'button',
},
]
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid) {
ElMessage.success('success')
console.log(formData.value, 'formData.value')
}
else {
console.log('error')
}
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:columns="columns"
:options="options"
label-width="90px"
size="small"
>
<template #labelSlot>
<span>性别</span>
</template>
<template #errorSlot>
<span>年龄必填</span>
</template>
<template #button>
<el-button class="w-full" type="primary" @click="submit">
提交
</el-button>
</template>
</z-form>
</template>
表单项顺序
表单项传入order
,支持自定义顺序,不传则默认按顺序放最后
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElInput } from 'element-plus'
const formRef = ref()
const formData = ref({
name: '',
gender: '',
time: [],
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = [
{
component: 'el-input',
field: 'name',
label: '地址',
},
{
component: ElInput,
field: 'name',
label: '姓名',
order: 2,
},
{
component: 'select',
field: 'gender',
label: '性别',
order: 3,
},
{
component: 'el-date-picker',
field: 'time',
label: '出生日期',
order: 1,
},
{
slot: 'button',
},
]
function reset() {
formRef.value.resetFields()
}
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid)
console.log(formData.value, 'config.formData')
else
console.log('error')
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:options="options"
:columns="columns"
label-width="80px"
size="small"
>
<template #button>
<div class="w-full flex">
<el-button class="w-full" @click="reset">
重置
</el-button>
<el-button type="primary" class="w-full" @click="submit">
提交
</el-button>
</div>
</template>
</z-form>
</template>
表格结合
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
interface RowData {
name: string
address: string
}
const formRef = ref()
const formData = ref({
name: '',
gender: '',
time: [],
tableData: [{ name: 'steven', address: '' }],
})
const options = {
gender: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
}
const columns = computed(() => [
{
component: 'el-input',
field: 'name',
label: '姓名',
onInput: (val: string) => {
console.log(val, 'input event')
},
onChange: (val: string) => {
console.log(val, 'change event')
},
required: true,
},
{
'component': 'z-table',
'field': 'tableData',
'label': '表格',
'onUpdate:data': (data: RowData[]) => {
console.log(data, 'data')
formData.value.tableData = data
},
'fieldProps': {
data: formData.value.tableData,
toolBar: false,
columns: [
{
label: '名称',
prop: 'name',
},
{
component: 'el-input',
label: '地址',
prop: 'address',
},
],
},
},
{
slot: 'button',
},
])
function reset() {
formRef.value.resetFields()
}
function submit() {
formRef.value.validate((valid: boolean) => {
if (valid)
console.log(formData.value, 'config.formData')
else
console.log('error')
})
}
</script>
<template>
<z-form
ref="formRef"
v-model="formData"
:options="options"
:columns="columns"
label-width="80px"
size="small"
>
<template #button>
<div class="w-full flex">
<el-button class="w-full" @click="reset">
重置
</el-button>
<el-button type="primary" class="w-full" @click="submit">
提交
</el-button>
</div>
</template>
</z-form>
</template>
z-form属性
属性名 | 说明 | 类型 | 默认 |
---|---|---|---|
modelValue | 表单数据对象 | object | — |
type | 表单类型 | normal / group / collapse / array / step | normal |
rules | 表单验证规则 | object | — |
columns | 表单项 | array | — |
options | 表单选择项数据源 | object | — |
column | 表单列数(便捷布局) | number | 1 |
colon | 表单项冒号 | boolean | false |
max | 数组项最大数量(type 为array 生效) | number | — |
gutter | 栅格间隔 | number | 0 |
justify | flex 布局下的水平排列方式 | 'start' | 'end' |
align | flex 布局下的垂直排列方式 | top / middle /bottom | — |
v-model:activeCollapse | 展开的collapse 项(type 为collapse 生效) | array | — |
label-position | 表单域标签的位置, 当设置为 left 或 right 时,则也需要设置 label-width 属性 | enum | right |
label-width | 标签的长度,例如 '50px' 。 作为 Form 直接子元素的 form-item 会继承该值。 可以使用 auto 。 | string / number | '' |
label-suffix | 表单域标签的后缀 | string | '' |
hide-required-asterisk | 是否隐藏必填字段标签旁边的红色星号。 | boolean | false |
require-asterisk-position | 星号的位置。 | left / right | left |
show-message | 是否显示校验错误信息 | boolean | true |
inline-message | 是否以行内形式展示校验信息 | boolean | false |
status-icon | 是否在输入框中显示校验结果反馈图标 | boolean | false |
validate-on-rule-change | 是否在 rules 属性改变后立即触发一次验证 | boolean | true |
size | 用于控制该表单内组件的尺寸 | large / default / small | — |
disabled | 是否禁用该表单内的所有组件。 如果设置为 true , 它将覆盖内部组件的 disabled 属性 | boolean | false |
scroll-to-error | 当校验失败时,滚动到第一个错误表单项 | boolean | false |
scroll-into-view-options | 当校验有失败结果时,滚动到第一个失败的表单项目 | object / boolean | — |
group表单属性
属性名 | 说明 | 类型 | 默认值 |
---|---|---|---|
border-style | 设置分隔符样式 | none / solid / hidden / dashed | solid |
content-position | 自定义分隔线内容的位置 | left / right / center | center |
array表单属性
属性名 | 说明 | 类型 | 默认值 |
---|---|---|---|
max | 最大表单项数量 | number | — |
step表单属性
属性名 | 说明 | 类型 | 默认值 |
---|---|---|---|
process-status | 设置当前步骤的状态 | wait / process / finish / error / success | process |
finish-status | 设置结束步骤的状态 | wait / process / finish / error / success | finish |
align-center | 居中对齐 | boolean | true |
z-form方法
名称 | 说明 | 类型 |
---|---|---|
validate | 对整个表单的内容进行验证。 接收一个回调函数,或返回 Promise 。 | Function |
validateField | 验证具体的某个字段。 | Function |
resetFields | 重置该表单项,将其值重置为初始值,并移除校验结果 | Function |
scrollToField | 滚动到指定的字段 | Function |
clearValidate | 清理某个字段的表单验证信息。 | Function |
z-form 事件
事件名 | 说明 | 类型 |
---|---|---|
update:modelValue | 表单项数据 | Function |
update:activeCollapse | 折叠表单的展开项 | Function |
update:activeStep | 步骤表单的当前步骤 | Function |
collapse-change | 折叠表单折叠项改变时触发 | Function |
next-step | 步骤表单点击下一步触发 | Function |
previous-step | 步骤表单点击上一步触发 | Function |
通常表单column属性
属性名 | 说明 | 类型 | 默认值 |
---|---|---|---|
component | 表单项组件 | input / checkbox / select / radio / 任意局部注册或全局注册组件 | — |
field | 字段名 | string | — |
fieldProps | component 组件属性 | object | — |
formItemProps | formItem 组件属性 | object | — |
label | 表单标签名 | string / () => VNode | — |
order | 表单排序 | number | — |
hide | 显隐 | boolean / (formData) => boolean | — |
show | 使用v-show 显隐 | boolean / (formData) => boolean | — |
slot | 表单项自定义内容插槽 | string | — |
render | 表单项自定义内容render | () => VNode | — |
required | 表单项是否必填 | boolean | — |
rules | 该表单项校验规则 | boolean | — |
error | 错误信息 | string / () => VNode | — |
tooltip | 提示信息 | string / () => VNode | — |
extra | 额外信息 | string / () => VNode | — |
children | 表单折叠模式下生效,column数组 | array | — |
span | 占据的单元格 | number | — |
offset | 左侧的间隔格数 | number | — |
pull | 向左移动格数 | number | — |
push | 向右移动格数 | boolean | — |
xs | <768px 响应式栅格数或者栅格属性对象 | number / object | — |
sm | ≥768px 响应式栅格数或者栅格属性对象 | number / object | — |
md | ≥992px 响应式栅格数或者栅格属性对象 | number / object | — |
lg | ≥1200px 响应式栅格数或者栅格属性对象 | number / object | — |
xl | ≥1920px 响应式栅格数或者栅格属性对象 | number / object | — |
array表单column属性
属性名 | 说明 | 类型 | 默认值 |
---|---|---|---|
label | 标签名 | string / () => VNode | — |
field | 字段名 | string | — |
max | 最大表单项数量 | number | — |
children | 当前步骤中的表单项(属性都为通常column 属性) | array | — |
step表单column属性
属性名 | 说明 | 类型 | 默认值 |
---|---|---|---|
label | 步骤标题 | string / () => VNode | — |
icon | 表单step 模式生效,图标 | string / Component | — |
description | 表单step 模式生效,描述 | boolean | — |
status | 表单step 模式生效,步骤条状态 | wait / process / finish / error / success | — |
children | 当前步骤中的表单项(属性都为通常column 属性) | array | — |
formItemProps属性
属性名 | 说明 | 类型 | Default |
---|---|---|---|
tooltip | 提示文案 | string / () => VNode | — |
extra | 额外信息 | string / () => VNode | — |
colon | 冒号 | boolean | — |
label | 标签文本 | string / () => VNode | — |
label-width | 标签宽度,例如 '50px' 。 可以使用 auto 。 | string / number | '' |
required | 是否为必填项,如不设置,则会根据校验规则确认 | boolean | — |
rules | 表单验证规则, 具体配置见下表, 更多内容可以参考async-validator | object | — |
error | 表单域验证错误时的提示信息。设置该值会导致表单验证状态变为 error,并显示该错误信息。 | string / () => VNode | — |
show-message | 是否显示校验错误信息 | boolean | true |
inline-message | 是否在行内显示校验信息 | string / boolean | '' |
size | 用于控制该表单域下组件的默认尺寸 | large / default / small | — |
for | 和原生标签相同能力 | string | — |
validate-status | formItem 校验的状态 | error / validating / success | — |