基于Element-UI的树形表格组件,根据需求扩展一些功能,如实现每次只能展开同级一行的手风琴效果,表格数据实现新增,编辑,查询,删除等。

[一] 每次只能展开同级一行的手风琴效果

<el-table
   ref="table"
   :data="tableData"
   :tree-props="{children: 'children', hasChildren:       'hasChildren'}"
   @expand-change="toggleRow"
>

树形table的expand-change方法,可以传两个参数,第一个参数传row,这是当前一行的表格数据。第二个参数传expanded.expanded 为 true 则展开,fasle则关闭。

toggleRow(row, expanded) {
    // orgId:id
    // orgPositionLevel:层级(后台返回代表层级的字段,如果后台不返回,则自己对后台返回的数据进行改造)
    const rowId = row.orgId;
    const rowLevel = row.orgPositionLevel;
    if (!expanded) {
      return
    }
    // 手动维护级别数据
    // levelData 全局变量, levelData:new Map()
    //new Map() 提供“键值对”的数据结构“key-value",通过set存值,get取值
    let list = this.levelData.get(rowLevel)
    if (!list) {
      list = []
    }
    list.push(rowId)
    //把当前点击层级作为key,id作为value
    //点击同一个层级, id都会push进该key对应的value中
    this.levelData.set(rowLevel, list)
     // 获取当前级别所有的树
     //通过遍历list 判断是否是同级的id,
     //如果是同级的id,通过table自带的闭合属性设置该id的节点关闭
    list.map(orgId => {
      if (orgId !== rowId) {
        this.$refs.table.toggleRowExpansion({ orgId }, false)
      }
    })
    },

实现效果如图,当前展开 你好16-1,再展开 你好76-2,你好16-1自动关闭,不论多少级同理,实现进阶版手风琴效果

vue element树形结构各种样式 element ui 树形表格_vue element树形结构各种样式

[二] 查询

查询成功,后端返回包含匹配数据的tableData,只有一条节点符合查询结果,根据currentOrgInfo字段判断,为true,代表该节点符合查询条件,其他节点皆为false,格式如下:

{
	"code": "00000000",
	"msg": "成功",
	"data": {
	"orgId": "1",
	"orgName": "新增成功,放回当条记录",
	"orgCode": "19",
	"createdDate": "2020-05-27 11:05",
	"parentId": "-1",
	"orgPositionLevel": "1",
	"status": "1",
	"currentOrgInfo": false,
	"hasChildren": null,
	"children": []
	},
	"redirectUrl": null
}
  • 高亮当前节点
    根据这个字段高亮当前节点,使用treetable的row-class-name属性
// 条件查询返回匹配的数据 使字体变色
<el-table
  ref="table"
  :row-class-name="rowClassName"
</el-table

 rowClassName({ row, rowIndex }) {
	   if (row.currentOrgInfo) {
	     return 'active-hover'
	   } else {
	     return ''
	   }
 },
  • 展开当前节点的所有父节点
    后台返回的是树形数据,可以将数据扁平化,有两种方法
    第一种 js递归
// 树形结构转扁平化
toLine(arr, result) {
  var self = this
  arr.forEach(item => {
    if (item.children) {
      result.push({
        id: item.orgId,
        name: item.orgName,
        pid: item.parentId,
        currentOrgInfo: item.currentOrgInfo,
        orgPositionLevel: item.orgPositionLevel
      })
      //可以存想要的字段,也可以直接存item
      self.toLine(item.children, result)
    } else {
      result.push({
        id: item.orgId,
        name: item.orgName,
        pid: item.parentId,
        currentOrgInfo: item.currentOrgInfo,
        orgPositionLevel: item.orgPositionLevel
      })
      self.toLine(item.children, result)
    }
  })
  return result
},

第二种 通过 new Map键值对递归

//这种更简单,更符合对数据的操作
flattenData(data,result=new Map()) {
      data.forEach(item => {
        result.set(item.orgId,item);
        if(item.children && item.children.length>0) {
          this.flattenData(item.children,result);
        }
      })
      return result;
    },

拿到扁平化后的数据orgListData,根据currentOrgInfo拿到当前节点的父节点id,

let newArr;
this.orgListData.forEach(orgId=>{
  if(orgId.currentOrgInfo) {
    newArr=orgId.parentId;
  }
})

通过父节点匹配到所有的父级id
第一种 扁平化递归

// orgId:当前节点的父节点id
getParentChains(orgId,result=[]) {
 //  通过机构id获取对应行数据
  const currentRow=this.orgList.get(orgId);
 //  插入到数组起始位置,让顶级的排前面
  result.unshift(currentRow);
 if(currentRow.orgPositionLevel !== "0") {
   this.getParentChains(currentRow.parentId,result);
 }
 return result;
},

第二种 遍历树结构 往上查找父节点 找到所有的父节点

setPid(data2, pid) {
var _this = this
var arrRes = []
if (data2.length === 0) {
  if (pid) {
    arrRes.unshift(pid)
  }
  return arrRes
}
let rev = (data, nodeId) => {
  for (var i = 0; i < data.length; i++) {
    let node = data[i]
    if (node.orgId === nodeId) {
      arrRes.unshift(nodeId)
      // 找到就返回父id
      arrRes.unshift(nodeId)
      rev(data2, node.parentId)
      break
    } else {
      if (node.children) {
        rev(node.children, nodeId)
      }
    }
  }
  // console.log(arrRes);
  return arrRes
}
arrRes = rev(data2, pid)
return arrRes
},

根据遍历展开每条父节点

parentChains.forEach(item => {
 const storeRow=this.$refs.table.store.states.treeData[item.orgId];
 if(storeRow) {
   storeRow.expanded=true;
   this.toggleRow(item,true);
 }
})

[三] 新增

新增树形结构分为新增同级跟下级两种情况。
请求新增接口成功后,后端返回该条新增数据newData,前端通过改变tableData从而刷新节点,避开全局刷新,有利于客户体验。


//需要根据当前行currentRow的pid去找到该行的上级,通过上级的children push进新增的数据
let data = _this.orgListData.get(_this.currentRow.parentId);
data.children.unshift(newData);

//直接通过当前行currentRow的children push进新增的数据
_this.currentRow.children.unshift(newData);

[四] 删除


//需要根据当前行currentRow的pid去找到该行的上级,通过上级的children 删除对应下标
const currentId=row.parentId;
const Id=row.orgId;
// 拿到父级数据
let deleteArr=self.orgListData.get(currentId);
deleteArr.children.forEach((item,index)=>{
   if(item.orgId===Id) {
     deleteArr.children.splice(index,1);
   }
 })

//直接通过tableData删除对应下标
this.tableData.forEach((item,index)=>{
    if(item.orgId===Id) {
      this.tableData.splice(index,1);
    }
  })

[五] 编辑

亦是同理,。。。。。