优雅的将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 相关文章推荐
激活 ActiveX 控件
Oct 09 Javascript
jquery购物车实时结算特效实现思路
Sep 23 Javascript
获得Javascript对象属性个数的示例代码
Nov 21 Javascript
基于Arcgis for javascript实现百度地图ABCD marker的效果
Sep 12 Javascript
Clipboard.js 无需Flash的JavaScript复制粘贴库
Oct 02 Javascript
详解Javascript中的Object对象
Feb 28 Javascript
总结JavaScript的正则与其他语言的不同之处
Aug 25 Javascript
AngularJS实现自定义指令与控制器数据交互的方法示例
Jun 19 Javascript
vue添加axios,并且指定baseurl的方法
Sep 19 Javascript
Bootstrap 按钮样式与使用代码详解
Dec 09 Javascript
Vue路由对象属性 .meta $route.matched详解
Nov 04 Javascript
JavaScript实现班级抽签小程序
May 19 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中使用与Perl兼容的正则表达式
2006/11/26 PHP
PHP的switch判断语句的“高级”用法详解
2014/10/01 PHP
PHP使用适合阅读的格式显示文件大小的方法
2015/03/05 PHP
PHP实践教程之过滤、验证、转义与密码详解
2017/07/24 PHP
laravel-admin 实现给grid的列添加行数序号的方法
2019/10/08 PHP
PHP实现笛卡尔积算法的实例讲解
2019/12/22 PHP
PHP优化之批量操作MySQL实例分析
2020/04/23 PHP
表单项的name命名为submit、reset引起的问题
2007/12/22 Javascript
Javascript Math ceil()、floor()、round()三个函数的区别
2010/03/09 Javascript
ECMAScript6函数剩余参数(Rest Parameters)
2015/06/12 Javascript
jquery删除table当前行的实例代码
2016/10/07 Javascript
微信JS-SDK选取手机照片上传功能
2017/04/21 Javascript
Angular2使用Guard和Resolve进行验证和权限控制
2017/04/24 Javascript
利用node.js+mongodb如何搭建一个简单登录注册的功能详解
2017/07/30 Javascript
Node.js五大应用性能技巧小结(必须收藏)
2017/08/09 Javascript
AjaxUpLoad.js实现文件上传
2018/03/05 Javascript
解决Vue 项目打包后favicon无法正常显示的问题
2018/09/01 Javascript
Vue瀑布流插件的使用示例
2018/09/19 Javascript
setTimeout与setInterval的区别浅析
2019/03/23 Javascript
[03:55]DOTA2完美大师赛选手传记——LFY.MONET
2017/11/18 DOTA
浅谈Python爬取网页的编码处理
2016/11/04 Python
python检测文件夹变化,并拷贝有更新的文件到对应目录的方法
2018/10/17 Python
python3 爬取图片的实例代码
2018/11/06 Python
Python二叉树的遍历操作示例【前序遍历,中序遍历,后序遍历,层序遍历】
2018/12/24 Python
Python 使用 docopt 解析json参数文件过程讲解
2019/08/13 Python
Django自定义模板过滤器和标签的实现方法
2019/08/21 Python
vscode+PyQt5安装详解步骤
2020/08/12 Python
CSS3 中filter(滤镜)属性使用详解
2020/04/07 HTML / CSS
详解HTML5 Canvas绘制不规则图形时的非零环绕原则
2016/03/21 HTML / CSS
eDreams葡萄牙:全球最大的在线旅行社之一
2019/04/15 全球购物
汽车运用工程毕业生自荐信
2013/10/29 职场文书
暑期社会实践感言
2014/02/25 职场文书
给公司的建议书范文
2014/05/13 职场文书
python tkinter模块的简单使用
2021/04/07 Python
详解如何用Python实现感知器算法
2021/06/18 Python
Python中如何处理常见报错
2022/01/18 Python