// 数据结构转换工具 // 定义数组项的数据类型,包含id、name、parentId基本属性 interface ArrayItem { pid: string id: string name?: string } // 定义树节点的数据类型,包含id、name、可能存在的子节点 interface TreeNode { id: string name?: string pid: string children?: TreeNode[] // 叶子节点没有子节点 } /** * 判断是否有转树的必要 * @param plainList 平行数据列表 * @param id 祖宗id * @returns {boolean} 有返回true,无返回false */ export function judgeTree(plainList: ArrayItem[], id?: '0') { if (plainList && plainList.length > 0) { let flag = false // 是否需要转成树结构 const pid = id for (const item of plainList) { if (item.pid !== pid) { // 只要有一个元素的pid没有指向第一个元素的父id,认为有必要转换成树,否则认为无必要 flag = true break } } return flag } else { return false } } /** * 平面数据数据转树结构 * @param plainList 平行数据列表 * @param id 祖宗id * @param isSelect 是否是下拉需要顶级的树 * @returns {*} */ export function toTreeList<T extends ArrayItem>(plainList: T[], rootId = '0', isSelect = false): T[] { const pid = findPid(plainList) if (pid.length > 1) { // 如果有多个pid,直接返回列表, 不去构造树 return plainList } else { const tree = cleanChildren(buildTree<T>(plainList, rootId, isSelect)) return tree } } // 构建树 /** * * @param plainList 待转换数组 * @param id 父节点 * @param isSelect 是否是下拉框所使用的树 * @returns 树节点列表 */ function buildTree<T extends TreeNode>(plainList: T[], id = '0', isSelect = false): T[] | [] { // 递归函数 const fa = (parentId: string): Array<T> | [] => { const temp = [] for (let i = 0; i < plainList.length; i++) { const n: TreeNode = { ...plainList[i] } const id = `${n.id}` const pid = `${n.pid}` if (pid === parentId) { n.children = fa(id) temp.push(n) } } return temp as T[] } // 如果是下拉框需要使用的树,首先寻找顶级,将顶级也放入列表 if (isSelect) { let flag = 1 const list = [] if (Array.isArray(plainList)) { for (const item of plainList) { const n: T = { ...item } const nid = `${n.id}` if (nid === id) { n.children = fa(id) flag = 0 list.push(n) return list } else { continue } } } if (flag === 1) { // 没有找到父级,按原流程走 return fa(id) } else { return [] } } else { return fa(id) } } // 清除children为空列表的children项 function cleanChildren<T extends TreeNode>(data: T[]): T[] { const fa = (list: TreeNode[]): T[] => { list.map((e) => { if (e && e.children && e.children.length) { fa(e.children) } else { delete e.children } return e }) return list as T[] } return fa(data) } /** * * @param plainList 寻找列表中的父id * @returns 父id列表 */ function findPid(plainList: Array<ArrayItem>): Array<string> { const pidList = new Set<string>() if (plainList) { for (const item of plainList) { // 1.添加所有的父id pidList.add(item.pid) } for (const item of plainList) { // 2.删除所有的子id if (pidList.has(item.id)) { pidList.delete(item.id) } } const arr = Array.from(pidList) // 剩下的就是最终的父节点 return arr } else { return [] } } // 从树列表中删除指定元素 export function deleteItem(list: Array<TreeNode>, des: TreeNode) { const del = (list: Array<TreeNode>, item: TreeNode) => { for (let i = 0; i < list.length; i++) { if (list[i].id === item.id) { list.splice(i, 1) return } else { // 遍历子孙,继续递归寻找 if (list[i].children && list[i].children!.length > 0) { del(list[i].children!, item) } } } } del(list, des) } interface treeItem { id: string open: string | boolean checked: string | boolean } /** *获取列表中的展开项和选中项 * @param plainList * @param id * @returns{展开项, 选中项} */ export function getShowItem(plainList: treeItem[]): { expandList: string[]; openedList: string[] } { const expandList = [] const openedList = [] for (let i = 0; i < plainList.length; i++) { if (plainList[i].open === 'true' || plainList[i].open === true) { expandList.push(plainList[i].id) } if (plainList[i].checked === 'true' || plainList[i].checked === true) { openedList.push(plainList[i].id) } } return { expandList, openedList } }