< element组件封装: element表格(element-table)实现表格列筛选功能组件 >


👉 前言

在 Vue + elementUi 开发中,element表格本身仅自带升序和降序排列,可能在日常开发中,不太能满足自定义筛选的要求,比如说,实现excel文档中的筛选个例的功能,今天小温就把这个干货给大家分享一下。本篇文章简单展示! 案例仅供参考!


👉 一、效果演示

话不多说,先上效果图! 白嫖万岁!当然,如果有帮助,希望不要吝啬你的点赞呀!

效果演示 ①
效果演示 ②
效果演示 ③

👉 二、原理

通过实现对鼠标点击的位置的获取,使指定的弹窗展示在指定的位置。然后便是自己自定义多选按钮和内容,以及实现关键词搜索功能!原理较为简单,直接上代码!

👉 三、实现案例

> HTML模板(组件)

<template>
  <div class="tableTool" :style="{top:filterToolTop + 15 +'px',left:filterToolLeft -20 +'px'}">
    <el-input
      v-model="keyword"
      prefix-icon="el-input__icon el-icon-search"
      type="text"
      placeholder="搜索"
      @input="seachKey"
      oninput="if(value.length>11)value=value.slice(0,100)"
    ></el-input>
    <div class="select-box">
      <el-checkbox
        v-if="this.seachType !== 'sfzkList'"
        :indeterminate="isIndeterminate"
        v-model="checkAll"
        @change="handleCheckAllChange"
        id="checkAll"
      >全选</el-checkbox>
      <el-checkbox-group
        v-if="this.seachType !== 'sfzkList'"
        v-model="checkedList"
        @change="handleCheckedCitiesChange"
      >
        <el-checkbox
          v-for="(item,index) in options"
          :label="`${item.NAME ? item.NAME : item}`"
          :key="index"
        >
          <span>{{item.NAME ? item.NAME : item}}</span>
          <span v-if="item.NUM">({{item.NUM}})</span>
        </el-checkbox>
      </el-checkbox-group>
      <el-radio-group
        v-if="this.seachType === 'sfzkList'"
        v-model="checkedList"
        @change="handleCheckedCitiesChange"
      >
        <el-radio v-for="(item,index) in options" :key="index" :label="`${item.NAME ? item.NAME : item}`">
          <span>{{item.NAME ? item.NAME : item}}</span>
          <span v-if="item.NUM">({{item.NUM}})</span>
        </el-radio>
      </el-radio-group>
    </div>
    <div class="bottom">
      <el-button size="mini" @click="$emit('closeTool')">取消</el-button>
      <el-button type="primary" size="mini" @click="save">确认</el-button>
    </div>
    <i class="el-icon-caret-top"></i>
  </div>
</template>
<script>
export default {
  name: "tableCol",
  props: {
    filterToolLeft: {
      required: true,
      type: Number
    },
    filterToolTop: {
      required: true,
      type: Number
    },
    // 已经选中的数据
    seachData: {
      required: true,
      type: Object
    },
    // 正在更改哪个
    seachType: {
      required: true,
      type: String
    },
    // 需要后端给的所有数据
    allOptionsObj: {
      required: true,
      type: Object
    },
    useTableToolComponent: {
      default: null
    }
  },
  data() {
    return {
      keyword: "",
      checkAll: false,
      checkedList: [],
      options: [],
      isIndeterminate: true,
      allOptions: []
    };
  },
  methods: {
    handleCheckAllChange(val) {
      const arr = JSON.parse(JSON.stringify(this.allOptions));
      const arrOptions = arr.map(item => {
        return item.NAME;
      });
      this.checkedList = val ? arrOptions : [];
      this.isIndeterminate = false;
    },
    handleCheckedCitiesChange(value) {
      let checkedCount = value.length;
      this.checkAll = checkedCount === this.options.length;
      this.isIndeterminate =
        checkedCount > 0 && checkedCount < this.options.length;
    },
    save() {
      this.seachData[this.seachType.replace("List", "")] = this.checkedList;
      //  把查询条件给父组件
      this.$emit("saveSeach", this.seachData);
    },
    seachKey() {
      this.allOptions = this.allOptionsObj[this.seachType].filter(
        item => (item.NAME || item).indexOf(this.keyword) != -1
      );
      this.options = this.allOptions;
      this.checkedList = [];
      this.checkAll = false;
    },
    setposition() {
      let boxEl = document.querySelector(".tableTool");
      let icon = document.querySelector(".tableTool .el-icon-caret-top");
      if (
        this.filterToolLeft + boxEl.offsetWidth + 48 >
        document.body.clientWidth
      ) {
        boxEl.style.transform = "translateX(-90%)";
        icon.style.left = "90%";
      }
    }
  },
  created() {},
  async mounted() {
    this.allOptions = this.allOptionsObj[this.seachType];
    const selectOption = this.$parent.seachData[
      `${this.seachType.replace("List", "")}`
    ];
    this.options = this.allOptions;
    if (selectOption.length || this.seachType === "sfzkList") {
      this.checkedList =
        this.seachType === "sfzkList" ? selectOption.join("") : selectOption;
    } else {
      document.querySelector("#checkAll").click();
    }
    this.setposition();
  },
  beforeMount() {
    let that = this;
    this._close = e => {
      let isFlag =
        that.$el.contains(e.target) ||
        e.target.className.indexOf("el-icon-caret-bottom") != -1;
      // if (!isFlag) {
      //   that.$emit("closeTool");
      // }
    };
    document.body.addEventListener("click", this._close);
  },
  beforeDestroy() {
    document.body.removeEventListener("click", this._close);
  }
};
</script>

<style lang="scss" scoped>
.tableTool {
  position: fixed;
  background: #fff;
  box-shadow: 0 0 5px #ccc;
  padding: 10px;
  z-index: 999;
  /deep/.el-input__inner {
    padding-left: 30px;
  }
  .select-box {
    border: #ccc solid 1px;
    padding: 10px;
    margin-top: 10px;
    max-height: 280px;
    overflow: auto;
    max-width: 400px;
  }
  /deep/ .el-checkbox {
    display: block;
    margin-top: 5px;
  }
  /deep/ .el-radio-group {
    display: flex;
    flex-direction: column;
  }
  .bottom {
    display: flex;
    justify-content: flex-end;
    margin-top: 10px;
  }
  .el-icon-caret-top {
    position: absolute;
    color: #fff;
    top: -13px;
    font-size: 20px;
  }
}
</style>

> HTML模板(父组件使用)

<el-table-column
	:key="item.comments"
	v-for="item in columnTables['fixed']"
	:prop="item.comments"
	:label="item.columnName"
	:filters="item.filters"
	align="center"
 	fixed="left"
	:show-overflow-tooltip="item.overflow"
	:min-width="`${item.width ? item.width : '80px'}`">
	<!-- 自定义头部 -->
	<template v-slot:header>
	  <i class="el-icon-search"
	     style="margin-right: 15px;"
	     @click.stop="filterData($event, item.comments)"
	     v-if="item.screen"
	  ></i>
	  <span class="table-heard-filter">{{item.columnName}}</span>
	</template>
	<template slot-scope="scope">
	  <span>{{ scope.row[item.comments] }}</span>
	</template>
</el-table-column>

<selectTool
	v-if="showFilterTool"
	ref="selectTool"
	:filterToolLeft="filterToolLeft"
	:filterToolTop="filterToolTop"
	:seachData="seachData"
	@saveSeach="saveSeach"
	:allOptionsObj="allOptionsObj"
	:seachType="seachType"
	@closeTool="closeTool"
/>
<script>
export default {
  components: { selectTool },
  data () {
    return {
    	showFilterTool: false,
		filterToolTop: 0,
		filterToolLeft: 0,
		seachData: {
			bdfl: [],
			xmbm: []
		},
		allOptionsObj: {
			bdflList: [],
			xmbmList: [],
		},
		seachType: "bdflList",
		columnTables: {
			fixed: [
				{
					comments: "bdfl",
					columnName: "标的分类",
					width: "120px",
					screen: true
				},
				{
					comments: "xmbm",
					columnName: "项目编码",
					width: "120px",
					overflow: true,
					screen: true
				},
			]
		}
	}
  },
  methods: {
    filterData(e, type) {
      this.showFilterTool = false;
      this.$nextTick(() => {
        this.seachType = type + 'List';
        this.filterToolTop = e.pageY;
        this.filterToolLeft = e.pageX;
        this.showFilterTool = true;
      });
    },
    closeTool() {
      this.showFilterTool = false;
    },
    saveSeach(data) {
      console.log(data, "data.");
      this.seachData = JSON.parse(JSON.stringify(data));
      
      for(let key in this.seachData) {
        this.seachData[key] = this.seachData[key].filter(el => {
          return el != null;
        });
      }
      
      this.closeTool();
      this.handleSearch();
    },
	}
}
</script>

案例较为粗浅,仅供参考!


往期内容 💨

🔥 < 今日份知识点:谈谈内存泄漏 及 在 Javascript 中 针对内存泄漏的垃圾回收机制 >

🔥 < 今日份知识点:浅述对 “ Vue 插槽 (slot) ” 的理解 以及 插槽的应用场景 >

🔥 <恢复更新进度ing:今天浅聊一下前端CSS样式 及 书写的规范 >

🔥 < 每日份知识快餐:axios是什么?如何在Vue中 封装 axios ? >

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术宅小温

你小小的鼓励,是我搬砖的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值