el-transfer 高级用法

1,386 阅读1分钟

背景:由于列表数据过多,上千甚至上万条数据,如果全部加载,页面交互很卡,所以改为滚动加载,原理就是分页加载。

  1. 监听左侧滚动条滚动到底
  2. 监听左侧搜索条件的变化
  3. 监听左侧全选是否选中
  4. 搜索条件如果从接口查询,过滤函数永远返回true
  5. 手动使滚动条置顶
  6. 判断是否点击全选(选中)
<template>
  <div id="transfer-form">
    <el-transfer
      ref="myTransfer"
      :titles="['全选', '全选']"
      :props="{key: 'key',label: 'name'}"
      :left-default-checked="leftDefaultChecked"
      filterable
      :filter-method="scrollTransferFilterMethod"
      filter-placeholder="请输入需要检索的表名"
      @change="transferChange"
      v-model="selectedData"
      :data="tableList">
    </el-transfer>
  </div>
</template>
<script>
export default {
  data () {
    return {
      tableList: [],

      leftDefaultChecked: [],
      selectedData: [],
      selectedDataMap: new Map(), // 缓存选中数据,解决在改变搜索条件时,列表清空,右侧选中数据也会丢失

      scrollQuery: '',
      scrollPageSize: 20,
      scrollPageNum: 1,
      scrollPageTotal: 0,
      // this.$watch监听返回的取消监听函数,重新监听前执行取消
      scrollQueryWatch: null,
      scrollAllCheckedWatch: null
    }
  },
  mounted () {
    this.watchScroll()
  },
  methods: {
    /* 监听左侧滚动条 */
    watchScroll () {
      const me = this
      const scrollContainer = this.getScrollContainer()
      scrollContainer && scrollContainer.addEventListener('scroll', debounce(function () {
        // 在分辨率高的屏幕上,scrollTop可能会出现小数使左边的值小于右边的值,永远不会触发判断
        const scrollTop = Math.ceil(scrollContainer.scrollTop)
        const clientHeight = scrollContainer.clientHeight
        const scrollHeight = scrollContainer.scrollHeight
        if (!clientHeight || !scrollHeight) return
        if (scrollTop + clientHeight >= scrollHeight) {
          // 滚动到底,加载下一页数据,因为数据量比较大,改成分页加载
          me.scrollLoadList()
        }
      }, 100))      
    },

    getScrollContainer () {
      /* id自定义 */
      const packageDom = document.getElementById('transfer-form')
      const scrollContainer = packageDom.querySelector('.el-transfer-panel__list')
      return scrollContainer
    },

    scrollLoadList () {
      if (this.scrollPageTotal > this.tableList.length) {
        this.scrollPageNum += 1
        // this.searchList()
      }
    },

    /* 手动使滚动条置顶 */
    scrollToTop () {
      const scrollContainer = this.getScrollContainer()
      scrollContainer.scrollTop = 0
      // scrollContainer.scrollTo({
      //   top: 0
      // })
    },

    // 监听左侧搜索条件的变化
    watchLeftPanelQuery () {
      const me = this
      this.scrollQueryWatch && this.scrollQueryWatch()
      const transferVm = this.$refs.myTransfer
      transferVm.$refs.leftPanel.query = ''
      this.scrollQuery = ''
      this.scrollQueryWatch = transferVm.$refs.leftPanel.$watch('query', function (val) {
        me.scrollQuery = val
        me.scrollDebounceQuery()
      })
    },

    // 监听左侧全选是否选中
    watchLeftAllChecked () {
      const me = this
      this.scrollAllCheckedWatch && this.scrollAllCheckedWatch()
      const transferVm = this.$refs.myTransfer
      this.scrollAllCheckedWatch = transferVm.$refs.leftPanel.$watch('allChecked', function (val) {
        ...
      })
    },    

    /* 搜索条件如果从接口查询,过滤函数永远返回true  */
    scrollTransferFilterMethod (query, item) {
      return true
    },

    async leftTransferChange (keys) {
      // 判断是否点击全选(选中)
      if (keys.length + this.selectedData.length === this.tableList.length) {
      }
    },

    /* 缓存选中的数据(移到右边的数据) */
    transferChange (keys) {
      keys.forEach(key => {
        const item = this.tableList.find(o => o.key === key)
        this.selectedDataMap.set(key, item)
      })
    }
  }
}
</script>

这里实现的是后端分页,就是每次都要请求接口。如果一次性获取到所有数据,前端只不过模拟分页请看这篇文章