在實際項目中很多列表頁面的篩選器組件重復(fù)性很高,本文進行嘗試性的封裝线椰,下方是一些偽代碼簿寂,主要是記錄一下自己做這個功能時的想法股囊,可供大家參考蝉衣,不足處歡迎補充。
注:都是手擼代碼赏陵,部分標點符號不準確
主要思路:
1.把常用的篩選項抽出來進行封裝饼齿,如input輸入框,select下拉框蝙搔,date日期選擇器
2.每個列表使用一個數(shù)組對象來維護當(dāng)前列表要使用什么篩選器
3.以當(dāng)前列表頁面的route.path為唯一值缕溉,在vuex里面維護一個object對象,動態(tài)的設(shè)置對象中的ke值吃型,如object[route.path]
4.不同頁面篩選器的數(shù)據(jù)存儲在vuex里的object[route.path]证鸥,從列表點進去詳情的時候再返回列表可以保存上次的篩選條件
常用的篩選器
SearchSelect:下拉框
SearchInput:輸入框
SearchDate:日期選擇器
列表頁面中的關(guān)鍵代碼:
ListSearch組件就是封裝后的全局篩選器
首先在頁面引入ListSearch組件,需要下拉框?qū)崟r搜索的頁面勤晚,在對應(yīng)頁面import引入api枉层,然后在searchCom中對應(yīng)項的options屬性中加入remoteMethod屬性,把要用到的api傳進去赐写,在SearchSelect組件中正常的調(diào)用接口拿數(shù)據(jù)就好了
searchCom中每一項:
key:調(diào)用后端接口要傳的參數(shù)鸟蜡,也是vuex中存的參數(shù)
com:要使用的對應(yīng)組件,本文只用了三種篩選項組件挺邀,可根據(jù)業(yè)務(wù)場景增加
options:有些組件需要傳一些配置文件揉忘,如是否多選跳座,是否需要遠程實時搜索等等
<ListSearch :searchCom="searchCom" :routePath="$route.path"> // html直接用動態(tài)組件
import { getPlantList } '@/api'
// data中聲明的變量
searchCom:[
// 下拉框
{ key: 'createdBy', com: 'SearchSelect', options: { title:'創(chuàng)建人', optionKey: 'createdByOptions', plactholder: '請選擇創(chuàng)建人', multiple: ture } },
// 下拉框遠程實時搜索
{ key: 'plant', com: 'SearchSelect', options: { optionsKey: 'plantOptions', title:'工廠', plactholder: '請選擇工廠', multiple: ture, remoteMethod: getPlantList } }, // 直接把當(dāng)前頁面引入的api傳進去
// input輸入框
{ key: 'infomation', com: 'SearchInput', options: { title: '信息', plactholder: '請輸入信息' } },
]
ListSearch組件中的關(guān)鍵代碼:
<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: [] // 當(dāng)前篩選器要進行提前調(diào)用下拉框數(shù)據(jù)的接口
}
},
created() {
this.$store.commit('listSearch/setDefultRoutePath') // 首先把當(dāng)前路由告訴vuex
this.getOptions = []
// 循環(huán)傳過來的searchCom參數(shù)疲眷,渲染組件
for(let i = this.searchCom.length - 1; i >= 0; i--) {
this.renderCom(this.searchCom[i].com)
if(this.searchCom[i].options?.optionsKey) {
// 如果需要調(diào)用下拉框數(shù)據(jù)
this.getOptions.push(this.searchCom[i].options?.optionsKey)
}
}
},
mounted() {
this.getOptionsList()
}
methods:{
// 動態(tài)渲染組件
renderCom(com) {
// 手動給當(dāng)前實例掛載組件
this.constructor.component(com, () => {
return {
loading: comLoading, // 加載組件時loading效果,自己隨便配置
component: new Promise((resolve) => {
resovle(this.comMap[com])
})
}
})
}您朽,
getOptionsList() {
// 子組件加載完后狂丝,在mounted方法中調(diào)用此方法,獲取篩選錢中所有的下拉框數(shù)據(jù)
this.$store.dispatch('listSearch/getSearchOptions', this.getOptions)
}
}
<style lang="less" scoped>
// 樣式自適應(yīng)
.search-box{
display: grid;
grid-template-columns: repeat(4, 25%);
gap:12px 0;
}
<style/>
SearchSelect組件的關(guān)鍵代碼:
<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都有這兩個屬性就可以了,如果沒有魂奥,下方還有進行轉(zhuǎn)換的配置屬性
:value="item.value"
/>
</el-selec>
</div>
</template>
import {mapState} from 'vuex'
props:{
// 當(dāng)前組件綁定的key值,也是后續(xù)進行接口搜索時傳給后端的值
valueKey:{
type: String,
defualt: ''
},
options:{
type: Object,
defualt: () => ({})
},
routePath:{
type: String,
defualt: this.$route.path
},
},
data(){
return {
currentValue: '', // 當(dāng)前組件維護的module
returnValue: {}, // return出去的值耻煤,暫時沒什么用准颓,可以直接從vuex通過路由path來取
optionsList: [], // 當(dāng)前組件下拉框數(shù)據(jù)
loading: false // 遠程搜索時的loading
}
},
compunted: {
...mapState({
allOptions: state => state.listSearch.allOptions, // 所有的下拉框數(shù)據(jù)
optionsUpdate: state => state.listSearch.optionsUpdate, // 更新下拉框數(shù)據(jù)時
listSearchMap: state => state.listSearch.listSearchMap // 當(dāng)前頁面綁定的vuex對象
})
},
watch: {
currentValue: {
handler(val){
if(this.valueKey){
this.$set(this.returnValue, `${this.valueKey}`, val)
}
}
},
listSearchMap: {
handler(val){
// vuex中的對象更新了攘已,并且對象鍵名===當(dāng)前路由path炮赦,更新的key值和當(dāng)前頁面綁定的valueKey一樣,就刷新當(dāng)前組件的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 // 如果是遠程搜素或者是指定了下拉框數(shù)據(jù),不需要通過此方法來更新下拉框數(shù)據(jù)
this.optionsList = this.allOptions[this.options.optionsKey]
// 如果需要轉(zhuǎn)換下拉框數(shù)據(jù)
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
// 讓后端吧所有的下拉框接口統(tǒng)一峡眶,在接口成功返回后進行switch
options.remoteMethods({search:val,pageNum:1,pageSize:50}).then(res=>{
if(res.code === '200'){
switch(this.options.optionsKey === 'plant'){
// ...進行一些操作
}
}
})
}
},
onChange(val){
// 根據(jù)當(dāng)前的valueKey進行一些操作辫樱,某些操作可以放在vuex里峭拘,比如說實時更新vuex里對應(yīng)routepath的值,比如a篩選項依賴b篩選項狮暑, 只有選了c篩選項才能選d篩選項之類的邏輯鸡挠,這個看不同的業(yè)務(wù)場景搬男,可以持續(xù)的迭代
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的關(guān)鍵代碼:
// 跟SearchSelect差不多缔逛,邏輯更簡單溜腐,參考著寫吧
vuex的關(guān)鍵代碼:
const currentState= {
listSearchMap: {},// 所有列表篩選器的儲存對象瓜喇,動態(tài)添加routePath屬性
currentPath: '',// 當(dāng)前路由信息挺益,每次新頁面加載篩選器的時候先更新這個變量
originAllOptions: {},// 保留原始的下拉框?qū)ο蟪撕疵總€人不同的業(yè)務(wù)場景
allOptions: {},// 組件要用到的下拉框數(shù)據(jù)
optionsUpdate: 0 // 更新下拉框數(shù)據(jù)的時候改變此數(shù)值,讓篩選器里面的組件通過watch監(jiān)聽此變量伞辛,完成實時更新
}
const mutations ={
setListSearchMapPathKey(state,payload){
if(!state.listSearchMap[state.currentPath]){
// 以頁面路由為key設(shè)置listSearchMap ,用object.assign方法蚤氏,可以讓listSearchMap 中的新的屬性可以雙向綁定,類似于組件中的$set()的作用
state.listSearchMap = Object.assign({},state.listSearchMap ,{[state.currentPath]:[]})
}
state.listSearchMap[state.currentPath] = Object.assign({},state.listSearchMap[state.currentPath] ,{...payload})
}竿滨,
// 設(shè)置一些默認值,如當(dāng)前篩選器的路由毁葱,篩選器的默認選項之類的
setdefaultSearch(state, payload){
const {routePath} = payload
state.currentPath = routePath
},
plantChange(state, payload){
// SearchSelect組件里面的change事件倾剿,這里根據(jù)不同業(yè)務(wù)場景不同寫法
// 如果涉及到了篩選器下拉框數(shù)據(jù)變更,就加下方這段代碼
// state.optionsUpdate++
}
}
const actions= {
getSearchOptions({state},payload){
for(let i = 0;i<payload.length;i++){
switch(payload[i]){
case 'plant'{
//比如plant需要獲取下拉框數(shù)據(jù)前痘,就在這里執(zhí)行代碼
}
}
}
// 等所有的下拉框數(shù)據(jù)都獲取到后進行更新
state.optionsUpdate++
}
}
export default{
namespaced:true,
state:currentState,
mutations,
actions
}