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

本文介绍了如何在Vue.js项目中对列表页面的筛选器组件进行封装,通过维护一个数组对象,以页面路由作为唯一值存储在Vuex中。关键代码展示了如何使用动态组件渲染不同类型的筛选器(如SearchSelect和SearchInput),并利用Vuex进行数据同步,特别是处理远程搜索的下拉框数据。

在实际项目中很多列表页面的筛选器组件重复性很高,本文进行尝试性的封装,下方是一些伪代码,主要是记录一下自己做这个功能时的想法,可供大家参考,不足处欢迎补充。
注:都是手撸代码,部分标点符号不准确
主要思路:
1.每个列表使用一个数组对象来维护当前列表要使用的筛选器
2.以当前列表页面的route.path为唯一值
3.筛选器的数据存储在vuex里面,vuex中维护一个对象,键名为当前列表页的route.path
###列表页面中的关键代码:

<ListSearch :searchCom="searchCom" :routePath="$route.path"> // html直接用动态组件
import { getPlantList } '@/api'
// data中声明的变量
searchCom:[
     // 下拉框
     { key: 'createdBy', com: 'SearchSelect', options: { optionsKey: 'createdByOptions', title:'创建人', 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,
      SearchSelect 
    },
    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" 
     :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) 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('onChange', 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
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值