ElementUI制作树形表组件

提要

最近项目中需要用到树形表格来描述部门、区域之间的父子展开关系。但是已经在项目中使用的Vue的成熟组件ElementUI以及iViewUI组件都没有提供相应的树形表格组件,无奈找了其他替代方案也都被pass掉了,只能从改造现有组件放面着手。

在网上也找到了一些实践案例:http://blog.csdn.net/s8460049/article/details/61414751

第一种方案

第一种方案就是原作者介绍的,即将具有层级关系的数据进行提前处理。比如: 数据结构为:

[
    {
        id: 1,
        parentId: 0,
        name: ‘测试1‘,
        age: 18,
        sex: ‘男‘,
        children: [
            {
                id: 2,
                parentId: 1,
                name: ‘测试2‘,
                age: 22,
                sex: ‘男‘
            }
        ]
    },
    {
        id: 3,
        parentId: 0,
        name: ‘测试3‘,
        age: 23,
        sex: ‘女‘,
        children: [
            {
                id: 4,
                parentId: 3,
                name: ‘测试4‘,
                age: 22,
                sex: ‘男‘
            },
            {
                id: 5,
                parentId: 3,
                name: ‘测试5‘,
                age: 25,
                sex: ‘男‘
            },
            {
                id: 6,
                parentId: 3,
                name: ‘测试6‘,
                age: 26,
                sex: ‘女‘,
                children: [
                    {
                        id: 7,
                        parentId: 6,
                        name: ‘测试7‘,
                        age: 27,
                        sex: ‘男‘
                    }
                ]
            }
        ]
    },
    {
        id: 18,
        parentId: 0,
        name: ‘测试8‘,
        age: 18,
        sex: ‘男‘
    }
]

这样可以通过数据转换方法,把每一条数据从它的父级中取出来,把树形结构数据转换成数组数据。

dataTranslate.js内容:

import Vue from ‘vue‘
function DataTransfer (data) {
  if (!(this instanceof DataTransfer)) {
    return new DataTransfer(data, null, null)
  }
}  

DataTransfer.treeToArray = function (data, parent, level, expandedAll) {
  let tmp = []
  Array.from(data).forEach(function (record) {
    if (record._expanded === undefined) {
      Vue.set(record, ‘_expanded‘, expandedAll)
    }
    if (parent) {
      Vue.set(record, ‘_parent‘, parent)
    }
    let _level = 0
    if (level !== undefined && level !== null) {
      _level = level + 1
    }
    Vue.set(record, ‘_level‘, _level)
    tmp.push(record)
    if (record.children && record.children.length > 0) {
      let children = DataTransfer.treeToArray(record.children, record, _level, expandedAll)
      tmp = tmp.concat(children)
    }
  })
  return tmp
}  

export default DataTransfer

有了进行数据转换的方法之后,开始正式些数据TreeGrid.vue组件:

<template>
  <el-table
    :data="data"
    border
    style="width: 100%"
    :row-style="showTr">
    <el-table-column v-for="(column, index) in columns" :key="column.dataIndex"
      :label="column.text">
      <template scope="scope">
        <span v-if="spaceIconShow(index)" v-for="(space, levelIndex) in scope.row._level" class="ms-tree-space"></span>
        <button class="button is-outlined is-primary is-small" v-if="toggleIconShow(index,scope.row)" @click="toggle(scope.$index)">
          <i v-if="!scope.row._expanded" class="el-icon-caret-right" aria-hidden="true"></i>
          <i v-if="scope.row._expanded" class="el-icon-caret-bottom" aria-hidden="true"></i>
        </button>
        <span v-else-if="index===0" class="ms-tree-space"></span>
        {{scope.row[column.dataIndex]}}
      </template>
    </el-table-column>
    <el-table-column label="操作" v-if="treeType === ‘normal‘" width="260">
      <template scope="scope">
        <button type="button" class="el-button el-button--default el-button--small">
          <router-link
            :to="{ path: requestUrl + ‘edit‘, query: {id: scope.row.Oid} }"
            tag="span">
            编辑
          </router-link>
        </button>
        <el-button
          size="small"
          type="danger"
          @click="handleDelete()">
          删除
        </el-button>
        <button type="button" class="el-button el-button--success el-button--small">
          <router-link :to="{ path: requestUrl, query: {parentId: scope.row.parentOId} }"
                       tag="span">
            添加下级树结构
          </router-link>
        </button>
      </template>
    </el-table-column>
  </el-table>
</template>
<script>
  import DataTransfer from ‘../utils/dataTranslate.js‘
  import Vue from ‘vue‘
  export default {
    name: ‘tree-grid‘,
    props: {
// 该属性是确认父组件传过来的数据是否已经是树形结构了,如果是,则不需要进行树形格式化
      treeStructure: {
        type: Boolean,
        default: function () {
          return false
        }
      },
// 这是相应的字段展示
      columns: {
        type: Array,
        default: function () {
          return []
        }
      },
// 这是数据源
      dataSource: {
        type: Array,
        default: function () {
          return []
        }
      },
// 这个作用是根据自己需求来的,比如在操作中涉及相关按钮编辑,删除等,需要向服务端发送请求,则可以把url传过来
      requestUrl: {
        type: String,
        default: function () {
          return ‘‘
        }
      },
// 这个是是否展示操作列
      treeType: {
        type: String,
        default: function () {
          return ‘normal‘
        }
      },
// 是否默认展开所有树
      defaultExpandAll: {
        type: Boolean,
        default: function () {
          return false
        }
      }
    },
    data () {
      return {}
    },
    computed: {
    // 格式化数据源
      data: function () {
        let me = this
        if (me.treeStructure) {
          let data = DataTransfer.treeToArray(me.dataSource, null, null, me.defaultExpandAll)
          console.log(data)
          return data
        }
        return me.dataSource
      }
    },
    methods: {
    // 显示行
      showTr: function (row, index) {
        let show = (row._parent ? (row._parent._expanded && row._parent._show) : true)
        row._show = show
        return show ? ‘‘ : ‘display:none;‘
      },
    // 展开下级
      toggle: function (trIndex) {
        let me = this
        let record = me.data[trIndex]
        record._expanded = !record._expanded
      },
    // 显示层级关系的空格和图标
      spaceIconShow (index) {
        let me = this
        if (me.treeStructure && index === 0) {
          return true
        }
        return false
      },
    // 点击展开和关闭的时候,图标的切换
      toggleIconShow (index, record) {
        let me = this
        if (me.treeStructure && index === 0 && record.children && record.children.length > 0) {
          return true
        }
        return false
      },
      handleDelete () {
        this.$confirm(‘此操作将永久删除该记录, 是否继续?‘, ‘提示‘, {
          confirmButtonText: ‘确定‘,
          cancelButtonText: ‘取消‘,
          type: ‘error‘
        }).then(() => {
          this.$message({
            type: ‘success‘,
            message: ‘删除成功!‘
          })
        }).catch(() => {
          this.$message({
            type: ‘info‘,
            message: ‘已取消删除‘
          })
        })
      }
    }
  }
</script>
<style scoped>
  .ms-tree-space{position: relative;
    top: 1px;
    display: inline-block;
    font-family: ‘Glyphicons Halflings‘;
    font-style: normal;
    font-weight: 400;
    line-height: 1;
    width: 18px;
    height: 14px;}
  .ms-tree-space::before{content: ""}
  table td{
    line-height: 26px;
  }
</style>

写好了树形表格组件,使用方式和普通的Vue组件使用方法相同:

<template>
  <div class="hello">
    <tree-grid :columns="columns" :tree-structure="true" :data-source="dataSource"></tree-grid>
  </div>
</template>  

<script>
import {TreeGrid} from ‘./TreeGrid‘
export default {
  name: ‘hello‘,
  data () {
    return {
      columns: [
          {
            text: ‘姓名‘,
            dataIndex: ‘name‘
          },
          {
            text: ‘年龄‘,
            dataIndex: ‘age‘
          },
          {
            text: ‘性别‘,
            dataIndex: ‘sex‘
          }
        ],
      dataSource: [
        {
          id: 1,
          parentId: 0,
          name: ‘测试1‘,
          age: 18,
          sex: ‘男‘,
          children: [
            {
              id: 2,
              parentId: 1,
              name: ‘测试2‘,
              age: 22,
              sex: ‘男‘
            }
          ]
        },
        {
          id: 3,
          parentId: 0,
          name: ‘测试3‘,
          age: 23,
          sex: ‘女‘,
          children: [
            {
              id: 4,
              parentId: 3,
              name: ‘测试4‘,
              age: 22,
              sex: ‘男‘
            },
            {
              id: 5,
              parentId: 3,
              name: ‘测试5‘,
              age: 25,
              sex: ‘男‘
            },
            {
              id: 6,
              parentId: 3,
              name: ‘测试6‘,
              age: 26,
              sex: ‘女‘,
              children: [
                {
                  id: 7,
                  parentId: 6,
                  name: ‘测试7‘,
                  age: 27,
                  sex: ‘男‘
                }
              ]
            }
          ]
        },
        {
          id: 18,
          parentId: 0,
          name: ‘测试8‘,
          age: 18,
          sex: ‘男‘
        }
      ]
    }
  },
  components: {
    TreeGrid
  }
}
</script>

以上就是实现树形表格的方法,提前把树形表格数据处理成数组数据,然后针对父级和子集分别增加不同的表示,以实现不同的表格首行显示效果。

但是,该组件将说有数据都加在到Table中,然后采用操作css样式"display:none"的方式进行隐藏和显示。数量比较时候倒是可以完全满足使用,但是如果数据量超过100条或者更多就会出现页面卡顿的现象。

第二种方式

第二种方式在原方法的基础上进行操作,原理就是基于MVVM框架vue的数据驱动原理。采用操作数据的方式执行子级数据的显示隐藏。

根据ElementUI 的 Table组件会根据数据变化进行加载的原理,通过只传入需要展示的数据的方式,来提高浏览器渲染的速度。

直接上代码TreeGrid.vue:

<template>
    <div class="table-content">
        <el-table
            :data="TableDate"
            border
            style="width: 18.82rem">
            <el-table-column
              label="部门名称"
              min-width="400">
              <template scope="scope">
                  <span v-for="(space, levelIndex) in scope.row._level" :key="levelIndex" class="ms-tree-space"></span>
                  <span class="button is-outlined is-primary is-small" v-if="toggleIconShow(scope.row)" @click="toggle(scope.row)">
                      <i v-if="!scope.row._expanded" class="el-icon-arrow-right" aria-hidden="true"></i>
                      <i v-if="scope.row._expanded" class="el-icon-arrow-down" aria-hidden="true"></i>
                  </span>
                  <span v-else class="ms-tree-space"></span>
                  <span :title="scope.row.dpmName">
                      {{ scope.row.dpmName }}
                  </span>
              </template>
            </el-table-column>
            <el-table-column
              label="组织机构代码"
              min-width="300">
              <template scope="scope">
                  <span :title="scope.row.dpmAdc">
                      {{ scope.row.dpmAdc }}
                  </span>
              </template>
            </el-table-column>
            <el-table-column
              label="所属地区"
              min-width="300">
              <template scope="scope">
                  <span :title="scope.row.areaName">
                      {{ scope.row.areaName }}
                  </span>
              </template>
            </el-table-column>
            <el-table-column
              label="上级部门"
              min-width="315">
              <template scope="scope">
                  <span :title="scope.row.parentName">
                      {{ scope.row.parentName }}
                  </span>
              </template>
            </el-table-column>
        </el-table>
    </div>
</template>

<script>
    import {deepCopy} from "../utils/util.js"
    Array.prototype.removeByValue = function(val) {
        //对数组原型添加删除指定项的方法
        for(var i=0; i<this.length; i++) {
            if(this[i] == val) {
                this.splice(i, 1);
                break;
            }
        }
    };
    export default {
        name: ‘TreeGrid‘,
        components: {

        },
        data(){
            return {
                TableDate:[]
            }
        },
        computed:{
            allData(){
                let me = this;
                let newData = deepCopy(me.$store.getters.Data);
                return newData;
            }
        },
        watch: {
            allData(val){
                this.TableDate = deepCopy(val);
            }
        },
        methods: {
            toggleIconShow (record) {
                /**
                 * 点击展开和关闭的时候,图标的切换
                 */
                let me = this;
                if (record.children && record.children.length > 0) {
                    return true
                }
                return false
            },
            toggle(rowData) {
                let me = this;
                /**
                 * 展开下级
                 */
                let childLen = rowData.children.length;
                if(rowData._expanded){
                    let dataArr=[];
                    dataArr.push(rowData);
                    let arr = me.getChildFlowId(dataArr,[]);
                    for(let i=0; i < childLen; i++){
                        me.TableDate.map((value)=>{
                            if(arr.indexOf(value.parentId) > -1){
                                me.TableDate.removeByValue(value);
                            }
                        });
                    }
                } else {
                    rowData.children = me.setSpaceIcon(rowData.children,rowData._level);
                    let index = me.TableDate.indexOf(rowData);
                    let pre = me.TableDate.slice(0,index+1);
                    let last = me.TableDate.slice(index+1);
                    let concatChildren = pre.concat(rowData.children);
                    me.TableDate = concatChildren.concat(last);
                }
                rowData._expanded = !rowData._expanded;
            },
            getChildFlowId(data,emptyArr){
                // 获取子级的flowId
                let me = this;
                Array.from(data).forEach((record)=>{
                    emptyArr.push(record.flowId);
                    if(record.children&&record.children.length > 0){
                        let childFlowIdArr = me.getChildFlowId(record.children,emptyArr);
                        emptyArr.concat(childFlowIdArr);
                    }
                });
                return emptyArr;
            },
            setSpaceIcon(data,level){
                // 设置第一列的空格和方向按钮
                let me = this;
                let _level = 0;
                data.forEach((value)=>{
                    value._expanded = false;
                    if(level !== undefined && level !== null){
                        _level = level + 1;
                    } else {
                        _level = 1;
                    }
                    value._level = _level;
                    if(value.children&&value.children.length > 0){
                        me.setSpaceIcon(value.children, _level);
                    }
                });
                return data;
            }
        }
    }
</script>

虽然上了大段的代码,不过也有很多不细致的地方。重点还是理解实现的方式,尽量减少需要写个方法或者组件的时候,在网上Google一下就拿过来用,更多的应该是理解其中的原理,并且自己进行实践才能进步。

不明白的地方,欢迎提问交流!

时间: 2024-12-21 07:46:35

ElementUI制作树形表组件的相关文章

集成代码生成器 单表 多表 树形表 一对多 springmvc spring mybatis SSM 后台框架

获取[下载地址]   QQ: 313596790   [免费支持更新] 三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体 [新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单; QQ:313596790 freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块 B 集成阿里巴巴数据库连

【集成代码生成器】 单表 多表 树形表 一对多

获取[下载地址]     [免费支持更新]三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体[新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单; freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块B 集成阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.

第二百二十八节,jQuery EasyUI,TreeGrid(树形表格)组件

jQuery EasyUI,TreeGrid(树形表格)组件 学习要点: 1.加载方式 2.属性列表 3.事件列表 4.方法列表 本节课重点了解 EasyUI 中 TreeGrid(树形表格)组件的使用方法,这个组件依赖于 DataGrid(数据表格)组件. 一.加载方式 建立一个 JSON 文件 [ { "id": 1, "name": "系统管理", "date": "2015-05-10", &quo

delphi中用Table表组件和Query查询组件配合进行的增删改查

一.打开数据库表进入检索状态 var Table1:TTable; //定义Table1为TTable类型的变量 begin Table1:=TTable.Create(self);//自建表 Table1:=DatabaseName:='rsgl';//数据库名 Table1:=TableName:='operator.db' //打开数据库Table表 文件名是"operator.db" Table1.Open;   //Table1表开启状态 Table1.SetKey; //将

7.3 制作样式表文件并和其他页面绑定

问题:大家发现前面应用模板的页面中存在的问题了吗?如上一节应用模板图3.图4和图5所示,是不是页面中有些字体过大.颜色搭配不合理.超链接样式极其难看.文本排列不够整齐等,那如何解决这些问题? 大家肯定想到了使用样式表,由于有多个页面甚至整个站点都要应用统一的样式,所以应该创建样式表文件,方便多个页面同时引用. 一.制作样式表 根据上一节图3.图4和图5所示的页面效果,我们应该设置哪些样式?其实,这也不难,你只要看到页面中哪里不美观,那么那里就得应用样式.所以根据上一节图3.图4和图5所示的不美观

5.5 Dreamweaver制作样式表

建立样式表的意义在于实现了统一管理网页的外观,设计者不仅可以通过修改样式表来改变单个网页的外观,而且还可以改变多个网页甚至整个网站的外观,从而大大减轻工作量,提高效率.下面我们就使用Dreamweaver CS3方便地.快速地编写样式文件. 一.什么是样式 让我们先看看图1所示的效果,图中的颜色.字体.字体大小.边框.图片等都很漂亮,其实这就是样式的功劳.所以说,如果把网页内容比喻为一个女孩的话,样式就好比这位女孩穿的衣服.女孩喜欢用漂亮的衣装来打扮自己,同样,我们的网页也需要华丽的样式来包装.

【集成代码生成器】 单表 多表 树形表 一对多 springmvc spring mybatis SSM 后台框架

获取[下载地址]   [免费支持更新]三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体[新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单; freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块B 集成阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.Dr

【JEECG技术博文】JEECG表单配置-树形表单

表单配置支持树型表单了,具体效果如下图: 配置说明 1.是否树:选择是. 2.树形表单父Id:表的自关联外键. 3.树形表单列表:显示树形图标的列,如上图中为[组织机构名称]. 4.默认值:最外层数据的父Id值,具体看表的设计.上图中在数据库表中的默认值为null.

EXTJS 4 树形表格组件使用示例

EXTJS 4 树形表格组件使用示例 一.整体效果图 二.使用说明及效果图 2.1.程序代码及说明: 2.1.1.表格存储部分的代码说明 //开启tooltip工具 Ext.QuickTips.init(); //定义model Ext.define('partModel', { extend: 'Ext.data.Model', fields: [ {name: 'partNo', type: 'string'}, {name: 'partName', type: 'string'}, {na