Vue3(递归组件) + 原生Table 实现树结构复杂表格

一、递归组件

什么是递归,Javascript中经常能接触到递归函数。也就是函数自己调用自己。那对于组件来说也是一样的逻辑。平时工作中见得最多应该就是菜单组件,大部分系统里面的都是递归组件。文章中我做了按需引入的配置,所以看不到我引用组件、Vue3的相关API等等。需要了解的小伙伴可以看我的另一篇文章 Vite4+Pinia2+vue-router4+ElmentPlus搭建Vue3项目(组件、图标等按需引入)

二、Table的合并

复杂的表格无非就是行或者列的合并。主要涉及到colspan和rowspan。colspan属性规定单元格可横跨的列数。rowspan属性规定单元格可横跨的行数。比如下面的列子。第一行为标题。表格最多为5列。所以第一行的列需要全部合并,colspan的值就为5。第三行和第四行都是统一的食品分类,需要合并,所以第三行的第一列就需要往下合并。往下合并两行,所以rouspan就为2。但是这里要注意,列合并不用管。行的话被合并的列就需要删除。是不是很简单。两个设置就能实现下面的列子。

<template>
  <table class="table">
    <tr>
      <td colspan="5">某某小卖部</td>
    </tr>
    <tr>
      <td>商品分类</td>
      <td>商品</td>
      <td>价格</td>
      <td>库存</td>
      <td>描述</td>
    </tr>
    <tr>
      <td rowspan="2">食品</td>
      <td>瓜子</td>
      <td>5元</td>
      <td>20</td>
      <td>可以吃的瓜子</td>
    </tr>
    <tr>
      <td>花生</td>
      <td>6元</td>
      <td>30</td>
      <td>可以吃的花生</td>
    </tr>
  </table>
</template>

<style lang="scss">
.table {
  width: 100%;
  margin-left: 0;
  text-align: center;
  font-size: 12px;
}

.table th,
.table td {
  border: 1px solid #070707 !important;
  padding: 0.35rem !important;
  font-size: 16px;
  vertical-align: middle !important;
}

.ts-table-bold {
  td {
    font-weight: bold;
    font-size: 18px;
  }
}
</style>

三、核心方法

核心的方法主要就是行和列的合并规则,不管是我现在这个表格还是其他复杂的,只要你细心的观察。总会发现规则。然后就能利用js去实现。

因为需要知道树结构总共拥有多少节点,树结构有多少层级。我在列子中也用到了递归函数。比如下面的树结构转平行结构。参数tree的话表示树节点。第二个list表示我需要存储对象,第三个参数表示父节点的id。

// 获取整个树数据的长度  用于行的合并
const treeToList = (tree: TreeType[], list: TreeType[], parentId: string | null) => {
  for (let i in tree) {
    const nodeData = tree[i];
    list.push({
      id: nodeData.id,
      title: nodeData.title,
      parentId: parentId
    });
    if (nodeData.children && nodeData.children.length !== 0) {
      treeToList(nodeData.children, list, nodeData.id)
    }
  }
}

三、组件的封装完整代码

上面的列子是写死的,所以实现起来比较的简单,接下来就需要获取动态的数据,动态进行行或者列的合并实现复杂的表格展示。组件中props里面的参数:data就是数据源,level表示整个数据的层级,currentLevel表示当前递归到第几层。

<template>
  <tr v-if="!data.children || data.children.length === 0">
    <td :colspan="(level - currentLevel + 1) / 2">{{ data.title }}</td>
  </tr>
  <tr v-else>
    <td :rowspan="getTreeToArr(data.children) + 1">
      {{ data.title }}
    </td>
  </tr>
  <template v-for="it in data.children" :key="it.id">
    <ts-recursion-table :data="it" :level="level" :currentLevel="currentLevel + 1" />
  </template>
</template>

<script lang="ts">

type TreeType = {
  title: string
  id: string
  parentId: string | null
  children?: Array<TreeType>
}

export default defineComponent({
  name: 'TsRecursionTable',
  props: ['data', 'level', 'currentLevel'],
  setup() {

    // 获取整个树数据的长度  用于行的合并
    const treeToList = (tree: TreeType[], list: TreeType[], parentId: string | null) => {
      for (let i in tree) {
        const nodeData = tree[i];
        list.push({
          id: nodeData.id,
          title: nodeData.title,
          parentId: parentId
        });
        if (nodeData.children && nodeData.children.length !== 0) {
          treeToList(nodeData.children, list, nodeData.id)
        }
      }
    }

    const getTreeToArr = (data: any) => {
      let result:TreeType[] = []
      if (!data) {
        return 0
      }
      treeToList(data, result,null)
      return result.length
    }

    return {
      getTreeToArr
    }
  }
})
</script>

四、组件的使用完整代码

<template>
  <div style="width: 1000px;margin: 200px auto auto auto;">
    <table class="table">
      <tr>
        <td :colspan="level">某某区人数统计</td>
      </tr>
      <ts-recursion-table v-for="(item, index) in tableData" :key="item.id" :data="item" :level="level" :currentLevel="1" />
    </table>
  </div>
</template>

<script lang="ts">

type TreeType = {
  title: string
  id: string
  parentId: string | null
  children?: Array<TreeType>
}

export default defineComponent({
  setup() {
    const rowLength = ref<number>(0)
    const level = ref<number>(0)

    const state = reactive({
      tableData: [
        {
          title: '社区一',
          id: '1',
          parentId: null,
          children: [
            {
              title: '街道一',
              id: '1-1',
              parentId: '1',
              children: [
                {
                  id: '1-1-1',
                  parentId: '1-1',
                  title: '小区1',
                  children: [
                    {
                      id: '1-1-1-1',
                      parentId: '1-1-1',
                      title: '单元1',
                      children: [
                        {
                          id: '1-1-1-1-1',
                          parentId: '1-1-1-1',
                          title: '住户1'
                        },
                        {
                          id: '1-1-1-1-2',
                          parentId: '1-1-1-1',
                          title: '住户2'
                        }
                      ]
                    },
                    {
                      id: '1-1-1-2',
                      parentId: '1-1-1',
                      title: '单元2'
                    },
                  ]
                },
                {
                  id: '1-1-2',
                  parentId: '1-1',
                  title: '小区2'
                },
              ]
            },
            {
              title: '街道二',
              id: '1-2',
              parentId: '1',
              children: [
                {
                  id: '1-2-1',
                  parentId: '1-2',
                  title: '小区1'
                },
                {
                  id: '1-2-2',
                  parentId: '1-2',
                  title: '小区2'
                }
              ]
            }
          ]
        },
        {
          title: '社区二',
          id: '2',
          parentId: null,
          children: [
            {
              title: '街道一',
              id: '2-1',
              parentId: '2',
              children: [
                {
                  id: '2-1-1',
                  parentId: '2-1',
                  title: '小区1'
                },
                {
                  id: '2-1-2',
                  parentId: '2-1',
                  title: '小区2'
                },
                {
                  id: '2-1-3',
                  parentId: '2-1',
                  title: '小区3'
                },
              ]
            }
          ]
        }
      ] as TreeType[]
    })

    // 获取整个树数据的长度  用于行的合并
    const treeToList = (tree: TreeType[], list: TreeType[], parentId: string | null) => {
      for (let i in tree) {
        const nodeData = tree[i];
        list.push({
          id: nodeData.id,
          title: nodeData.title,
          parentId: parentId
        });
        if (nodeData.children && nodeData.children.length !== 0) {
          treeToList(nodeData.children, list, nodeData.id)
        }
      }
    }

    // 获取整个树数据的层级 用于列的合并
    const getTreeLevel = (arr: TreeType[]) => {
      let maxLevel = 0;
      (function callBack(arr, level) {
        ++level;
        maxLevel = Math.max(level, maxLevel);
        for (let i = 0; i < arr.length; i++) {
          let item = arr[i];
          if (item.children && item.children.length > 0) {
            callBack(item.children, level);
          } else {
            delete item.children;
          }
        }
      })(arr, 0);
      return maxLevel;
    }

    onMounted(() => {
      const list: TreeType[] = []
      treeToList(state.tableData, list, null)
      rowLength.value = list.length || 0
      let length = getTreeLevel(JSON.parse(JSON.stringify(state.tableData)))
      if (length > 2) {
        level.value = length * 2
      } else {
        level.value = 2
      }
    })

    return {
      ...toRefs(state),
      rowLength,
      level
    }
  }
})
</script>

<style lang="scss">
.table {
  width: 100%;
  margin-left: 0;
  text-align: center;
  font-size: 12px;
}

.table th,
.table td {
  border: 1px solid #070707 !important;
  padding: 0.35rem !important;
  font-size: 16px;
  vertical-align: middle !important;
}

.ts-table-bold {
  td {
    font-weight: bold;
    font-size: 18px;
  }
}
</style>

五、最终效果

因为这里我只是为了做个demo,里面的Type还有公共的方法以及样式都是可以提取出来放到一个公共的文件里面。这个的话自己去完成。其实表格也不算复杂。

我是Etc.End。如果文章对你有所帮助,能否帮我点个免费的赞和收藏😍。同时欢迎各位小伙伴一起学习,一起成长WX:👉SH--TS👈

❤️ 💓 💗 💖 ✨ ⭐️ 🌟 💥 💥

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是让ChatGPT爆火的大语言模型(LLM)

什么是让ChatGPT爆火的大语言模型(LLM) 更多精彩内容: https://www.nvidia.cn/gtc-global/?ncidref-dev-876561 文章目录什么是让ChatGPT爆火的大语言模型(LLM)大型语言模型有什么用&#xff1f;大型语言模型如何工作&#xff1f;大型语言模型的热门应用在哪里可以找到大型语言…

西安石油大学C语言期末真题实战

很简单的一道程序阅读题&#xff0c;pa’默认为a【0】&#xff0c;接下来会进行3次循环 0 1 2 输出结果即可 前3题就是一些基础定义&#xff0c;在此不多赘述 要注意不同的数据类型的字节数不同 a<<2 b>>1&#xff08;b>>1;就是说b自身右位移一位&#xff08…

支付系统设计:消息重试组件封装

文章目录前言一、重试场景分析一、如何实现重试1. 扫表2. 基于中间件自身特性3. 基于框架4. 根据公司业务特性自己实现的重试二、重试组件封装1. 需求分析2. 模块设计2.1 持久化模块1. 表定义2. 持久化接口定义3. 持久化配置类2.2 重试模块1.启动2.重试3. 业务端使用1. 引入依赖…

Linux基础(3) Vim编辑器与Shell命令脚本

1、VIM文本编辑器 VIM编辑器的三大模式 命令模式&#xff1a; 控制光标移动&#xff0c;可对文本进行复制、粘贴和查找等工作输入模式&#xff1a; 正常的文本录入。末行模式&#xff1a; 保存或退出文档&#xff0c;以及设置编辑环境三种模式的切换&#xff1a; ​注意&…

app自动化测试——Android studio安装与配置

文章目录一、Appium框架介绍二、Appium 生态工具三、环境安装四、安装Android studio五、配置环境变量六、创建模拟器查看设备启动模拟器一、Appium框架介绍 1、跨语言&#xff1a;java、python等 2、跨平台&#xff1a;Android、IOS、Windows、Mac 3、底层多引擎切换 4、生态…

(待补充)小蒟蒻的刷题成长之路-------2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛)

小蒟蒻的刷题成长之路 蓝桥杯的比赛流程和必考点_蓝桥杯省赛考点_时雨h的博客-CSDN博客 大一学生一周十万字爆肝版C语言总结笔记_大一c语言笔记_时雨h的博客-CSDN博客 程序设计与 C 语言期末复习_时雨h的博客-CSDN博客 P8597 [蓝桥杯 2013 省 B] 翻硬币个人思考总结第五届传智杯…

西瓜视频登录页面

题目 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>登录页面</title><style>td{width: 160px;height: 25px;}img{width: 20px;height: 20px;}.number, .password{background: rgba(0,0,0,.05);}.numbe…

指针进阶(上)

内容小复习&#x1f431;&#xff1a; 字符指针:存放字符的数组 char arr1[10]; 整型数组:存放整型的数组 int arr2[5]; 指针数组:存放的是指针的数组 存放字符指针的数组(字符指针数组) char* arr3[5]; 存放整型指针的数组(整型指针数组) int* arr[6]; 下面进入学习了哦~&…

【二分查找】

二分查找704. 二分查找35. 搜索插入位置34. 在排序数组中查找元素的第一个和最后一个位置结语704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在…

mybatis中获取参数的两种方式:${}和#{}

目录 1.#{} 2.${} 3.总结 1.#{} 本质是占位符赋值 示例及执行结果&#xff1a; 结论&#xff1a;通过执行结果可以看到&#xff0c;首先对sql进行了预编译处理&#xff0c;然后再传入参数&#xff0c;有效的避免了sql注入的问题&#xff0c;并且传参方式也比较简单&#xf…

Python制作9行最简单音乐播放器?不,我不满足

人生苦短 我用python 好久不见啦~这次就来给大家整个大福利 ~ 源码资料电子书:点击此处跳转文末名片获取 最简单的9行代码音乐播放器如下&#xff1a; import time import pygamefile r歌曲路径 pygame.mixer.init() print(正在播放,file) track pygame.mixer.music.load(f…

计算机面试常见问答题目

英语口语 自我介绍 Hello, teachers. My name is Wang Xu. I come from Ningxia. I graduated from the School of Computer Science, Xi an Jiaotong University, majoring in Internet of Things. Next, I will introduce myself from four aspects. First of all, I studi…

Java开发 - ELK初体验

前言 前面我们讲过消息队列&#xff0c;曾提到消息队列也具有保存消息日志的能力&#xff0c;今天要说的EL看也具备这个能力&#xff0c;不过还是要区分一下功能的。消息队列的日志主要指的是Redis的AOF&#xff0c;实际上只是可以利用了消息队列来保存&#xff0c;却并不是消…

网络编程1(网络背景知识)

A给B发送消息如何保证数据一定能够发送到B的主机上&#xff0c;而不是其他地方 通过IP地址可以实现网络中制定的两个主机之间的通信&#xff0c;除此之外还要确定是哪个进程来处理&#xff0c;这里就用到端口&#xff08;port&#xff09; 端口—在一台主机上用于唯一标识一个…

MySQL索引特性

文章目录为什么要有索引&#xff1f;认识磁盘磁盘的结构磁盘的盘片结构定位扇区磁盘随机访问 (Random Access)与连续访问 (Sequential Access)MySQL与磁盘交互索引的理解测试主键索引索引的原理索引结构是否可以使用其他数据结构B树 vs B树聚簇索引 vs 非聚簇索引为什么要有索引…

基于深度学习的犬种识别软件(YOLOv5清新界面版,Python代码)

摘要&#xff1a;基于深度学习的犬种识别软件用于识别常见多个犬品种&#xff0c;基于YOLOv5算法检测犬种&#xff0c;并通过界面显示记录和管理&#xff0c;智能辅助人们辨别犬种。本文详细介绍博主自主开发的犬种检测系统&#xff0c;在介绍算法原理的同时&#xff0c;给出Py…

分布式微服务架构下网络通信的底层实现原理

在分布式架构中&#xff0c;网络通信是底层基础&#xff0c;没有网络&#xff0c;也就没有所谓的分布式架构。只有通过网络才能使得一大片机器互相协作&#xff0c;共同完成一件事情。 同样&#xff0c;在大规模的系统架构中&#xff0c;应用吞吐量上不去、网络存在通信延迟、我…

Qt音视频开发26-监控画面各种图形绘制设计

一、前言 视频监控系统做到后面&#xff0c;逐渐需要搭配人工智能算法&#xff0c;将算法计算后的信息以OSD标签以及方框各种图形的信息显示到视频中&#xff0c;这种当然和OSD一样也是有两种方式&#xff0c;一种是源头就贴好了&#xff0c;一种是将结果发给软件这边解析绘制…

专项攻克——死锁

文章目录O、死锁定义一、 常见的java死锁代码1. synchronized等待对象释放&#xff0c;导致死锁2. CountDownLatch计数等待&#xff0c;导致死锁二、怎么避免死锁2.1 死锁的四个必要条件2.2 避免死锁2.3 常见的避免死锁技术三、java程序出现死锁&#xff0c;怎么解除&#xff1…

Vue使用的编辑器

作者简介&#xff1a;一名计算机萌新、前来进行学习VUE,让我们一起进步吧。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;我叫于豆豆吖的主页 目录 前言 一.vue常用的IDE工具Visual Studio Code 3. 汉化教程 4.常用快捷键 5. Visual Studio C…