前言
我们在项目开发过程中遇到最多就是表单页面的开发,那么使用频率比较高的就是Form
组件,无论是vue
亦或者是react
,我们在项目中使用到UI库都会有Form
组件。多数情况下都是用到了Form
组件,我们先根据UI库或者其他类似的页面直接进行ctrl+c、v
操作,然后再进行修改,最终按照交互稿和视觉稿完成页面开发。这样做的弊端是,每次用到都需要重新写一遍,是不符合前端组件化的开发思想的。那么我们需要对Form
组件进行二次封装,避免重复的代码书写,提高开发效率,增强代码的可读性和可扩展性。
介绍
本篇文章分享的是基于Element Plus
的Form
组件的二次封装,为什么是Element Plus
呢?因为正好我的项目中用到的是此UI库,项目中我对Form
组件及逆行了二次封装,因为将这块的内容整理出来,输出一篇技术文档,希望对大家有帮助!UI库都是类似的,大家可以参照我的做法对其他UI库的Form
组件进行封装。
技术栈
Vue3
+Ts
+Element Plus
效果图
源码
// components/CForm/index.vue
<!--
@author: duanfc
@time: 2024-08-16 10:00:00
@description: 描述
@path: /demo
@lastChange: duanfc
-->
<template>
<el-form v-bind="$attrs" ref="elFormRef" class="el-form-style">
<el-row :gutter="16">
<slot name="formItem"></slot>
</el-row>
</el-form>
</template>
<script lang="ts">
import { ref } from "vue";
import type { ElForm } from "element-plus";
export default {
name: "CForm",
props: {},
components: {},
setup(props, context) {
const elFormRef = ref<InstanceType<typeof ElForm> | null>(null);
return {
elFormRef,
};
},
};
</script>
<style lang="scss" scoped>
.el-form-style {
width: calc(100% - 32px);
margin-left: 16px;
border-bottom: 1px solid #f0f0f0;
.search-item:last-child {
padding-right: 0 !important;
}
.search-item:first-child {
padding-left: 0 !important;
}
}
// 屏幕宽度小于992px
@media only screen and (max-width: 992px) {
.search-item {
padding: 0 !important;
}
}
// 屏幕宽度大于等于992px但小于1200px
@media screen and (min-width: 992px) and (max-width: 1200px) {
.search-item:nth-child(3n) {
padding-right: 0 !important;
}
.search-item:nth-child(3n + 1) {
padding-left: 0 !important;
}
}
// 屏幕宽度大于等于1200px
@media screen and (min-width: 1200px) {
.search-item:nth-child(4n) {
padding-right: 0 !important;
}
.search-item:nth-child(4n + 1) {
padding-left: 0 !important;
}
}
</style>
// components/CFormItem/index.vue
<!--
@author: duanfc
@time: 2024-08-16 10:00:00
@description: 描述
@path: /demo
@lastChange: duanfc
-->
<template>
<el-col
:md="baseSpan * span"
:lg="baseSpan * span"
:xl="baseSpan * span"
:class="[isBtn ? 'btn-style' : '', 'search-item']"
:offset="offset"
v-show="formItemStatus"
>
<el-form-item v-bind="$attrs"><slot></slot></el-form-item>
</el-col>
</template>
<script lang="ts">
import { computed, ref } from "vue";
import { useResize } from "@/layout/hooks/useResize";
export default {
name: "cFormItem",
components: {},
props: {
span: {
type: Number,
default: 1,
},
isBtn: {
type: Boolean,
default: false,
},
isExpand: {
type: Boolean,
required: true,
},
colTotal: {
type: Number,
default: 1,
},
itemIndex: {
type: Number,
required: true,
},
},
setup(props, context) {
const baseSpan = ref(0);
// 计算按钮的offset值
const calculateOffset = (
val: number,
colTotalValue: number,
status: boolean
): number => {
if (val) {
// 每一个筛选项的份数
// 每行可放表单项的数量
const itemSpan = 24 / val;
// 最后一行筛选项的个数
const remainder = colTotalValue % itemSpan;
if (status) {
// 展开状态
if (screenWidth.value >= 992) {
if (colTotalValue == 1) return 0;
return (itemSpan - remainder - 1) * val;
} else if (screenWidth.value < 992) {
return 0;
}
// if (screenWidth.value >= 1200) {
// if (colTotalValue == 1) return 0;
// return (itemSpan - remainder - 1) * val;
// } else if (screenWidth.value >= 992) {
// if (colTotalValue == 1) return 0;
// return (itemSpan - remainder - 1) * val;
// } else if (screenWidth.value < 992) {
// return 0;
// }
} else {
// 收起状态
const itemSpan = 24 / val;
const overNum = colTotalValue - itemSpan;
if (screenWidth.value >= 1200) {
if (colTotalValue < 4) {
return (itemSpan - colTotalValue - 1) * val;
}
return overNum >= 3
? 0
: (itemSpan - overNum - 1) * val;
} else if (screenWidth.value >= 992) {
return overNum >= 2 || colTotalValue < itemSpan
? 0
: (itemSpan - overNum - 1) * val;
} else if (screenWidth.value < 992) {
return 0;
}
}
}
};
const screenWidth = ref(0);
// 表单项的显示与隐藏
const formItemStatus = computed(() => {
let bool = true;
if (!props.isExpand) {
if (screenWidth.