import { Injectable } from '@angular/core' ; import { NzTreeNode } from 'ng-zorro-antd' ; @ Injectable ( { providedIn : 'root' } ) export class TreeService { /**
     * 将树结构转换成数组结构
     */ treeToArr ( tree , options ? ) { options = Object . assign ( { deepMapName : 'deep' , parentMapName : 'parent' , childrenMapName : 'children' , clearChildren : true , cb : null , } , options ) ; const result = [ ] ; const inFn = ( list , parent , deep ) => { for ( const i of list ) { i [ options . deepMapName ] = deep ; i [ options . parentMapName ] = parent ; if ( options . cb ) options . cb ( i , parent , deep ) ; result . push ( i ) ; const children = i [ options . childrenMapName ] ; if ( children != null && Array . isArray ( children ) && children . length > 0 ) { inFn ( children , i , deep + 1 ) ; } if ( options . clearChildren ) delete i [ options . childrenMapName ] ; } } ; inFn ( tree , 1 , null ) ; return result ; } /**
     * 数组转换成树数据
     */ arrToTree ( arr , options ? ) { options = Object . assign ( { idMapName : 'id' , pidMapName : 'parent_id' , childrenMapName : 'children' , cb : null , } , options ) ; const tree = [ ] ; const childrenOf = { } ; arr . forEach ( ( item ) => { const exist = arr . findIndex ( a => item [ options . pidMapName ] === a [ options . idMapName ] ) > - 1 ; if ( ! exist ) { delete item [ options . pidMapName ] ; } } ) ; for ( const item of arr ) { const id = item [ options . idMapName ] , pid = item [ options . pidMapName ] ; childrenOf [ id ] = childrenOf [ id ] || [ ] ; item [ options . childrenMapName ] = childrenOf [ id ] ; if ( options . cb ) options . cb ( item ) ; if ( pid ) { childrenOf [ pid ] = childrenOf [ pid ] || [ ] ; childrenOf [ pid ] . push ( item ) ; } else { tree . push ( item ) ; } } return tree ; } /**
     * 数组转换成 `nz-tree` 数据源，通过 `options` 转化项名，也可以使用 `options.cb` 更高级决定数据项
     */ arrToTreeNode ( arr , options ? ) { options = Object . assign ( { expanded : false , idMapName : 'id' , pidMapName : 'parent_id' , titleMapName : 'title' , isLeafMapName : 'isLeaf' , checkedMapname : 'checked' , selectedMapname : 'selected' , expandedMapname : 'expanded' , disabledMapname : 'disabled' , cb : null , } , options ) ; const tree = this . arrToTree ( arr , { idMapName : options . idMapName , pidMapName : options . pidMapName , childrenMapName : 'children' , } ) ; this . visitTree ( tree , ( item , parent , deep ) => { item . key = item [ options . idMapName ] ; item . title = item [ options . titleMapName ] ; item . checked = item [ options . checkedMapname ] ; item . selected = item [ options . selectedMapname ] ; item . expanded = item [ options . expandedMapname ] ; item . disabled = item [ options . disabledMapname ] ; if ( item [ options . isLeafMapName ] == null ) { item . isLeaf = item . children . length === 0 ; } else { item . isLeaf = item [ options . isLeafMapName ] ; } if ( options . cb ) options . cb ( item , parent , deep ) ; } ) ; return tree . map ( node => new NzTreeNode ( node ) ) ; } /**
     * 递归访问整个树
     */ visitTree ( tree , cb , options ? ) { options = Object . assign ( { childrenMapName : 'children' , } , options ) ; const inFn = ( data , parent , deep ) => { for ( const item of data ) { cb ( item , parent , deep ) ; const childrenVal = item [ options . childrenMapName ] ; if ( childrenVal && childrenVal . length > 0 ) { inFn ( childrenVal , item , deep + 1 ) ; } } } ; inFn ( tree , null , 1 ) ; } /**
     * 获取所有已经选中的 `key` 值
     */ getKeysByTreeNode ( tree , options ? ) { options = Object . assign ( { includeHalfChecked : true , } , options ) ; const keys = [ ] ; this . visitTree ( tree , ( item , parent , deep ) => { if ( item . isChecked || ( options . includeHalfChecked && item . isHalfChecked ) ) { keys . push ( options . cb ? options . cb ( item , parent , deep ) : options . keyMapName ? item . origin [ options . keyMapName ] : item . key ) ; } } ) ; return keys ; } }