优雅的将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 相关文章推荐
Jquery 组合form元素为json格式,asp.net反序列化
Jul 09 Javascript
JQuery 弹出框定位实现方法
Dec 02 Javascript
JavaScript中指定函数名称的相关方法
Jun 04 Javascript
js结合正则实现国内手机号段校验
Jun 19 Javascript
通用javascript代码判断版本号是否在版本范围之间
Nov 29 Javascript
javascript获取wx.config内部字段解决微信分享
Mar 09 Javascript
BootStrap.css 在手机端滑动时右侧出现空白的原因及解决办法
Jun 07 Javascript
EasyUI实现下拉框多选功能
Nov 07 Javascript
详解Angular5 路由传参的3种方法
Apr 28 Javascript
关于在vue 中使用百度ueEditor编辑器的方法实例代码
Sep 14 Javascript
jQuery操作元素的内容和样式完整实例分析
Jan 10 jQuery
js 实现验证码输入框示例详解
Sep 23 Javascript
详解用场景去理解函数柯里化(入门篇)
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 smarty模版引擎中的缓存应用
2009/12/11 PHP
JS与PHP向函数传递可变参数的区别实例代码
2011/05/18 PHP
PHP查询MySQL大量数据的时候内存占用分析
2011/07/22 PHP
php中strtotime函数用法详解
2014/11/15 PHP
简单理解PHP的面向对象编程方式
2016/05/17 PHP
PHP编程实现的TCP服务端和客户端功能示例
2018/04/13 PHP
基于JQuery的asp.net树实现代码
2010/11/30 Javascript
一个简单的弹性返回顶部JS代码实现介绍
2013/06/09 Javascript
js获取指定日期前后的日期代码
2013/08/20 Javascript
Javascript中String的常用方法实例分析
2015/06/13 Javascript
JavaScript实现音乐自动切换和轮播
2017/11/05 Javascript
Element Collapse 折叠面板的使用方法
2020/07/26 Javascript
Python检测QQ在线状态的方法
2015/05/09 Python
Python字符串处理实现单词反转
2017/06/14 Python
django数据库migrate失败的解决方法解析
2018/02/08 Python
python如何修改装饰器中参数
2018/03/20 Python
对Python实现简单的API接口实例讲解
2018/12/10 Python
pyqt5移动鼠标显示坐标的方法
2019/06/21 Python
Python实现Restful API的例子
2019/08/31 Python
Python目录和文件处理总结详解
2019/09/02 Python
Python 3.8 新功能大揭秘【新手必学】
2020/02/05 Python
CSS实现圆形放大镜狙击镜效果 只有圆圈里的放大
2012/12/10 HTML / CSS
基于 HTML5 WebGL 实现的垃圾分类系统
2019/10/08 HTML / CSS
HTML5中的拖放实现详解
2017/08/23 HTML / CSS
澳大利亚天然护肤品、化妆品和健康产品一站式商店:Nourished Life
2018/12/02 全球购物
MYSQL基础面试题
2012/05/13 面试题
求职简历的自我评价
2014/01/31 职场文书
旅行社各个岗位职责
2014/03/15 职场文书
学校教师安全责任书
2014/07/23 职场文书
2014年酒店工作总结与计划
2014/11/17 职场文书
2014年学校食堂工作总结
2014/11/25 职场文书
中学生勤俭节约倡议书
2015/04/29 职场文书
会议室管理制度范本
2015/08/06 职场文书
python引入其他文件夹下的py文件具体方法
2021/05/23 Python
Java比较两个对象中全部属性值是否相等的方法
2021/08/07 Java/Android
IDEA中sout快捷键无效问题的解决方法
2022/07/23 Java/Android