先看效果图
一、前言
甲方放着好好的导出,好好的excel的高级筛选不用,非要在网页中实现一个。。。。
最开始尝试使用的element官方的筛选,给甲方看后 说和excel的筛选相差很大。。好在官方有提供自定义表头,那自己手动实现一个了。最后有完整代码
其实此功能挺简单的,只不过麻烦一点。我把筛选分为了四个类型
- txt 文本型 (就是input输入框)
- scope 范围型 (查询金额区间)
- date 时间型 (查询日期)
- select 下拉框 (就是下拉框呗)
整体逻辑就是:子组件把筛选好的数据,给父组件,父组件进行筛选过滤,再给table展示
二、父组件
分为两个部分
1.tag部分,显示所用到的筛选条件
2.table部分
html
<!-- 条件tag -->
<div style="margin-bottom: 10px" v-if="conditionList.length != 0">
<span>条件:</span>
<el-tag
@close="conditionClose(index)"
style="margin-left: 10px"
v-for="(tag, index) in conditionList"
:key="index"
closable
:type="tag.prop"
>
{{ tag.label }} :<span style="color: red">{{ tag.value.value1 }}</span>
<span v-if="tag.value.value2" style="color: red"
>- {{ tag.value.value2 }}</span
>
</el-tag>
</div>
<!-- 表格 -->
<el-table :data="tableData" style="width: 100%" border stripe>
<template v-for="(item, index) in tableConfig">
<el-table-column
sortable
:key="index"
:label="item.label"
align="center"
:prop="item.prop"
:width="item.width"
:filters="[]"
>
<template slot="header" slot-scope="scope">
<custom-header
v-if="customFlag"
:column="scope.column"
:item="item"
:customParams="customParams"
:labelColorList="labelColorList"
@tableUpdate="tableUpdate"
></custom-header>
</template>
</el-table-column>
</template>
</el-table>
custom-header 为子组件,里面写着四个类型的代码
父data里的数据
data() {
return {
customFlag: false, // 自定义筛选是否显示
customParams: {}, //自定义筛选参数
conditionList: [], //自定义筛选条件
labelColorList: [], //已经在使用的筛选条件,染色用
// table数据
tableData: [],
// table数据 拷贝,我们不操作原数据
tableDataCopy: [],
// table配置
tableConfig: [
{
label: "姓名",
prop: "name",
width: "150px",
conditionType: "txt", // 条件类型
},
{
label: "身价",
prop: "amount",
width: "150px",
conditionType: "scope", // 条件类型
},
{
label: "生日",
prop: "date",
width: "150px",
conditionType: "date", // 条件类型
},
{
label: "所在城市",
prop: "city",
conditionType: "select", // 条件类型
conditionListName: "cityList", //条件类型下拉框数据
fuzzyQuery: false, //是否模糊查询
},
{
label: "所在城市(模糊查询)",
prop: "city2",
conditionType: "select", // 条件类型
conditionListName: "cityList", //条件类型下拉框数据
fuzzyQuery: true, //是否模糊查询
},
],
};
},
tableConfig里的数据会被el-table遍历
其中:
conditionType : 条件的类型,前言已经说了就是那四个类型(txt,scope,date,select)
conditionListName: 如果是select类型的话,肯定有下拉框数据对吧,这个就是下拉框数据的List名,只是名字不是数据哦(不明白的话看到子组件那里就懂了)
fuzzyQuery:是否模糊查询
父methods几个关键方法
methods: {
// 请求下拉框数据
getCustomData() {},
//给使用筛选条件的标题加颜色
setlabelColor() {},
//自定义检索发射出来的事件
tableUpdate() {},
//筛选数据
customSearch() {},
}
getCustomData():汇总下拉框的数据
customSearch():筛选逻辑处理,后面想改筛选的逻辑就来这。后面贴出的完整代码里写的也很清楚,不明白可以问我
三、子组件
接收的props
column:当前列数据,用于显示表头
tableConfig :处理类型判断
customParams :select下拉框数据
labelColorList :正在使用的筛选条件,染色用
computed
computed: {
selectList() {
return function (data) {
return this.customParams[data.conditionListName];
};
},
},
根据下拉框名称,找到对应的下拉框List数据
其他没什么说的,子组件比较简单,看完整代码一眼就明白了
四、完整代码
父组件
<template>
<div class="app">
<!-- 条件tag -->
<div style="margin-bottom: 10px" v-if="conditionList.length != 0">
<span>条件:</span>
<el-tag
@close="conditionClose(index)"
style="margin-left: 10px"
v-for="(tag, index) in conditionList"
:key="index"
closable
:type="tag.prop"
>
{{ tag.label }} :<span style="color: red">{{ tag.value.value1 }}</span>
<span v-if="tag.value.value2" style="color: red"
>- {{ tag.value.value2 }}</span
>
</el-tag>
</div>
<!-- 表格 -->
<el-table :data="tableData" style="width: 100%" border stripe>
<template v-for="(item, index) in tableConfig">
<el-table-column
sortable
:key="index"
:label="item.label"
align="center"
:prop="item.prop"
:width="item.width"
:filters="[]"
>
<template slot="header" slot-scope="scope">
<custom-header
v-if="customFlag"
:column="scope.column"
:item="item"
:customParams="customParams"
:labelColorList="labelColorList"
@tableUpdate="tableUpdate"
></custom-header>
</template>
</el-table-column>
</template>
</el-table>
</div>
</template>
<script>
import customHeader from "./components/customHeader.vue";
export default {
name: "app",
data() {
return {
customFlag: false, // 自定义筛选是否显示
customParams: {}, //自定义筛选参数
conditionList: [], //自定义筛选条件
labelColorList: [], //已经在使用的筛选条件,染色用
// table数据
tableData: [],
// table数据 拷贝,我们不操作原数据
tableDataCopy: [],
// table配置
tableConfig: [
{
label: "姓名",
prop: "name",
width: "150px",
conditionType: "txt", // 条件类型
},
{
label: "身价",
prop: "amount",
width: "150px",
conditionType: "scope", // 条件类型
},
{
label: "生日",
prop: "date",
width: "150px",
conditionType: "date", // 条件类型
},
{
label: "所在城市",
prop: "city",
conditionType: "select", // 条件类型
conditionListName: "cityList", //条件类型下拉框数据
fuzzyQuery: false, //是否模糊查询
},
{
label: "所在城市(模糊查询)",
prop: "city2",
conditionType: "select", // 条件类型
conditionListName: "cityList", //条件类型下拉框数据
fuzzyQuery: true, //是否模糊查询
},
],
};
},
methods: {
getCustomData() {
/*
这里的数据有必要注意下:
1.数据格式这里处理好 全部保持一致,这样customHeader就不用再处理了
3.因为我们后面筛选的时候查找的是文字,所以这里的value始终和列表展示的值保持一致,也是文字。
3.可以写个Promise.All,把下拉框所需要的数据都请求到 然后再打开customFlag
*/
this.customParams = {
//城市列表
cityList: [
{ value: "北京市" },
{ value: "南京市" },
{ value: "上海市" },
{ value: "广州市" },
{ value: "深圳市" },
{ value: "杭州市" },
{ value: "成都市" },
],
// ...
};
this.customFlag = true;
},
// 给使用筛选条件的标题加颜色
setlabelColor() {
this.labelColorList = [];
this.conditionList.forEach((_item) => {
this.labelColorList.push(_item.prop);
});
},
// 自定义检索发射出来的事件
tableUpdate(data) {
console.log(data, "condition");
let flag = true;
// 筛选条件如果已经存在,就更新
this.conditionList.forEach((item, index) => {
if (item.prop == data.prop) {
item.value = data.value;
flag = false;
}
});
// 如果没有就添加
if (flag) {
this.conditionList.push(data);
}
this.customSearch(); //筛选数据
},
// 筛选数据
customSearch() {
/*
这里可以说是筛选的核心部分吧,自定义的筛选规则都在这。
以后想改什么筛选规则就来这找
*/
console.log(this.conditionList, "this.conditionList");
this.setlabelColor(); //设置使用自定义检索的表头颜色
// 如果自定义检索 为空了,就重新调用查询
if (this.conditionList.length == 0) {
this.search();
return false;
}
const result = [];
// 遍历列表数据
for (let i = 0; i < this.tableDataCopy.length; i++) {
const dataItem = this.tableDataCopy[i];
// 遍历自定义筛选条件,符合规则就push出来
let flag = true;
for (let l = 0; l < this.conditionList.length; l++) {
const item = this.conditionList[l];
// 属性名 属性值 类型 是否模糊查询
const { prop, value, conditionType, fuzzyQuery } = item;
// txt类型
if (conditionType == "txt") {
if (dataItem[prop].indexOf(value.value1) != -1) {
flag = true;
} else {
flag = false;
}
//范围类型
} else if (conditionType == "scope") {
if (
dataItem[prop] >= value.value1 &&
dataItem[prop] <= value.value2
) {
flag = true;
} else {
flag = false;
}
// 时间类型
} else if (conditionType == "date") {
// 转换为时间戳然后判断
let current = new Date(dataItem[prop]).getTime();
let value1 = new Date(value.value1).getTime();
let value2 = new Date(value.value2).getTime();
if (current >= value1 && current <= value2) {
flag = true;
} else {
flag = false;
}
}
// 下拉框类型
else if (conditionType == "select") {
// fuzzyQuery 为true代表模糊查询,否则为精确查询
if (fuzzyQuery) {
if (dataItem[prop].indexOf(value.value1) != -1) {
flag = true;
} else {
flag = false;
}
} else {
if (dataItem[prop] == value.value1) {
flag = true;
} else {
flag = false;
}
}
}
if (flag === false) break;
}
if (flag) result.push(dataItem);
}
console.log(result, "result");
this.tableData = result;
// this.totalSize = result.length;
},
search() {
this.tableData = [
{
name: "王小虎",
amount: 100,
date: "2018-05-02",
city: "北京市",
city2: "北京市",
},
{
name: "张二宝",
amount: 200,
date: "2019-05-04",
city: "上海市",
city2: "上海市",
},
{
name: "王二丫",
amount: 500,
date: "2020-05-01",
city: "深圳市",
city2: "深圳市",
},
{
name: "胡图图",
amount: 1000,
date: "2021-05-03",
city: "广州市",
city2: "广东省广州市",
},
{
name: "张小龙",
amount: 2000,
date: "2022-05-03",
city: "杭州市",
city2: "浙江省杭州市",
},
];
// copy 一份数据出来
this.tableDataCopy = JSON.parse(JSON.stringify(this.tableData));
},
// 关闭条件tag
conditionClose(index) {
this.conditionList.splice(index, 1);
this.customSearch(); //筛选数据
},
},
mounted() {
// 请求自定义筛选下拉框数据
this.getCustomData();
// 请求table数据
this.search();
},
components: {
customHeader,
},
};
</script>
<style scoped lang='scss'>
// 占位,解决点击自己写的自定义筛选 会冒泡到排序
/deep/ .el-table__column-filter-trigger {
display: none !important;
}
</style>
子组件
<template>
<!-- 注意:逻辑部分尽量不好写到这个组件内,因为这个组件是根据外面table循环创建的,在这里写逻辑会非常影响性能 -->
<div class="customHeader" @click.stop style="display: inline-block">
<el-popover
placement="bottom"
title="查询条件"
width="300"
trigger="click"
ref="popover"
>
<!-- txt 文本 -->
<div v-if="item.conditionType == 'txt'">
<el-input
v-model.trim="conditions.value1"
placeholder="请输入查询内容"
@keyup.native.enter="confirm()"
></el-input>
</div>
<!-- scope 范围-->
<div v-else-if="item.conditionType == 'scope'">
<el-input
style="width: 120px"
v-model.trim="conditions.value1"
placeholder="请输入条件1"
></el-input>
-
<el-input
style="width: 120px"
v-model.trim="conditions.value2"
placeholder="请输入条件2"
></el-input>
</div>
<!-- date 日期-->
<div v-else-if="item.conditionType == 'date'">
<el-date-picker
v-model="conditions.value1"
type="date"
clearable
placeholder="开始时间"
value-format="yyyy-MM-dd"
></el-date-picker>
<el-date-picker
style="margin-top: 10px"
v-model="conditions.value2"
type="date"
clearable
placeholder="结束时间"
value-format="yyyy-MM-dd"
></el-date-picker>
</div>
<!-- select 选择框-->
<div v-else-if="item.conditionType == 'select'">
<el-select
v-model="conditions.value1"
placeholder="请选择"
style="width: 100%"
clearable
>
<el-option
v-for="(item, index) in selectList(item)"
:key="index"
:label="item.value"
:value="item.value"
>
</el-option>
</el-select>
</div>
<!-- confirm 确定框-->
<div style="text-align: center">
<el-button @click="confirm" type="primary" size="mini" class="confirm"
>确定</el-button
>
</div>
<!-- label 标题显示-->
<span
slot="reference"
onselectstart="return false"
oncontextmenu="return false"
class="label"
:class="{ labelColor: labelColorList.includes(item.prop) }"
>{{ column.label }} <i class="el-icon-arrow-down"></i>
</span>
</el-popover>
</div>
</template>
<script>
export default {
name: "customHeader",
// column 当前列数据,tableConfig 内数据,customParams 下拉框数据, labelColorList 正在使用的筛选条件
props: ["column", "item", "customParams", "labelColorList"],
data() {
return {
conditions: {
value1: "",
value2: "",
},
};
},
methods: {
confirm() {
if (!this.conditions.value1 && !this.conditions.value2) {
return this.$message.warning("请选择筛选条件");
}
// 关闭popover
this.$refs.popover.doClose();
this.$emit("tableUpdate", {
value: this.conditions, //所筛选的数据
...this.item, //table 配置
});
},
},
computed: {
selectList() {
return function (data) {
return this.customParams[data.conditionListName];
};
},
},
};
</script>
<style scoped>
.confirm {
margin-top: 10px;
}
/* 禁止双击选中文字 */
.label {
-moz-user-select: none; /*火狐*/
-webkit-user-select: none !important; /*webkit浏览器*/
-ms-user-select: none; /*IE10*/
-khtml-user-select: none; /*早期浏览器*/
user-select: none;
}
.labelColor {
color: #409eff;
}
</style>
肯定有写的不完美的地方,请指教