Vue3可媲美Element Plus Tree组件开发之append节点

news2024/9/19 13:20:15

在前面的章节,我们完成了可媲美Element Plus Tree组件的基本开发。通过实现各种计算属性,tree数据状态变化引起的视图更新被计算属性所接管了,无需我们再手动做各种遍历、查找以及手动监听操作,这样后续开发高级功能变得易如反掌啦!!

在这里插入图片描述

看下提供给用户的vitepress文档说明:

在这里插入图片描述

在这里插入图片描述

操作演示:

在这里插入图片描述

前面我们实现了几个计算属性:

  • index

    节点在扁平化列表中的位置索引

  • length

    父节点的所有子孙节点的长度

  • visibleLength

    可见子孙节点的长度

  • lineLength

    参照线的长度

这些计算属性在新增一个节点,尤其是子节点时都会被影响到,触发重新计算以保证前面实现的基本功能是完好的。而无需我们在实现新增节点时再去兼顾基础功能,这就是Vue3 composition api的计算属性的魅力,让复杂的功能变得简单,组件的开发者只需要把关注点放到影响计算属性变化的数据上即可,Life is so easy!

新增类型、接口

定义ts的类型和接口,注意给用户提供的接口一定要遵循“迪米特法则”。

在这里插入图片描述

核心插入逻辑

/**
 * 新增顶级节点
 * @param child 要新增的叶子节点
 * @param data 扁平化节点列表
 * @param treeData 结构化节点树
 * @param optionProps 组件配置选项
 */
export function appendTop(child: ILeafNode, data: IFlatTreeNode[], treeData: ITreeNode[], optionProps: OptionProps) {
  // 节点id命名逻辑:如果指定了就用用户指定的,否则按照列表长度生成
  child.id = child.id || 'id-' + (data.length + 1)
  // 从新增节点拷贝数据作为original child node
  const ocNode = { ...child }
  // 扁平化new child node
  const ncNode = {
    ...child,
    level: 1,
    isLeaf: true,
    originalNode: ocNode
  } as IFlatTreeNode
  // 要插入的位置为列表最后
  const insertIndex = data.length
  // 绑定新插入的扁平化节点的前置节点
  ncNode.prev = data[data.length - 1]
  // 对新的扁平化节点进行初始化
  initFlatTreeNode(ncNode, optionProps)
  // 原始树结构中新增节点,注意!!操作的是响应式数据
  ref(treeData).value.push(ocNode as never)
  // 扁平化列表中插入新节点
  ref(data).value.splice(insertIndex, 0, ncNode as never)
}

function initFlatTreeNode(node: IFlatTreeNode, optionProps: OptionProps) {
  ...

  /**
   * 给扁平化节点绑定新增子节点的方法
   * @param child 新增的子节点
   * @param data 扁平化列表
   */
  node.append = (child: ILeafNode, data: IFlatTreeNode[]) => {
    // 同新增一级节点
    child.id = child.id || 'id-' + (data.length + 1)
    // 当前节点原始节点
    const oNode = node.originalNode
    // 新增节点原始节点
    const ocNode = { ...child }
    // 新增节点扁平化节点
    const ncNode = {
      ...child,
      parent: node, // 绑定父节点
      level: node.level + 1,
      isLeaf: true,
      originalNode: ocNode
    } as IFlatTreeNode
    // 计算插入位置
    const insertIndex = calcInsertIndex(node)
    // 插入到最后的情况下,设置前置节点
    if (insertIndex === data.length) {
      ncNode.prev = data[data.length - 1]
    } else {
      // 插入到中间,绑定prev的逻辑,把prev链接起来
      const next = data[insertIndex]
      ncNode.prev = next.prev
      // 注意操作的是响应式对象,以确保可以触发index属性重新计算!!
      ref(next).value.prev = ncNode as never
    }
    // 初始化扁平化节点
    initFlatTreeNode(ncNode, optionProps)
    // 通过响应式对象获取其操作对象
    const oNodeVal = ref(oNode).value
    const nodeVal = ref(node).value
    const childrenName = optionProps.childrenName as 'children'
    // 对原先的叶子节点进行设置和初始化,变为非叶子节点
    if (!oNodeVal[childrenName]) {
      oNodeVal[childrenName] = []
      initParentNode(oNode, optionProps)
      nodeVal.isLeaf = false
    }
    // 插入到原始结构化节点
    oNodeVal[childrenName].push(ocNode as never)
    // 所在的节点将其展开(如果折叠的话)
    oNodeVal.expanded = true
    nodeVal.expanded = true
    // 插入到扁平化节点列表
    ref(data).value.splice(insertIndex, 0, ncNode as never)
  }
}

/**
 * 插入子节点位置逻辑:如果是叶节点,则为下一个位置,否则要加上子一代节点的长度
 * @param node
 */
function calcInsertIndex(node: IFlatTreeNode): number {
  return node.index.value + 1 + (node.isLeaf ? 0 : node.originalNode.length!.value)
}

Tree组件模板调整

原先给icon插槽传入的节点参数,不符合迪米特法则,暴露了内部操作属性和方法,规范的做法是拷贝一个副本!!只给用户提供其关心的几个属性,调整为:

在这里插入图片描述

对于一级节点新增操作,我们将对tree组件expose一个可操作的对象,为此把这个对象中要定义的方法抽取到ts接口中,以方便客户端API的使用:

// Tree组件对外导出的方法定义
export interface ExposeProps {
  appendTop: (newNode: ILeafNode) => void
}

导出逻辑:

在这里插入图片描述

而针对节点的操作,给用户提供的API,包装一个函数来返回要操作的接口:

// 返回节点操作方法的函数
const nodeOperation = (node: IFlatTreeNode): ITreeNodeOperation => {
  // 注意,这里不应该直接给用户提供node,而是要包成对外公开的ITreeNodeOperation,遵循迪米特法则!!
  return {
    append(newNode: ILeafNode) {
      node.append(newNode, originalFlatData)
    }
  }
}

对应的插槽实现的地方:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1945662.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

插入和选择排序

1.1直接插入排序 void InsertSort(int* a, int n) {for (int i 1; i < n - 1; i) {//i的范围要注意的&#xff0c;防止指针越界int end i;int tmp a[end 1];while (end>0) {if (tmp< a[end]) {a[end 1] a[end];//小于就挪动&#xff0c;虽然会覆盖后面空间的值…

Qt Creator平台编译snmp++

声明 &#xff1a;本文的大部分资源参考自文章&#xff0c;编译snmp的方法我也是在这里学习的&#xff0c;结合自己的需求&#xff0c;做了snmp和Agent的混合编译。需要了解更多的详情可以点击链接去看原文&#xff0c;我总结了自己的编译过程&#xff0c;并写下此文作为一个回…

Springboot+vue自制可爱英语日记系统-XD动画测试版

目录 项目背景与愿景 项目流程 需求分析 设计之美 技术实现 部署策略 未来展望 项目寄语 项目预览 项目页面展现 引导页(3张) 首页 日记模块 日记模块-写日记 信箱模块 回收箱模块 前端开发 前端开发概述 关键技术选型 开发流程 后端开发 后端开发概述 …

【算法/训练】:动态规划

一、路径类 1. 字母收集 思路&#xff1a; 1、预处理 对输入的字符矩阵我们按照要求将其转换为数字分数&#xff0c;由于只能往下和往右走&#xff0c;因此走到&#xff08;i&#xff0c;j&#xff09;的位置要就是从&#xff08;i - 1&#xff0c; j&#xff09;往下走&#x…

C++笔记3:基类指针delete子类对象的内存泄漏问题

根据《effective C》第7章所述&#xff0c;new的一个子类对象赋值给基类指针delete的时候为了防止子类的析构函数没有调用要在基类的析构函数加上virtual 关键字&#xff1a; #include <stdint.h> #include <iostream> #include <iomanip> #include <vec…

零代码实现GIS视效提升,一键添加体积云体积雾

在三维GIS开发中&#xff0c;场景的真实感和高效性始终是用户的核心需求。为此&#xff0c;山海鲸可视化提供了完美的解决方案。这款免费可视化工具不仅支持多种GIS影像协议&#xff08;TMS、WMS、WMTS等&#xff09;&#xff0c;还可以一键添加体积云和体积雾效果&#xff0c;…

FastGPT 知识库搜索测试功能解析(一)

本文以 FastGPT 知识库的搜索测试功能为入口,分析 FastGPT 的知识检索流程。 一、搜索功能介绍 1.1 整体介绍 搜索测试功能包含三种类型:语义检索、全文检索、混合检索。 语义检索:使用向量进行文本相关性查询,即调用向量数据库根据向量的相似性检索; 全文检索:使用…

Axure软件新功能解析与应用技巧分享

Axure是一种用于创建原型和交互设计的软件工具&#xff0c;广泛应用于操作界面。&#xff08;UI&#xff09;和客户体验&#xff08;UX&#xff09;为了展示和测试应用程序、网站或其他数据产品的性能和操作界面&#xff0c;设计帮助产品经理、设计师和开发者制作具有交互性的原…

17.jdk源码阅读之LinkedBlockingQueue

1. 写在前面 LinkedBlockingQueue 是 Java 并发包中的一个重要类&#xff0c;常用于生产者-消费者模式等多线程编程场景。上篇文章我们介绍了ArrayBlockingQueue&#xff0c;并且与LinkedBlockingQueue做了简单的对比&#xff0c;这篇文章我们来详细分析下LinkedBlockingQueue…

RAS--APEI 报错解析流程(2)

RAS--APEI 报错解析流程(1) 除了APEI 中除了GHES会记录错误&#xff0c;在Post过程中的错误通常是通过BERT Table汇报 1.BERT Boot Error Record Table is used to report unhandled errors that occurred in a previous boot&#xff0c;it is reported as a ‘one-time polle…

一文弄懂JVM类加载器与双亲委派机制

类的加载器完成类的加载环节中的装载阶段的工作&#xff08;通过一个类的全限定名来获取该类的二进制字节流&#xff0c;且这个动作在虚拟机**外部实现**&#xff0c;即开发者可以决定如何去获取所需的类&#xff09;&#xff0c;且**不会影响后续的链接和初始化阶段&#xff0…

《算法笔记》总结No.10——链表

从第10期破例插叙一期单链表的实现&#xff0c;这个东东相当重要&#xff01;考研的同学也可以看&#xff1a;相较于王道考研的伪码不太相同&#xff0c;专注于可以运行。如果是笔试中的伪码&#xff0c;意思正确即可~ 注&#xff1a;博主之前写过一个版本的顺序表和单链表的C实…

谷粒商城实战笔记-56~57-商品服务-API-三级分类-修改-拖拽功能完成

文章目录 一&#xff0c;56-商品服务-API-三级分类-修改-拖拽功能完成二&#xff0c;57-商品服务-API-三级分类-修改-批量拖拽效果1&#xff0c;增加按钮2&#xff0c;多次拖拽一次保存完整代码 在构建商品服务API中的三级分类修改功能时&#xff0c;拖拽排序是一个直观且高效的…

Linux:Linux权限

目录 1. Linux权限的概念 2. Linux权限管理 2.1 文件访问者的分类 2.2 文件类型和访问权限 2.2.1 文件类型 2.2.2 基本权限 2.3 文件权限值的表示方法 2.4 文件访问权限的相关设置方法 2.4.1 chmod 2.4.2 chown 2.4.3 chgrp 2.4.4 umask 3. file指令 4. Linux目…

如何学习EMR:糙快猛的大数据之路(建立整体框架)

目录 初学EMREMR是什么&#xff1f;我的EMR学习故事糙快猛学习法则代码示例: 你的第一个EMR任务学习EMR的深入步骤EMR进阶技巧实用资源推荐常见挑战和解决方案 EMR生态EMR生态系统深度探索1. EMR上的Hadoop生态系统2. EMR Studio3. EMR on EKS 高级EMR配置和优化1. EMR实例集策…

音视频入门基础:PCM专题(3)——使用Audacity工具分析PCM音频文件

音视频入门基础&#xff1a;PCM专题系列文章&#xff1a; 音视频入门基础&#xff1a;PCM专题&#xff08;1&#xff09;——使用FFmpeg命令生成PCM音频文件并播放 音视频入门基础&#xff1a;PCM专题&#xff08;2&#xff09;——使用Qt播放PCM音频文件 音视频入门基础&am…

ICML 2024最佳论文开奖了!今年的热门投稿方向有这些

ICML 2024最近也放榜啦&#xff01;今年共有10篇论文夺得最佳论文奖&#xff0c;包括火爆的Stable Diffusion 3、谷歌VideoPoet以及世界模型Genie。 ICML是国际机器学习顶会&#xff0c;也是CCF-A类学术会议。今年这届顶会一共收到了9473篇论文&#xff0c;其中2610篇被录用&am…

昇思25天学习打卡营第22天|基于MindNLP+MusicGen生成自己的个性化音乐

文章目录 昇思MindSpore应用实践1、MusicGen模型简介残差矢量量化&#xff08;RVQ&#xff09;SoundStreamEncodec 2、生成音乐无提示生成文本提示生成音频提示生成 Reference 昇思MindSpore应用实践 本系列文章主要用于记录昇思25天学习打卡营的学习心得。 1、MusicGen模型简…

Qt基础 | Qt SQL模块介绍 | Qt SQL模块常用类及其常用函数介绍

文章目录 一、Qt SQL模块概述1.Qt sql 支持的数据库2.SQLite 数据库3.Qt SQL 模块的主要类 一、Qt SQL模块概述 Qt SQL 模块提供数据库编程的支持&#xff0c;Qt 支持多种常见的数据库&#xff0c;如MySQL、Oracle、MS SQL Server、SQLite 等。Qt SQL 模块包括多个类&#xff0…

phpstorm配置xdebug3

查看php路径相关信息 php --ini安装xdebug https://www.jetbrains.com/help/phpstorm/2024.1/configuring-xdebug.html?php.debugging.xdebug.configure php.ini 配置 在最后添加&#xff0c;以下是我的配置 [xdebug] zend_extension/opt/homebrew/Cellar/php8.1/8.1.29/p…