【vue】解决el-tree或el-table中树形列表懒加载数据改变后不能实时刷新问题以及使用

文章介绍了在Vue项目中遇到el-table树型结构数据修改后无法自动刷新的问题,提出了解决方案。通过使用Map存储数据,监听并处理load方法和expand-change事件,确保在增删改查操作后能实时刷新树形结构。同时,文章还详细解释了Map对象的使用以及如何在el-tree的懒加载场景下手动刷新数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.问题描述

在项目中遇到一个关于el-table的树型结构修改数据后不刷新的问题,需要手动刷新之后才能刷新

2.问题解决

1.首先在data return中加入一个map来用于存取数据

data() {
    return {
      maps: new Map()
    }
  }

知识补充:
Map对象:
1.Map是用来寸键值对的数据,它与Object不同在于:

  • Object中的属性名只能是字符串或符号,如果传递了一个其他类型的属性名,JS解释器会自动将其转换为字符串
  • Map中任何类型的值都可以成为数据的key
  • 2.创建Map对象
    const map = new Map()
    
    map.set("key", "value")
    
    
    map.delete(key)
    
    console.log(map.get("key"))   //value
    console.log(map.has("key"))  //ture or false
    

    3.常用方法介绍

  • map.size() 获取map中键值对的数量
  • map.set(key, value) 向map中添加键值对
  • map.get(key) 根据key获取值
  • map.delete(key) 删除指定数据
  • map.has(key) 检查map中是否包含指定键
  • map.clear() 删除全部的键值对
  • map.keys() 获取map的所有的key
  • map.values() 获取map的所有的value
  • 4.Map转数组

    // 方法一:
    const arr = Array.from(map) // [["name","孙悟空"],["age",18]]
    // 方法二:
    const arr = [...map]
    

    参考借鉴于:Map对象及常用方法介绍

    2.在el-tree或树形列表组件加载子级的方法中,用map存下每次被加载的父节点

    async loadChildren(tree,treeNode,rsolve){
    	this.maps.set(tree.id,{tree,treeNode,resolve}) //储存数据
    	// 调用接口,回显树型内容
    	const resp =  await this.getChildren(tree.id)
    	resolve(resp.data)
    }
    

    3.封装一个refreshLoadTree方法,每次增删改查操作之后都调用一下,以此达到实时刷新的目的

    refreshLoadTree(lazyTreeNodeMap, maps, parentId) {
          if (maps.get(parentId)) {
            const { tree, treeNode, resolve } = maps.get(parentId)
            this.$set(lazyTreeNodeMap, parentId, [])
            if (tree) { // 重新执行父节点加载子级操作
              this.loadChildren(tree, treeNode, resolve)
              if (tree.parentId) { // 若存在爷爷结点,则执行爷爷节点加载子级操作,防止最后一个子节点被删除后父节点不显示删除按钮
                const a = maps.get(tree.parentId)
                this.loadChildren(a.tree, a.treeNode, a.resolve)
              }
            }
          }
        }
    

    传入的三个参数分别是:组件懒加载数据的节点,存储的maps数据,被操作节点的父节点

    使用

    this.refreshLoadTree(this.$refs.table.store.states.lazyTreeNodeMap, this.maps, this.temp.parentId)
    

    组件

    <el-table ref="table"></el-table>
    

    原理

    首先从map中取出刚刚架子啊过子级节点的数据,再用this.$set清空对应父节点的数据,实现视图实时刷新,再通过取出的数据重新加载父节点

    以上内容参考:解决el-tree或树形列表懒加载数据改变后不能实时刷新问题

    3.重新发现问题

    3.1问题描述

    在使用el-table表格懒加载子级数据,懒加载仅会在第一次展开时加载,后续会使用第一次缓存的数据,而不会重新加载。

    3.2 问题解决

     <el-table
        :data="tableData1"
        style="width: 100%"
        row-key="id"
        border
        lazy
        :load="load"
        :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
        @expand-change="hanleExpandChange">
        <!--   内容省略   -->
    </el-table>
    

    字段描述

    row-key:渲染树型结构数据的时候为必填
    lazy :属性为true的时候加载函数load
    @expand-change : 当用户对某一行展开或者关闭的时候会触发该事件,(展开行时,回调的第二个参数为 expandedRows;树形表格时第二参数为 expanded)

    1.load方法中的内容

    async load(tree,treeNode,resolve){
    	// 在声明的全局变量中,增加一个key为本条数据的id,id可替换为数据中的任意值
    	this.tableTreeRefreshTool[tree.id] = {}
        // 重要!保存resolve方法,以便后续使用
        this.tableTreeRefreshTool[tree.id].resolve = resolve
        // 记录展开次数,具体作用后续介绍
        this.tableTreeRefreshTool[tree.id].expandCount = 0
    	// 调用接口
    	const resp =  await this.getChildren(tree.id)
    	resolve(resp.data)
    }
    

    2.设置expand-change事件

    async hanleExpandChange(row,expanded){
    	// 获取到之前保存的全局变量
    	const curr = this.tableTreeRefreshTool[row.id]
    	// 展开次数+1
    	curr.expandCount++
    	// 如果是展开状态,且展开次数大于1,且上一次的状态为折叠,则请求api数据更新子菜单
    	 if (expanded && curr.expandCount > 1 && !curr.prevStatus) {
            // api请求
            const resp =  await this.getChildren(tree.id)
    		curr.resolve(resp.data)
          
        }
        // 保存本次的展开或折叠状态,用于下次判断
        curr.prevStatus = expanded
    
    }
    

    数据解释

    expandCount 代表展开次数,这是为了防止第一次展开时,已经被lozyload加载过了,这里再次重复加载导致冲突
    使用prevStatus是因为调用resolve方法后,会再次触发这个expand-change方法,如果不进行判断,会导致死循环

    以上内容参考:ElementUI el-table树形表格懒加载如何手动刷新 el-table树状表格手动刷新的方法

    至此,有关于load的bug修改完成,完整代码如下

    <template>
    	 <el-table
    	    :data="tableData1"
    	    style="width: 100%"
    	    row-key="id"
    	    border
    	    lazy
    	    :load="load"
    	    :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
    	    @expand-change="hanleExpandChange">
    	    <!--   内容省略   -->
    	</el-table>
    </template>
    <script>
    export default {
    	data(){
    		return {
    			 mapstableTree: new Map(),
         		 tableTreeRefreshTool: {},
    		}
    	},
    	methods:{
    		load(tree, treeNode, resolve){
    	 	 //增删改的时候存储数据
    	      this.mapstableTree.set(tree.id, { tree, treeNode, resolve })
    	      // 在之前声明的全局变量中,增加一个key为 本条数据的id,id可替换为你数据中的任意唯一值
    	      this.tableTreeRefreshTool[tree.id] = {}
    	      //  重要!保存resolve方法,以便后续使用
    	      this.tableTreeRefreshTool[tree.id].resolve = resolve
    	      //  记录展开次数,具体作用后续介绍
    	      this.tableTreeRefreshTool[tree.id].expandCount = 0
    	
    	
    	
    	      getAxios(tree.id).then(res => {
    	        resolve(res.data.erpOrderProductList)
    	      })
    		},
    		 //封装一个增删改刷新table数据的方法,组件懒加载数据的节点,data return的maps,及被操作节点的父节点
    	    refreshLoadTree (lazyTreeNodeMap, maps, parentId) {
    	      console.log("单个刷新的tree", lazyTreeNodeMap, maps, parentId);
    	      if (maps.get(parentId)) {
    	        const { tree, treeNode, resolve } = maps.get(parentId)
    	        this.$set(lazyTreeNodeMap, parentId, [])
    	        if (tree) { // 重新执行父节点加载子级操作
    	          this.loadTreeChild(tree, treeNode, resolve)
    	          if (tree.parentId) { // 若存在爷爷结点,则执行爷爷节点加载子级操作,防止最后一个子节点被删除后父节点不显示删除按钮
    	            const a = maps.get(tree.parentId)
    	            this.loadTreeChild(a.tree, a.treeNode, a.resolve)
    	          }
    	        }
    	      }
    	    },
    	     handleExpandChange (row, expanded) {
    
    	      const curr = this.tableTreeRefreshTool[row.id]
    	
    	      // this.refreshLoadTree(this.$refs.table.store.states.lazyTreeNodeMap, this.mapstableTree, this.form.productId)
    	      curr.expandCount++
    	      // 如果是展开状态,且展开次数大于1,且上一次的状态为折叠,则请求api数据,更新子菜单
    	      if (expanded && curr.expandCount > 1 && !curr.prevStatus) {
    	     
    	        getAxios(row.id).then(res => {
    	          curr.resolve(res.data.shopProductVariantList)
    	        })
    	      }
    	      // 保存本次的展开或折叠状态,用于下次判断
    	      curr.prevStatus = expanded
    	    },
    		// 提交修改按钮
    		submitForm(){
    			 this.$refs["form"].validate(valid => {
            if (valid) {
              this.$refs['tableform'].validate(table => {
                if (table) {
                  if (this.form.id != null) {
                    updateErpOrder(this.form).then(response => {
                      this.$modal.msgSuccess("修改成功");
                      this.open = false;
                      this.getList();
                      // 调用刷新展开的table
                      this.refreshLoadTree(this.$refs.table.store.states.lazyTreeNodeMap, this.mapstableTree, this.form.id)
                    });
                  } else {
                    addErpOrder(this.form).then(response => {
                      this.$modal.msgSuccess("新增成功");
                      this.open = false;
                      this.getList();
                    });
                  }
                }
              })
    
    
            }
          });
    		}
    	}
    	
    }
    </script>
    
    
### 实现 Vue3 中 Element Plus 表格组件的树形结构懒加载 为了实现 `el-table` 组件中的树形数据懒加载功能,在定义表格列时需设置特定属性来支持这一特性。对于每一行的数据对象,应包含一个名为 `_expand` 的布尔值字段用于指示该节点是否已展开;以及一个函数类型的 `children` 属性用来异步获取子项列表[^1]。 下面是一个简单的例子展示如何配置 `el-table-column` 和编写相应的事件处理逻辑: ```html <template> <div id="app"> <el-table :data="tableData" row-key="id" :load="load" lazy> <!-- 定义其他常规表头 --> <el-table-column prop="name" label="Name"></el-table-column> <!-- 添加带有 expand-icon-class 的特殊列以启用折叠图标 --> <el-table-column type="expand"> <template slot-scope="props"> <p v-for="(item, index) in props.row.children">{{ item.name }}</p> </template> </el-table-column> <!-- 更多列... --> </el-table> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; const tableData = ref([ { id: 1, name: "Parent Node", hasChildren: true // 假设我们知道哪些记录有孩子节点 }, ]); // 当用户点击展开按钮时触发此方法 function loadChildren(parentId:number):Promise<Array<{id:number,name:string}>>{ return new Promise((resolve,reject)=>{ setTimeout(() => { resolve([{"id":parentId*10,"name":"Child A"},{"id":parentId*10+1,"name":"Child B"}]); }, 200); }); } async function load(tree, treeNode, resolve){ const children=await loadChildren(tree.id); resolve(children); } </script> ``` 在这个案例里,当某一行被首次展开的时候会调用 `load()` 方法并传入当前行的信息作为参数。此时可以发起网络请求其他方式去实际拉取对应父级下的所有子项目,并通过回调传递给框架完成渲染更新操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值