elementPlus-Form封装动态表单-[从零开始vue3+vite+ts+pinia+router4后台管理(8)
系列文章
从零开始vue3+vite+ts+pinia+router4后台管理(1)
从零开始vue3+vite+ts+pinia+router4后台管理(2)-页面布局
从零开始vue3+vite+ts+pinia+router4后台管理(3)-动态路由
从零开始vue3+vite+ts+pinia+router4后台管理(4)-导航标签栏和keep-alive缓存
从零开始vue3+vite+ts+pinia+router4后台管理(5)-二次封装表格1.0
从零开始vue3+vite+ts+pinia+router4后台管理(6)-全局自定义指令实现节流与防抖
什么才是完美的表格二次封装elementPlus表格?-从零开始vue3+vite+ts+pinia+router4后台管理(7)
bash
# 克隆项目 gitee地址
git clone https://gitee.com/3439/Spurs-Admin.git
# 进入项目目录
cd Spurs-Admin
# 安装依赖
npm install
# 本地开发 启动项目
npm run dev
1.前言
一个管理系统的表单肯定很多,但是很多表单都大同小异,可复用的表单,可以提升实际工作的效率,今天我们就是使用Vue3+Element plus 来实现动态表单,实现思路大致就是通过JSON配置,动态生成表单页面
当然还会有自定义插槽
,这样我们碰到特使情况可以灵活使用,以及表单校验
2.目录结构
bash
├── src
│ ├── components
│ │ └── SpursForm
│ │ └── formType.ts # 表单类型的配置,每个表单配置,整体表单的配置
│ │ └── index.vue # 表单二次封装的主要业务代码
│ ├── views # 页面
│ │ ├── simpleForm
│ │ └── formConfig.ts # 表单配置json
│ │ │ └── index.vue # 表单入口
效果图
3.表单类型的配置,每个表单配置,整体表单的配置
具体每个字段的意思可以看字段后面的注释
typescript
type FormType =
|'input'
| 'password'
| 'select'
| 'datepicker'
| 'timepicker'
| 'switch'
| 'radio'
| 'textarea'
interface ItemOption {
label: string
value: string | number
}
export interface FormItem {
field: string //字段名
type?: FormType //输入框类型
label: string //输入框标题
colSpan?: number// 栅格占据的列数默认24
disabled?:boolean//表单是否可修改 默认false
placeholder?: any //输入框默认显示内容
prop?: string //表单校验
options?: ItemOption[] //选择器的可选子选项 select
otherOptions?: any//特殊情况
isHidden?: boolean
slotName?: string//处理一些自定义内容
}
export interface FormOption {
formItems: FormItem[]
labelWidth?: string//标签的长度
}
4.表单配置json
typescript
import {FormOption} from "@/components/SpursForm/formType.ts";
export const formConfig: FormOption = {
formItems: [
{
field: 'id',
type: 'input',
label: '用户id',
placeholder: '请输入用户id',
colSpan:9,
prop:"id"
},
{
field: 'account',
type: 'input',
label: '用户名',
disabled:true,
placeholder: '请输入用户名'
},
{
field: 'realname',
type: 'input',
label: '真实姓名',
placeholder: '请输入真实姓名'
},
{
field: 'cellphone',
type: 'input',
label: '电话号码',
placeholder: '请输入电话号码'
},
{
field: 'enable',
type: 'select',
label: '用户状态',
placeholder: '请选择用户状态',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
},
{
field: 'createAt',
type: 'datepicker',
label: '创建时间',
otherOptions: {
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
type: 'daterange'
}
},
{
field: 'special',
slotName:'special',
label: '自定义内容',
colSpan:12
},
{
field: 'special2',
slotName:'special2',
label: '自定义内容2',
colSpan:12
},
],
labelWidth: '120px'//标签的长度
}
5.表单二次封装的主要业务代码
html
<template>
<div class="header">
<slot name="header"> </slot>
</div>
<el-form
ref="ruleFormRef"
:label-width="labelWidth"
status-icon
:model="modelValue"
v-bind="$attrs"
>
<el-row>
<template v-for="item in formItems" :key="item.label">
<el-col :span="item.colSpan??24">
<el-form-item
v-if="!item.isHidden"
:label="item.label"
:prop="item.field"
>
<template v-if="item.type === 'input' || item.type === 'password'">
<el-input
:disabled="item.disabled??false"
:placeholder="item.placeholder"
:show-password="item.type === 'password'"
v-model="modelValue[`${item.field}`]"
clearable
/>
</template>
<template v-else-if="item.type === 'select'">
<el-select
:placeholder="item.placeholder"
v-model="modelValue[`${item.field}`]"
style="width: 100%"
clearable
>
<el-option
v-for="option in item.options"
:key="option.value"
:value="option.value"
:label="option.label"
>
</el-option>
</el-select>
</template>
<template v-else-if="item.type === 'datepicker'">
<el-date-picker
unlink-panels
value-format="YYYY-MM-DD"
v-bind="item.otherOptions"
v-model="modelValue[`${item.field}`]"
></el-date-picker>
</template>
<template v-if="item.slotName!=undefined">
<slot :name="item.slotName"></slot>
</template>
</el-form-item>
</el-col>
</template>
</el-row>
<el-form-item>
<slot name="footer"></slot>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import {FormItem} from "@/components/SpursForm/formType.ts";
import { ref } from 'vue'
import type { FormInstance } from 'element-plus'
// 定义属性
interface Props {
formItems: FormItem[] // 表单配置项
labelWidth?: string // 每个表单标题宽度
modelValue: object //绑定表单的每个数据
}
const props = withDefaults(defineProps<Props>(), {
formItems: () => [],
})
const ruleFormRef = ref<FormInstance>()
defineExpose({
ruleFormRef
})
</script>
获取表单配置json配置的json后通过表单类型去生成不同类型的表单
6.表单入口文件
html
<template>
<div class="role-form">
<spurs-form
ref="spursFormRef"
v-bind="formConfig"
:modelValue="modelValue"
:rules="rules"
>
<template #header>
<div class="header">
<h1>我是头部</h1>
</div>
</template>
<template #special>
<div class="special">
我是自定义内容
</div>
</template>
<template #special2>
<div class="special2">
我是自定义内容2
</div>
</template>
<template #footer>
<el-button type="primary" @click="submitForm(spursFormRef.ruleFormRef)">确 定</el-button>
<el-button @click="resetClick">取 消</el-button>
</template>
</spurs-form>
</div>
</template>
<script setup lang="ts">
import SpursForm from '@/components/SpursForm/index.vue'
import {onMounted, reactive, ref} from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import {formConfig} from './formConfig'
const modelValue = reactive({
id: '12',
account: '用户名1',
realname: '张三',
})
// console.log(formConfig.formItems[4].options);
onMounted(() => {
// formConfig.formItems[4].options = [];
// console.log("222",formConfig);
})
const spursFormRef = ref <any> ()
const rules = reactive<FormRules>({
id: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
createAt: [
{ type: 'date', required: true, message: '请选择日期', trigger: 'change' }
]
})
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 resetClick = () => {
formConfig.formItems[4].options = [];
console.log(formConfig);
console.log(modelValue);
}
7.表单宽度间距
表单整体的宽度我们引用的时候可以在外层div自己去定义 ,表单每一项的宽度用的是elementplus自带的24三个栅格colSpan?: number// 栅格占据的列数默认24
默认是24用的时候只需在json里面配置就行了colSpan:9,
然后页面上
这样表单的整体宽度 和每个单项表单的宽度我们都可以灵活布局
8. 表单的数据
表单的数据分两种 一种是页面进来我们需要赋值给表单 一种是 select选择框下拉的options数据等,我们都可以在页面进来是通过后来接口或者自己去定义比如
typescript
const modelValue = reactive({
id: '12',
account: '用户名1',
realname: '张三',
});
onMounted(() => {
// formConfig.formItems[4].options = [];
//modelValue
//去改变modelValue或者去改变formConfig的值
})
8.自定义情况的的处理
一个封装如果没有对自定义情况的处理就不是一个好的封装,遇到自定义的情况,在json中定义slotName
json
{
field: 'special',
slotName:'special',
label: '自定义内容',
colSpan:12
},
然后页面中去处理自定义的情况
html
<template v-if="item.slotName!=undefined">
<slot :name="item.slotName"></slot>
</template>
当然整个表单也定义了 头部和底部的插槽,大家可以自定义去使用
9.继承原生elementPlus表单所有属性,方法,事件
使用原生elementPlus表单的所有属性,方法,事件等,可以用属性透传**v-bind="$attrs"
**来实现 vue官方文档透传 Attributes,也可以阅读文章vue3中的$attrs的使用来了解和使用它
比如我们想要用表单的尺寸控制size
属性我们直接使用
然后表格就变小了这就是vue3属性透传**v-bind="$attrs"
**的强大功能
10. 表单校验
elementplus使用的是async-validator表单校验 我们在每个el-form-item去定义prop
然后自己去定义rules规则
这里有个需要注意的地方我们的表单在子组件,使用在父组件我们需要把表单给暴露出去
html
<el-form
ref="ruleFormRef"
></el-form>
<script setup lang="ts">
import type { FormInstance } from 'element-plus'
</script>
defineExpose({
ruleFormRef
})
然后使用
html
<el-button type="primary" @click="submitForm(spursFormRef.ruleFormRef)">确 定</el-button>
//表单效验
<script setup lang="ts">
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 rules = reactive<FormRules>({
id: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
createAt: [
{ type: 'date', required: true, message: '请选择日期', trigger: 'change' }
]
})
</script>
欢迎点赞和收藏 关于二次表单的封装,如果你有更好的想法欢迎评论区交流!感谢