element-ui在国产vue项目中使用的比列还是非常高的,UI框架在帮助我们省力的同时也束缚住了我们的手脚,比如一些复杂的定制化需求使用框架自由的组件并不能满足我们的需求。这种需求假如在一开始就有也比较省事,麻烦就在于使用框架的组件之后产生的新需求要如何应对。
后台系统对table组件的需求是最常见的,不过element-ui的el-table组件只是能满足最基本的需求而已。比如复杂表头,动态列,列拖拽,列筛选,以及大数据的渲染上就不太行了。
今天这篇文章要讲的就是给表头加上自定义的筛选功能,效果如下:
当然还有其它类型就不一一截图了,我根据自己的业务封装了【文本,日期,数值,复选框,单选按钮,下拉框】等通用筛选项,希望对大家有所帮助。
使用方式:
1、引入组件
import FilterHeader from '@/components/FilterHeader'
2、注册组件
components: {
FilterHeader
}
3、在el-table-column的header插槽中插入组件,如
<el-table-column label="类型">
<template slot="header" slot-scope="scope">
<FilterHeader
:column="scope.column"
field-name="type"
filter-type="checkbox"
:custom-arr-list="dictList"
@tableFilter="tableFilter"
@resetFilter="tableFilterReset"
/>
</template>
</el-table-column>
组件属性及事件解释
:column="scope.column" // 当前列的信息
field-name="type" // 当前字段名
filter-type="checkbox" // 筛选的方式为checkbox即复选框
:custom-arr-list="dictList" // 这个是筛选项的列表,格式为[{label:'',value:''},{label:'',value:''}]
@tableFilter="tableFilter" // 触发筛选
@resetFilter="tableFilterReset" // 触发重置
FilterHeader组件代码如下:
<template>
<div style="display: inline-block" @click.stop>
<el-popover
ref="popover"
placement="bottom"
title="查询条件"
width="300"
trigger="click"
>
<!-- 单个文本框 -->
<div v-if="filterType == 'text'">
<el-input
v-model.trim="conditions.text"
size="mini"
clearable
placeholder="请输入查询内容"
@keyup.native.enter="confirm()"
/>
</div>
<!-- 数值范围 -->
<div v-else-if="filterType == 'number'">
<el-input
v-model.trim="conditions.number1"
size="mini"
clearable
type="number"
step="0.01"
placeholder="请输入开始数值"
/>
<el-input
v-model.trim="conditions.number2"
size="mini"
clearable
step="0.01"
style="margin-top: 10px"
placeholder="请输入结束数值"
/>
</div>
<!-- 日期-->
<div v-else-if="filterType == 'date'">
<el-date-picker
v-model="conditions.date1"
type="date"
clearable
size="mini"
placeholder="开始时间"
value-format="yyyy-MM-dd"
/>
<el-date-picker
v-model="conditions.date2"
style="margin-top: 10px"
type="date"
size="mini"
clearable
placeholder="结束时间"
value-format="yyyy-MM-dd"
/>
</div>
<!-- 下拉框-->
<div v-else-if="filterType == 'select'">
<el-select
v-model="conditions.select"
placeholder="请选择"
size="mini"
clearable
>
<el-option
v-for="(item, index) in customArrList"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<!-- 复选框-->
<div v-else-if="filterType == 'checkbox'">
<el-checkbox-group v-model="conditions.checkbox">
<el-checkbox
v-for="(item, index) in customArrList"
:key="index"
:label="item.value"
>{{ item.label }}</el-checkbox>
</el-checkbox-group>
</div>
<!--单选按钮-->
<div v-else-if="filterType == 'radio'">
<el-radio-group v-model="conditions.radio">
<el-radio
v-for="(item, index) in customArrList"
:key="index"
:label="item.value"
border
size="mini"
>{{ item.label }}</el-radio>
</el-radio-group>
</div>
<!-- confirm 确定框-->
<div class="text-right mgt10">
<el-button
type="warning"
size="mini"
@click="reset"
>重置</el-button>
<el-button
type="primary"
size="mini"
@click="confirm"
>确定</el-button>
</div>
<!-- 标题-->
<span
slot="reference"
onselectstart="return false"
oncontextmenu="return false"
:class="labelColor"
>{{ column.label }} <i />
</span>
</el-popover>
</div>
</template>
<script>
export default {
name: 'FilterHeader',
props: {
column: {
type: Object,
defalut: null
},
fieldName: {
type: String,
defalut: ''
},
filterType: {
type: String,
defalut: 'txt'
},
customArrList: {
type: Array,
defalut: []
}
},
data() {
return {
conditions: {
text: '',
number1: '',
number2: '',
date1: '',
date2: '',
select: '',
checkbox: [],
radio: ''
}
}
},
computed: {
// 有条件的话高亮显示标题
labelColor() {
switch (this.filterType) {
case 'text':
if (this.conditions.text) { return 'heighLight' }
return ''
case 'number':
if (this.conditions.number1 || this.conditions.number2) { return 'heighLight' }
return ''
case 'date':
if (this.conditions.date1 || this.conditions.date2) { return 'heighLight' }
return ''
case 'select':
if (this.conditions.select) { return 'heighLight' }
return ''
case 'checkbox':
if (this.conditions.checkbox.length > 0) { return 'heighLight' }
return ''
case 'radio':
if (this.conditions.radio !== '') { return 'heighLight' }
return ''
}
return ''
}
},
methods: {
confirm() {
this.$refs.popover.doClose()
this.$emit('tableFilter', {
filterType: this.filterType,
fieldName: this.fieldName,
conditions: this.conditions
})
},
reset() {
switch (this.filterType) {
case 'text':
this.conditions.text = ''
break
case 'number':
this.conditions.number1 = ''
this.conditions.number2 = ''
break
case 'date':
this.conditions.date1 = ''
this.conditions.date2 = ''
break
case 'select':
this.conditions.select = ''
break
case 'checkbox':
this.conditions.checkbox = []
break
case 'radio':
this.conditions.radio = ''
break
}
this.$refs.popover.doClose()
this.$emit('resetFilter', {
filterType: this.filterType,
fieldName: this.fieldName,
conditions: this.conditions
})
}
}
}
</script>
<style scoped>
.label {
user-select: none;
}
.heighLight {
color: #409eff;
}
.filter-radio{
display: flex;justify-content: space-between;flex-wrap: wrap;
.el-radio{width: 45%;margin-right: 0}
}
</style>
如有特殊需求自己可以改一改。组件为了做到通用性,里面并无业务逻辑代码,所有逻辑都在我们的父组件页面中编写。
以下为tableFilter,tableFilterReset方法的业务逻辑代码,大家可以参考
我们在data中定义两个变量tableDataCopy,conditionsFields
data(){
tableData: [], // 表格的数据
tableDataCopy: {}, // 深度拷贝的tableData对象,用来筛选数据
conditionsFields: [], // 记录参与筛选的列信息
}
注:tableDataCopy为tableData的深拷贝
比如我们在列表请求中
getList().then(res=>{
this.tableData = res.data
this.tableDataCopy = JSON.parse(JSON.stringify(this.tableData))
})
表格列筛选
tableFilter(data, reset) {
let flag = true // 判断条件里有没有该列,用来判断是新增还是更新
let filterData = this.tableDataCopy // 最终过滤信息
if (!reset) {
// 参与筛选的列信息,有则更新
this.conditionsFields.forEach(item => {
if (item.fieldName === data.fieldName) {
item.conditions = data.conditions
flag = false
}
})
// 没有则添加
if (flag) {
this.conditionsFields.push(data)
}
}
// 遍历所有筛选条件进行过滤
this.conditionsFields.filter((fields, index) => {
filterData = filterData.filter(item => {
// 文本
if (fields.filterType === 'text' && fields.conditions.text !== '') {
return item[fields.fieldName] && item[fields.fieldName].indexOf(fields.conditions.text) > -1
// 复选框
} else if (fields.filterType === 'checkbox' && fields.conditions.checkbox.length !== 0) {
return fields.conditions.checkbox.includes(item[fields.fieldName])
// 单选按钮
} else if (fields.filterType === 'radio' && fields.conditions.radio !== '') {
return item[fields.fieldName] !== null && item[fields.fieldName].toString() === fields.conditions.radio.toString()
// 日期
} else if (fields.filterType === 'date' && (fields.conditions.date1 || fields.conditions.date2)) {
if (!fields.conditions.date1) {
return item[fields.fieldName] <= fields.conditions.date2
} else if (!fields.conditions.date2) {
return item[fields.fieldName] >= fields.conditions.date1
} else {
return item[fields.fieldName] >= fields.conditions.date1 && item[fields.fieldName] <= fields.conditions.date2
}
// 数值
} else if (fields.filterType === 'number' && (fields.conditions.number1 || fields.conditions.number2)) {
if (!fields.conditions.number1) {
return item[fields.fieldName] <= fields.conditions.number2
} else if (!fields.conditions.number2) {
return item[fields.fieldName] >= fields.conditions.number1
} else {
return item[fields.fieldName] >= fields.conditions.number1 && item[fields.fieldName] <= fields.conditions.number2
}
} else {
// 遍历完没找到符合条件的,则直接返回
return item
}
})
})
this.tableData = this.$set(this, 'tableData', filterData)
}
重置列筛选
tableFilterReset(data) {
// 清空当前列筛选条件
this.conditionsFields.forEach((item, index) => {
if (item.fieldName === data.fieldName) {
this.conditionsFields.splice(index, 1)
}
})
if (this.conditionsFields.length === 0) {
// 没有筛选条件了直接请求列表
this.getList()
} else {
// 有筛选条件就再去筛选
this.tableFilter(data, true)
}
}