[Vue+Element UI] el-tree 实现加载更多(分页加载)功能

请添加图片描述

前言

当树形图含有大量的数据时,一次性全部加载出来会很慢,用户体验不好,这时需要做数据的分级加载,也就是懒加载,但如果每级的数据量也很大,就需要对每一级都做分页加载了。

思路

  1. 点击一般父层级节点组件会自动触发懒加载, 而点击 LOAD_MORE_NODES 节点需在节点点击事件 handleNodeClick 触发懒加载请求数据;
  2. LOAD_MORE_NODES 节点数据需要额外添加三个字段:
    isLoadingMoreNode 为区分加载更多节点的标识;
    isloading 记录节点是否正在加载,用于避免数据请求时避免重复点击;
    pagination 用于记录当前层级下的分页状态
export const LOAD_MORE_NODES = ({ nodeId, currentPage, pageSize }) => {
  return {
    id: 'load-more-id',
    label: '加载更多',
    parentId: nodeId, 
    leaf: true,
    isLoadingMoreNode: true, // 区分加载更多节点的标识
    isloading: false, // 记录节点是否正在加载
    pagination: { // 记录分页数据
      currentPage,
      pageSize,
    },
  }
}
  1. 在懒加载方法中,
    请求数据前, 需要先判断 LOAD_MORE_NODES 的状态: 根据节点的isloading 字段状态来做避免重复点击处理。
    若为 true 则退出, 否则就将该字段状态更新为 true
    此外, LOAD_MORE_NODES对应的数据页码参数需传当前的下一页, 其他的则为初始值第一页。
// 在懒加载请求数据方法里, 对 loadmore 节点进行额外的处理
if (isLoadingMoreNode) {
  // 若正在加载更多, 则退出方法, 避免重复请求
  if (isloading) return
  // 若首次点击加载更多, 则更新状态
  node.data.isloading = true
}

// loadmore 节点请求的是上一级节点的数据, 传给后端的参数为 parentId
let nodeId = isLoadingMoreNode ? parentId : id
// 只有加载更多节点有 pagination 当点击别的节点传默认值
let { currentPage = 1, pageSize = 5 } = pagination || {}
const params = {
  displayTable: true,
  parentId: nodeId,
  pageNo: isLoadingMoreNode ? ++currentPage : 1,
  pageSize,
}

在请求的回调方法里, 需要根据返回的数据条数来判断是否还需显示加载更多节点, 在移除原本的加载更多节点后, 再调用 el-treeappend 方法依次遍历增加新的节点数据。

//  处理节点列表
const nodesList = res.list?.map(CONST.MAP_NODES_LIST(isMarket))

// 若返回条数等于每页条数, 表明当前目录可能存在更多节点,则添加加载更多节点
if (res.list?.length == pageSize)
  nodesList.push(
    CONST.LOAD_MORE_NODES({ nodeId, currentPage, pageSize })
  )

// 若点击的节点是加载更多, 需先移除更多节点, 再添加新节点
if (isLoadingMoreNode) {
  const treeRef = this.$refs.tree
  const parentNode = treeRef.getNode(parentId)

  // 移除加载更多节点
  parentNode.removeChild(node)

  if (_.isEmpty(nodesList)) return

  // 添加新节点
  for (let el of nodesList) {
    treeRef.append(el, parentNode)
  }
  1. 做请求报错恢复点击处理
    若请求错误, 则在 catch 方法里将状态更新为 false, 以恢复节点的点击
api
  .queryDirectoryLazyLoad(params)
  .then((res) => {
  ...
  .catch((error) => {
    console.log(error)
    // 若请求报错, 则恢复更多加载的点击
    node.data.isloading = false
  })

全部代码

 <el-tree
   ref="tree"
   lazy
   :data="directoryTreeData"
   :load="queryTreeData"
   :props="CONST.TREE_DEFAULT_PROPS"
   node-key="id"
   :expand-on-click-node="false"
   @node-click="handleNodeClick"
   ... // 省略其他代码
 >
   <div class="custom-tree-node" slot-scope="{ node, data }">
     .... // 省略其他代码
     <div
       class="custom-tree-node-label-box"
       :class="{ isLoadingMoreNode: data.isLoadingMoreNode }"
     >
    ... // 省略其他代码
     </div>
   </div>
 </el-tree>  
  
     handleNodeClick(data, node) {
      if (data.isLoadingMoreNode) {
        this.queryTreeData(node)
        return
      }
      ... // 省略其他代码
      }

     // 获取目录数据
    queryTreeData(node, resolve) {
      // 这里根节点无需请求数据
      if (node.level == 0) {
        resolve([CONST.ROOT_NODE])
        return
      }

      const {
        parentId,
        id,
        pagination,
        isLoadingMoreNode,
        isloading,
        isMarket,
      } = node.data || {}

      // 在懒加载请求数据方法里, 对 loadmore 节点进行额外的处理
      if (isLoadingMoreNode) {
        // 若正在加载更多, 则退出方法, 避免重复请求
        if (isloading) return
        // 若首次点击加载更多, 则更新状态
        node.data.isloading = true
      }

	  // loadmore 节点请求的是上一级节点的数据, 传给后端的参数为 parentId
      let nodeId = isLoadingMoreNode ? parentId : id
      // 只有加载更多节点有 pagination 当点击别的节点传默认值
      let { currentPage = 1, pageSize = 5 } = pagination || {}
      const params = {
        displayTable: true,
        parentId: nodeId,
        pageNo: isLoadingMoreNode ? ++currentPage : 1,
        pageSize,
      }
      
      api
        .queryDirectoryLazyLoad(params)
        .then((res) => {
          // 处理节点列表
          const nodesList = res.list?.map(CONST.MAP_NODES_LIST(isMarket))

          // 若返回条数等于每页条数, 表明当前目录可能存在更多节点,则添加加载更多节点
          if (res.list?.length == pageSize)
            nodesList.push(
              CONST.LOAD_MORE_NODES({ nodeId, currentPage, pageSize })
            )

          // 若点击的节点是加载更多, 需先移除更多节点, 再添加新节点
          if (isLoadingMoreNode) {
            const treeRef = this.$refs.tree
            const parentNode = treeRef.getNode(parentId)

            // 移除加载更多节点
            parentNode.removeChild(node)

            if (_.isEmpty(nodesList)) return

            // 添加新节点
            for (let el of nodesList) {
              treeRef.append(el, parentNode)
            }
          } else {
          // 普通节点直接返回
            resolve(nodesList)
          }
        })
        .catch((error) => {
          console.log(error)
          // 若请求报错, 则恢复更多加载的点击
          node.data.isloading = false
        })
    },
// const.js

// 目录树 - 默认props
export const TREE_DEFAULT_PROPS = {
  children: 'children',
  label: 'label',
  isLeaf: 'leaf',
}

// 目录树 - 根节点
export const ROOT_NODE = {
  id: '-1',
  label: '全部',
  leaf: false,
  isFolder: true,
}

// 目录树 - 默认节点
export const MAP_NODES_LIST = (isMarket) => {
  return ({ id, name, hasChildren, nodeType, code }) => ({
    id,
    label: nodeType == 1 ? name : name || code,
    leaf: !hasChildren,
    isFolder: nodeType == 1,
    isMarket: isMarket || name == '数据集市',
  })
}

// 目录树 - 加载更多节点
export const LOAD_MORE_NODES = ({ nodeId, currentPage, pageSize }) => {
  return {
    id: 'load-more-id',
    label: '加载更多',
    parentId: nodeId,
    isLoadingMoreNode: true,
    leaf: true,
    pagination: {
      currentPage,
      pageSize,
    },
  }
}
// 加载更多样式
.custom-tree-node-label-box.isLoadingMoreNode {
  color: #2590ff;
}

::v-deep .el-tree-node__content:has(.isLoadingMoreNode):hover,
::v-deep .el-upload-list__item:has(.isLoadingMoreNode):hover,
::v-deep .el-tree-node__content:has(.isLoadingMoreNode) {
  background-color: transparent !important;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值