基于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自动关闭,不论多少级同理,实现进阶版手风琴效果
[二] 查询
查询成功,后端返回包含匹配数据的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);
}
})
[五] 编辑
亦是同理,。。。。。