antd的TreeSelect组件在处理例如公司层级、学科系统、分类目录等等的树形选择需求时很好用。node
在使用这个组件时,咱们每每须要获取所选中的全部节点以及所选中的全部子节点的数量。api
查看TreeSelect的api找到了组件的选中回调方法onChange数组
在理解onChange方法的参数含义前,要先知道TreeSelect组件的数据格式antd
onChange方法有三个参数,value表示所选中的节点的value字段值,label表明所选中的节点的title字段值。而最后一个extra参数是获取所选中子节点数量的关键,它是一个Object对象。spa
经过查看extra的数据,找到了allCheckedNodes这个字段。这个字段里放置着全部被选中的节点的数据。其格式以下code
[ { "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字段,若是没有,说明它是子节点,那直接让数量加一;若是有,则说明是父节点,则递归调用方法去取其子节点内的数据。通过这样的递归取数,就能计算出全部被选中的子节点的数量了。rem
方法代码以下:get
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 [] } }