Vue2 + Element UI 实战:手把手教你封装一个高复用的 SearchForm 搜索组件
前言 👋
在后台管理系统中,搜索区域(SearchForm) 几乎存在于每一个列表页中。
如果不加以封装,你的代码可能会变成这样:
<el-form :inline="true" :model="queryParams"> <el-form-item label="用户名"> <el-input v-model="queryParams.username" placeholder="请输入"></el-input> </el-form-item> <el-form-item label="状态"> <el-select v-model="queryParams.status" placeholder="请选择"> <el-option label="启用" value="1"></el-option> <el-option label="禁用" value="0"></el-option> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery">搜索</el-button> <el-button @click="resetQuery">重置</el-button> </el-form-item> </el-form>问题很明显:
😩 每个页面都要复制粘贴
😩 样式、布局难以统一
😩 新增/修改搜索项极其繁琐
今天,我们就来解决这个痛点,封装一个高内聚、低耦合的SearchForm组件!
一、设计目标 🎯
我们要封装的组件需要具备以下特性:
配置驱动:通过一个
config数组即可生成表单项。内置类型:支持
input、select、date、daterange等常用类型。自动双向绑定:无需手动
v-model。一键重置:内置重置逻辑。
高度可定制:支持传入
el-form-item的任意原生属性。
二、组件实现 (src/components/SearchForm/index.vue) ⚙️
这是我们的核心代码,请仔细看注释。
<template> <el-form ref="searchFormRef" :inline="true" :model="model" :label-width="labelWidth" class="search-form" > <template v-for="(item, index) in config"> <!-- 输入框 --> <el-form-item v-if="item.type === 'input'" :key="index" :label="item.label" :prop="item.prop" > <el-input v-model="model[item.prop]" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable ?? true" :style="{ width: item.width || '200px' }" /> </el-form-item> <!-- 下拉框 --> <el-form-item v-else-if="item.type === 'select'" :key="index" :label="item.label" :prop="item.prop" > <el-select v-model="model[item.prop]" :placeholder="item.placeholder || '请选择'" :clearable="item.clearable ?? true" :style="{ width: item.width || '200px' }" > <el-option v-for="opt in item.options" :key="opt.value" :label="opt.label" :value="opt.value" /> </el-select> </el-form-item> <!-- 日期选择器 --> <el-form-item v-else-if="item.type === 'date'" :key="index" :label="item.label" :prop="item.prop" > <el-date-picker v-model="model[item.prop]" :type="item.dateType || 'date'" :placeholder="item.placeholder || '请选择日期'" :value-format="item.valueFormat || 'yyyy-MM-dd'" :style="{ width: item.width || '200px' }" /> </el-form-item> </template> <!-- 操作区 --> <el-form-item class="search-buttons"> <el-button type="primary" icon="el-icon-search" @click="handleSearch"> 搜索 </el-button> <el-button icon="el-icon-refresh" @click="handleReset"> 重置 </el-button> <!-- 插槽,用于扩展按钮 --> <slot /> </el-form-item> </el-form> </template> <script> export default { name: 'SearchForm', props: { // 搜索配置数组 config: { type: Array, required: true, default: () => [] }, // 搜索参数对象 (v-model) model: { type: Object, required: true }, // label宽度 labelWidth: { type: String, default: '80px' } }, methods: { // 触发父组件的搜索事件 handleSearch() { this.$emit('search') }, // 重置表单 handleReset() { this.$refs.searchFormRef.resetFields() // 重置后自动触发一次搜索 this.$emit('search') } } } </script> <style scoped lang="scss"> .search-form { background-color: #fff; padding: 18px 20px 0; border-radius: 4px; margin-bottom: 16px; .search-buttons { margin-bottom: 18px; } } </style>三、如何使用?只需 3 步 🚀
Step 1:引入并注册组件
// 在页面组件中 import SearchForm from '@/components/SearchForm/index.vue' export default { components: { SearchForm } }Step 2:定义配置项和搜索参数
export default { data() { return { // 搜索参数,字段名需与 config 中的 prop 对应 queryParams: { username: '', status: '', dateRange: [] }, // 搜索配置 searchConfig: [ { label: '用户名', prop: 'username', type: 'input' }, { label: '状态', prop: 'status', type: 'select', options: [ { label: '启用', value: 1 }, { label: '禁用', value: 0 } ] }, { label: '创建时间', prop: 'dateRange', type: 'date', dateType: 'daterange', valueFormat: 'yyyy-MM-dd' } ] } } }Step 3:在模板中使用
<template> <div class="page-container"> <SearchForm :config="searchConfig" :model.sync="queryParams" @search="handleSearch" > <!-- 通过插槽添加额外按钮 --> <el-button type="success" icon="el-icon-download">导出</el-button> </SearchForm> <!-- 表格 --> <el-table :data="tableData"> <!-- ... --> </el-table> </div> </template> <script> export default { methods: { handleSearch() { console.log('开始搜索,参数为:', this.queryParams) // 调用 API 获取列表数据 // this.getList() } } } </script>四、进阶技巧:动态显隐与联动 🔗
1. 动态显示/隐藏表单项
利用v-if的特性,我们可以通过计算属性来控制config。
computed: { dynamicConfig() { const config = [...this.searchConfig] // 如果不是管理员,隐藏“所属部门”搜索项 if (!this.isAdmin) { return config.filter(item => item.prop !== 'deptId') } return config } }2. 表单联动(例如:选择了A,B才可选)
// 在 config 中添加 disabled 逻辑 { label: '子选项', prop: 'child', type: 'select', disabled: () => !this.queryParams.parentId // 利用函数返回 }五、总结 📝
通过封装SearchForm组件,我们将原本几十行的模板代码缩减到了几行配置。
封装的好处显而易见:
✅开发效率翻倍:新页面只需配置
config。✅风格高度统一:再也不会出现 A 页面搜索框宽 200px,B 页面宽 220px 的情况。
✅维护成本低:修改搜索逻辑只需改一处。
这种配置化的思想,是中级向高级前端进阶的必经之路。
