封装表单组件实现通过配置动态生成表单
提示
适用于可以通过 v-model
绑定值的任意组件,例如:
el-input
el-switch
input
…el-upload
… (需要将相关组件重新封装为支持 v-model
的组件)当 columns 绑定的是一个具有响应式的数组时,数组的变动会影响表单变动(及动态表单)。如果不需要动态表单推荐绑定一个普通数组
<template>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
export default defineComponent({
setup() {
const form = ref({})
const columns = [
{
label: 'Name',
prop: 'name',
component: 'el-input',
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
},
]
const submit = (done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
}
return {
form,
columns,
submit,
}
},
})
</script>
通过辅助函数 defineFormColumns
defineFormMenuColumns
defineFormSubmit
defineComponentProps
提供智能提示
<template>
<pro-form
v-model="form"
:columns="columns"
:menu="menu"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
defineFormColumns,
defineFormMenuColumns,
defineFormSubmit,
} from 'element-pro-components'
export default defineComponent({
setup() {
const menu = defineFormMenuColumns({
submitText: 'Create',
reset: false,
})
const form = ref({})
const columns = defineFormColumns([
{
label: 'Name',
prop: 'name',
component: 'el-input',
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
menu,
form,
columns,
submit,
}
},
})
</script>
支持直接对具有嵌套结构的对象或数组进行赋值,仅需要配置 prop
{}
当 prop
存在默认值时会阻止对嵌套键值赋值
<template>
<p>{{ form }}</p>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const form = ref({ 'a.b': undefined })
const columns = defineFormColumns([
{
label: 'Break',
prop: 'a.b',
component: 'el-input',
},
{
label: 'Object',
prop: 'a.b.c',
component: 'el-input',
},
{
label: 'Array',
prop: 'b[0]',
component: 'el-input',
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
form,
columns,
submit,
}
},
})
</script>
通过 columns 的 component
定义该项生成什么组件,要求对应组件可以通过 v-model
绑定值。通过 props
可以向组件中传值,通过 props
里面的 slots
可以向组件传递简单的 渲染函数
<template>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, markRaw, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const form = ref({})
const list = ref([
{ value: 'Go', label: 'go' },
{ value: 'JavaScript', label: 'javascript' },
{ value: 'Python', label: 'python' },
{ value: 'Dart', label: 'dart' },
{ value: 'V', label: 'v' },
])
const columns = defineFormColumns([
{
label: 'input',
prop: 'input',
component: 'el-input',
props: {
clearable: true,
placeholder: 'Please input',
prefixIcon: markRaw(Search),
slots: {
append: () => 'Search',
},
},
},
{
label: 'input-tag',
prop: 'inputTag',
component: 'pro-input-tag',
props: {
placeholder: 'Please click the space button after input',
},
},
{
label: 'radio',
prop: 'radio',
component: 'pro-radio',
props: {
data: list.value,
},
},
{
label: 'checkbox',
prop: 'checkbox',
component: 'pro-checkbox',
props: {
data: list.value,
},
},
{
label: 'select',
prop: 'select',
component: 'pro-select',
props: {
data: list.value,
},
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
form,
columns,
submit,
}
},
})
</script>
通过 component
也可以直接传入局部组件 (请使用 markRaw
标记)
<template>
<pro-form
v-model="form"
:columns="columns"
:gutter="20"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref, markRaw } from 'vue'
import { ElSwitch, ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const list = ref([
{ value: 'Go', label: 'go' },
{ value: 'JavaScript', label: 'javascript' },
{ value: 'Python', label: 'python' },
{ value: 'Dart', label: 'dart' },
{ value: 'V', label: 'v' },
])
const form = ref({})
const columns = ref(
defineFormColumns([
{
label: 'radio',
prop: 'radio',
component: 'pro-radio',
props: {
data: list,
},
},
{
label: 'switch',
prop: 'switch',
component: markRaw(ElSwitch),
},
])
)
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
form,
columns,
submit,
}
},
})
</script>
默认情况下 ProForm 组件仅支持使用 v-model
绑定值的组件,如果需要使用其他参数绑定值,可以通过 modelKey
配置
modelKey 除了支持字符串,还支持传入 [prop, event]
(prop
用于配置绑定值的参数,event
用于配置绑定值的事件)
<template>
<div>
<span class="el-form-item__label">Use outside the ProForm</span>
<my-input v-model:value="form.value" />
</div>
<pro-form
v-model="form"
:columns="columns"
inline
@submit="submit"
/>
</template>
<script>
import { defineComponent, h, markRaw, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
const MyInput = {
name: 'MyInput',
props: {
value: {
type: String,
default: '',
},
},
emits: ['update:value'],
setup(props, { emit }) {
return () =>
h('input', {
value: props.value,
onInput: (e) => emit('update:value', e.target.value),
})
},
}
export default defineComponent({
components: { MyInput },
setup() {
const form = ref({})
const columns = defineFormColumns([
{
label: 'Use in the ProForm',
prop: 'value',
component: markRaw(MyInput),
modelKey: 'value', // or ['value', 'onUpdate:value']
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
form,
columns,
submit,
}
},
})
</script>
提示
从 1.2.0
起,[prop]
相关插槽需要增加前缀 form-
使用
直接在模版中增加带 form-[prop]
相关的插槽即可使用。虽然在启用插槽后可以通过 v-model="form.slot"
这种方式绑定值,但更推荐使用 value
与 setValue
<template>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
>
<template #form-slot-label>
<picture-rounded class="icon-picture" />
<span>picture</span>
</template>
<template #form-slot="{ value, setValue }">
<el-upload
:show-file-list="false"
:before-upload="(file) => beforeUpload(file, setValue)"
action=""
class="avatar-uploader"
>
<img
v-if="value"
:src="value"
class="avatar"
>
<plus
v-else
class="icon-uploader"
/>
</el-upload>
</template>
</pro-form>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, PictureRounded } from '@element-plus/icons-vue'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
components: { Plus, PictureRounded },
setup() {
const form = ref({})
const columns = defineFormColumns([
{
prop: 'slot',
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
function beforeUpload(file, setValue) {
// Simulate uploading pictures
const fileReader = new FileReader()
fileReader.onloadend = (e) => setValue(e.target.result)
fileReader.readAsDataURL(file)
return false
}
return {
form,
columns,
submit,
beforeUpload,
}
},
})
</script>
<style>
.icon-picture {
margin-right: 6px;
width: 16px;
height: 16px;
}
.avatar-uploader .el-upload {
width: 178px;
height: 178px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
overflow: hidden;
text-align: center;
line-height: 200px;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader .icon-uploader {
width: 50px;
height: 50px;
color: #8c939d;
}
.avatar-uploader .avatar {
display: block;
width: 100%;
height: 100%;
}
</style>
通过 menu
配置按钮显示、文字、参数
按钮文字也可以通过 国际化 来配置
<template>
<pro-form
v-model="form"
:columns="columns"
:menu="menu"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
defineFormColumns,
defineFormMenuColumns,
defineFormSubmit,
} from 'element-pro-components'
export default defineComponent({
setup() {
const menu = defineFormMenuColumns({
submitText: 'Create',
reset: false,
})
const form = ref({})
const columns = defineFormColumns([
{
label: 'Name',
prop: 'name',
component: 'el-input',
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
menu,
form,
columns,
submit,
}
},
})
</script>
通过 columns 的 children
配置子表单,当然你也可以配置多层的 children
结构实现反复套娃
通过 max
限制子表单的最大数量
<template>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const form = ref({})
const columns = defineFormColumns([
{
label: 'Goods',
prop: 'name',
component: 'el-input',
},
{
label: 'Spec',
prop: 'spec',
size: 'small',
max: 3,
children: [
{
label: 'Weight',
prop: 'weight',
component: 'el-input',
},
{
label: 'Size',
prop: 'size',
max: 1,
children: [
{
label: 'Length',
prop: 'length',
component: 'el-input',
},
{
label: 'Width',
prop: 'width',
component: 'el-input',
},
{
label: 'Height',
prop: 'height',
component: 'el-input',
},
],
},
],
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
form,
columns,
submit,
}
},
})
</script>
像 el-form 一样可以通过 rules
配置表单验证。对于子表单更推荐使用 columns 里面的 rules
字段实现验证。否则你需要通过 ${父级的 prop}.${当前项的 index}.${当前的 prop}
这种方式配置子表单的验证
submit 方法第二个参数表示是否通过表单验证
<template>
<pro-form
v-model="form"
:columns="columns"
:rules="rules"
label-width="100px"
@submit="submit"
@reset="resetForm"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const form = ref({})
const rules = {
date: { required: true, message: 'Please input date', trigger: 'blur' },
user: { required: true, message: 'Please input user', trigger: 'blur' },
}
const columns = defineFormColumns([
{
label: 'Date',
prop: 'date',
component: 'el-input',
},
{
label: 'User',
prop: 'user',
max: 3,
size: 'small',
children: [
{
label: 'Name',
prop: 'name',
component: 'el-input',
rules: {
required: true,
message: 'Please input Name',
trigger: 'blur',
},
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
},
],
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
function resetForm() {
ElMessage('reset form')
console.log('reset form')
}
return {
form,
rules,
columns,
submit,
resetForm,
}
},
})
</script>
如果传入的 columns
是一个响应性数据,动态的修改 columns 表单也会随之改变
<template>
<pro-form
v-model="form"
:columns="columns"
:menu="menu"
label-width="100px"
@submit="submit"
>
<template #menu-left>
<el-button
v-show="columns.length < 5"
@click="add"
>
Add One
</el-button>
</template>
<template #menu-right>
<el-button
v-show="columns.length"
@click="del"
>
Delete One
</el-button>
</template>
</pro-form>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
defineFormColumns,
defineFormMenuColumns,
defineFormSubmit,
} from 'element-pro-components'
export default defineComponent({
setup() {
const menu = defineFormMenuColumns({
submit: false,
reset: false,
})
const count = ref(0)
const form = ref({})
const columns = ref(
defineFormColumns([
{
label: 'Label-0',
prop: 'prop0',
component: 'el-input',
},
])
)
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
function add() {
count.value++
columns.value.push({
label: 'Label-' + count.value,
prop: 'prop' + count.value,
component: 'el-input',
})
}
function del() {
const index = Math.floor(Math.random() * columns.value.length)
columns.value.splice(index, 1)
}
return {
menu,
form,
columns,
submit,
add,
del,
}
},
})
</script>
通过控制 columns
中的 show
字段可以动态控制表单的显示,很容易实现联动表单
<template>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { computed, defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const form = ref({})
const columns = computed(() =>
defineFormColumns([
{
label: 'Plan',
prop: 'plan',
component: 'pro-radio',
rules: {
required: true,
message: 'Please select a plan',
trigger: 'blue',
},
props: {
data: [
{ label: 'Basic', value: 'basic' },
{ label: 'Pro', value: 'pro' },
],
},
},
{
label: 'Name',
prop: 'name',
component: 'el-input',
show: !!form.value.plan,
},
getContentColumn(form.value.plan === 'basic'),
{
label: 'List',
prop: 'list',
max: 5,
show: form.value.plan === 'pro',
children: [
{
label: 'Date',
prop: 'date',
component: 'el-date-picker',
},
getContentColumn(form.value.plan === 'pro'),
],
},
])
)
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
function getContentColumn(show) {
return {
label: 'Content',
prop: 'content',
component: 'el-input',
show,
props: {
type: 'textarea',
rows: 2,
},
}
}
return {
form,
columns,
submit,
}
},
})
</script>
与使用 el-row
和 el-col
组件相同 (el-row
对应 pro-form
;el-col
对应 columns
),通过相关配置可以自由地组合布局。当 inline
为 true
时无效
<template>
<pro-form
v-model="form"
:columns="columns"
:gutter="20"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const form = ref({})
const columns = defineFormColumns([
{
label: 'Goods',
prop: 'name',
component: 'el-input',
span: 24,
},
{
label: 'Weight',
prop: 'weight',
component: 'el-input',
xs: 24,
md: 12,
},
{
label: 'Count',
prop: 'count',
component: 'el-input',
xs: 24,
md: 12,
},
{
label: 'Length',
prop: 'length',
component: 'el-input',
xs: 24,
md: 8,
},
{
label: 'Width',
prop: 'width',
component: 'el-input',
xs: 24,
md: 8,
},
{
label: 'Height',
prop: 'height',
component: 'el-input',
xs: 24,
md: 8,
},
{
label: 'Price',
prop: 'price',
component: 'el-input',
xs: 24,
md: 12,
},
{
label: 'MarketPrice',
prop: 'marketPrice',
component: 'el-input',
xs: 24,
md: 12,
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
form,
columns,
submit,
}
},
})
</script>
通过设置 inline
属性为 true
可以让表单域变为行内的表单域
<template>
<pro-form
v-model="form"
:columns="columns"
:menu="menu"
inline
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
defineFormColumns,
defineFormMenuColumns,
defineFormSubmit,
} from 'element-pro-components'
export default defineComponent({
setup() {
const menu = defineFormMenuColumns({
submitText: 'Search',
reset: false,
})
const form = ref({})
const columns = defineFormColumns([
{
label: 'Name',
prop: 'name',
component: 'el-input',
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
menu,
form,
columns,
submit,
}
},
})
</script>
通过设置 label-position
属性可以改变表单域标签的位置
当断点是 xs
时,默认为 top
;其余则是 right
<template>
<pro-radio-button
v-model="labelPosition"
:data="data"
style="margin-bottom: 18px"
/>
<pro-form
v-model="form"
:columns="columns"
:label-position="labelPosition"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const labelPosition = ref('left')
const form = ref({})
const columns = defineFormColumns([
{
label: 'Name',
prop: 'name',
component: 'el-input',
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
},
])
const data = [
{ label: 'Left', value: 'left' },
{ label: 'Right', value: 'right' },
{ label: 'Top', value: 'top' },
]
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
labelPosition,
data,
form,
columns,
submit,
}
},
})
</script>
数组表单与子表单相同,用于处理需要输入一个数组的情况。仅需配置 array
即可启用
通过 max 控制表单的最大数量
<template>
<pro-form
v-model="form"
:columns="columns"
:max="3"
array
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
export default defineComponent({
setup() {
const form = ref()
const columns = [
{
label: 'Name',
prop: 'name',
component: 'el-input',
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
},
]
const submit = (done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
}
return {
form,
columns,
submit,
}
},
})
</script>
分组表单用于将普通表单进行分组展示
<template>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const form = ref({})
const columns = defineFormColumns([
{
label: 'Name',
type: 'group',
children: [
{
label: 'First Name',
prop: 'firstName',
component: 'el-input',
md: 12,
},
{
label: 'Last Name',
prop: 'lastName',
component: 'el-input',
md: 12,
},
],
},
{
label: 'Address',
type: 'group',
children: [
{
label: 'City',
prop: 'city',
component: 'el-input',
md: 12,
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
md: 12,
},
],
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
form,
columns,
submit,
}
},
})
</script>
可折叠显示的分组表单
<template>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const form = ref({})
const columns = defineFormColumns([
{
label: 'Name',
type: 'collapse',
children: [
{
label: 'First Name',
prop: 'firstName',
component: 'el-input',
md: 12,
},
{
label: 'Last Name',
prop: 'lastName',
component: 'el-input',
md: 12,
},
],
},
{
label: 'Address',
type: 'collapse',
children: [
{
label: 'City',
prop: 'city',
component: 'el-input',
md: 12,
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
md: 12,
},
],
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
form,
columns,
submit,
}
},
})
</script>
通过标签页切换不同的表单
<template>
<pro-form
ref="formRef"
v-model="form"
:columns="columns"
:rules="rules"
label-position="top"
class="docs-tabs-form"
@submit="submit"
@tab-change="tabChange"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const loginRules = {
username: {
required: true,
message: 'Please input name',
trigger: 'blur',
},
password: {
required: true,
message: 'Please input password',
trigger: 'blur',
},
}
const registerRules = {
name: { required: true, message: 'Please input name', trigger: 'blur' },
email: { required: true, message: 'Please input email', trigger: 'blur' },
password: {
required: true,
message: 'Please input password',
trigger: 'blur',
},
}
const form = ref({})
const formRef = ref()
const rules = ref(loginRules)
const columns = defineFormColumns([
{
label: 'Sign in',
prop: 'login',
type: 'tabs',
children: [
{
label: 'Name',
prop: 'username',
component: 'el-input',
props: {
clearable: true,
placeholder: 'Please input name',
},
},
{
label: 'Password',
prop: 'password',
component: 'el-input',
props: {
type: 'password',
clearable: true,
showPassword: true,
placeholder: 'Please input password',
},
},
],
},
{
label: 'Sign up',
prop: 'register',
type: 'tabs',
children: [
{
label: 'Name',
prop: 'name',
component: 'el-input',
props: {
clearable: true,
placeholder: 'Please input name',
},
},
{
label: 'Email',
prop: 'email',
component: 'el-input',
props: {
clearable: true,
placeholder: 'Please input email',
},
},
{
label: 'Password',
prop: 'password',
component: 'el-input',
props: {
clearable: true,
placeholder: 'Please input password',
},
},
],
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
const tabChange = (prop) => {
rules.value = prop === 'login' ? loginRules : registerRules
setTimeout(() => {
formRef.value.resetFields()
})
}
return {
form,
formRef,
columns,
rules,
submit,
tabChange,
}
},
})
</script>
<style scoped>
.docs-tabs-form {
margin: 0 auto;
width: 50%;
min-width: 300px;
max-width: 500px;
}
.docs-tabs-form :deep(.pro-tabs-form .el-tabs__header) {
text-align: center;
}
.docs-tabs-form :deep(.pro-tabs-form .el-tabs__header .el-tabs__nav-wrap) {
--el-font-size-base: 20px;
display: inline-block;
}
</style>
将一个普通的表单分割成几步输入
<template>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const form = ref({})
const columns = defineFormColumns([
{
label: 'Name',
type: 'steps',
children: [
{
label: 'First Name',
prop: 'firstName',
component: 'el-input',
rules: { required: true, message: 'Please input', trigger: 'blur' },
},
{
label: 'Last Name',
prop: 'lastName',
component: 'el-input',
rules: { required: true, message: 'Please input', trigger: 'blur' },
},
],
},
{
label: 'Address',
type: 'steps',
children: [
{
label: 'City',
prop: 'city',
component: 'el-input',
rules: { required: true, message: 'Please input', trigger: 'blur' },
},
{
label: 'Address',
prop: 'address',
component: 'el-input',
rules: { required: true, message: 'Please input', trigger: 'blur' },
},
],
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
return {
form,
columns,
submit,
}
},
})
</script>
想要实现异步表单 columns 必须绑定一个动态数组
<template>
<div style="margin-bottom: 20px">
<el-button
type="primary"
@click="createForm"
>
Load Form
</el-button>
<el-button
type="info"
@click="createDict"
>
Load Dict
</el-button>
<el-button
type="danger"
@click="destroyForm"
>
Destroy
</el-button>
</div>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'
export default defineComponent({
setup() {
const list = ref([])
const form = ref({})
const columns = ref(defineFormColumns([]))
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
const createForm = () => {
columns.value = defineFormColumns([
{
label: 'Name',
prop: 'name',
component: 'el-input',
},
{
label: 'Language',
prop: 'language',
component: 'pro-radio',
props: {
data: list,
},
},
])
}
const createDict = () => {
list.value = [
{ value: 'Go', label: 'go' },
{ value: 'JavaScript', label: 'javascript' },
{ value: 'Python', label: 'python' },
{ value: 'Dart', label: 'dart' },
{ value: 'V', label: 'v' },
]
}
const destroyForm = () => {
columns.value = []
list.value = []
}
return {
form,
columns,
submit,
createForm,
createDict,
destroyForm,
}
},
})
</script>
defineFormColumns
支持传入一个泛型用来推断 prop
值;defineComponentProps
支持传入一个泛型用来辅助输入 props
值
<template>
<pro-form
v-model="form"
:columns="columns"
label-width="100px"
@submit="submit"
/>
</template>
<script setup lang="ts">
import { markRaw, ref } from 'vue'
import { ElMessage, ElSwitch } from 'element-plus'
import {
defineFormColumns,
defineFormSubmit,
defineComponentProps,
} from 'element-pro-components'
interface Form {
name?: string
status?: boolean
}
const form = ref<Form>({})
const columns = defineFormColumns<Form>([
{
label: 'Name',
prop: 'name',
component: 'ElInput',
props: defineComponentProps<'ElInput'>({
clearable: true,
placeholder: 'Please input your name',
slots: {
append: () => 'Search',
},
}),
},
{
label: 'Status',
prop: 'status',
component: markRaw(ElSwitch),
props: defineComponentProps<typeof ElSwitch>({
inlinePrompt: true,
activeText: 'Y',
inactiveText: 'N',
}),
},
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
ElMessage(`submit: ${isValid}`)
console.log(form.value, isValid, invalidFields)
setTimeout(() => {
done()
}, 1000)
})
</script>
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
v-model | 绑定值 | object / array | - | - |
columns | 表单配置参考下面 columns | array | - | - |
menu | 按钮配置参考下面 menu | object | - | - |
rules | 表单验证规则 | object | - | - |
inline | 行内表单模式 | boolean | - | false |
array | 是否启用数组表单 | boolean | - | |
max | 限制数组表单的最大数量 | number | - | - |
label-position | 表单域标签的位置,如果值为 left 或者 right 时,则需要设置 label-width | string | right / left / top | right |
label-width | 表单域标签的宽度,例如 ‘50px’ 或 ‘auto’ | string | - | - |
label-suffix | 表单域标签的后缀 | string | - | - |
hide-required-asterisk | 是否显示必填字段的标签旁边的红色星号 | boolean | - | false |
show-message | 是否显示校验错误信息 | boolean | - | true |
inline-message | 是否以行内形式展示校验信息 | boolean | - | false |
status-icon | 是否在输入框中显示校验结果反馈图标 | boolean | - | false |
validate-on-rule-change | 是否在 rules 属性改变后立即触发一次验证 | boolean | - | true |
size | 用于控制该表单内组件的尺寸 | string | large / default /small | - |
disabled | 是否禁用该表单内的所有组件 | boolean | - | false |
scroll-to-error | 当校验失败时,滚动到第一个错误表单项 | boolean | - | false |
gutter | 栅格间隔 | number | - | 0 |
justify | flex 布局下的水平排列方式 | string | start / end / center / space-around / space-between / spacing-evenly | start |
align | flex 布局下的垂直排列方式 | string | top / middle / bottom | top |
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
prop | v-model 绑定的字段名 (需要是唯一值) | string | - | - |
label | 标签文本 | string | - | - |
component | 当前项对应的组件,可以直接传入局部组件 | string / Component | - | - |
props | 传递的对应的组件的参数 | object | - | - |
modelKey | 当前项对应的组件的 v-model 绑定的字段名 | string / [string, string] | - | - |
children | 分组表单或子表单内容 | array | - | - |
type | children 内部表单的类型 | string | array / group / tabs / collapse / steps | array |
max | 限制 type=array 时子表单的最大数量 | number | - | - |
show | 是否在表单中显示当前项 | boolean | - | true |
labelWidth | 表单域标签的宽度,例如 ‘50px’ 或 ‘auto’ | string | - | - |
required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | - | false |
rules | 表单验证规则 | object / array | - | - |
error | 表单域验证错误信息, 设置该值会使表单验证状态变为error ,并显示该错误信息 | string | - | - |
showMessage | 是否显示校验错误信息 | boolean | - | true |
inlineMessage | 以行内形式展示校验信息 | boolean | - | false |
size | 用于控制该表单域下组件的尺寸 | string | large / default /small | - |
span | 栅格占据的列数 | number | - | 24 |
offset | 栅格左侧的间隔格数 | number | - | 0 |
push | 栅格向右移动格数 | number | - | 0 |
pull | 栅格向左移动格数 | number | - | 0 |
xs | <768px 响应式栅格数或者栅格属性对象 | number / object | - | - |
sm | ≥768px 响应式栅格数或者栅格属性对象 | number / object | - | - |
md | ≥992px 响应式栅格数或者栅格属性对象 | number / object | - | - |
lg | ≥1200px 响应式栅格数或者栅格属性对象 | number / object | - | - |
xl | ≥1920px 响应式栅格数或者栅格属性对象 | number / object | - | - |
disabled | 是否禁用, 当 type=tabs 或者 type=collapse 时生效 | boolean | — | false |
closable | 标签是否可关闭, 当 type=tabs 时生效 | boolean | — | false |
lazy | 标签是否延迟渲染, 当 type=tabs 时生效 | boolean | — | false |
description | 分步描述文案, 当 type=steps 时生效 | string | — | — |
icon | 自定义分步图标, 当 type=steps 时生效 | string / Component | — | — |
status | 设置当前步骤的状态, 当 type=steps 时生效 | string | wait / process / finish / error / success | — |
关于 props
props 的属性将全部传递给 component 指定的组件
slots
可以向组件传递简单的渲染函数on[Event]
驼峰这种形式绑定。如:change
-> onChange
, input
-> onInput
props: {
clearable: true,
'prefix-icon': 'el-icon-search',
suffixIcon: 'el-icon-date',
slots: {
prefix: () => h('i', { className: 'el-input__icon el-icon-search' }),
append: () => '搜索'
},
onChange: e => console.log(e),
}
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
submit | 是否显示 submit 按钮 | boolean | true |
submitText | submit 按钮显示的文字 | string | Submit |
submitProps | submit 按钮的配置,参考 el-button | object | { type: ‘primary’ } |
reset | 是否显示 reset 按钮 | boolean | true |
resetText | reset 按钮显示的文字 | string | Reset |
resetProps | reset 按钮的配置,参考 el-button | object | - |
prevText | prev 按钮显示的文字 | string | Prev |
prevProps | prev 按钮的配置,参考 el-button | object | - |
nextText | next 按钮显示的文字 | string | Next |
nextProps | next 按钮的配置,参考 el-button | object | - |
事件名 | 说明 | 参数 |
---|---|---|
submit | submit 被点击后触发 | done, isValid, invalidFields |
reset | reset 按钮被点击后触发 | - |
validate | 任一表单项被校验后触发 | prop, isValid, invalidFields |
add-item | add 被点击后触发 | indexes: number[] |
remove-item | remove 被点击后触发 | indexes: number[] |
collapse-change | collapse 改变时触发 | active: CollapseModelValue |
tab-change | tab 改变时触发 | name: TabPaneName |
step-change | step 改变时触发 | active: string | number |
方法名 | 说明 | 参数 |
---|---|---|
validate | 对整个表单进行校验的方法,参数为一个回调函数。该回调函数会在校验结束后被调用,并传入两个参数:是否校验成功和未通过校验的字段。若不传入回调函数,则会返回一个 promise | Function(callback: Function(boolean, object)) |
validateField | 对部分表单字段进行校验的方法 | Function(props: array | string, callback: Function(errorMessage: string)) |
resetFields | 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果 | - |
scrollToField | 滚动到指定表单字段 | Function(prop: string) |
clearValidate | 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果 | Function(props: array | string) |
名称 | 说明 | 类型 |
---|---|---|
- | 在底部菜单前插入的任意内容 | - |
menu-left | 表单底部按钮左侧 | { loading: boolean } |
menu-right | 表单底部按钮右侧 | { loading: boolean } |
form-[prop] | 当前这项的 Form Item 的内容 | { item: object, indexes?: number[], value: any, setValue: (value: any) => void } |
form-[prop]-label | 当前这项的标签文本的内容 | { item: object, indexes?: number[] } |
form-[prop]-error | 当前这项的自定义表单校验信息的显示方式 | { error, item: object, indexes?: number[] } |
提示
[prop] 为 columns 中定义的 prop