封装一个搜索区域 SearchForm.vue组件
父组件
<template><div><SearchForm:form-items="searchItems":initial-values="initialValues"@search="handleSearch"@reset="handleReset"><!-- 自定义插槽内容 --><template #custom-slot="{ form }"><el-inputv-model="form.customField"placeholder="自定义字段"></el-input></template><!-- 额外操作按钮 --><template #extra-actions><el-button type="warning" @click="handleExport">导出</el-button></template></SearchForm><!-- 其他页面内容 --></div>
</template><script>import SearchForm from '@/views/largeScreen/productionBusiness/components/searchForm.vue'export default {name: 'WorkOrderDetailList',components: { SearchForm },data() {return {initialValues: {name: '默认值',status: '1'},searchItems: [{label: '姓名',prop: 'name',type: 'input',placeholder: '请输入姓名'},{label: '状态',prop: 'status',type: 'select',options: [{ label: '启用', value: '1' },{ label: '禁用', value: '0' }]},{label: '创建日期',prop: 'createTime',type: 'date',dateType: 'daterange',valueFormat: 'yyyy-MM-dd'},{label: '自定义字段',prop: 'customField',type: 'slot',slotName: 'custom-slot'}]}},methods: {handleSearch(params) {console.log('搜索参数:', params)// 调用API获取数据},handleReset() {console.log('表单已重置')},handleExport() {console.log('导出操作')}}}
</script>
子组件
<template><div><el-formref="searchFormRef":model="formModel":inline="true":label-width="labelWidth"class="search-form"@submit.prevent="handleSearch"><!-- 动态渲染搜索表单项 --><template v-for="(item, index) in formItems"><!-- 输入框 --><el-form-item:key="index"v-if="item.type === 'input' || !item.type":label="item.label":prop="item.prop"><el-inputv-model="formModel[item.prop]":placeholder="item.placeholder || `请输入${item.label}`"clearable@keyup.enter="handleSearch"/></el-form-item><!-- 选择器 --><el-form-itemv-else-if="item.type === 'select'":label="item.label":prop="item.prop"><el-selectv-model="formModel[item.prop]":placeholder="item.placeholder || `请选择${item.label}`"clearablefilterablestyle="width: 100%"><el-optionv-for="opt in item.options":key="opt.value":label="opt.label":value="opt.value"/></el-select></el-form-item><!-- 日期选择器 --><el-form-itemv-else-if="item.type === 'date'":label="item.label":prop="item.prop"><el-date-pickerv-model="formModel[item.prop]":type="item.dateType || 'date'":placeholder="item.placeholder || `请选择${item.label}`":value-format="item.valueFormat || 'yyyy-MM-dd'"clearable/></el-form-item><!-- 日期范围选择器 --><el-form-itemv-else-if="item.type === 'daterange'":label="item.label":prop="item.prop"><el-date-pickerv-model="formModel[item.prop]"type="daterange":start-placeholder="item.startPlaceholder || '开始日期'":end-placeholder="item.endPlaceholder || '结束日期'":value-format="item.valueFormat || 'yyyy-MM-dd'"clearable/></el-form-item><!-- 自定义插槽 --><el-form-itemv-else-if="item.type === 'slot'":label="item.label":prop="item.prop"><slot :name="item.slotName" :form="formModel"></slot></el-form-item></template><!-- 操作按钮区域 --><el-form-item class="search-form-actions"><el-button type="primary" @click="handleSearch">搜索</el-button><el-button @click="handleReset">重置</el-button><slot name="extra-actions"></slot></el-form-item></el-form></div>
</template><script>export default {name: 'SearchForm',props: {// 表单配置项formItems: {type: Array,required: true,validator: (value) => {return value.every((item) => {return item.prop && item.label})}},// 初始值initialValues: {type: Object,default: () => ({})},// 标签宽度labelWidth: {type: String,default: '100px'}},data() {return {formModel: {}}},watch: {initialValues: {immediate: true,handler(val) {this.initFormModel(val)}}},methods: {// 初始化表单数据initFormModel(initialValues) {const defaultModel = {}this.formItems.forEach((item) => {defaultModel[item.prop] = initialValues[item.prop] ?? null})this.formModel = defaultModel},// 搜索事件handleSearch() {this.$emit('search', this.formModel)},// 重置表单handleReset() {this.$refs.searchFormRef.resetFields()this.$emit('reset')},// 获取表单数据getFormData() {return { ...this.formModel }},// 设置表单数据setFormData(data) {Object.keys(data).forEach((key) => {if (this.formModel.hasOwnProperty(key)) {this.formModel[key] = data[key]}})}}
}
</script><style lang="scss" scoped>
.search-form {margin-bottom: 20px;padding: 20px;background: #f5f7fa;border-radius: 4px;&-actions {float: right;margin-right: 0;}
}
</style>