antd的TreeSelect组件在处理例如公司层级、学科系统、分类目录等等的树形选择需求时很好用。
在使用这个组件时,我们往往需要获取所选中的所有节点以及所选中的所有子节点的数量。
查看TreeSelect的api找到了组件的选中回调方法onChange
在理解onChange方法的参数含义前,要先知道TreeSelect组件的数据格式
onChange方法有三个参数,value表示所选中的节点的value字段值,label代表所选中的节点的title字段值。而最后一个extra参数是获取所选中子节点数量的关键,它是一个Object对象。
通过查看extra的数据,找到了allCheckedNodes这个字段。这个字段里放置着所有被选中的节点的数据。其格式如下
[
{
"node":{
"key":"1",
"ref":null,
"props":{
"title":"全体老师",
"value":"1",
"children":[
{
"key":"1-1",
"ref":null,
"props":{
"title":"老师1",
"value":"1-1",
"children":[
]
},
"_owner":null,
"_store":{
}
},
{
"key":"1-2",
"ref":null,
"props":{
"title":"老师2",
"value":"1-2",
"children":[
]
},
"_owner":null,
"_store":{
}
},
{
"key":"1-3",
"ref":null,
"props":{
"title":"老师3",
"value":"1-3",
"children":[
]
},
"_owner":null,
"_store":{
}
},
{
"key":"1-4",
"ref":null,
"props":{
"title":"老师4",
"value":"1-4",
"children":[
]
},
"_owner":null,
"_store":{
}
},
{
"key":"1-5",
"ref":null,
"props":{
"title":"老师5",
"value":"1-5",
"children":[
]
},
"_owner":null,
"_store":{
}
},
{
"key":"1-6",
"ref":null,
"props":{
"title":"老师6",
"value":"1-6",
"children":[
]
},
"_owner":null,
"_store":{
}
},
{
"key":"1-7",
"ref":null,
"props":{
"title":"老师7",
"value":"1-7",
"children":[
]
},
"_owner":null,
"_store":{
}
},
{
"key":"1-8",
"ref":null,
"props":{
"title":"老师8",
"value":"1-8",
"children":[
]
},
"_owner":null,
"_store":{
}
},
{
"key":"1-9",
"ref":null,
"props":{
"title":"老师9",
"value":"1-9",
"children":[
]
},
"_owner":null,
"_store":{
}
}
]
},
"_owner":null,
"_store":{
}
},
"pos":"0-1",
"children":[
{
"pos":"0-1-0"
},
{
"pos":"0-1-1"
},
{
"pos":"0-1-2"
},
{
"pos":"0-1-3"
},
{
"pos":"0-1-4"
},
{
"pos":"0-1-5"
},
{
"pos":"0-1-6"
},
{
"pos":"0-1-7"
},
{
"pos":"0-1-8"
}
]
}
]
分析发现,allCheckedNodes对象里的pos字段内容是当前被选中的节点的key值,而children字段是这个节点的被选中的所有子节点的集合数组,而children内的对象又有可能有下一层级的子节点。
为了计算这种嵌套的多层级关系的子节点数量,我们需要用到递归方法。
方法思路是,一层一层的去看当前对象有没有children字段,如果没有,说明它是子节点,那直接让数量加一;如果有,则说明是父节点,则递归调用方法去取其子节点内的数据。经过这样的递归取数,就能计算出所有被选中的子节点的数量了。
方法代码如下:
export const getCheckedNodes = (extra) => {
let checkedNodes = extra.allCheckedNodes || [extra.triggerNode]
// let count = getCheckedCount(checkedNodes)
if (isObjEmpty(checkedNodes)) {
checkedNodes = []
}
checkedNodes = getNodes(checkedNodes)
console.log('checkNodes', checkedNodes)
return {checkedNodes, count: checkedNodes.length}
}
export const getNodes = (checkedNodes) => {
if (!isObjEmpty(checkedNodes)) {
let childNodes = []
for (let i = 0; i < checkedNodes.length; i++) {
let checkedNode = checkedNodes[i]
if (checkedNode) {
if (checkedNode.node && checkedNode.node.props) {
const checkProps = checkedNode.node.props
if (!isObjEmpty(checkProps.children)) {
checkedNode = checkProps.children
childNodes = childNodes.concat(getNodes(checkedNode))
} else {
let exist = false
for (let j = 0; j < childNodes.length; j++) {
if (checkProps && checkProps.value == childNodes[j].value) {
exist = true
break
}
}
if (!exist) {
childNodes.push(checkProps)
}
continue
}
} else {
if (checkedNode.props) {
const checkProps = checkedNode.props
if (!isObjEmpty(checkProps.children)) {
checkedNode = checkProps.children
childNodes = childNodes.concat(getNodes(checkedNode))
} else {
let exist = false
for (let j = 0; j < childNodes.length; j++) {
if (checkProps && checkProps.value == childNodes[j].value) {
exist = true
break
}
}
if (!exist) {
childNodes.push(checkProps)
}
continue
}
}
}
} else {
continue
}
}
return childNodes
} else {
return []
}
}