当前位置: 首页 > news >正文

el-tree(append与getNode与过滤)、深拷贝deepClone、监听addEventListener、

场景
(1)在父组件Parent里监听到数据变化,调用子组件Child暴露出对应的方法;
(2)子组件里切换el-tab时展示onRef树或inRef树(需要深拷贝数据,导致两个树数据一样)
问题:如果两个树中存在相同的父节点 ID(例如从同一接口获取的初始数据),且未对这些父节点做深拷贝,当调用 append 方法时,可能会错误地将子节点添加到两个树的同一父节点引用中。
(3)根据树id更新树节点,而不是通过遍历,避免搜索到某个节点后,做一些编辑就刷新整个列表。
树结构(每个学生节点下再分子节点In内地或者on外地节点):
Student1 》onData1
》InData1
Student2 》Student2-1 》onData2-1
》Student2 -2 》Student2-2-1 》onData2-2-1
》InData2
》InData2
Parent.vue

<template>
 <div>
     <Child ref="childRef"></Child >
  </div>
</template>
onMounted(() => {
  loadData();
});

function loadData(){
//监听学生增加事件
studentData.addEventListener(studentAdd, (e: Event) => {
    let dataEvent = e as DataEvent;
    if(dataEvent){
      if(childRef.value){
        //监听事件里获取新增学生信息
        const studentData= dataEvent.studentData;
        if(studentData){
          //增加此数据分别到子组件的onRef树与inRef树里
          //(此处需要深拷贝,因为节点对象的 parent 引用需要隔离,否则实际共享同一 parent 对象!
          //此时,向任一树追加子节点时,两个树的父节点 children 数组会共享引用,就是改一个树另一个树也改变,困扰我好久..)
		  childRef.value.addOnNode(deepClone(studentData));
		  childRef.value.addInNode(deepClone(studentData));
        }
      }  
    }
  });
  //监听学生修改事件
  studentData.addEventListener(studentUpdate, (e: Event) => {
    let dataEvent = e as DataEvent;
    if(dataEvent){
      if(childRef.value){
         //监听事件里获取修改学生信息
         const studentData= dataEvent.studentData;
          childRef.value.updateChildNode(studentData);
        }
      }
    }
  });
  //监听学生移除事件
  studentData.addEventListener(studentRemove, (e: Event) => {
    let dataEvent = e as DataEvent;
    if(dataEvent){
      if(childRef.value){
         //监听事件里获取修改学生信息
         const studentData= dataEvent.studentData;
          childRef.value..removeChildNode(studentData);
      }
    }
  });
  //监听外地增加事件
studentData.onData.addEventListener(studentOnAdd, (e: Event) => {
    let dataEvent = e as DataEvent;
    if(dataEvent){
      if(childRef.value){
        //监听事件里获取新增外地信息
        const studentData= dataEvent.studentData;
        if(studentData){
		  childRef.value.addOnNode(studentData);
        }
      }  
    }
  });
....
}
function deepClone(target: any) {
        // 定义一个变量
        let result: any;
        // 如果当前需要深拷贝的是一个对象的话
        if (typeof target === 'object') {
        // 如果是一个数组的话
            if (Array.isArray(target)) {
                result = []; // 将result赋值为一个数组,并且执行遍历
                for (let i in target) {
                    // 递归克隆数组中的每一项
                    result.push(this.deepClone(target[i]))
                }
                // 判断如果当前的值是null的话;直接赋值为null
            } else if(target===null) {
                result = null;
                // 判断如果当前的值是一个RegExp对象的话,直接赋值    
            } else if(target.constructor===RegExp){
                result = target;
            }else {
                // 否则是普通对象,直接for in循环,递归赋值对象的所有值
                result = {};
                for(let key in target){
                    result[key] = this.deepClone(target[key]);
                }
            }
            // 如果不是对象的话,就是基本数据类型,那么直接赋值
        } else {
            result = target;
        }
            // 返回最终结果
        return result;
    },

Child.vue

<template>
  <div>
      <el-tabs class="tabs" @tab-click="handleClick" v-model="unitModal">
        <el-tab-pane label="外地" name="on">
          <el-input
              v-model="onFilterText"
              placeholder="搜索"
              clearable
          />
          <el-scrollbar>
            <el-tree 
              ref="onRef"
              :data="onStudent" 
              node-key="id" 
              :filter-node-method="filterOnNode"
              default-expand-all 
              >
              <template #default="{ node, data }">
                <slot :node="node" :data="data">
                  <img :src="data.icon" style="height: 25px;width: 25px">
                  <span style="margin: 0 10px;">{{ data.name }}</span>
                </slot>
              </template>
            </el-tree>
          </el-scrollbar>
        </el-tab-pane>
        <el-tab-pane label="内地" name="in">
          <el-input
              v-model="inFilterText"
              placeholder="搜索"
          />
          <el-scrollbar>
            <el-tree
              ref="inRef"
              :data="inStudent"
              node-key="id" 
              :filter-node-method="filterInNode"
              default-expand-all
              >
              <template #default="{ node, data }">
                <slot :node="node" :data="data">
                  <img :src="data.icon" style="height: 25px;width: 25px">
                  <span style="margin: 0 10px;">{{ data.name }}</span>
                </slot>
              </template>
            </el-tree>
          </el-scrollbar>
        </el-tab-pane>
      </el-tabs>
  </div>
</template>
const handleClick = (tab: TabsPaneContext) => {
  
}
//外地过滤
const onFilterText = ref("")
watch(onFilterText, (val) => {
  onRef.value!.filter(val)
})
const filterOnNode = (value: string, data: Tree) => {
  if (!value) return true
  return data.name.includes(value);
}

//内地过滤
const inFilterText = ref("")
watch(inFilterText, (val) => {
  inRef.value!.filter(val)
})
const filterInNode = (value: string, data: Tree) => {
  if (!value) return true
  return data.name.includes(value);
}

//外地树
function addOnNode(data: any) {
  if(onRef.value){
    //是否有父节点,没有的话通过parentId拿到编制树的父节点
    if(data.parentId){
        const key = data.parentId;
        const node = onRef.value.getNode(key);
        if (node) {
        //通过父节点为编制树追加一个子节点
        const nodeParent = onRef.value.getNode(data.parentId);
        onRef.value.append(data,nodeParent);
        }
      }else{
        onRef.value.append(data,null);
      }
  }
}
//内地树
function addInNode(data: any) {
  if(inRef.value){
    //是否有父节点,没有的话通过parentId拿到编制树的父节点
    if(data.parentId){
        const key = data.parentId;
        const node = inRef.value.getNode(key);
        if (node) {
        //通过父节点为编制树追加一个子节点
        const nodeParent = inRef.value.getNode(data.parentId);
        inRef.value.append(data,nodeParent);
        }
      }else{
        inRef.value.append(data,null);
      }
  }
}

//修改节点
function updateChildNode(data: any){
  if(onRef.value){
    const key = data.id;
    const node = onRef.value.getNode(key);
    if (node) {
      // 保留子节点数据
      const childrenBackup = node.data.children || [];
      // 合并数据
      Object.assign(node.data, {
        ...data,
        children: childrenBackup
      });
    }
  }
  if(inRef.value){
    const key = data.id;
    const node = inRef.value.getNode(key);
    if (node) {
      // 保留子节点数据
      const childrenBackup = node.data.children || [];
      // 合并数据
      Object.assign(node.data, {
        ...data,
        children: childrenBackup
      });
    }
  }
}

//移除节点
function removeChildNode(data: any){
  if(onRef.value){
    onRef.value.remove(data);
  }
  if(inRef.value){
    inRef.value.remove(data);
  }
}


defineExpose({
  addOnNode,
  addInNode,
  updateChildNode,
  removeChildNode
})

相关文章:

  • Aruco 库详解:计算机视觉中的高效标记检测工具
  • C++ 接口(抽象类)
  • 深入理解 CAS 与 ABA 问题
  • 基于Docker去创建MySQL的主从架构
  • Xss漏洞问题
  • 特定领域软件架构DSSA
  • 五、数组维度
  • 批量合并 Word 文档,支持合并成一个 Word,也支持按文件夹合并
  • 网络安全技术整体架构 一个中心三重防护
  • 进程间的通信1
  • Jetson Orin 安装 onnxruntime
  • Web3中的AI:一种去中心化智能的完整指南
  • 奥运会运动员年龄规定·棒球1号位
  • 【C++】5.6 try语句和异常处理
  • C# 命名空间(Namespace)详解
  • 稀疏注意力:打破Transformer计算瓶颈,实现高效长序列建模
  • ES02 - ES语句
  • TMS320F28P550SJ9学习笔记7:结构体寄存器方式配置SCI通信收发_SCI通信收发测试
  • C/C++ 实现由用户通过键盘输入自然数并判断其是不是素数(带清空缓冲区等考虑)
  • 【MySQL-数据类型】数据类型分类+数值类型+文本、二进制类型+String类型
  • 喜欢做木工 网站/东莞做网站seo
  • 专业网站设计建设服务/怎样在百度答题赚钱
  • 做一个网站需要多少钱 怎么做/百度收录查询方法
  • 如何用dreamweaver做网站/seo外包服务方案
  • 湖南省人民政府网站集约化建设/全专业优化公司
  • 台州网站seo外包/霸屏seo服务