简单但是实用的表单组件封装
实现功能
通过传递配置实现表单开发
通过插槽
使用Element Plus组件库
Form表单的各种属性,自行查看文档
Form 表单 | Element Plus
Layout 布局 | Element Plus
Form组件基本使用+Layout布局
<el-form :model="form" label-width="120px">
<el-row>
<el-col :span="6">
<el-form-item label="Activity name">
<el-input v-model="form.name" />
</el-form-item>
</el-col>
</el-row>
</el-form>
正式开始封装
文件目录:
users.vue是爷爷组件
page-search.vue是爸爸组件
form.vue是子组件
解释:为什么要这样分
users是主页面 —— 负责使用组件,展示页面的
page-search.vue —— 配置本项目可能用到的关于搜索框所有的公共部分,包括插槽的使用
form.vue —— 为确保其他项目也能使用此组件,只能封装其他项目也能用的公共代码
是层层递进的,确保代码更好地复用
第一步:设置接口,继承Form表单的属性值/设置自己需要的属性:
// 设置表单类型
type IFormType = 'input' | 'password' | 'select' | 'datepicker'
// 定义接口,继承FormItem的属性 以下简单写了几个属性,定义
export interface IFormItem {
// 将此属性值设置为当我们进行搜索功能时,后端需要我们传递搜索数据的属性名
field?: string
type: IFormType
label: any
rules?: any[]
placeholder?: any
options?: any[]
otherOptions?: any // 设置一些特殊的属性
}
// 定义接口,继承Form的属性,和定义布局属性
export interface IForm {
formItems: IFormItem[]
labelWidth?: string
itemStyle?: any
colLayout?: any
}
第二步:编写动态生成表单的配置
配置一
import type { IForm } from '@/base-ui/form/type'
export const formConfig: IForm = {
formItems: [
{
field: 'id',
type: 'input',
label: 'id',
placeholder: '请输入id'
},
{
field: 'name',
type: 'input',
label: '用户名',
placeholder: '请输入用户名'
},
{
field: 'realname',
type: 'input',
label: '真实姓名',
placeholder: '请输入真实姓名'
},
{
field: 'cellphone',
type: 'input',
label: '电话号码',
placeholder: '请输入电话号码'
},
{
field: 'enable',
type: 'select',
label: '用户状态',
options: [
{ title: '启用', value: 1 },
{ title: '禁用', value: 2 }
]
},
{
field: 'createAt',
type: 'datepicker',
label: '创建时间',
otherOptions: {
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
type: 'daterange'
}
}
],
labelWidth: '120px',
itemStyle: { padding: '10px 40px' },
colLayout: {
span: 8
}
}
配置二
import type { IForm } from '@/base-ui/form/type'
export const modalConfig: IForm = {
formItems: [
{
field: 'name',
type: 'input',
label: '用户名',
placeholder: '请输入用户名'
},
{
field: 'realname',
type: 'input',
label: '真实姓名',
placeholder: '请输入真实姓名'
},
{
field: 'cellphone',
type: 'input',
label: '电话号码',
placeholder: '请输入电话号码'
},
{
field: 'password',
type: 'input',
label: '用户密码',
placeholder: '请输入密码',
isHidden: true
},
{
field: 'departmentId',
type: 'select',
label: '选择部门',
placeholder: '请选择部门',
options: []
},
{
field: 'roleId',
type: 'select',
label: '选择角色',
placeholder: '请选择角色',
options: []
}
],
labelWidth: '120px',
itemStyle: {},
colLayout: {
span: 24
}
}
第三步:根据配置,动态生成表单Form
思路:采用template标签,进行v-for遍历配置formItems.type确定表单类型,再将相应属性绑定在表单上
// form.vue
<template>
<div class="tt-form">
// 头部插槽
<div class="header"><slot name="header"></slot></div>
// 中间表格代码
<el-form :label-width="labelWidth">
<el-row>
// 遍历配置formItems数组,拿到各个表单配置对象
<template v-for="item in formItems" :key="item.label">
// el-col 确定一个表单占几分(共24份)
<el-col v-bind="colLayout">
// lable:标签文本
// style:设置表单样式:这里设置了边距
<el-form-item :label="item.label" :style="itemStyle">
// 判断表单类型
<template v-if="item.type === 'input' || item.type === 'password'">
// 绑定表单属性 设置双向绑定
<el-input
v-bind="item.otherOptions"
:placeholder="item.placeholder"
:model-value="modelValue[`${item.field}`]"
@update:model-value="handleDate($event, item.field)"
></el-input>
</template>
// 判断表单类型
<template v-else-if="item.type === 'select'">
// 绑定表单属性 设置双向绑定
<el-select
style="width: 100%"
v-bind="item.otherOptions"
:placeholder="item.placeholder"
:model-value="modelValue[`${item.field}`]"
@update:model-value="handleDate($event, item.field)"
><el-option
v-for="option in item.options"
:key="option.value"
:value="option.title"
:model-value="modelValue[`${item.field}`]"
@update:model-value="handleDate($event, item.field)"
>
{{ option.title }}</el-option
></el-select
></template
>
<template v-else-if="item.type === 'datepicker'">
<el-date-picker
style="width: 100%"
v-bind="item.otherOptions"
:model-value="modelValue[`${item.field}`]"
@update:model-value="handleDate($event, item.field)"
></el-date-picker
></template>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
</div>
// 下面插槽
<div class="footer"><slot name="footer"></slot></div>
</template>
<script setup lang="ts">
import type { IFormItem } from '../type'
const props = withDefaults(
defineProps<{
formItems: IFormItem[]
labelWidth?: any
itemStyle?: any
colLayout?: any
modelValue: any
}>(),
{
formItems: () => [],
labelWidth: '200px',
itemStyle: () => ({ padding: '10px 20px' }),
colLayout: () => ({ span: 8 }),
modelValue: () => ({})
}
)
const emit = defineEmits(['update:modelValue'])
const handleDate = (value: any, field: any) => {
emit('update:modelValue', { ...props.modelValue, [field]: value })
}
</script>
第四步:传配置
传配置
// users.vue
<template>
<div class="user">
<pageSearch
:searchFormConfig="formConfig"
@reset-btn-click="getRetsetQuest"
@query-btn-click="getQueryQuest"
></pageSearch>
</template>
<script lang="ts" setup>
import { formConfig } from './config/search-config'
import pageSearch from '@/components/page-search'
const getRetsetQuest = () => {
// 网络请求代码
}
const getQueryQuest = (queryInfo: any) => {
// 网络请求代码
}
</script>
接配置
// page-search.vue
<template>
<div class="page-search">
<HyForm v-bind="searchFormConfig" v-model="formDate">
<template #header>
<h1 class="header">高级检索</h1>
</template>
<template v-slot="footer">
<div class="handle-btns">
<el-button @click="handleResetClick"
><el-icon class="icon"><refresh /></el-icon>重置</el-button
>
<el-button type="primary" @click="handlequeryClick"
><el-icon class="icon"><Search /></el-icon>搜索</el-button
>
</div>
</template>
</HyForm>
</div>
</template>
<script setup lang="ts">
import HyForm from '@/base-ui/form/index'
import { ref } from 'vue'
const props = withDefaults(
defineProps<{
searchFormConfig: any
}>(),
{}
)
const emit = defineEmits(['resetBtnClick', 'queryBtnClick'])
// 拿到配置的表单数组
const formItems = props.searchFormConfig.formItems ?? []
const formDateOrigin: any = {}
//
for (const data of formItems) {
formDateOrigin[data.field] = ''
}
// 收集表单数据
const formDate = ref(formDateOrigin)
// 重置
const handleResetClick = () => {
formDate.value = formDateOrigin
emit('resetBtnClick')
}
// 搜索,传递表单数据进行搜索
const handlequeryClick = () => {
emit('queryBtnClick', formDate.value)
}
</script>
**写在最后
文中封装的代码中涉及不少细节,包括组件中使用v-model,如何遵守单向数据流原则......
但本篇文章目的目的在于分享封装思路,一些细节就不多加赘述,后面会更新文章细说,小白刚开始写文章,有什么写的不好的,欢迎各位批评指正,一起讨论一起进步!加油**