JSCharting组织结构图:实现节点拖拽修改parent属性方案
如何为JSCharting组织结构图节点添加拖拽事件调整层级归属
要实现拖拽节点调整层级的功能,我们可以通过原生JavaScript监听鼠标事件,结合JSCharting的API修改节点parent属性并重新渲染图表。以下是改造后的完整代码及关键步骤说明:
完整实现代码
<!DOCTYPE html> <html lang="zh-CN"> <head> <title>可拖拽调整层级的组织结构图 | JSCharting</title> <meta http-equiv="content-type" content="text-html; charset=utf-8" /> <script src="https://code.jscharting.com/latest/jscharting.js"></script> <script type="text/javascript" src="https://code.jscharting.com/latest/modules/types.js"></script> <script type="text/javascript" src="https://code.jscharting.com/latest/modules/toolbar.js"></script> <style> .chartDiv { margin: 8px auto; padding: 15px; border-radius: 10px; } /* 拖拽状态样式 */ .dragging-node { opacity: 0.6; cursor: move; } .target-node { outline: 2px solid #2196F3; } </style> </head> <body> <div id="chartDiv1" class="chartDiv" style="max-width: 700px;height: 400px;"></div> <script type="text/javascript"> var chart = JSC.chart('chartDiv1', { debug: true, type: 'organizational', defaultAnnotation: { padding: [5, 10], margin: [15, 5] }, defaultTooltip_enabled: false, defaultSeries: { color: '#dde1e8', defaultPoint: { label_maxWidth: 90, connectorLine: { radius: [0, 5], color: '#424242', width: 1, caps: { end: { type: 'arrow', size: 6 } } }, // 绑定节点鼠标按下事件 events_mousedown: startDrag } }, series: [ { points: [ { x: '项目经理', id: 'PM' }, { x: '副项目经理', id: 'DPM', parent: 'PM' }, { x: '系统工程部', id: 'SE', parent: 'DPM' }, { x: '独立测试组', id: 'ITG', parent: 'DPM' }, { x: '项目技术负责人', id: 'PTL', parent: 'DPM' }, { x: '质量保证部', id: 'QA', parent: 'DPM' }, { x: '配置管理员', id: 'CM', parent: 'DPM' }, { x: '软件子项目经理1', id: 'SM1', parent: 'PTL' }, { x: '团队1', id: 'team11', parent: 'SM1' }, { x: '团队2', id: 'team12', parent: 'SM1' }, { x: '团队3', id: 'team13', parent: 'SM1' }, { x: '软件子项目经理2', id: 'SM2', parent: 'PTL' }, { x: '团队1', id: 'team21', parent: 'SM2' }, { x: '团队2', id: 'team22', parent: 'SM2' }, { x: '软件子项目经理3', id: 'SM3', parent: 'PTL' }, { x: '团队1', id: 'team31', parent: 'SM3' }, { x: '团队2', id: 'team32', parent: 'SM3' } ] } ], toolbar: { defaultItem: { margin: 5, events_click: orientChart }, items: { Left_icon: 'system/default/zoom/arrow-left', Right_icon: 'system/default/zoom/arrow-right', Down_icon: 'system/default/zoom/arrow-down', Up_icon: 'system/default/zoom/arrow-up' } } }); // 拖拽状态变量 let draggedNode = null; let targetNode = null; // 启动拖拽 function startDrag(e) { draggedNode = this; draggedNode.annotation.element.classList.add('dragging-node'); // 绑定全局鼠标事件 document.addEventListener('mousemove', onDrag); document.addEventListener('mouseup', endDrag); e.preventDefault(); } // 拖拽过程 function onDrag(e) { // 清除之前的目标节点样式 if (targetNode) { targetNode.annotation.element.classList.remove('target-node'); } // 获取鼠标位置对应的节点 targetNode = chart.get('point', e.clientX, e.clientY); // 标记有效目标节点(排除自身) if (targetNode && targetNode.id !== draggedNode.id) { targetNode.annotation.element.classList.add('target-node'); } else { targetNode = null; } } // 结束拖拽 function endDrag() { // 清除拖拽样式 if (draggedNode) { draggedNode.annotation.element.classList.remove('dragging-node'); } // 更新层级(防止循环归属) if (targetNode && draggedNode && !isDescendant(targetNode, draggedNode)) { draggedNode.options({ parent: targetNode.id }); chart.update(); } // 移除全局事件监听 document.removeEventListener('mousemove', onDrag); document.removeEventListener('mouseup', endDrag); // 重置状态 draggedNode = null; targetNode = null; } // 检查节点是否为目标节点的后代,避免循环层级 function isDescendant(child, parent) { let current = child; while (current.parent) { if (current.parent.id === parent.id) { return true; } current = current.parent; } return false; } // 调整图表方向 function orientChart(direction) { var isVertical = /up|down/.test(direction.toLowerCase()); chart.options({ type: 'organizational ' + direction, defaultPoint_annotation: { syncWidth: !isVertical, syncHeight: isVertical, margin: isVertical ? [15, 5] : [5, 15] } }); } </script> </body> </html>
关键实现说明
拖拽事件绑定
- 在
defaultPoint配置中添加events_mousedown: startDrag,为所有节点绑定鼠标按下事件,触发拖拽启动逻辑。 - 通过全局监听
mousemove和mouseup事件,实现跨节点的拖拽跟踪。
- 在
目标节点检测
- 使用
chart.get('point', e.clientX, e.clientY)方法,根据鼠标实时位置获取对应节点。 - 为拖拽节点和目标节点添加CSS样式,提升交互视觉反馈。
- 使用
层级更新与防循环
- 拖拽结束时修改拖拽节点的
parent属性为目标节点ID,调用chart.update()重新渲染图表。 - 新增
isDescendant函数,避免将父节点拖到子节点下造成循环层级结构。
- 拖拽结束时修改拖拽节点的
交互体验优化
- 添加拖拽半透明效果和目标节点高亮边框,清晰展示拖拽状态。
- 阻止鼠标按下默认事件,避免节点文字被选中影响拖拽体验。
内容的提问来源于stack exchange,提问作者RG Servers
相关产品推荐
相关产品推荐

