/** * Js树型数据转换工具类 * @author cls * @date 2019-07-01 */ import { dataGroupKey } from "@birt/funclib/DataGroup.js"; /** * showdoc * @catalog JS工具类/DataTree * @title 根据分组字段配置,转换成两层树型数据 * @description 根据传入对象数组,树型配置转换成树结构数据 * @method static * @url import { groupToTreeData } from '@birt/utils/DataTree.js' * @param datas 必选 [{},{}] 记录集对象,树分组字段值冗余 * @param tree 必选 {groupBy:['f1','f2'],childNodeKeyField:'methodName',childrenKey:'children'} 树型配置 * @param tree.groupBy 必选 groupBy:['f1','f2'] 树分组字段 * @param tree.childNodeKeyField 必选 'methodName' 子节点拼接id唯一值 * @param tree.parentLabelField 必选 'labelFieldName' 树父节点标签绑定字段 * @param tree.childLabelField 必选 'labelFieldName' 树子节点标签绑定字段 * @param tree.childrenKey 可选 'children' 字节点key值 * @param tree.nodeKey 可选 'treeId' 树节点唯一标识Id字段 * @return [{'f1':'v1','f2':'v2'}] * @return_param f1 string 记录字段名 * @return_param v1 object 记录字段名对应值 * @remark 测试用例 * groupToTreeData( * [{"methodName":"abs","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"取绝对值","refClassImport":"com.zhc.eap.funclib"},{"methodName":"directSuperiors","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"直属上级","refClassImport":"com.zhc.eap.funclib"},{"methodName":"curtOperator","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"当前操作者","refClassImport":"com.zhc.eap.funclib"},{"methodName":"plus","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"浮点数相加","refClassImport":"com.zhc.eap.funclib"},{"methodName":"curtDate","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期","refClassImport":"com.zhc.eap.funclib"},{"methodName":"curtDateTime","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期时间","refClassImport":"com.zhc.eap.funclib"}], * {groupBy:["refClassImport","className"],parentLabelField:'classNameCn',childLabelField:'methodNameCn',childrenKey:"children"} * ) * 返回==> * [{"methodName":"abs","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"取绝对值","refClassImport":"com.zhc.eap.funclib","label":"数学函数","children":[{"methodName":"abs","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"取绝对值","refClassImport":"com.zhc.eap.funclib","label":"取绝对值"},{"methodName":"plus","className":"MathFunc","classNameCn":"数学函数","methodNameCn":"浮点数相加","refClassImport":"com.zhc.eap.funclib","label":"浮点数相加"}]},{"methodName":"directSuperiors","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"直属上级","refClassImport":"com.zhc.eap.funclib","label":"组织机构函数","children":[{"methodName":"directSuperiors","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"直属上级","refClassImport":"com.zhc.eap.funclib","label":"直属上级"},{"methodName":"curtOperator","className":"OrgFunc","classNameCn":"组织机构函数","methodNameCn":"当前操作者","refClassImport":"com.zhc.eap.funclib","label":"当前操作者"}]},{"methodName":"curtDate","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期","refClassImport":"com.zhc.eap.funclib","label":"日期函数","children":[{"methodName":"curtDate","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期","refClassImport":"com.zhc.eap.funclib","label":"当前日期"},{"methodName":"curtDateTime","className":"DateFunc","classNameCn":"日期函数","methodNameCn":"当前日期时间","refClassImport":"com.zhc.eap.funclib","label":"当前日期时间"}]}] */ export function groupToTreeData(datas, tree) { let nodeKey = tree.nodeKey || "treeId" let childrenKey = tree.childrenKey || "children"; var groupJson = {}; datas.forEach(data => { const groupKey = dataGroupKey(data, tree.groupBy, ".") if (!groupJson[groupKey]) { groupJson[groupKey] = {}; groupJson[groupKey][childrenKey] = []; } let treeNode = { label: data[tree.childLabelField] } treeNode[nodeKey] = groupKey + "." + data[tree.childNodeKeyField]; treeNode = { ...treeNode, ...data }; groupJson[groupKey][childrenKey].push(treeNode) }) var treeRecds = [] for (var gKey in groupJson) { var groupRecd = groupJson[gKey]; var firstChild = groupJson[gKey][childrenKey][0]; let treeNode = { label: firstChild[tree.parentLabelField] } treeNode[nodeKey] = gKey; var recd = { ...firstChild, ...treeNode, ...groupRecd } treeRecds.push(recd) } return treeRecds } /** * showdoc * @catalog JS工具类/DataTree * @title convertToTreeData 转换成树型结构数据 * @description 根据传入数据数组,树型配置转换成树结构数据 * @method static * @url import { convertToTreeData } from '@birt/utils/DataTree.js' * @param data 必选 [{},{}] 记录集对象 * @param setting 必选 {id:'',parentId:'',children:''} 树型配置 * @param setting.id 必选 string 树Id字段 * @param setting.parentId 必选 string 树父Id字段 * @param setting.children 可选 string 孩子节点key值,默认children * @return [{'f1':'v1','f2':'v2',children:[{},{}]}] 树型结构数据 * @remark 测试用例 * convertToTreeData([{id:1,pid:0},{id:2,pid:0},{id:3,pid:1}],{id:"id",parentId:"pid"}) * 返回==> * [{"id":1,"pid":0,"children":[{"id":3,"pid":1}]},{"id":2,"pid":0}] */ export function convertToTreeData(data, setting) { let i, l; let key = setting.id; let parentKey = setting.parentId; let childKey = setting.children || "children"; if (!key || key == "" || !data) return []; if (Array.isArray(data)) { var r = []; var tmpMap = []; for (i = 0, l = data.length; i < l; i++) { tmpMap[data[i][key]] = data[i]; } for (i = 0, l = data.length; i < l; i++) { if (tmpMap[data[i][parentKey]] && data[i][key] != data[i][parentKey]) { if (!tmpMap[data[i][parentKey]][childKey]) tmpMap[data[i][parentKey]][childKey] = []; tmpMap[data[i][parentKey]][childKey].push(data[i]); } else { r.push(data[i]); } } return r; } else { return [data]; } } /** * 递归过滤节点,生成新的树结构 * @param {Node[]} datas 要过滤的节点 * @param {recd => boolean} filterFunc 过滤条件,符合条件的节点保留 * @return 过滤后的节点 */ export function filterTreeData(datas, filterFunc) { // 如果已经没有节点了,结束递归 if (!(datas && datas.length)) { return []; } const newChildren = []; for (const recd of datas) { if (filterFunc.call(this, recd)) { // 如果节点符合条件,直接加入新的节点集 newChildren.push(recd); recd.children = filterTreeData.call(this, recd.children, filterFunc); } else { // 如果当前节点不符合条件,递归过滤子节点, // 把符合条件的子节点提升上来,并入新节点集 newChildren.push(...filterTreeData.call(this, recd.children, filterFunc)); } } return newChildren; } /** * showdoc * @catalog JS工具类/DataTree * @title findParentTreeData 查找树父节点数据 * @description 根据树当前节点id值,返回所有父级节点数据 * @method static * @url import { findParentTreeData } from '@birt/funclib/DataTree.js' * @param dataMap 必选 {key1:{},key2:{}} map缓存映射 * @param pidField 必选 string 树父id字段名 * @param value 必选 string 查找id值 * @param existIds 可选 Array [id1,id2,...] 已存在的记录id集合,去重添加 * @return [{'f1':'v1','f2':'v2'},{},...] * @remark 测试用例 * findParentTreeData( * {1:{id:1,v:"a",pid:"-1"},2:{id:2,v:"b",pid:"-1"},3:{id:"3",v:"a.1",pid:"1"},4:{id:"4",v:"a.1.1",pid:"3"}}, * "pid", * 3 * ) * 返回==> * [{"id":1,"v":"a","pid":"-1"},{"id":"3","v":"a.1","pid":"1"}] */ export function findParentTreeData(dataMap, pidField, value, existIds) { let parentData = []; if (dataMap) { do { let parentRecd = dataMap[value]; if (parentRecd) { // 如果存在去重数组,则判断记录id是否有存在 if (existIds) { if (existIds.indexOf(value) == -1) { parentData.unshift(parentRecd); existIds.push(value); } } else { parentData.unshift(parentRecd); } if (value === parentRecd[pidField]) { console.error("dataMap 数据映射有问题,父节点值[" + value + "]找到自身或是父ID字段名错误,出现死循环,异常退出") break; } value = parentRecd[pidField]; } else { break; } } while (true) } else { console.error("dataMap数据键值缓存为空,找不到父节点,可能数据集未配置rowKey主键属性") } return parentData } /** * showdoc * @catalog JS工具类/DataTree * @title getTreeParentChildrenMap 父节点与孩子映射列表 * @description 根据树型数据结构,转换成父节点与孩子映射关系数据 * @method static * @url import { getTreeParentChildrenMap } from '@birt/funclib/DataTree.js' * @param treeData 必选 树型数据 * @param pidField 必选 父节点字段名 * @param mapData 可选 已映射数据 * @param indexPath 可选 树节点位置下标 * @return [pid1:{children:[]},pid2:{children:[]}] * @remark 测试用例 */ export function getTreeParentChildrenMap(treeData, pidField, mapData, indexPath) { if (treeData && (treeData.length > 0)) { mapData = mapData || {}; indexPath = indexPath || []; treeData.forEach((recd, i) => { let childIndexPath = indexPath.concat(i); if (recd.children && (recd.children.length > 0)) { getTreeParentChildrenMap(recd.children, pidField, mapData, childIndexPath); delete recd.children; recd.hasChildren = true; } }) let pidVal = treeData[0][pidField]; mapData[pidVal] = { children: JSON.parse(JSON.stringify(treeData)), _indexPath: indexPath }; return mapData } } /** * showdoc * @catalog JS工具类/DataTree * @title getTreeChildrenByIndexPath 根据下标路径,获取树型节点 * @description 根据树型层级下标路径,获取该路径下的所有孩子节点 * @method static * @url import { getTreeChildrenByIndexPath } from '@birt/funclib/DataTree.js' * @param treeData 必选 树型数据 * @param indexPath 必选 下标路径 * @return {} 返回路径对应记录对象 * @remark 测试用例 */ export function getTreeChildrenByIndexPath(treeData, indexPath) { if (indexPath) { let expr = ""; for (let i = 0, l = indexPath.length; i < l; i++) { let ti = indexPath[i]; if (i == 0) { expr = "treeData[" + ti + "]"; } else { expr += ".children[" + ti + "]"; } } return eval(expr); } } /** * showdoc * @catalog JS工具类/DataTree * @title getTreeParentRowAndSelfIndex 获取父节点记录 * @description 根据树型数据结构,层次遍历,获取父节点记录和当前记录在该父节点孩子节点中位置下标 * @method static * @url import { getTreeParentRowAndSelfIndex } from '@birt/funclib/DataTree.js' * @param treeData 必选 树型数据 * @param curtRow 可选 当前查找记录 * @param idField 必选 节点字段名 * @return {children:{},index:0} * @remark 测试用例 */ export function getTreeParentRowAndSelfIndex(treeData, curtRow, idField) { if (treeData && (treeData.length > 0)) { for (let i = 0, l = treeData.length; i < l; i++) { let recd = treeData[i]; let idValue = recd[idField]; let curtIdValue = curtRow[idField]; if (idValue == curtIdValue) { return { children: treeData, index: i }; } if (recd.children && (recd.children.length > 0)) { let retu = getTreeParentRowAndSelfIndex(recd.children, curtRow, idField); if (retu) { return retu; } } } } }