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 [] } }