vue项目基于element动态封装列表筛选器

在实际项目中很多列表页面的筛选器组件重复性很高,本文进行尝试性的封装,下方是一些伪代码,主要是记录一下自己做这个功能时的想法,可供大家参考,不足处欢迎补充。
注:都是手撸代码,部分标点符号不准确
主要思路:
1.把常用的筛选项抽出来进行封装,如input输入框,select下拉框,date日期选择器
2.每个列表使用一个数组对象来维护当前列表要使用什么筛选器
3.以当前列表页面的route.path为唯一值,在vuex里面维护一个object对象,动态的设置对象中的ke值,如object[route.path]
4.不同页面筛选器的数据存储在vuex里的object[route.path],从列表点进去详情的时候再返回列表可以保存上次的筛选条件

常用的筛选器

SearchSelect:下拉框
SearchInput:输入框
SearchDate:日期选择器

列表页面中的关键代码:

ListSearch组件就是封装后的全局筛选器
首先在页面引入ListSearch组件,需要下拉框实时搜索的页面,在对应页面import引入api,然后在searchCom中对应项的options属性中加入remoteMethod属性,把要用到的api传进去,在SearchSelect组件中正常的调用接口拿数据就好了
searchCom中每一项:
key:调用后端接口要传的参数,也是vuex中存的参数
com:要使用的对应组件,本文只用了三种筛选项组件,可根据业务场景增加
options:有些组件需要传一些配置文件,如是否多选,是否需要远程实时搜索等等

<ListSearch :searchCom="searchCom" :routePath="$route.path"> // html直接用动态组件
import { getPlantList } '@/api'
// data中声明的变量
searchCom:[
     // 下拉框
     { key: 'createdBy', com: 'SearchSelect', options: { title:'创建人', optionKey: 'createdByOptions',  plactholder: '请选择创建人', multiple: ture } },
     // 下拉框远程实时搜索
     { key: 'plant', com: 'SearchSelect', options: { optionsKey: 'plantOptions', title:'工厂', plactholder: '请选择工厂', multiple: ture, remoteMethod: getPlantList } }, // 直接把当前页面引入的api传进去
     // input输入框
     { key: 'infomation', com: 'SearchInput', options: { title: '信息', plactholder: '请输入信息'  } },
]

ListSearch组件中的关键代码:

<template>
  <div class="search-box">
    <component v-for="item in searchCom"
     v-bind="$attrs" 
     v-on="$listeners"
     :key="item.key" 
     :valueKey="item.valueKey" 
     :options="item.options" 
     :routePath="routePath" 
     :is="item.com"/>
     <!-- 这里可以放搜索按钮 -->
  </div>
</template>
// 这里引入要用到的组件,以这两个为案例
import SearchSelect from './components/SearchSelect'
import SearchInput from './components/SearchInput'
props:{
  // 要渲染的筛选项
  searchCom:{
    type: Array,
    defualt: () => []
  },
  routePath:{
    type: String,
    defualt: () => this.$route.path
  }
},
data(){
  return {
    comMap:{
      SearchSelect,
      SearchInput
    },
    getOptions: [] // 当前筛选器要进行提前调用下拉框数据的接口
  }
},
created() {
  this.$store.commit('listSearch/setDefultRoutePath') // 首先把当前路由告诉vuex
  this.getOptions = []
  // 循环传过来的searchCom参数,渲染组件
  for(let i = this.searchCom.length - 1; i >= 0; i--) {
    this.renderCom(this.searchCom[i].com)
    if(this.searchCom[i].options?.optionsKey) {
      // 如果需要调用下拉框数据
      this.getOptions.push(this.searchCom[i].options?.optionsKey)
    }
  }
},
mounted() {
  this.getOptionsList()
}
methods:{
  // 动态渲染组件
  renderCom(com) {
    // 手动给当前实例挂载组件
    this.constructor.component(com, () => {
      return {
        loading: comLoading, // 加载组件时loading效果,自己随便配置
        component: new Promise((resolve) => {
          resovle(this.comMap[com])
        })
      }
    })
  },
  getOptionsList() {
    // 子组件加载完后,在mounted方法中调用此方法,获取筛选钱中所有的下拉框数据
    this.$store.dispatch('listSearch/getSearchOptions',  this.getOptions)
  }
}
<style lang="less" scoped>
   // 样式自适应
  .search-box{
    display: grid;
    grid-template-columns: repeat(4, 25%);
    gap:12px 0;
  }
<style/>

SearchSelect组件的关键代码:

<template>
  <div class="search-item">
    <div class="title">{{options.title}}</div>
    <el-select v-module="currentValue"
     @change="onChange" 
     :clearable="options.clearable || true"
     :filterable="options.filterable || true" 
     :collapse-tags="options.collapseTags || true" 
     :multiple="options.multiple || false" 
     :remote=method="val => remoteMethod(val, options)" // 通过父组件直接把api传过来
     :remote="options.remoteMethod?ture:false" 
     :placeholder="item.placeholder || '请选择'" 
     :loading="loading">
      <el-options v-for="item in optionsList"
        :key="item.label + item.value"
        :label="item.label" // 默认就是value和label,可以不用写这两行,只要保证options都有这两个属性就可以了,如果没有,下方还有进行转换的配置属性
        :value="item.value"
      />
     </el-selec>
  </div>
</template>
import {mapState} from 'vuex'
props:{
   // 当前组件绑定的key值,也是后续进行接口搜索时传给后端的值
  valueKey:{
    type: String,
    defualt: ''
  },
  options:{
    type: Object,
    defualt: () => ({})
  },
  routePath:{
    type: String,
    defualt: this.$route.path
  },
},
data(){
  return {
    currentValue: '', // 当前组件维护的module
    returnValue: {}, // return出去的值,暂时没什么用,可以直接从vuex通过路由path来取
    optionsList: [], // 当前组件下拉框数据
    loading: false // 远程搜索时的loading
  }
},
compunted: {
  ...mapState({
    allOptions: state => state.listSearch.allOptions, // 所有的下拉框数据
    optionsUpdate: state => state.listSearch.optionsUpdate, // 更新下拉框数据时
    listSearchMap: state => state.listSearch.listSearchMap // 当前页面绑定的vuex对象
  })
},
watch: {
  currentValue: {
    handler(val){
      if(this.valueKey){
        this.$set(this.returnValue, `${this.valueKey}`, val)
      }
    }
  },
  listSearchMap: {
    handler(val){
      // vuex中的对象更新了,并且对象键名===当前路由path,更新的key值和当前页面绑定的valueKey一样,就刷新当前组件的currentValue
      if(val[this.routePath][this.valueKey]){
        this.currentValue = val[this.routePath][this.valueKey]
      }
    },
    deep: true
  },
  optionsUpdate: {
    handler(val){
      if(this.options.remoteMethod || this.options.specifyOptions) reutrn // 如果是远程搜素或者是指定了下拉框数据,不需要通过此方法来更新下拉框数据
      this.optionsList = this.allOptions[this.options.optionsKey]
      // 如果需要转换下拉框数据
      if(this.options.transferOPtions){
        const {label,value} = this.options.transferOPtions
        const options = this.options.map(e=>({...e,{label:e[label],value:e[value]}}))
      }
    }
  }
}
methods: {
  remoteMethod(val,options){
    if(!val) return
    if(options.remoteMethods){
      this.loading = true
      // 让后端吧所有的下拉框接口统一,在接口成功返回后进行switch
      options.remoteMethods({search:val,pageNum:1,pageSize:50}).then(res=>{
        if(res.code === '200'){
          switch(this.options.optionsKey === 'plant'){
            // ...进行一些操作
          }
        }
      })
    }
  },
  onChange(val){
    // 根据当前的valueKey进行一些操作,某些操作可以放在vuex里,比如说实时更新vuex里对应routepath的值,比如a筛选项依赖b筛选项, 只有选了c筛选项才能选d筛选项之类的逻辑,这个看不同的业务场景,可以持续的迭代
    switch(this.valueKey){
      case 'plant':
        this.$store.commit('listSearch/plantChange')
        this.$store.commit('listSearch/setListSearchRoutePathKey', {plant:val,other1:[],other2:[]})// 选择plant的时候清空other1和other2逻辑,
        break
      // ...
    }
    this.$emit('listSearch/setListSearchMapKey', { [this.valueKey]: val})
  }
}

SearchInput的关键代码:

// 跟SearchSelect差不多,逻辑更简单,参考着写吧

vuex的关键代码:

const currentState= {
  listSearchMap: {},// 所有列表筛选器的储存对象,动态添加routePath属性
  currentPath: '',// 当前路由信息,每次新页面加载筛选器的时候先更新这个变量 
  originAllOptions: {},// 保留原始的下拉框对象,看每个人不同的业务场景
  allOptions: {},// 组件要用到的下拉框数据
  optionsUpdate: 0 // 更新下拉框数据的时候改变此数值,让筛选器里面的组件通过watch监听此变量,完成实时更新
}
const mutations ={
  setListSearchMapPathKey(state,payload){
    if(!state.listSearchMap[state.currentPath]){
      // 以页面路由为key设置listSearchMap ,用object.assign方法,可以让listSearchMap 中的新的属性可以双向绑定,类似于组件中的$set()的作用
      state.listSearchMap = Object.assign({},state.listSearchMap ,{[state.currentPath]:[]})
    }
    state.listSearchMap[state.currentPath] = Object.assign({},state.listSearchMap[state.currentPath] ,{...payload})
  },
    // 设置一些默认值,如当前筛选器的路由,筛选器的默认选项之类的
   setdefaultSearch(state, payload){
    const {routePath} = payload
    state.currentPath = routePath
   },
  plantChange(state, payload){
    // SearchSelect组件里面的change事件,这里根据不同业务场景不同写法
    // 如果涉及到了筛选器下拉框数据变更,就加下方这段代码
    // state.optionsUpdate++
  }
}
const actions= {
  getSearchOptions({state},payload){
    for(let i = 0;i<payload.length;i++){
      switch(payload[i]){
        case 'plant'{
        //比如plant需要获取下拉框数据,就在这里执行代码
        }
      }
    }
    // 等所有的下拉框数据都获取到后进行更新
    state.optionsUpdate++
  }
}
export default{
namespaced:true,
state:currentState,
mutations,
actions
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容