背景
1、本来table表自带一个默认全部展开,但是是初次渲染的时候,需求是增加一个按钮实现一键展开和关闭的切换。
2、其实简单的一键展开实现还好,但需求有增加了难度,表格是懒加载的,那意思就是,需要先获取所有孩子,再展开所有孩子。
懒加载全选
思路
1、既然是懒加载,那我们需要一个或者列表的接口,一个点击展开获取孩子的接口。
el-table有load属性,接受一个方法 resolve 孩子的值。
我的方法是写在组件里面的,相当于是用el-table 封装了一个table组件,不是直接用el-table
/**
*
* @param {*} tree
* @param {*} resolve
* 懒加载获取孩子数据
*/
async loadChildren(tree, treeNode, resolve) {
const code = tree[this.rowKey]
const { hasChildren = 'testcaseCount' } = this.tableOptions.treeProps || {}
// 有懒加载的方法情况下 loadMethod 为load方法 ,单独点击展开键的时候的获取孩子的操作
if (typeof this.loadMethod === 'function' && !this.isExpandAll) {
const data = await this.loadMethod (tree) // 拿到孩子的值
if (tree[hasChildren]) {
const checked = this.isChecked(tree) // 懒加载情况下判定孩子是否勾选
data.forEach(item => {
this.$refs.table.toggleRowSelection(item, !!checked)
})
}
resolve(data)
return
}
// 是已经打开了一键展开,说明已经获取过孩子,直接读取缓存
resolve(this.tableKeyMap[code] || []) //
this.$nextTick(() => {
this.expandChildren(this.tableKeyMap[code], code)
})
},
isChecked(row) {
// 判定点击展开获取孩子的这项是否在已选项中,这样判定是否需要复选
return this.$refs.table.selection.some(item => item[this.rowKey] === row[this.rowKey])
},
/**
*
* @param {*} data
* @param {*} code
* 一键展开后的操作孩子数据
*/
expandChildren(data, code) {
for (const item of data) {
if (item[hasChildren]) {
const treeData = this.$refs.table.store.states.treeData // 可以打印出来看一下,可以拿到当前所有可展开的节点
const code = item[this.rowKey]
const els = document.querySelector(`.can-click${code}`)
if (!els) return
els.click() // 模拟点击箭头展开操作,这样就会触发上面的 loadChildren 方法,去掉接口拿孩子的数据
if (treeData[code]) {
this.$set(treeData[code], 'lazy', false)
treeData[code][children] = this.tableKeyMap[code] || []
}
}
}
},
2、 现在来实现一键展开操作
/**
* 一键展开功能
*/
async handleExpandAll() {
this.isExpandAll = !this.isExpandAll
const treeData = this.$refs.table.store.states.treeData
const keys = Object.keys(treeData)
// 懒加载情况下
if (this.tableOptions.lazy) {
this.tableLoading = true
if (typeof this.tableOptions.expandMethod === 'function' && this.isExpandAll) {
const { data = [] } = await this.tableOptions.expandMethod()
// 接口提供查询出所有孩子的数据,这是对于大数据的思路,直接resolve对于code的children。避开展开一个掉一次孩子查询接口
this.setTableKeyMap(data)
}
keys.forEach(item => {
if (treeData[item].expanded !== this.isExpandAll) {
// 模拟点击展开
if (this.isExpandAll) {
const els = document.querySelector(`.can-click${item}`)
if (!els) return
els.click()
} else {
// 收起
this.$set(treeData[item], 'expanded', false)
}
this.$set(treeData[item], 'loading', false)
treeData[item][children] = this.tableKeyMap[item] || []
}
})
this.tableLoading = false
} else {
// 正常数据下直接展开,改属性
keys.forEach(item => {
this.$set(treeData[item], 'expanded', this.isExpandAll)
})
}
},
/**
*
* @param {*} list
* 获取每个key值的孩子,存起来
*/
setTableKeyMap(list) {
for (const item of list) {
if (Array.isArray(item.children)) {
this.setTableKeyMap(item.children)
}
this.tableKeyMap[item[this.rowKey]] = item.children || []
}
},
3、还有一个需要解决的就是,展开的时候需要判定孩子是否需要勾选起来,根据展开的选项复选框来判定
/**
* 节点展开事件
*
* @description 通过 expandMap 记录展开的行
* @param row 当前行
* @param expanded 展开状态
*/
onExpandChange(row, expanded) {
expanded && this.handleReverseSelect(row, !!this.isChecked(row))
},
// 判断是否需要 选中孩子
handleReverseSelect(row, expanded) {
// 是懒加载
if (this.tableOptions.lazy) {
const children = this.tableKeyMap[row[this.rowKey]] || []
if (children.length) {
for (const item of children) {
if (this.isChecked(item) === expanded) {
return
}
}
children.forEach(item => {
this.$refs.table.toggleRowSelection(item, !!expanded)
})
}
}
},