视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
优雅的将ElementUI表格变身成树形表格的方法步骤
2020-11-27 21:58:59 责编:小采
文档


由于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;
 }

下载本文
显示全文
专题