antd 表格树如何展开_vue表格树状结构的实现

这绝对是我最后一次写树状结构,我非常的确定一定以及肯定! 上一篇文章总结了vue树状目录组件,忘记的可以点击传送门回顾一下: vue目录树组件

7ad3b72ede82004c88dfb85073667223.png

还是这个目录树,改版成 表格树状结构

我这里采用的UI库是Element,库自带了一种实现方式:

a02fc4cf2858c56611bdbd03e3098c23.png

但是这种自带的实现方式,可扩展性很差,就很low,而且这样偷懒会让自己的技术止步不前,心里不安呀。

首先明确实现逻辑,表格树状结构中所谓的树状,并不是数据结构上的树状,只是布局上的效果而已,这里要求的数据结构是平级的数据列表

还是上篇文章中说的,后端直接返回树状结构数据。

f243da8f58c364bbdb76851e1b66c99c.png

所以这里需要对数据处理一下,将树状结构的数据转换成平级的。getTreeList 方法中调用接口先获取到后端数据。

getTreeList() {    this.treeList = [];    Catalog.getListByParent(this.catalogId).then((res) => {    if (res.status === 0) {        const treeList = res.data || [];        const tableList: ITree[] = [];        this.setCatalogList(tableList, treeList);        this.treeList = treeList;        this.tableList = tableList;    }  });},

这里有个需要注意的点,接口返回的数据并没有直接赋给 this.treeList ,因为直接赋值之后 vue 不会一直监听数据的变化,而树状结构是需要在原有数据上修改的,例如 setCatalogList 方法中将树状结构数据转换成平级列表数据,所以定义个常量中间过渡一下,防止数据不实时更新。

setCatalogList(arr: ITree[], treeList: ITree[]) {  treeList.forEach((item) => {    arr.push(item);    if (item.catalogTreeVoList && item.catalogTreeVoList.length > 0) {        this.setCatalogList(arr, item.catalogTreeVoList);    }  });},

因为树状列表中一些交互操作,如收起展开子节点,需要前端自定义一些字段属性用来控制,所以还需要将拿到的数据再构建一个map对象,更方面后面对自定义字段属性的操作。

将this.treeList 完善一下。

getTreeList() {    this.treeList = [];    Catalog.getListByParent(this.catalogId).then((res) => {    if (res.status === 0) {        const treeList = res.data || [];        const tableList: ITree[] = [];        const treeMap: ITreeMap = {};        this.setCatalogList(tableList, treeList);        this.setCatalogMap(treeMap, treeList, 0);        this.treeList = treeList;        this.tableList = tableList;        this.treeMap = treeMap;    }  });},setCatalogMap(catalogMap: ITreeMap , list: ITree[], level: number) {  list.forEach((item, index) => {    catalogMap[item.catalogId] = item;    item.level = level;    item.index = index;    if (item.catalogTreeVoList && item.catalogTreeVoList.length > 0) {        this.setCatalogMap(catalogMap, item.catalogTreeVoList, level + 1);    }  });},

这时候打印一下treeList,tableList或者treeMap,会发现数据中已经添加了level和index字段。但这里暂时不用,后面其他操作的时候需要用的。

那对于树状列表最基础的收起展开操作,需要的字段是isOpen和isShow,我们这里规定isOpen控制展开收起图标的切换,isShow控制子节点的显示和隐藏。

这两个字段的添加还是在this.treeList 中,完整的this.treeList 代码如下:

getTreeList() {    this.treeList = [];    Catalog.getListByParent(this.catalogId).then((res) => {    if (res.status === 0) {        const treeList = res.data || [];        const tableList: ITree[] = [];        const treeMap: ITreeMap = {};        this.setCatalogList(tableList, treeList);        this.setCatalogMap(treeMap, treeList, 0);        Object.keys(treeMap).forEach((key) => {          const item = treeMap[key];          if (this.treeMap[key]) {              item.isOpen = this.treeMap[key].isOpen;              item.isShow = this.treeMap[key].isShow;          } else {              item.isOpen = true;              item.isShow = true;          }        });        this.treeList = treeList;        this.tableList = tableList;        this.treeMap = treeMap;    }  });},

其实,应该先给出页面结构的代码

  ref="table"  :data="tableList"  :row-class-name="cellClass">            
{{scope.row.catalogName}}
上移 下移 编辑 删除

展开收起操作的实现逻辑,就是修改isOpen的值,但是相应的判断当前节点的子节点的显示和隐藏

当子节点存在的时候,如果是要收起当前节点,就很简单的不用管子节点的状态,全部隐藏就好。如果是要展开当前节点,isOpen为真所有子节点显示,否则隐藏。

tabNode(node: ITree) {  this.treeMap[node.catalogId].isOpen = !this.treeMap[node.catalogId].isOpen;  this.statusChange(node, node.isOpen);},statusChange(data: ITree, status: boolean) {  if (data.catalogTreeVoList) {    data.catalogTreeVoList.forEach((childEl: ITree) => {        if (status) {            if (data.isOpen) {                childEl.isShow = true;            } else {                childEl.isShow = false;            }        } else {            childEl.isShow = status;        }        if (childEl.catalogTreeVoList) {            this.statusChange(childEl, status);        }    });  }},

isShow控制子节点显示或隐藏,是基于Element表格自带的一个方法,这里的类名rowNone就是display: none。

cellClass({ row, rowIndex }: ITableRow) {    if (!row.isShow) {        return "rowNone";    }},

上篇文章结尾处提到的上下移排序的问题,这里需要做相应的改变,就是用到了前文写的index字段。

上移排序的逻辑,修改当前节点和上一个节点的下标值,就是经典的基础题:a=1,b=2,交换a,b的值,这就需要定义个中间变量过渡一下。最后会发现点击上移之后,打印出来的数据修改了,但是页面并没有更新。

这是因为index字段是在构建map对象时定义的,修改index的值只会让treeList更新,而此时排序需要修改的数据是tableList,所以最后还需要重新调用 setCatalogList ,将最新的treeList转换成tableList。

doUp(node: ICatalogModel, index: number) {    if (index === 0) {        return;    }    const parentId: string = node.catalogParent as string;    const parentItem: ICatalogModel = this.treeMap[parentId];    let dataList: ICatalogModel[] = [];    // 如果为空则是顶级    if (parentItem) {        if (parentItem.catalogTreeVoList) {            dataList = parentItem.catalogTreeVoList;        }    } else {        dataList = this.treeList;    }    const beforeItem = dataList[index - 1];  // a    const item = dataList[index];  // b    // 中间变量    let itemIndex: number = 0;       // c    itemIndex = item.index;         // c = b    item.index = beforeItem.index; // b = a    beforeItem.index = itemIndex; // a = c    dataList.splice(index, 1);    dataList.splice(index - 1, 0, item);    this.doSaveSort(dataList);    const tableList: ITree[] = [];    this.setCatalogList(tableList, this.treeList);    this.tableList = tableList;},

相应的下移操作,修改的是当前节点和下一个节点的下标值,就不赘述了。

最后效果如下(样式没咋优化):

957a16785cf21039c88a531a4bb6f9a3.png


最后的上移下移的操作只是附带的,这篇文章主要总结的还是表单树状结构的实现,磨了一下午,这算是最彻底完整的一次总结了,但其中还是存在了一些没理解透的点,如表单选中的问题,还需要多练习。 over~ 下班

版权声明:本文为weixin_31632529原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。