/**
* 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;
}
}
}
}
}