基于element-ui [ 搜索过滤条件 ] 组件

4,847 阅读3分钟

项目背景

日前在一些中后台项目中遇到到了类似如下图这样的需求,产品的列表页(还有其他的很多列表页面),首先就是一个大大的Table,然后页顶一个搜索栏,同时右边一个操作按钮(也可能是多个)每次都要去写各种方法去处理整个过滤栏的数据,很麻烦,写多了会吐的...

预览图

需求分析

搜索栏无非就是一些表单组件,常用的应该是Select(本地写死,或者远程拉取option),Input,单选多选等等,dom都可以用对象表示,我们为何不用一个json结构去描述呢?我们在点击搜索的时候我们希望得到的是一个以拼装好的json键值对结构,直接请求后端岂不快哉?赶紧写一波!

定义数据结构

每一个对象包括一个type(描述对象是什么组件),name(输出的表单key值),placeholder,async等属性,如下所示。

let filterBar = [
     {
      key: 1,
      type: 'select',
      async: true,
      searchType: 'vendor',
      placeholder: '请选择品牌',
      name: 'vendor_id'
    },
    {
      key: 2,
      type: 'select', 
      async: false,
      placeholder: '请选择状态',
      name: 'status',
      options: [
        { key: 1, label: '等待接入', value: 0 },
        { key: 2, label: '开发中', value: 1 },
      ]
    },
    {
      key: 3,
      type: 'input',
      async: false,
      placeholder: '请输入产品名称',
      name: 'keyword'
    }
]

开撸

我们期望的组件是这样子的:

 <Option :option="filterBar" @onSearch="onSearch" />

中首先我们先给它遍历一波,根据不容的type去渲染不同的组件:

<template>
  <div class="optionBar">
    <div class="leftBar">
      <template v-for="(item,index) in optionData">
        <el-input
          class="inputstyle"
          v-if="item.type=='input'"
          :key="item.key"
          :placeholder="item.placeholder"
          v-model="optionData[index].value"
          @input="onChange"
        ></el-input>
        <AsyncSelector
          v-if="item.type=='select' && item.async"
          :allOption="{optionData,connectName:optionData[index].connect || ''}"
          :key="item.key"
          :placeholder="item.placeholder"
          :searchType="item.searchType"
          :showCheckAll="item.showCheckAll"
          :defaultFetchKey="optionData[index].defaultfetch"
          v-model="optionData[index].value"
          :emitEvent="emitEvent"
          @onSelect="(value)=>onSelect({connectName:optionData[index].connect || '',index,value})"
          @changeDefaultfetch="changeDefaultfetch"
        />
        <el-select
          class="inputstyle option_margin"
          v-if="item.type=='select' && item.async != true"
          v-model="optionData[index].value"
          :placeholder="item.placeholder"
          :key="item.key"
          @change="onChange"
        >
          <el-option
            v-for="item in item.options"
            :key="item.key"
            :label="item.label"
            :value="item.value"
          ></el-option>
        </el-select>
      </template>
      <el-button type="primary" size="mini" icon="el-icon-search" round @click="onSearch">搜索</el-button>
    </div>
    <div class="rightBar">
      <Render :render="extra" />
    </div>
  </div>
</template>

然后我们的所有状态都在当前filterBar中维护,我们在组件created时候把传入的json描述对象给它添加到当前filterBar的数据中供template使用v-model等等

export default {
  props: {
    option: {
      type: Array,
      required: true
    },
    extra: {
      type: Function,
      required: false
    }
  },
  components: { AsyncSelector, Render },
  data: function() {
    return {
      optionData: []
    }
  },
  // 动态生成model
  created: function() {
    let length = this.option.length
    for (let i = 0; i < length; i++) {
      this.optionData.push({
        ...this.option[i],
        value:
          this.option[i].defaultValue === 0
            ? 0
            : this.option[i].defaultValue || '',
        defaultfetch: ''
      })
    }
  },
  mounted() {},
  methods: {
    onSelect: function({ connectName, index, value }) {
      // 当前下拉框选择后,查询与之联动查询的selectorName
      if (connectName !== '') {
        // 找出对应联合查询的seletorIndex
        let optionSelectorIndex = this.optionData.findIndex((v, k) => {
          return v.name === connectName
        })
        // 修改对应selector的默认选项,在AsyncSelector组建中观测defaultfetch改变,作为参数请求另外的selector的options
        this.optionData[optionSelectorIndex].defaultfetch = value
      }
      this.emitEvent()
    },
    onChange: function(e) {
      this.emitEvent()
    },
    onSearch: function() {
      this.$emit('onSearch', this.formatData())
    },
    // 格式化输出数据,转化为{key,value}形式
    formatData: function() {
      let searchData = {}
      if (this.optionData.length !== 0) {
        this.optionData.forEach(v => {
          searchData[v.name] = v.value
        })
      }
      return searchData
    },
    emitEvent: function() {
      let data = { ...this.formatData() }
      this.$emit('onChange', data)
    },
    changeDefaultfetch: function({ optionSelectorIndex, value }) {
      // 反馈下一个seletor的搜索参数
      this.optionData[optionSelectorIndex].defaultfetch = value
    }
  }
}

实现过程还是蛮艰辛的,毕竟随着项目的需求变动,一直在修修改改的,不过好在是完成了,想要做搜索过滤的地方,几行json就可以生成,太特么的爽了,对了那个异步select组件也是基于可远程获取数据的select组件只是用了一个searchType去区分到底是请求哪些数据,同时呢也支持多级联动查询,原理也就是通过vue的双向数据绑定以及组件内部的watch检测数据变动的原理去动态的改变相关联项select的搜索参数,从而达到联动的效果。