zoukankan      html  css  js  c++  java
  • 数据量庞大的分页穿梭框实现

    博客地址:https://ainyi.com/63

    昨天偶然看到评论区一位老哥的需求,一时兴起,就答应了当天写好源码写个博客

    回来的晚,第二天才写好。。

    写个分页的穿梭框,从而解决数据量庞大的问题

    我之前写过一篇博客:关于 Element 组件的穿梭框的重构 介绍并实现的方法

    但是第二个分页的 demo 没有,在上一家公司匆匆解决后,没有写入自己的 GitHub,有点可惜...

    当时可是在上班,而且太忙了,不过既然答应了这位老哥写个 demo,就要做到,也是给自己一个挑战

    进入正题

    看实现效果图

    既然之前博客谈过,这里就不仔细谈了,主要放主要的源码

    问题

    Element 官方组件目前(18年)明显对于多选三级联动的穿梭框没有解决方案,也对数据量庞大的穿梭框没有结局方案(各位看官可以试一下,放入几千条数据到穿梭框,卡到爆...),遂只能自己重写组件,完成业务需求

    功能

    1. 实现分页
    2. 搜索,做成在所有数据里搜索,不是在当前分页的数据里搜索,这样就不用在每个分页都搜索一次了。搜索后的结果也会自动分页。(全部数据和仅作展示的数据存都是存放在不同变量)
    3. 全选只在当前页里的全选
    4. 穿梭框左右两个框的联动

    关键点

    1. 每个框作为一个子组件(组件化思想)
    2. 分页关键判断临界点
    3. 搜索,监听 keyword 的变化,传递到父组件搜索,从全局数据搜索
    4. 把备选的数据当做已选的过滤数组,把已选的数据当做备选的过滤数组,在全局 data 进行过滤,最后再进行一次搜索(备选、已选)(考虑到是在搜索过后点击的)
    5. 中间的左右箭头(加入已选和移除已选)放在父组件控制数据流动
    6. 数据流动:子备选框 -> 父组件 -> 子已选框 (移除已选相反)

    源码

    1. Districts.vue(包裹两个穿梭框的父组件)
    export default {
      props: {
        data: {
          type: Array,
        },
      },
      data () {
        return {
          dataList: [], // 未选中(已过滤出已选)的数据
          selectList: [], // 已选中的数据,传递到子组件的数据
    
          dataListNoCheck: [], // 未选中的(或已搜索)传递到子组件的数据
          selectListCheck: [], // 已选中的(或已搜索)传递到子组件的数据
    
          checkData: [], // 已勾选的数据(待添加或删除数据)
    
          noSelectkeyword: '',
          haSelectkeyword: '',
    
          disablePre: true,
          disableNex: true,
        };
      },
      created () {
        this.getDistrict();
      },
      methods: {
        // 分页数据
        getDistrict () {
          this.dataList = this.data;
          this.dataListNoCheck = this.dataList;
        },
        searchWord (keyword, titleId) {
          // 过滤掉数据,保留搜索的数据
          if (titleId === 0) {
            this.noSelectkeyword = keyword;
            this.dataListNoCheck = this.dataList.filter(val => val.name.includes(keyword));
          } else {
            this.haSelectkeyword = keyword;
            this.selectListCheck = this.selectList.filter(val => val.name.includes(keyword));
          }
          let refsName = titleId === 0 ? 'noSelect' : 'hasSelect';
          // 延迟执行
          setTimeout(() => {
            this.$refs[refsName].getDistrict();
          }, 0);
        },
        // 检查左右按钮可用性
        checkDisable (data, id) {
          if (id === 0) {
            data.length > 0 ? (this.disableNex = false) : (this.disableNex = true);
          } else {
            data.length > 0 ? (this.disablePre = false) : (this.disablePre = true);
          }
        },
        // 选择
        checkSelect (val) {
          this.checkData = val;
        },
        // 关键:把未选择的数据当做已选择的过滤数组,把已选择的数据当做未选择的过滤数组,在全局data进行过滤,最后进行一次搜索
        // 添加至已选
        addData () {
          let dataFilter = [
            ...this.selectList,
            ...this.checkData,
          ];
          this.dataList = this.data.filter(item1 => {
            return dataFilter.every(item2 => item2 !== item1);
          });
          this.selectList = this.data.filter(item1 => {
            return this.dataList.every(item2 => item2 !== item1);
          });
          // 搜索一次
          this.searchWord(this.noSelectkeyword, 0);
          this.searchWord(this.haSelectkeyword, 1);
        },
        // 从已选中删除
        deleteData () {
          let dataFilter = [
            ...this.dataList,
            ...this.checkData,
          ];
          this.selectList = this.data.filter(item1 => {
            return dataFilter.every(item2 => item2 !== item1);
          });
          this.dataList = this.data.filter(item1 => {
            return this.selectList.every(item2 => item2 !== item1);
          });
          // 搜索一次
          this.searchWord(this.noSelectkeyword, 0);
          this.searchWord(this.haSelectkeyword, 1);
        },
      },
      components: {
        Transfer,
      },
    };
    </script>
    
    1. Transfer.vue(穿梭框子组件)
    export default {
      props: {
        titleId: {
          type: Number,
        },
        districtList: { // 父组件传递的数据
          type: Array,
        },
      },
      data () {
        return {
          title: ['渠道', '已选中'],
          districtListMock: [], // 展示的数据 (搜索和分页会自动修改这个数组)
          checkedCities: [], // 已选择,数据格式:[id,id,id...]
          isIndeterminate: false,
          checkAll: false,
          searchWord: '',
          len: 0,
          total: 0,
          pageIndex: 0,
          disabledPre: true,
          disabledNex: false,
        };
      },
      created () {
        this.getDistrict();
      },
      watch: {
        // 搜索框的监听器
        searchWord (newWord) {
          this.$emit('search-word', newWord, this.titleId);
        },
        // districtListMock 和 checkAll 的监听器
        districtListMock () {
          // 当方框中无已选择的数据时,不能勾选checkBox
          if (this.checkedCities.length === 0) {
            this.checkAll = false;
            this.isIndeterminate = false;
          }
        },
        checkedCities (newWord) {
          this.$emit('check-disable', newWord, this.titleId);
        },
        // 当列表中无数据时,不能勾选checkBox
        checkAll () {
          this.checkAll = this.districtListMock.length === 0 ? false : this.checkAll;
        },
      },
      methods: {
        // 分页数据
        getDistrict () {
          this.len = this.districtList.length;
          this.total = Math.ceil(this.len / 200);
          this.pageIndex = 0;
          this.pageData();
        },
        pageData () {
          this.checkedCities = [];
          if (this.total > 1 && this.pageIndex < (this.total - 1)) {
            this.pageIndex === 0 ? this.disabledPre = true : this.disabledPre = false;
            this.disabledNex = false;
            this.districtListMock = this.districtList.slice(this.pageIndex \* 200, this.pageIndex \* 200 + 200);
          } else {
            this.total > 1 ? this.disabledPre = false : this.disabledPre = true;
            this.disabledNex = true;
            this.districtListMock = this.districtList.slice(this.pageIndex \* 200, this.len);
          }
        },
        // 上一页
        prev () {
          this.pageIndex > 0 && --this.pageIndex;
          this.pageData();
        },
        // 下一页
        next () {
          this.pageIndex <= (this.total - 1) && ++this.pageIndex;
          this.pageData();
        },
        // 单选
        handleCheckedChange (value) {
          let checkedCount = value.length;
          this.checkAll = checkedCount === this.districtListMock.length;
          this.isIndeterminate = checkedCount > 0 && checkedCount < this.districtListMock.length;
          // 子传父
          this.$emit('check-district', value);
        },
        // 全选
        handleCheckAllChange (val) {
          this.checkedCities = val ? this.districtListMock.map(val => val) : [];
          this.isIndeterminate = false;
          // 子传父
          this.$emit('check-district', this.checkedCities);
        },
      },
    };
    </script>
    

    具体源码可前往 Github:https://github.com/Krryxa/my-transfer

    欢迎 start

    呼呼,双休好好休息了~~

    博客地址:https://ainyi.com/63

  • 相关阅读:
    阿里Canal中间件的初步搭建和使用
    深入理解Java String类
    深入理解Java中的String
    Docker 容器数据卷(挂载)
    在docker容器下利用数据卷实现在删除了mysql容器或者镜像的情况下恢复数据
    EFK(Elasticsearch+Filebeat+Kibana)收集容器日志
    Docker 容器日志占用空间过大解决办法
    四种软件架构,看看你属于哪个层次
    Kubernetes 如何只授予某一 Namespace 的访问权限
    Docker实验Docker的网络配置
  • 原文地址:https://www.cnblogs.com/ainyi/p/10125443.html
Copyright © 2011-2022 走看看