如何实现支持任意层级任意顺序的d3 SunBurst图动态数据生成
动态生成d3 SunBurst层级数据方案
核心改造逻辑
原有代码硬编码了4层嵌套的匹配逻辑,要支持任意层级、任意顺序排列,核心优化点如下:
- 把层级顺序作为可配置参数传入,参数顺序就是最终的层级展示顺序
- 用逐层遍历的方式替代固定嵌套的
forEach逻辑,自动适配层级数量 - 自动识别最末层级,生成不带
children、仅携带必填的name和count字段的节点
完整改造代码
// 生成非末层级节点(带children字段,用于挂载下一层数据) function createNonLeafNode(name) { return { name: name, children: [] } } // 生成末层级节点(不带children字段,携带必填count) function createLeafNode(name, count) { return { name: name, count: count } } /** * 动态生成SunBurst要求的树形结构 * @param {string} rootName 根节点显示名称 * @param {Array} levelDatasets 按层级顺序排列的数据集数组,顺序即层级从高到低的顺序 * @param {Array} matchFieldMap 匹配字段映射,第n项对应第n+1层数据集需要匹配的父字段列表 * @returns {Object} 符合d3渲染要求的树形数据 */ function createDynamicPayload(rootName, levelDatasets, matchFieldMap) { const root = { name: rootName, children: [] } // 无层级数据直接返回根节点 if (!levelDatasets || levelDatasets.length === 0) return root // 初始化第一层节点 root.children = levelDatasets[0].map(item => createNonLeafNode(item.name)) // 仅一层数据直接返回 if (levelDatasets.length === 1) return root // 逐层处理后续层级 for (let levelIdx = 1; levelIdx < levelDatasets.length; levelIdx++) { const currentDataset = levelDatasets[levelIdx] const isLastLevel = levelIdx === levelDatasets.length - 1 // 递归遍历上一层所有节点,匹配子数据 const traverseParentNodes = (nodes, currentDepth, parentNamePath = []) => { // 未到目标父层级,继续往下遍历 if (currentDepth !== levelIdx - 1) { nodes.forEach(node => { traverseParentNodes(node.children, currentDepth + 1, [...parentNamePath, node.name]) }) return } // 给当前父层级的每个节点匹配子数据 nodes.forEach(parentNode => { const fullParentPath = [...parentNamePath, parentNode.name] // 筛选属于当前父节点的子数据 const matchedChildren = currentDataset.filter(dataItem => { return matchFieldMap[levelIdx - 1].every((field, idx) => { return dataItem[field] === fullParentPath[idx] }) }).map(dataItem => { return isLastLevel ? createLeafNode(dataItem.name, dataItem.count) : createNonLeafNode(dataItem.name) }) parentNode.children = matchedChildren }) } traverseParentNodes(root.children, 0, []) } return root }
使用示例
原固定4层级的调用方式
// 层级顺序:项目类型 > 子项目 > 状态 > 负责人 const levelDatasets = [projectType, subProject, status, assignedTo] // 匹配字段映射:对应每一层数据集需要匹配的父字段 const matchFieldMap = [ ['projecttype'], // 子项目层匹配父级(项目类型)的projecttype字段 ['projecttype', 'subproject'], // 状态层匹配父级的projecttype、subproject字段 ['projecttype', 'subproject', 'status'] // 负责人层匹配前三层的对应字段 ] // 生成最终数据 const payload = createDynamicPayload('My Project', levelDatasets, matchFieldMap)
调整层级顺序的调用示例
如果要把状态调整到第二层,仅需要修改两个配置项即可,不需要修改核心逻辑:
// 调整层级顺序:项目类型 > 状态 > 子项目 > 负责人 const levelDatasets = [projectType, status, subProject, assignedTo] // 对应修改匹配字段映射 const matchFieldMap = [ ['projecttype'], // 状态层匹配项目类型的projecttype字段 ['projecttype', 'status'], // 子项目层匹配projecttype、status字段 ['projecttype', 'status', 'subproject'] // 负责人层匹配前三层对应字段 ] const payload = createDynamicPayload('My Project', levelDatasets, matchFieldMap)
注意事项
- 最末层级的数据集每个项必须携带name和count字段,符合要求
- 层级数量无限制,最少1层,最多可自定义任意数量
- 生成的结构完全符合d3 SunBurst的渲染要求,无需额外转换
内容的提问来源于stack exchange,提问作者Srinivas Naik
相关产品推荐
相关产品推荐

