优雅的将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 相关文章推荐
重载toString实现JS HashMap分析
Mar 13 Javascript
jQuery对表单元素的取值和赋值操作代码
May 19 Javascript
js获取本机的外网/广域网ip地址完整源码
Aug 12 Javascript
jQuery实现在最后一个元素之前插入新元素的方法
Jul 18 Javascript
jQuery dataTables与jQuery UI 对话框dialog的使用教程
Sep 02 Javascript
jQuery中show与hide方法用法示例
Sep 16 Javascript
基于JavaScript实现窗口拖动效果
Jan 18 Javascript
从零学习node.js之简易的网络爬虫(四)
Feb 22 Javascript
JavaScript实现简单图片轮播效果
Aug 21 Javascript
JavaScript的级联函数用法简单示例【链式调用】
Mar 26 Javascript
微信小程序页面调用自定义组件内的事件详解
Sep 12 Javascript
在Vue 中获取下拉框的文本及选项值操作
Aug 13 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
轻松修复Discuz!数据库
2008/05/03 PHP
php实现读取超大文件的方法
2014/07/28 PHP
php身份证号码检查类实例
2015/06/18 PHP
php表单文件iframe异步上传实例讲解
2017/07/26 PHP
自适应高度框架 ----属个人收藏内容
2007/01/22 Javascript
22点关于jquery性能优化的建议
2014/05/28 Javascript
js限制文本框只能输入中文的方法
2015/08/11 Javascript
跟我学习javascript的执行上下文
2015/11/18 Javascript
极易被忽视的javascript面试题七问七答
2016/02/15 Javascript
文件上传,iframe跨域数据提交的实现
2016/11/18 Javascript
微信小程序tabBar模板用法实例分析【附demo源码下载】
2017/11/28 Javascript
Vue头像处理方案小结
2018/07/26 Javascript
关于微信公众号开发无法支付的问题解决
2018/12/28 Javascript
详解React项目中碰到的IE问题
2019/03/14 Javascript
Vue实现点击当前元素以外的地方隐藏当前元素(实现思路)
2019/12/04 Javascript
编写v-for循环的技巧汇总
2020/12/01 Javascript
Python构造函数及解构函数介绍
2015/02/26 Python
使用Python的Zato发送AMQP消息的教程
2015/04/16 Python
python os.listdir按文件存取时间顺序列出目录的实例
2018/10/21 Python
对python3中的RE(正则表达式)-详细总结
2019/07/23 Python
python 实现return返回多个值
2019/11/19 Python
pytorch实现MNIST手写体识别
2020/02/14 Python
html5设计原理(推荐收藏)
2014/05/17 HTML / CSS
KIKO MILANO俄罗斯官网:意大利领先的化妆品和护肤品品牌
2021/01/09 全球购物
ORACLE十问
2015/04/20 面试题
优秀求职自荐信怎样写
2013/12/18 职场文书
新书吧创业计划书
2014/01/31 职场文书
单位承诺书格式
2014/05/21 职场文书
电子商务专业应届生求职信
2014/05/28 职场文书
社会工作专业求职信
2014/07/15 职场文书
ktv周年庆活动方案
2014/08/18 职场文书
2014法院干警廉洁警示教育思想汇报
2014/09/13 职场文书
党的群众路线对照检查材料
2014/09/22 职场文书
2014年社区民政工作总结
2014/12/02 职场文书
担保书范文
2015/01/20 职场文书
JavaScript实现简单图片切换
2021/04/29 Javascript