【a-form-model+vue2封装搜索框,最终版】

概要

当使用a-form-model组件进行表单数据绑定时,可以使用自定义组件封装一个通用的表单组件,将其作为一个单独的模块进行复用。以下是对给定代码的详细解析

整体架构流程

  1. 在模板部分,使用a-form-model包裹表单元素,并在其ref属性设置为"ruleForm",以方便在代码中对该表单进行操作。

  2. 使用a-rowa-col组件来布局表单项,通过v-for指令循环渲染formItems中的每个表单项。

  3. 对于每个表单项,使用a-form-model-item组件来包裹该项,并通过传递属性来设置该项的验证规则和其他属性。该项的组件类型由item.type决定,通过componentMap对象进行映射。

  4. 在模板中使用component标签来动态渲染相应的组件,并通过v-model指令绑定formInline对象中对应的属性。

  5. 在事件处理器中,handleDateChange方法用于处理日期选择器的改变事件,并将选择的日期值存储到formInline对象中。

  6. handleSubmit方法用于处理表单的提交事件,通过验证表单的合法性后,将当前表单的值通过$emit方法发送给父组件。

  7. handleReset方法用于重置表单,通过resetFields方法将表单重置为初始状态。

  8. created生命周期钩子中,遍历formItems数组,并根据具体的表单项类型进行初始化操作,如设置默认选中第一项。

  9. 在样式部分,使用scoped属性将样式限定在当前组件内部。

用到组件

  • ant-design-vue
  • vue2

技术细节

组件如下,增加了高级搜索功能,目前是最多展示六个,超过六个展示高级搜索按钮

<template>
  <div class="uniform-search-form">
    <a-form-model
      ref="ruleForm"
      layout="inline"
      :model="formInline"
      @submit="handleSubmit"
      @submit.native.prevent
      class="form-container"
      :label-col="{ span:6 }"
      :wrapper-col="{span:18}"
    >
        <a-row>
          <a-col
            :xs="24"
            :sm="12"
            :md="8"
            :lg="8"
            :xl="8"
            :span="8"
            v-for="item in visibleFormItems"
            :key="item.key"
          >
            <a-form-model-item
              :ref="item.model"
              :rules="item.rules"
              :label="item.label"
              :prop="item.model"
            >
            <template>
              <component
                style="width: 100%;"
                :is="componentMap[item.type]"
                v-model="formInline[item.model]"
                v-bind="{ ...item.componentProps }"
                :placeholder="item.placeholder"
                @change="handleDateChange"
                @calendarChange="getmodel(item.model)"
              />
              </template>
            </a-form-model-item>
          </a-col>
          <a-col  v-if="formItems.length<=1" :xs="24"
            :sm="12"
            :md="8"
            :lg="8"
            :xl="8"
            :span="8">
            <a-button style="margin-right: 10px;" type="primary" icon="search" @click="handleSubmit()">
                查询
            </a-button>
            <a-button icon="undo" @click="handleReset()">
                重置
            </a-button>
          </a-col>
          <div v-else style="text-align: right; padding-right: 20px;">
             <a-button style="margin-right: 10px;" type="primary" icon="search" @click="handleSubmit()">
                查询
            </a-button>
            <a-button icon="undo" @click="handleReset()">
                重置
            </a-button>
	       <a-button v-if="formItems.length>6" style="margin-left: 10px;" @click="toggleComplexSearch">
	       {{ complexSearchVisible ?'收起搜索':'高级搜索'}}
	</a-button>
          </div>
        </a-row>
        </a-form-model>
      </div>
</template>

<script>
export default {
  props:{
   formItems:{
      type:Array,
      required:true
   }
  },
  data() {
    return {
      complexSearchVisible: false, // 控制复杂搜索的展示状态
      visibleFormItems: [], // 当前显示的搜索框
      formInline: {},
      model:'',

     /*   //formItems从父组件传过来 格式如下
    formItems: [
        // 表单项数据
        {
          key: 'username',
          model: 'username',
          label: '用户名',
          type: 'input',
          rules: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
          componentProps: {}
        },
        // 其他表单项
      ],
      */
      componentMap: {
        input: 'a-input',
        select:'a-select',
        date:'a-date-picker',
        number:'a-input-number',
        autoComplete:'a-auto-complete',
        treeSelect:'a-tree-select',
        time:'a-time-picker',
        rangePicker:'a-range-picker'
        // 其他组件映射
      }
    };
  },
  computed:{
    isFormValid(){
       return Object.values(this.formInline).every((value)=> value !== '')
    }
  },
  watch:{
  formItems: {
      handler(newVal) {
        this.updateVisibleFormItems();
      },
      immediate: true
    }
   },
  methods: {
   toggleComplexSearch() {
      this.complexSearchVisible = !this.complexSearchVisible;
      this.updateVisibleFormItems();
    },
    updateVisibleFormItems() {
      if (this.complexSearchVisible || this.formItems.length <= 6) {
        this.visibleFormItems = this.formItems;
      } else {
        this.visibleFormItems = this.formItems.slice(0, 6);
      }
    },
    getmodel(model){
      this.model = model
    },
    handleDateChange(dates,dateStrings){
      if(dates && dates.length===2){
        const startDate = dateStrings[0]
        const endDate = dateStrings[1]
        this.formInline[this.model]= [startDate,endDate]
      }
    },
    handleSubmit(e) {
      this.$refs.ruleForm.validate(valid => {
        if (valid) {
          alert('提交成功!');
          //这里就是表单的值 传到父组件去作为搜索条件
          this.$emit('submit',this.formInline)
        } else {
          console.log('验证失败');
          return false;
        }
      });
    },
    handleReset(){
      this.$refs.ruleForm.resetFields()
      this.formInline={}
    }
  },
  created(){
    this.formItems.forEach((item) => {
        if (item.type==='select'&& item.isSelect) {
            //设置默认选中第一项
            this.$set(this.formInline,item.model,item.componentProps.options[0].value)
        }
        
    });
    this.updateVisibleFormItems();
  }
};
</script>

<style scoped lang="less">
.uniform-search-form{
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}
.form-container{
   width: 100%; 
}
</style>

直接引入使用

主要就是不同type需要传递什么searchFormItems参数,具体如下

<template>
  <uniform-search-form  :formItems="searchFormItems" @submit="onSubmit"></uniform-search-form>
</template>

<script>
import UniformSearchForm from './UniformSearchForm.vue';

export default {
  components: {
    UniformSearchForm
  },
  data() {
    return {
      searchFormItems: [
     // 输入框
		{
		  key: 'username',//作为唯一值,请与model保持一致,不写也不会报错
		  model: 'username',//这个是字段的名字,搜索参数直接传递给后台的,跟之前的v-decorator/v-model一样
		  label: '用户名',//左侧label值
		  type: 'input',//输入框类型
		  placeholder: '请输入用户名',
		  rules: [{ required: true, message: '请输入用户名', trigger: 'blur' }],//校验
		  componentProps: { allowClear: true }//这个里面写输入框的各个属性,具体可参考下面案例
		}
		
		// 下拉选择框
		{
		  key: 'city',
		  model: 'city',
		  label: '城市',
		  type: 'select',
		  placeholder: '请选择城市',
		  isSelect:true,//默认展示下拉搜索第一项
		  componentProps: {
		    options: [
		      { value: 'beijing', label: '北京' },
		      { value: 'shanghai', label: '上海' },
		      // 其他选项...
		    ],//这里就是下拉选项
		    allowClear: true
		  },
		  rules: [{ required: true, message: '请选择城市', trigger: 'change' }]
		}
		
		// 日期选择器
		{
		  key: 'startDate',
		  model: 'startDate',
		  label: '开始日期',
		  type: 'date',
		  placeholder: '请选择开始日期',
		  rules: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
		  componentProps: { format: 'YYYY-MM-DD' }
		}
		
		// 日期范围选择器
		{
		  key: 'dateRange',
		  model: 'dateRange',
		  label: '日期范围',
		  type: 'RangePicker',
		  placeholder: ['开始日期', '结束日期'],
		  rules: [{ required: true, message: '请选择日期范围', trigger: 'change' }],
		  componentProps: { format: 'YYYY-MM-DD' }
		}
      
    };
  },
  methods: {
    onSubmit(formData) {
      // 处理表单提交
      //这里可以直接调用搜索方法 formData即传递出的参数,如果需要特殊处理 处理具体表单项即可!
      console.log('表单数据:', formData);
    }
  }
};
</script>

小结

本组件实现了搜索按钮一直居右展示 占三分之一 只有一个搜索框让搜索按钮紧跟着搜索框,超过六个搜索框就会有高级搜索按钮

最终效果:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

### Vue2 中搜索条件与表格组件分离封装的最佳实践 在 Vue2 开发中,为了提高代码的可维护性和一致性,通常会对重复使用的功能模块进行封装。对于带有搜索条件的表格场景,可以采用以下方式实现最佳实践。 #### 1. 封装独立的搜索组件 通过创建一个单独的 `SearchForm` 组件来管理所有的搜索逻辑和 UI 层面的内容。该组件可以通过 Props 接收配置项,并返回经过验证后的查询参数给父级组件[^1]。 ```vue <template> <el-form :model="form" @submit.native.prevent="$emit('search', form)"> <!-- 动态渲染表单项 --> <el-row :gutter="20"> <el-col v-for="(item, index) in fields" :key="index" :span="6"> <el-form-item :label="item.label"> <component :is="'el-' + item.type" v-model="form[item.prop]" v-bind="item.attrs" ></component> </el-form-item> </el-col> </el-row> <el-button type="primary" native-type="submit">查询</el-button> <el-button @click="resetForm">重置</el-button> </el-form> </template> <script> export default { props: { fields: { type: Array, required: true }, // 定义字段结构 [{ label: '名称', prop: 'name', type: 'input' }] }, data() { return { form: {} }; }, methods: { resetForm() { this.form = {}; this.$emit('search', {}); } } }; </script> ``` #### 2. 使用 Vuex 存储状态并同步数据 当涉及到复杂的状态管理和跨组件通信时,推荐引入 Vuex 来集中存储全局变量以及共享的数据流。例如,在调整窗口大小或者切换不同视图的情况下保持 table 的高度一致[^2]。 ```javascript // store/modules/table.js const state = () => ({ tableHeight: window.innerHeight - 200, }); const mutations = { SET_TABLE_HEIGHT(state, height) { state.tableHeight = height; }, }; const actions = { updateTableHeight({ commit }) { const newHeight = window.innerHeight - document.querySelector('.header').offsetHeight; commit('SET_TABLE_HEIGHT', newHeight); }, }; export default { namespaced: true, state, mutations, actions }; ``` 然后可以在 App.vue 或者其他地方监听 resize 事件: ```javascript mounted() { window.addEventListener('resize', this.resizeHandler); }, beforeDestroy() { window.removeEventListener('resize', this.resizeHandler); }, methods: { resizeHandler() { this.$store.dispatch('table/updateTableHeight'); }, } ``` #### 3. 创建通用表格组件支持动态列定义 基于 Element-UI 提供的基础 Table 组件进一步扩展其能力,允许外部传入 columns 配置从而灵活定制显示效果[^3]。 ```vue <template> <div class="custom-table-container"> <el-table :data="listData" :height="tableHeight" border stripe style="width: 100%"> <el-table-column v-for="col in cols" :prop="col.prop" :label="col.label" :formatter="col.formatter" align="center"></el-table-column> </el-table> <pagination v-show="total>0" :total="total" :page.sync="query.pageNum" :limit.sync="query.pageSize" @pagination="fetchList"/> </div> </template> <script> import Pagination from '@/components/Pagination'; export default { components: { Pagination }, props: ['cols'], computed: { ...mapState({ tableHeight: (state) => state.table.tableHeight, }), }, data() { return { listData: [], total: 0, query: {}, }; }, created() { this.fetchList(); }, watch: { '$route'(to, from){ if(to.name === 'YourRouteName'){ this.resetQuery(); } } }, methods:{ fetchList(){} } }; </script> ``` 以上就是一种较为完整的解决方案框架,它不仅实现了业务需求还兼顾到了性能调优方面的工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端小百科

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值