Adinnet-Web Adinnet-Web
首页
  • 技术沉淀

    • 技术沉淀
  • 前端文章

    • JavaScript
  • 前端笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 实用技巧
收藏
  • 分类
  • 标签
  • 归档
首页
  • 技术沉淀

    • 技术沉淀
  • 前端文章

    • JavaScript
  • 前端笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 实用技巧
收藏
  • 分类
  • 标签
  • 归档
  • JavaScript文章

  • 学习笔记

  • 技术沉淀

    • CSS矩形绘制
    • 仿微信通讯录
    • 微信小程序订阅消息
    • 保存海报
    • 微信小程序页面弹框
    • 微信小程序在指定距离范围内签到
    • h5嵌套u3d,使用iframe进行交互
    • 前端主导文件处理方法
    • 图片压缩方法封装
    • 金额大写函数
    • 前端主导的小程序一键导航功能
    • uniapp富文本组件(只支持H5)
    • uniapp二次确认弹框组件(依赖于uview)
    • 无缝滚动插件(vue3)
    • H5与unity通信(只针对unity嵌套H5)
    • 辅助触控
    • 关系图
    • h5&svg点赞动效
    • iframe高精地图文件预览
    • 自适应横向瀑布流布局
    • 考试功能
    • 仿小说翻页
    • 富文本
    • h5唤醒App
    • vue3 Count-to数字翻动
    • 小程序图片转base64
    • 第三方应用跳转微信小程序
    • 圆角TabBar
    • 大屏Header
    • Echarts中国地图
    • 使用computed拦截v-model
    • 消息订阅与发布
    • Html相对路径页面跳转
    • Vite打包本地访问
    • H5主题切换
    • 移动端列表分页
    • 每次进入页面都触发的功能(v3)
    • 智能导诊
    • js中几个优雅的运算符使用技巧
    • 其他CSS伪元素
    • 微信浏览器保存canvas海报
    • uniappH5手动拖拽缩放自定义绘制海报
    • uniapp小程序盒子拖拽排序
    • 微信小程序地图的使用
    • css静态漏斗绘制
    • 微信小程序图片流处理
    • ios时间问题
    • 文字围绕效果
    • 表格树动态计算
      • React中useEffect用法
      • React Hooks 陷阱
      • Naive UI修改默认样式
      • vue 根据div id 滚动到指定view到可视视图中
      • js将[1,2,3]排列组合成[1.12.123]
      • 移动端如何加载vite导出dist包
      • TS探索接口、泛型和自定义类型
      • WebSocket中加入Token
      • 幂方程指数衰减
      • pageSpy
      • 表格多条件搜索
      • axios get 请求 url 转码 空格转成+,导致请求失败
      • NaiveUI使用Message组件遇到的问题记录
      • 小程序多盒子拖拽排序
      • 对象快速筛key方法
      • 倒计时
      • nuxt3中的useAsyncData使用详解
      • nuxt3文件式路由
      • 播放amr格式音频
      • echarts瀑布图
      • vue中使用vueuse进行文件导出
      • 分享一个数据整理方法
      • naiveui表格shift多选
      • 数组递归转tree
      • 自定义指令图片懒加载
      • 将数组里面的对象转换成我们需要的键值对
      • 亚马逊云s3上传
      • 上拉加载更多封装
      • JS设计模式总结笔记
      • 适配移动端页面顶部安全区域
      • 【CryptoJS】使用介绍
      • 微信小程序富文本图片点击放大
      • 微信小程序省市区级联选择
      • uniapp拖动排序插件
      • element ui 的树形结构懒加载,局部更新
      • 表单搜索过滤空值
      • 判断设备类型
      • tsconfig配置打包问题
      • vant-uploader上传组件添加长按保存图片
      • BroadcastChannel跨页面通信
      • pnpm安装问题
      • echart阶梯图形绘制
      • js根据对象拼接参数
      • 集成免密登录
      • 输入框输入数字限制
      • js控制全局css变量
      • 流程图
      • 修改npm包
      • tsx在vue3中的应用
      • 109video播放视频流
      • echart默认展示某个点的tooltip
      • 大屏适配几种方案
      • 表单低代码自定义列表页
      • 表单低代码列表页配置端代码
      • 低代码平台使用的一些好用的js方法
      • vue3cookie的使用
      • 原生js下载文件
      • stompjs 创建socket,断线重连机制
      • iframe作为内嵌网页cookie设置
      • 前端对文件内容解析生成特定算法哈希值方法
      • vue3中前端自定义命名文件名称下载文件的方法
      • 前端项目中权限判断的方法配合路由守卫使用
      • 原生table:表格table中thead固定,tbody超出高度出现滚动条
      • vue中使用原生的方法
      • vue3中amchart的初步使用
      • base64图片加水印
      • 手写签名
      • base64转doc文件
    • 前端
    • 技术沉淀
    商会林
    2024-04-14
    目录

    表格树动态计算

    # 效果图

    Alt text

    # 逻辑思考整理

    1. 在考虑实现这个问题时,首先要和后端约定好数据格式,确定表格树的结构大致为什么样子。例如:
    // An highlighted block
    const tableData = ref([
      {
        id: '1',
        name: 'tem1',
        startTime: '',
        endTime: '',
        val: 0,
        parentId: '0',
        children: [
          {
            id: '1-1',
            parentId: '1',
            name: 'tem1-1',
            startTime: '',
            endTime: '',
            val: 0,
            children: [
              {
                id: '1-1-1',
                parentId: '1-1',
                name: 'tem1-1-1',
                startTime: '',
                endTime: '',
                val: 0,
                children: []
              },
              {
                id: '1-1-2',
                parentId: '1-1',
                name: 'tem1-1-2',
                startTime: '',
                endTime: '',
                val: 0,
                children: []
              }
            ]
          },
          {
            id: '1-2',
            parentId: '1',
            name: 'tem1-2',
            startTime: '',
            endTime: '',
            val: 0,
            children: []
          }
        ]
      },
      {
        id: '2',
        parentId: '0',
        name: 'tem2',
        startTime: '',
        endTime: '',
        val: 0,
        children: []
      }
    ])
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    1. 然后开始思考如何根据id等标识去计算父级数据:
    // 动态计算逻辑
    const calculate = (row, rowKey) => {
      // 数值计算(子集求和)
      if (rowKey === 'val') {
        const valArr = row.children.map(el => Number(el.val))
        row.val = valArr.reduce((acc, cur) => acc + cur, 0)
      }
      // 开始日期
      if (rowKey === 'startTime') {
        if (row.children.length === 1) {
          row.startTime = row.children[0].startTime
        } else {
          for (let item of row.children) {
            if (!item.startTime) {
              return
            }
          }
          const startTimeArr = row.children.map(el => new Date(el.startTime).getTime())
          if (startTimeArr.length) {
            row.startTime = startTimeArr.reduce((acc, cur) => acc < cur ? acc : cur)
          }
        }
      }
      // 结束日期
      if (rowKey === 'endTime') {
        if (row.children.length === 1) {
          row.endTime = row.children[0].endTime
        } else {
          for (let item of row.children) {
            if (!item.endTime) {
              return
            }
          }
          const endTimeArr = row.children.map(el => new Date(el.endTime).getTime())
          if (endTimeArr.length) {
            row.endTime = endTimeArr.reduce((acc, cur) => acc > cur ? acc : cur)
          }
        }
      }
      // 如果不是最顶级父级树,要继续递归查找父级并计算
      if (row.parentId !== '0') {
        setParentData(row, rowKey)
      }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    1. 然后再计算出父级后,考虑是否为顶级树,如果不是,需要递归处理:
    // 递归设置父级数据(父ID为0时,代表当前内容为最顶级树)
    const setParentData = (row, rowKey) => {
      const traverseTree = (tree) => {
        if (row.parentId == tree.id) {
          calculate(tree, rowKey)
          return
        }
        if (tree.children.length) {
          for (let el of tree.children) {
            if (el.id == row.parentId) {
              calculate(el, rowKey)
              return
            } else {
              traverseTree(el)
            }
          }
        }
      }
      // 父ID不为0,执行递归
      if (row.parentId !== '0') {
        tableData.value.forEach(tree => {
          traverseTree(tree)
        })
      }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    1. 源码(KekeEditor.vue)
    <template>
      <div>
        <h2>表格树动态计算日期和值</h2>
        <el-table style="width: 800px;" :data="tableData" row-key="id" :tree-props="{ children: 'children' }"
          default-expand-all>
          <el-table-column prop="name" label="模板名称" />
          <el-table-column prop="startTime" label="开始日期">
            <template #default="{ row }">
              <el-date-picker v-model="row.startTime" type="date" placeholder="选择日期" @change="calculate(row, 'startTime')" />
            </template>
          </el-table-column>
          <el-table-column prop="endTime" label="结束日期">
            <template #default="{ row }">
              <el-date-picker v-model="row.endTime" type="date" placeholder="选择日期" @change="calculate(row, 'endTime')" />
            </template>
          </el-table-column>
          <el-table-column prop="val" label="数值">
            <template #default="{ row }">
              <el-input v-model="row.val" @change="setParentData(row, 'val')"></el-input>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    
    // 表格数据
    const tableData = ref([
      {
        id: '1',
        name: 'tem1',
        startTime: '',
        endTime: '',
        val: 0,
        parentId: '0',
        children: [
          {
            id: '1-1',
            parentId: '1',
            name: 'tem1-1',
            startTime: '',
            endTime: '',
            val: 0,
            children: [
              {
                id: '1-1-1',
                parentId: '1-1',
                name: 'tem1-1-1',
                startTime: '',
                endTime: '',
                val: 0,
                children: []
              },
              {
                id: '1-1-2',
                parentId: '1-1',
                name: 'tem1-1-2',
                startTime: '',
                endTime: '',
                val: 0,
                children: []
              }
            ]
          },
          {
            id: '1-2',
            parentId: '1',
            name: 'tem1-2',
            startTime: '',
            endTime: '',
            val: 0,
            children: []
          }
        ]
      },
      {
        id: '2',
        parentId: '0',
        name: 'tem2',
        startTime: '',
        endTime: '',
        val: 0,
        children: []
      }
    ])
    
    
    // 动态计算逻辑
    const calculate = (row, rowKey) => {
      // 数值计算(子集求和)
      if (rowKey === 'val') {
        const valArr = row.children.map(el => Number(el.val))
        row.val = valArr.reduce((acc, cur) => acc + cur, 0)
      }
      // 开始日期
      if (rowKey === 'startTime') {
        if (row.children.length === 1) {
          row.startTime = row.children[0].startTime
        } else {
          for (let item of row.children) {
            if (!item.startTime) {
              return
            }
          }
          const startTimeArr = row.children.map(el => new Date(el.startTime).getTime())
          if (startTimeArr.length) {
            row.startTime = startTimeArr.reduce((acc, cur) => acc < cur ? acc : cur)
          }
        }
      }
      // 结束日期
      if (rowKey === 'endTime') {
        if (row.children.length === 1) {
          row.endTime = row.children[0].endTime
        } else {
          for (let item of row.children) {
            if (!item.endTime) {
              return
            }
          }
          const endTimeArr = row.children.map(el => new Date(el.endTime).getTime())
          if (endTimeArr.length) {
            row.endTime = endTimeArr.reduce((acc, cur) => acc > cur ? acc : cur)
          }
        }
      }
      // 如果不是最顶级父级树,要继续递归查找父级并计算
      if (row.parentId !== '0') {
        setParentData(row, rowKey)
      }
    }
    
    // 递归设置父级数据(父ID为0时,代表当前内容为最顶级树)
    const setParentData = (row, rowKey) => {
      const traverseTree = (tree) => {
        if (row.parentId == tree.id) {
          calculate(tree, rowKey)
          return
        }
        if (tree.children.length) {
          for (let el of tree.children) {
            if (el.id == row.parentId) {
              calculate(el, rowKey)
              return
            } else {
              traverseTree(el)
            }
          }
        }
      }
      // 父ID不为0,执行递归
      if (row.parentId !== '0') {
        tableData.value.forEach(tree => {
          traverseTree(tree)
        })
      }
    }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160

    这里的难点在于何时用递归;递归时,在什么时候跳出递归并调用计算。这个捋清楚,此需求就不难解决。

    上次更新: 2024/04/14, 17:23:46
    文字围绕效果
    React中useEffect用法

    ← 文字围绕效果 React中useEffect用法→

    最近更新
    01
    base64转doc文件
    01-03
    02
    手写签名
    01-03
    03
    base64图片加水印
    01-03
    更多文章>
    Theme by Vdoing | Copyright © 2023-2025 Adinnet
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式