基于GoJS的响应式组织结构图布局优化技术问询
GoJS响应式组织结构图优化方案
针对你提出的三个优化需求,以下是具体实现方案:
1. 员工节点文本横竖自适应显示
实现思路
- 给员工节点(EMP1-EMP9)添加专属标识,区分层级节点与员工节点
- 通过绑定
TextBlock.angle属性,结合屏幕尺寸动态切换文本方向:大屏幕(>768px)下员工文本垂直旋转90度,小屏幕下保持水平 - 窗口尺寸变化时,同步更新所有节点的文本方向
代码修改
第一步:更新模型数据,添加员工节点标识
myDiagram.model = new go.TreeModel([ { key: "CEO", parent: "", text: "CEO" }, { key: "DIRECTOR", parent: "CEO", text: "DIRECTOR" }, { key: "MANAGER1", parent: "DIRECTOR", text: "MANAGER1" }, { key: "MANAGER2", parent: "DIRECTOR", text: "MANAGER2" }, { key: "MANAGER3", parent: "DIRECTOR", text: "MANAGER3" }, { key: "EMP1", parent: "MANAGER1", text: "EMP1", isEmployee: true }, { key: "EMP2", parent: "MANAGER1", text: "EMP2", isEmployee: true }, { key: "EMP3", parent: "MANAGER1", text: "EMP3", isEmployee: true }, { key: "EMP4", parent: "MANAGER2", text: "EMP4", isEmployee: true }, { key: "EMP5", parent: "MANAGER2", text: "EMP5", isEmployee: true }, { key: "EMP6", parent: "MANAGER2", text: "EMP6", isEmployee: true }, { key: "EMP7", parent: "MANAGER3", text: "EMP7", isEmployee: true }, { key: "EMP8", parent: "MANAGER3", text: "EMP8", isEmployee: true }, { key: "EMP9", parent: "MANAGER3", text: "EMP9", isEmployee: true } ]);
第二步:修改节点模板,添加动态角度绑定
myDiagram.nodeTemplate = $(go.Node, "Auto", $(go.Shape, "Rectangle", { fill: "white" }), $(go.TextBlock, { margin: 4, textAlign: "center" }, new go.Binding("text", "text"), // 根据节点类型和屏幕尺寸动态设置文本角度 new go.Binding("angle", "", function(data) { if (!data.isEmployee) return 0; return window.innerWidth > 768 ? 90 : 0; }).ofObject() ) );
第三步:更新resize事件处理函数,同步文本方向
function updateLayout() { const isSmallScreen = window.innerWidth <= 768; myDiagram.layout.direction = isSmallScreen ? 0 : 90; // 触发所有节点的绑定更新,同步文本角度 myDiagram.nodes.each(node => node.updateTargetBindings()); myDiagram.layoutDiagram(true); }
2. 节点链接规整化优化
实现思路
- 使用
Orthogonal正交路由,让链接自动生成直角路径,保证线条笔直 - 调整
LayeredDigraphLayout的布局参数,统一层级间距与节点对齐方式,强化层级结构 - 根据布局方向动态调整链接的连接点,确保路径逻辑清晰
代码修改
第一步:优化链接模板
myDiagram.linkTemplate = $(go.Link, { routing: go.Link.Orthogonal, // 正交路由生成直角路径 corner: 0, // 取消圆角,保持线条笔直 fromSpot: go.Spot.Right, toSpot: go.Spot.Left }, $(go.Shape, { stroke: "black", strokeWidth: 1 }) );
第二步:优化布局参数
const myDiagram = $(go.Diagram, "myDiagramDiv", { initialContentAlignment: go.Spot.Center, layout: $(go.LayeredDigraphLayout, { direction: 90, layerSpacing: 80, // 层级间垂直间距 columnSpacing: 40, // 同层级节点水平间距 alignOption: go.LayeredDigraphLayout.AlignAll // 所有节点对齐到同一水平线 }) });
第三步:在resize函数中动态调整链接连接点
function updateLayout() { const isSmallScreen = window.innerWidth <= 768; myDiagram.layout.direction = isSmallScreen ? 0 : 90; // 根据布局方向切换链接的连接点 myDiagram.linkTemplate.fromSpot = isSmallScreen ? go.Spot.Bottom : go.Spot.Right; myDiagram.linkTemplate.toSpot = isSmallScreen ? go.Spot.Top : go.Spot.Left; myDiagram.nodes.each(node => node.updateTargetBindings()); myDiagram.layoutDiagram(true); }
3. 小屏幕响应式适配
实现思路
- 小屏幕下切换为垂直布局,让节点从上到下堆叠
- 调整节点尺寸与布局间距,让结构更紧凑
- 开启图表滚动功能,避免内容溢出;设置文本自动换行,保证内容完整显示
代码修改
第一步:增强节点模板的自适应能力
myDiagram.nodeTemplate = $(go.Node, "Auto", $(go.Shape, "Rectangle", { fill: "white" }), $(go.TextBlock, { margin: 4, textAlign: "center", wrap: go.TextBlock.WrapFit, // 文本过长时自动换行 width: window.innerWidth > 768 ? 80 : NaN // 大屏幕固定宽度,小屏幕自适应 }, new go.Binding("text", "text"), new go.Binding("angle", "", function(data) { if (!data.isEmployee) return 0; return window.innerWidth > 768 ? 90 : 0; }).ofObject() ) );
第二步:配置图表滚动与自适应
const myDiagram = $(go.Diagram, "myDiagramDiv", { initialContentAlignment: go.Spot.Center, layout: $(go.LayeredDigraphLayout, { direction: 90, layerSpacing: 80, columnSpacing: 40, alignOption: go.LayeredDigraphLayout.AlignAll }), allowHorizontalScroll: true, // 允许水平滚动 allowVerticalScroll: true, // 允许垂直滚动 autoScale: go.Diagram.UniformToFill // 自动缩放适配容器 });
第三步:在resize函数中调整布局间距
function updateLayout() { const isSmallScreen = window.innerWidth <= 768; myDiagram.layout.direction = isSmallScreen ? 0 : 90; // 小屏幕下缩小布局间距,让结构更紧凑 myDiagram.layout.layerSpacing = isSmallScreen ? 60 : 80; myDiagram.layout.columnSpacing = isSmallScreen ? 30 : 40; myDiagram.linkTemplate.fromSpot = isSmallScreen ? go.Spot.Bottom : go.Spot.Right; myDiagram.linkTemplate.toSpot = isSmallScreen ? go.Spot.Top : go.Spot.Left; // 动态更新节点宽度 myDiagram.nodes.each(node => { node.width = isSmallScreen ? NaN : 80; node.updateTargetBindings(); }); myDiagram.layoutDiagram(true); }
完整优化后代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Hierarchical Organization Chart</title> <script src="https://cdn.jsdelivr.net/npm/gojs@2.1.35/release/go.js"></script> </head> <body> <div id="myDiagramDiv" style="width:100%; height:600px;"></div> <script> const $ = go.GraphObject.make; // Create a new diagram const myDiagram = $(go.Diagram, "myDiagramDiv", { initialContentAlignment: go.Spot.Center, layout: $(go.LayeredDigraphLayout, { direction: 90, layerSpacing: 80, columnSpacing: 40, alignOption: go.LayeredDigraphLayout.AlignAll }), allowHorizontalScroll: true, allowVerticalScroll: true, autoScale: go.Diagram.UniformToFill }); // Define the node template myDiagram.nodeTemplate = $(go.Node, "Auto", $(go.Shape, "Rectangle", { fill: "white" }), $(go.TextBlock, { margin: 4, textAlign: "center", wrap: go.TextBlock.WrapFit, width: window.innerWidth > 768 ? 80 : NaN }, new go.Binding("text", "text"), new go.Binding("angle", "", function(data) { if (!data.isEmployee) return 0; return window.innerWidth > 768 ? 90 : 0; }).ofObject() ) ); // Define the link template myDiagram.linkTemplate = $(go.Link, { routing: go.Link.Orthogonal, corner: 0, fromSpot: go.Spot.Right, toSpot: go.Spot.Left }, $(go.Shape, { stroke: "black", strokeWidth: 1 }) ); // Set the model data for the chart myDiagram.model = new go.TreeModel([ { key: "CEO", parent: "", text: "CEO" }, { key: "DIRECTOR", parent: "CEO", text: "DIRECTOR" }, { key: "MANAGER1", parent: "DIRECTOR", text: "MANAGER1" }, { key: "MANAGER2", parent: "DIRECTOR", text: "MANAGER2" }, { key: "MANAGER3", parent: "DIRECTOR", text: "MANAGER3" }, { key: "EMP1", parent: "MANAGER1", text: "EMP1", isEmployee: true }, { key: "EMP2", parent: "MANAGER1", text: "EMP2", isEmployee: true }, { key: "EMP3", parent: "MANAGER1", text: "EMP3", isEmployee: true }, { key: "EMP4", parent: "MANAGER2", text: "EMP4", isEmployee: true }, { key: "EMP5", parent: "MANAGER2", text: "EMP5", isEmployee: true }, { key: "EMP6", parent: "MANAGER2", text: "EMP6", isEmployee: true }, { key: "EMP7", parent: "MANAGER3", text: "EMP7", isEmployee: true }, { key: "EMP8", parent: "MANAGER3", text: "EMP8", isEmployee: true }, { key: "EMP9", parent: "MANAGER3", text: "EMP9", isEmployee: true } ]); // Update layout direction based on screen size function updateLayout() { const isSmallScreen = window.innerWidth <= 768; myDiagram.layout.direction = isSmallScreen ? 0 : 90; // Adjust layout spacing myDiagram.layout.layerSpacing = isSmallScreen ? 60 : 80; myDiagram.layout.columnSpacing = isSmallScreen ? 30 : 40; // Adjust link spots myDiagram.linkTemplate.fromSpot = isSmallScreen ? go.Spot.Bottom : go.Spot.Right; myDiagram.linkTemplate.toSpot = isSmallScreen ? go.Spot.Top : go.Spot.Left; // Update node width and text angle myDiagram.nodes.each(node => { node.width = isSmallScreen ? NaN : 80; node.updateTargetBindings(); }); myDiagram.layoutDiagram(true); } // Call the function initially and on window resize updateLayout(); window.addEventListener('resize', updateLayout); </script> </body> </html>
内容的提问来源于stack exchange,提问作者sps
相关产品推荐
相关产品推荐

