JavaScript

超轻量级php框架startmvc

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

更新时间:2020-08-21 11:48:01 作者:startmvc
由于ElementUI目前还未开发树形表格组件,也参阅了网络上部分基于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;
 }

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

ElementUI表格变树形表格 Element 树形表格