递归循环获得tree树形结构数据
VUE + ElementUI tree组件树形数据处理一、tree组件代码<el-treeclass="tree_s":data="treeData"highlight-current:expand-on-click-node="false"node-key="id"default-expand-all:props="defaultProps...
·
VUE + ElementUI tree组件树形数据处理(不限层级)
一、tree组件代码
<el-tree
class="tree_s"
:data="treeData"
highlight-current
:expand-on-click-node="false"
node-key="id"
default-expand-all
:props="defaultProps"
@node-click="nodeClickFn"
></el-tree>
二、数据分析
后台返回数据:id表示项目唯一性id、depth表示层级、parentId表示父级id、name表示节点名称。
[
{
"id":1,//唯一性id
"parentId":0,//父级id
"depth":1,//层级
"name":"第一节点",//节点名称
},
{
"id":2,
"parentId":1,
"depth":2,
"name":"第二节点",
},
{
"id":3,
"parentId":2,
"depth":3,
"name":"第三节点",
}
]
三、数据处理
转换数据结构,json转化为树形结构
methods: {
//获取后台数据
getDataList(){
//AJAX获取数据
this.data = res;//示例
this.handleData(this.data);
},
//先找出第一层级,然后往里追加数据
handleData(arr) {
let dataArray = [];
arr.forEach(function (data) {
let parentId = data.parentId;
if ( parentId == 0 ) {
let objTemp = {
id: data.id,
label: data.name,
depth: data.depth,
parentId: parentId,
}
dataArray.push(objTemp);
}
})
this.data2treeDG(arr, dataArray)
},
//递归追加数据
data2treeDG(datas, dataArray) {
for (let j = 0; j < dataArray.length; j++) {
let dataArrayIndex = dataArray[j];
let childrenArray = [];
let Id = dataArrayIndex.id;
for (let i = 0; i < datas.length; i++) {
let data = datas[i];
let parentId = data.parentId;
if (parentId == Id) {//判断是否为儿子节点
let objTemp = {
id: data.id,
label: data.name,
depth: data.depth,
parentId: parentId,
}
childrenArray.push(objTemp);
}
}
dataArrayIndex.children = childrenArray;
//有儿子节点则递归
if (childrenArray.length > 0) {
this.data2treeDG(datas, childrenArray)
}
}
this.treeData = dataArray;
return dataArray;
},
}
四、格式化后数据结构
格式化后数据结构可满足tree组件需求
[
{
"id":1,
"parentId":0,
"depth":1,
"label":"第一节点",
"children": [
{
"id":2,
"parentId":1,
"depth":2,
"label":"第二节点",
"children": [
{
"id":3,
"parentId":2,
"depth":3,
"label":"第三节点",
}
]
},
]
}
]
五、总结
此解决问题思路,也可用于动态导航栏数据处理。
六、优化
VUE3版本:——推荐
import * as R from 'remeda';
export enum TREE_DATA_TYPE {
TREE = 'tree',
LIST = 'list',
}
export interface TreeConfig {
key?: string | undefined;
label?: string | undefined;
parentKey?: string | undefined;
children?: string | undefined;
dataType?: `${TREE_DATA_TYPE}` | undefined;
list?: any[]; // 指定tree——treeList数据
}
export default () => {
/**
* Descriptions 树形数据处理公用方法——先找出第一层级,然后往children里追加数据
* @param {Array} treeList 列表原始数据
* @param {Array} dataKeys 指定 其他数据展示字段
* @param {Number} originId 指定 根目录id,默认为0
* @param {Object} treeKeys 指定 生成结构树主节点字段,默认{key: 'id', label: 'name', level: 'level', parentKey: 'parentId'}
* @param {number} levelLimitNumber 指定 结构树展示层级 默认展示所有
* @returns
*/
const handleListDataToTree = (
treeList: any[],
dataKeys: string[],
originId = 0,
treeKeys = {
key: 'id',
label: 'name',
level: 'level',
parentKey: 'parentId',
},
levelLimitNumber?: number | undefined,
filterStr?: string,
) => {
const { key, label, level, parentKey } = treeKeys;
if (levelLimitNumber) {
// 只显示指定层级数据
treeList = treeList.filter((item) => item[level] <= levelLimitNumber);
}
const tree = [];
// 找出根目录 并生成根节点
let originData = treeList.find((item: any) => item[key] == originId);
// 找不到根目录 就自动生成根节点(提示:若需自定义根节点,可在外层源数据list数组内 手动追加根节点数据)
if (!originData) {
originData = handleNodeData(
{
[key]: originId,
[label]: '全部',
[level]: 0,
[parentKey]: -1,
},
dataKeys,
treeKeys,
);
}
// 根节点数据
const originNodeTemp = handleNodeData(originData, dataKeys, treeKeys);
tree.push(originNodeTemp);
// 收集子节点数据
handleListToTreeChildNodes(treeList, tree, dataKeys, originId, treeKeys, levelLimitNumber);
// 只显示包含(筛选)指定字符串数据
if (filterStr) {
const _filterList = getTreeDataByFilterStr(tree, filterStr, {
...treeKeys,
list: treeList, // 已有list 无需再扁平处理
dataType: TREE_DATA_TYPE.TREE,
});
return _filterList;
}
return tree;
// 递归追加数据
function handleListToTreeChildNodes(
treeList: any[],
dataArray: any[],
dataKeys: string[],
originId = 0,
treeKeys = {
key: 'id',
label: 'name',
level: 'level',
parentKey: 'parentId',
},
levelLimitNumber?: number | undefined,
) {
const { key, parentKey } = treeKeys;
for (let j = 0; j < dataArray.length; j++) {
const parentNodeData = dataArray[j];
const childrenArray = [];
for (let i = 0; i < treeList.length; i++) {
const data = treeList[i];
if (data[parentKey] == parentNodeData[key]) {
const nodeTemp = handleNodeData(data, dataKeys, treeKeys);
childrenArray.push(nodeTemp);
}
}
// 有儿子节点则递归
if (childrenArray.length > 0) {
parentNodeData.children = childrenArray;
handleListToTreeChildNodes(
treeList,
childrenArray,
dataKeys,
originId,
treeKeys,
levelLimitNumber,
);
}
}
return dataArray;
}
// 自动追加数据
function handleNodeData(
nodeData: any,
dataKeys: string[],
treeKeys = {
key: 'id',
label: 'name',
level: 'level',
parentKey: 'parentId',
},
) {
const { key, label, level, parentKey } = treeKeys;
// 判断是否为儿子节点
const nodeTemp: any = {
[key]: nodeData[key] || 0,
[label]: nodeData[label] || '全部',
[level]: nodeData[level] || 0,
[parentKey]: nodeData[parentKey] !== undefined ? nodeData[parentKey] : -1,
};
const objTemp: any = {
...nodeTemp,
id: nodeData[key],
key: nodeData[key],
value: nodeData[key],
level: nodeData[level],
label: nodeData[label],
title: nodeData[label],
name: nodeData[label],
parentId: nodeData[parentKey],
};
// 追加额外参数**********
if (Array.isArray(dataKeys)) {
dataKeys.forEach((key) => {
objTemp[key] =
typeof nodeData[key] == 'boolean' ? nodeData[key] : nodeData[key] ? nodeData[key] : '';
});
} else if (dataKeys && typeof dataKeys === 'string') {
objTemp[dataKeys] =
typeof nodeData[key] == 'boolean' ? nodeData[key] : nodeData[key] ? nodeData[key] : '';
}
return objTemp;
}
};
/**
* Descriptions 将树形数组扁平化
* @param tree 列表原始(树形)数据
* @param parentId 指定将要被扁平化的节点ID
*/
const handleTreeDataToList = (tree: any[], parentId?: any, config?: TreeConfig): T[] => {
const { children = 'children', key = 'id' } = config || {};
return tree.reduce((t, _) => {
const child = _[children];
return [
...t,
parentId ? { ..._, parentId } : _,
...(child && child.length ? handleTreeDataToList(child, _[key]) : []),
];
}, []);
};
/**
* Descriptions 树形数据字符串搜索(筛选)
* @param tree 列表原始(树形)数据
* @param str 将要过滤字符串
*/
const getTreeDataByFilterStr = (tree: any[], str?: any, config?: TreeConfig): T[] => {
const filterStr = str || '';
const {
children = 'children',
key = 'id',
label = 'name',
parentKey = 'parentId',
} = config || {};
// 先扁平化处理
const flatArray = config?.list || handleTreeDataToList(tree);
// 只显示包含(筛选)指定字符串数据
const filterIds = flatArray.filter((_) => _[label].indexOf(filterStr) > -1).map((_) => _[key]);
const filterNodes = getTreeNodeParentNodes(tree, filterIds, {
key,
children,
label,
parentKey,
dataType: TREE_DATA_TYPE.TREE,
});
return filterNodes || [];
};
/**
* Descriptions 获取树形数组中 指定id相关联的(关系链)所有父级节点id集合
* @param {Array} tree 列表原始(树形)数据
* @param {string | number} nodeId 节点id
*/
const getTreeNodeParentIds = (tree: any[], nodeId: any, config?: TreeConfig) => {
const { key = 'id', parentKey = 'parentId' } = config || {};
// 先将tree树形数据扁平化
const flatTree = handleTreeDataToList(tree, undefined, config);
if (Array.isArray(nodeId)) {
let ids = [] as any[];
nodeId.forEach((_id) => {
ids.push(_id);
ids = [...getTreeNodeParentIds(tree, _id, config), ...ids];
});
return Array.from(new Set(ids));
}
return getIds(flatTree);
function getIds(flatArray: any[], _nodeId?: any) {
const __nodeId = _nodeId || nodeId;
let ids = [__nodeId];
let child = flatArray.find((_) => _[key] === __nodeId);
while (child && child[parentKey]) {
ids = [child[parentKey], ...ids];
child = flatArray.find((_) => _[key] === child[parentKey]);
}
return ids;
}
};
/**
* Descriptions 获取树形数组中 指定id相关联的(关系链)所有父级节点数据集合
* @param {Array} tree 列表原始(树形)数据
* @param {string | number} nodeId 指定节点ID
*/
const getTreeNodeParentNodes = (tree: any[], nodeId: any, config?: TreeConfig) => {
const {
children = 'children',
key = 'id',
parentKey = 'parentId',
dataType = '',
} = config || {};
// 先将tree树形数据扁平化
const flatTree = handleTreeDataToList(tree, undefined, config);
const parentNodeIds = getTreeNodeParentIds(tree, nodeId, config);
// return getDatas(flatTree);
return getDatas(parentNodeIds);
// 返回父级节点数据(新)
function getDatas(flatIdsArray: any[]) {
const datas = flatIdsArray
.map((_nodeId) => {
return flatTree.find((node) => node[key] === _nodeId);
})
.filter((n) => n);
return dataType === TREE_DATA_TYPE.TREE ? getTreeDatas(datas) : datas;
}
// 返回父级节点数据(旧)
// function getDatas(flatArray: any[]) {
// let child = flatArray.find((_) => _[key] === nodeId);
// let datas = [child];
// while (child && child[parentKey]) {
// const parent = flatArray.find((_) => _[key] === child[parentKey]);
// datas = [parent, ...datas];
// child = flatArray.find((_) => _[key] === child[parentKey]);
// }
// return dataType === TREE_DATA_TYPE.TREE ? getTreeDatas(datas) : datas;
// }
// 返回父级节点树形数据
function getTreeDatas(_flatArray: any[]) {
const flatArray = R.clone(_flatArray);
// * 先生成parent建立父子关系
const objMap: any = {};
flatArray.forEach((item) => {
if (item[key]) objMap[item[key]] = item;
delete item[children];
});
// * objMap -> {1001: {id: 1001, parentId: 0, name: 'AA'}, 1002: {...}}
const parentList: any[] = [];
flatArray.forEach((item) => {
const parent = item[parentKey] ? objMap[item[parentKey]] : null;
if (parent) {
// * 当前项有父节点
parent.children = parent.children || [];
parent.children.unshift(item);
} else {
// * 当前项没有父节点 -> 顶层
parentList.push(item);
}
});
return parentList;
}
};
/**
* Descriptions:获取树形数组中 指定id对应的节点数据
* @param {Array} tree 数据数组
* @param {Number} nodeId 指定节点ID
*/
const getTreeNodeData = (tree: any[], nodeId: any, config?: TreeConfig): any => {
const { children = 'children', key = 'id' } = config || {};
//判断list是否是数组
if (!Array.isArray(tree)) {
return null;
}
//遍历数组
for (const i in tree) {
const item = tree[i];
if (item[key] && item[key] == nodeId) {
return item;
}
//查不到继续遍历
if (item[children]) {
const value = getTreeNodeData(item[children], nodeId, config);
//查询到直接返回
if (value) {
return value;
}
}
}
};
/**
* Descriptions:获取树形数组中 指定id对应的节点下标(层级)信息
* @param {Array} tree 数据数组
* @param {Number, String} nodeId 指定节点数据 如:123 {id: '123'}
* @param {String} config.key 取值键名 默认id
* @returns {Array} 例如:[0,0]表示数组中 第一元素的 第一个子元素的层级下标
*/
const getTreeNodeIndex = (tree: any[], nodeId: any, config?: TreeConfig): any => {
const { children = 'children', key = 'id' } = config || {};
//判断tree是否是数组
if (!Array.isArray(tree)) {
return null;
}
//遍历数组
for (const i in tree) {
const item = tree[i];
if (item[key] && item[key] == nodeId) {
return [i];
}
//查不到继续遍历
if (item[children]) {
const el = getTreeNodeIndex(item[children], nodeId, config);
//查询到直接返回
if (el) {
return [i, ...el].map((num) => {
return num - 0;
});
}
}
}
};
return {
handleTreeDataToList,
handleListDataToTree,
getTreeNodeParentIds,
getTreeNodeParentNodes,
getTreeNodeData,
getTreeNodeIndex,
getTreeDataByFilterStr,
};
};
VUE2版本
methods: {
// 树形数据处理公用方法——先找出第一层级,然后往children里追加数据
/**
* @param {Array} arr resData列表原始数据
* @param {Array} keys 自定义字段数组
* @param {Number} originId 根目录parentId,默认为0, 可自定义传入 如‘-1’
* @returns
*/
handleData(arr = [], keys = [], originId = 0) {
//如果后端没有返回根目录节点数据,则需要自定义根节点 如下:
if (originId < 0) arr.push({ id: 0, value: 0, label: '全部', name: '全部', depth: 0, parentId: originId })
const dataArray = []
for (let i = 0; i < arr.length; i++) {
const data = arr[i]
const parentId = data.parentId
if (parentId == originId) {
const objTemp = {
id: data.id,
value: data.id,
label: data.name,
name: data.name,
depth: data.depth,
parentId: parentId,
disabled: data.disabled
}
// 追加额外参数**********
if (Array.isArray(keys)) {
keys.forEach(key => {
objTemp[key] = data[key] ? data[key] : ''
})
} else if (keys && typeof keys === 'string') {
objTemp[keys] = data[key] ? data[key] : ''
}
dataArray.push(objTemp)
break
}
}
return this.data2treeDG(arr, dataArray, keys, originId)
},
// 递归追加数据
data2treeDG(datas, dataArray, keys, originId) {
for (let j = 0; j < dataArray.length; j++) {
const dataArrayIndex = dataArray[j]
const childrenArray = []
const Id = dataArrayIndex.id
for (let i = 0; i < datas.length; i++) {
const data = datas[i]
const parentId = data.parentId
if (parentId == Id) { // 判断是否为儿子节点
const objTemp = {
id: data.id,
value: data.id,
name: data.name,
label: data.name,
depth: data.depth,
parentId: parentId,
disabled: data.disabled
}
// 追加额外参数**********
if (Array.isArray(keys)) {
keys.forEach(key => {
objTemp[key] = data[key]
})
} else if (keys && typeof keys === 'string') {
objTemp[keys] = data[keys]
}
childrenArray.push(objTemp)
}
}
// 有儿子节点则递归
if (childrenArray.length > 0) {
dataArrayIndex.children = childrenArray
this.data2treeDG(datas, childrenArray, keys)
}
}
return originId ? dataArray[0].children : dataArray
},
// 获取父子级字符串组合
getCascaderStr(id) {
if (!id) return []
const resData_ = this.resData
const typesStrArr = []
const nowTypeObj = this.checkTypeObj(id)
typesStrArr.unshift(nowTypeObj.name)
checkId(nowTypeObj.parentId)
return typesStrArr
function checkId(parentId) {
for (let i = 0; i < resData_.length; i++) {
const el = resData_[i]
if (parentId == el.id && parentId != 0) {
typesStrArr.unshift(el.name)
checkId(el.parentId)
break
}
}
}
},
// 获取父子级ID组合数组
getCascaderArr(id) {
if (!id) return []
const resData_ = this.resData
const typesArr = []
const nowTypeObj = this.checkTypeObj(id)
typesArr.unshift(nowTypeObj.id)
checkId(nowTypeObj.parentId)
return typesArr
function checkId(parentId) {
for (let i = 0; i < resData_.length; i++) {
const el = resData_[i]
if (parentId == el.id && parentId != 0) {
typesArr.unshift(el.id)
checkId(el.parentId)
break
}
}
}
},
//根据id 遍历数组 返回目标对象
checkTypeObj(id) {
const resData_ = this.resData
let nowTypeObj_ = null
for (let i = 0; i < resData_.length; i++) {
const el = resData_[i]
if (el.id == id) {
nowTypeObj_ = el
break
}
}
return nowTypeObj_
}
}```
更多推荐
已为社区贡献1条内容
所有评论(0)