优雅的将ElementUI表格变身成树形表格的方法步骤


Posted in Javascript onApril 11, 2019

由于ElementUI目前还未开发树形表格组件,也参阅了网络上部分基于ElementUI表格封装的开源树形组件,如果想进行二次开发的话都不太理想,所以就萌生了自行开发树形表格。

本示例提供开发思路,移除了多余的样式,比较适合新手入门学习,如果应用于实际项目还请自行封装。

目前还仅仅实现了视觉的树结构的层级效果和控制结构的显示隐藏,后续还会进行不断的完善和优化,有必要的话会对组件进行二次封装,有点在重复造论的感觉哈。

效果图

优雅的将ElementUI表格变身成树形表格的方法步骤

完整代码

页面(tree-table.vue)

<template>
 <div>
  TreeTable
  <el-table :data="list" :row-style="tableRowStyle" border>
   <el-table-column type="selection" width="55"></el-table-column>
   <el-table-column prop="id" label="ID" width="180">
    <template slot-scope="scope">
     <span class="collapse"
        :class="collapseClass(scope.row)"
        :style="tableRowPaddingStyle(scope.row)"
        @click="handleCollapseClick(scope.row)">
     </span>
     <span>{{ scope.row.id }}</span>
    </template>
   </el-table-column>
   <el-table-column prop="name" label="NAME"></el-table-column>
  </el-table>
 </div>
</template>

<script lang="ts">
  import {Component, Vue} from 'vue-property-decorator'
  // 引入两个封装好的工具方法
  import { arrayToTree } from './utils/array.js'
  import { ergodicTree } from './utils/tree.js'

  @Component
  export default class TreeTable extends Vue {
    private list: object[] = [];
    private tree: object[] = [];

    created() {
      // 准备一组含有父子级关系的一维数组方便示例测试
      // 在实际项目应用中,理应通过后端接口返回
      let _list = [
        {
          id: 'a',
          pid: '',
          name: '部门a',
          children: []
        },
        {
          id: 'a1',
          pid: 'a',
          name: '子部门a1',
          children: []
        },
        {
          id: 'a2',
          pid: 'a',
          name: '子部门a2',
          children: []
        },
        {
          id: 'a2-1',
          pid: 'a2',
          name: '子部门a2-1',
          children: []
        },
        {
          id: 'a2-2',
          pid: 'a2',
          name: '子部门a2-2',
          children: []
        },
        {
          id: 'a3',
          pid: 'a',
          name: '子部门a3',
          children: []
        },
        {
          id: 'a3-1',
          pid: 'a3',
          name: '子部门a3-1',
          children: []
        },
        {
          id: 'b',
          pid: '',
          name: '部门b',
          children: []
        },
        {
          id: 'b1',
          pid: 'b',
          name: '子部门b1',
          children: []
        },
        {
          id: 'c',
          pid: '',
          name: '部门c',
          children: []
        },
      ];
      
      // 将一维数组转成树形结构并存储于tree变量
      this.tree = arrayToTree(_list);
      
      // 考虑到实际应用过程中接口返回的数据是无序的,所以我们对tree进行先序遍历将节点一一插入到list变量
      this.list = [];
      ergodicTree(this.tree, (node: any) => {
        this.list.push(node);
        
        // 遍历过程中并对每个节点挂载open和show属性
        // open:控制节点的打开和关闭
        // show:控制节点的显示和隐藏
        this.$set(node, 'open', true);
        this.$set(node, 'show', true)
      })
    }

    // 控制行的显示和隐藏
    tableRowStyle(scope: any) {
      return {
        'display': scope.row.show ? '' : 'none'
      }
    }

    // 通过每个节点的深度,设置行的缩进实现视觉上的层级效果
    tableRowPaddingStyle(row: any) {
      return {
        'margin-left': `${(row._depth - 1) * 24}px`
      }
    }

    // 控制展开按钮的展开和关闭状态
    collapseClass(row: any) {
      return {
        'collapse--open': row.open == false && row.children && row.children.length > 0,
        'collapse--close': row.open == true && row.children && row.children.length > 0
      }
    }

    // 处理展开按钮的点击事件
    handleCollapseClick(row: any) {
      const _open = row.open;
      // 通过节点访问路径控制节点的显示隐藏,由于内存指针的关系list和tree的节点操作都会相互影响
      ergodicTree(this.tree, (node: any) => {
        node._idPath.forEach((pathId: any) => {
          if (pathId == row.id) {
            this.$set(node, 'show', !_open);
            this.$set(node, 'open', !_open)
          }
        })
      });
      row.show = true;
      row.open = !_open;
    }
  }
</script>

<style lang="scss" scoped>
 .collapse {
  display: inline-block;
  width: 8px;
  cursor: pointer;
  margin-right: 8px;
 }

 .collapse--open:before {
  content: '+';
 }

 .collapse--close:before {
  content: '-';
 }
</style>

工具方法

考虑数组转树和遍历树都是在实际项目中都是非常常用的,所以这边对这两个方法进行了封装。

数组转树结构(./utils/array.ts)

export function arrayToTree(list: object[], props = {id: 'id', pid: 'pid', children: 'children'}) {
      let tree: object[] = [];
      let map: any = {};

      let listLength = list.length;
      for (let i = 0; i < listLength; i++) {
        let node: any = list[i];
        let nodeId: any = node[props.id];
        map[nodeId] = node;
      }

      for (let i = 0; i < listLength; i++) {
        let node: any = list[i];
        let nodePid: any = node[props.pid];
        let parentNode: any = map[nodePid];
        if (parentNode) {
          parentNode[props.children] = parentNode[props.children] || [];
          parentNode[props.children].push(node)
        } else {
          tree.push(node)
        }
      }

      return tree
    }

遍历树结构(./utils/tree.ts)

结合实际项目应用,我们采用了先序遍历法对树进行遍历,为了方便在业务代码里的应用,在遍历过程中会对每个节点挂载节点访问路径 _idPath 属性和节点深度 _depth 属性。

export function ergodicTree(tree: object[], callback: any = () => {}, props = {id: 'id', pid: 'pid', children: 'children'}) {
      function _ergodicTree(tree: object[], parentIdPath?: any[], depth: number = 0) {
        const treeLength = tree.length;
        for (let i = 0; i < treeLength; i++) {
          let node: any = tree[i];
          const _idPath: any[] = parentIdPath ? [...parentIdPath, node[props.id]] : [node[props.id]];
          const _depth: number = depth + 1;
          node._idPath = _idPath;
          node._depth = _depth;
          callback(node);
          if (node[props.children] && node[props.children] instanceof Array) {
            _ergodicTree(node[props.children], _idPath, _depth)
          }
        }
      }

      _ergodicTree(tree);
      return tree;
    }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript高级程序设计(第3版)学习笔记10 再访js对象
Oct 11 Javascript
js动态删除div元素基本思路及实现代码
May 08 Javascript
深入理解Javascript中this的作用域
Aug 12 Javascript
防止Node.js中错误导致进程阻塞的办法
Aug 11 Javascript
Bootstrap源码解读下拉菜单(4)
Dec 23 Javascript
vue 全选与反选的实现方法(无Bug 新手看过来)
Feb 09 Javascript
JavaScript中变量提升与函数提升经典实例分析
Jul 26 Javascript
js图数据结构处理 迪杰斯特拉算法代码实例
Sep 11 Javascript
微信小程序实现搜索功能
Mar 10 Javascript
Bootstrap实现前端登录页面带验证码功能完整示例
Mar 26 Javascript
Vue+Openlayers自定义轨迹动画
Sep 24 Javascript
jQuery实现简单评论区功能
Oct 26 jQuery
详解用场景去理解函数柯里化(入门篇)
Apr 11 #Javascript
Vue开发Html5微信公众号的步骤
Apr 11 #Javascript
跟混乱的页面弹窗说再见
Apr 11 #Javascript
vue实现todolist功能、todolist组件拆分及todolist的删除功能
Apr 11 #Javascript
vue实现todolist基本功能以及数据存储功能实例详解
Apr 11 #Javascript
JavaScript高阶教程之“==”隐藏下的类型转换
Apr 11 #Javascript
使用Vue父子组件通信实现todolist的功能示例代码
Apr 11 #Javascript
You might like
用PHP连接Oracle for NT 远程数据库
2006/10/09 PHP
推荐一篇入门级的Class文章
2007/03/19 PHP
phpnow php探针环境检测代码
2014/11/04 PHP
Add a Table to a Word Document
2007/06/15 Javascript
toString()一个会自动调用的方法
2010/02/08 Javascript
Raphael带文本标签可拖动的图形实现代码
2013/02/20 Javascript
js showModalDialog参数的使用详解
2014/01/07 Javascript
Bootstrap每天必学之滚动监听
2016/03/16 Javascript
深入理解jquery中的事件与动画
2016/05/24 Javascript
Bootstrap在线电子商务网站实战项目5
2016/10/14 Javascript
javascript实现用户点击数量统计
2016/12/25 Javascript
JS实现iframe自适应高度的方法示例
2017/01/07 Javascript
nodeJs链接Mysql做增删改查的简单操作
2017/02/04 NodeJs
Node.js中多进程模块Cluster的介绍与使用
2017/05/27 Javascript
JQuery基于FormData异步提交数据文件
2020/09/01 jQuery
讲解Python中if语句的嵌套用法
2015/05/14 Python
Python与R语言的简要对比
2017/11/14 Python
python+splinter自动刷新抢票功能
2018/09/25 Python
python解压TAR文件至指定文件夹的实例
2019/06/10 Python
Python+threading模块对单个接口进行并发测试
2019/06/25 Python
浅谈Python小波分析库Pywavelets的一点使用心得
2019/07/09 Python
简单介绍python封装的基本知识
2019/08/10 Python
Python实现FLV视频拼接功能
2020/01/21 Python
基于python3的socket聊天编程
2020/02/17 Python
浅谈ROC曲线的最佳阈值如何选取
2020/02/28 Python
查看已安装tensorflow版本的方法示例
2020/04/19 Python
Python pip安装模块提示错误解决方案
2020/05/22 Python
Django通过设置CORS解决跨域问题
2020/11/26 Python
使用HTML5的Notification API制作web通知的教程
2015/05/08 HTML / CSS
澳大利亚实惠时尚女装商店:Katies
2019/06/16 全球购物
网页设计个人找工作求职信
2013/11/28 职场文书
大学奖学金获奖感言
2014/08/15 职场文书
意外死亡赔偿协议书
2014/10/14 职场文书
关于召开会议的通知
2015/04/15 职场文书
工作简报范文
2015/07/21 职场文书
pytorch finetuning 自己的图片进行训练操作
2021/06/05 Python