打造你的专属Vue组件:超实用“高级筛选弹窗组件“实战
在现代前端开发中,组件化思想是提高开发效率、维护性和代码复用性的关键。本文将通过一个实例——创建一个自定义的“高级筛选”弹窗组件,来展示如何在Vue框架下利用Composition API和Element Plus构建功能丰富且易于集成的UI组件。下面的代码示例不仅展示了组件的结构、样式定制,还涉及了事件处理和属性传递,是一个全面的组件开发教程。
组件效果
组件概览
我们所创建的“高级筛选”组件基于Element Plus的el-popover
,旨在为用户提供一个包含筛选条件输入、清除、提交和取消操作的交互界面。组件设计考虑到了外观自定义、事件触发方式的灵活性以及对父组件的通信支持。
组件代码解析
结构与绑定 (<template>
)
组件的模板部分定义了弹窗的结构,利用插槽(<slot>
)允许外部插入具体的筛选内容。el-popover
包裹了整个组件,并通过:title
、:trigger
、:width
等属性接收外部配置,确保组件的灵活性。值得注意的是,通过:teleported="false"
保持弹窗在DOM树中的位置不变,这在某些特定布局需求下非常有用。
逻辑处理 (<script setup>
)
- 引入依赖: 首先引入了自定义按钮组件
MButton
以及图标资源。 - Composition API: 使用
defineOptions
定义组件名,ref
管理弹窗实例,defineProps
和defineEmits
分别用来声明组件的属性和事件。 - 事件处理: 定义了
clearClick
、submitClick
和cancelClick
方法,用于响应清除、提交和取消操作,并通过emits
触发外部监听的事件。 - 暴露方法: 通过
defineExpose
对外暴露close
方法,使得外部可以控制弹窗的隐藏。
样式定制 (<style>
)
- SCSS: 利用SCSS进行了详细的样式定制,包括按钮的大小调整、图标定位、红色提示点的实现以及弹窗内部元素的布局和间距调整,确保组件的视觉效果与项目整体风格相协调。
实践要点
- 组件通信: 通过props接收外部传入的数据和配置,使用emits触发事件,实现了组件间的解耦与消息传递。
- 样式封装: 利用scoped样式保护组件内部CSS不污染全局,同时通过类名自定义实现了弹窗内容区域的样式扩展。
- 可维护性: 通过Composition API的组合式函数逻辑,使得组件内部状态和行为逻辑清晰,便于维护和测试。
- 灵活性与可扩展性: 通过插槽设计,允许组件在不同场景下承载多样的筛选表单内容,提高了组件的复用性和灵活性。
代码展示
<template>
<el-popover popper-class="searchadvanced" ref="setRemovePop" :title="title" :trigger="trigger" :teleported="false"
:width="width" v-bind="$attrs">
<template #reference>
<m-button type="primary" class="searchadvanced-icon-btn" :class="{ 'searchadvanced-badge': isDot }"
:icon="Filter"></m-button>
</template>
<slot></slot>
<div class="searchadvanced-operitoin">
<m-button class="clearbtn" type="primary" :icon="Delete" link @click="clearClick">清空</m-button>
<m-button class="opbtn cancelbtn" @click="cancelClick">取消</m-button>
<m-button class="opbtn submitbtn" type="primary" @click="submitClick">确认</m-button>
</div>
</el-popover>
</template>
<script setup lang="ts">
import MButton from '../button'
import { ref } from 'vue'
import { Delete, Filter } from '@element-plus/icons-vue'
defineOptions({
name: 'MSearchAdvanced'
})
const setRemovePop = ref<any>(null)
const prop = defineProps({
title: {
type: String,
default: '高级筛选',
},
trigger: {
type: String,
default: 'click',
},
width: {
type: [String, Number],
default: 900,
},
isDot: {
type: Boolean,
default: false,
},
})
const emits = defineEmits(['clear', 'submit', 'cancel'])
const clearClick = () => {
emits('clear')
}
const submitClick = () => {
setRemovePop.value.hide()
emits('submit')
}
const cancelClick = () => {
setRemovePop.value.hide()
emits('cancel')
}
const close = () => {
setRemovePop.value.hide()
}
defineExpose({
close,
})
</script>
<style lang="scss">
.searchadvanced-icon-btn {
display: inline-block !important;
line-height: 32px;
width: 32px;
font-size: 16px;
padding: 0 0 0 6px !important;
.el-icon {
font-size: 16px;
}
}
.searchadvanced-badge {
position: relative;
&::after {
content: '';
position: absolute;
width: 10px;
height: 10px;
background: red;
border: 2px solid #fff;
border-radius: 50%;
right: -5px;
top: -5px;
}
}
.searchadvanced {
&.el-popper {
padding: 24px;
background: #fff;
border-radius: 2px;
}
.el-popover__title {
margin-bottom: 26px;
}
&-operitoin {
display: flex;
justify-content: flex-end;
margin-top: 30px;
.opbtn {
width: 100px;
}
}
}
</style>