LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

使用 HTML + JavaScript 实现组织架构图(附完整代码)

admin
2025年12月14日 16:54 本文热度 742
在企业管理系统或团队协作平台中,组织结构图是一种重要的可视化工具,能够清晰地展示公司的层级关系和人员分布。本文将详细介绍如何使用 HTML、CSS 和 JavaScript 从零开始构建一个具有交互功能的组织结构图。

效果演示

本项目呈现了一个典型的公司组织架构,从上级开始逐层向下展示各个部门及员工职位。用户可以通过点击节点上的 +/- 按钮来展开或收起子节点,也可以通过顶部的“展开/收起全部”按钮一键控制所有节点的状态。默认情况下,部分节点被设置为收起状态以避免图表过于庞大,用户可以根据需要自由展开查看详细信息。

页面结构

系统主要包括以下几个功能区域:

操作区域

该区域位于主内容上方,提供全局操作按钮,目前只有一个“展开/收起全部”的功能按钮,用户可以一键控制所有节点的展开状态。
<div class="actions">  <button onclick="toggleAll()">展开/收起全部</button></div>

组织结构图区域

这是实际渲染组织结构图的核心区域,使用 #orgChart 作为根节点容器。
<div class="org-chart-container">  <div class="org-chart" id="orgChart"></div></div>

核心功能实现

数据结构定义

组织结构图的数据源是一个嵌套的 json 数据,每个节点包含 id、name、position 和 children 属性。这种树形数据结构非常适合表示层级关系,并且易于遍历和渲染。
var orgData = {  id1,  name"张伟",  position"CEO",  children: [    {      id2,      name"李娜",      position"技术总监",      children: [        // ... 更多子节点      ]    },    // ... 更多同级节点  ]};

节点渲染逻辑

使用递归方式渲染整个组织结构图,通过递归遍历数据结构,为每个节点创建对应的DOM元素,并根据是否有子节点添加相应的CSS类名。
function renderOrgChart(data, container) {  // 创建当前节点  var nodeEl = document.createElement('div');  nodeEl.className = 'node';  nodeEl.dataset.id = data.id;
  // 如果有子节点,添加标识类  if (data.children && data.children.length > 0) {    nodeEl.classList.add('has-children');  }
  // 设置节点内容  nodeEl.innerHTML = `      <div class="title">${data.name}</div>      <div class="position">${data.position}</div>      <div class="toggle-btn"></div>    `;  // 为切换按钮添加点击事件  if (data.children && data.children.length > 0) {    const toggleBtn = nodeEl.querySelector('.toggle-btn');    toggleBtn.addEventListener('click', function(e) {      e.stopPropagation();      nodeEl.classList.toggle('collapsed');    });  }
  container.appendChild(nodeEl);
  // 如果有子节点,创建子节点容器  if (data.children && data.children.length > 0) {    var childrenEl = document.createElement('div');    childrenEl.className = 'children';
    data.children.forEach((child, index) => {      var childWrapper = document.createElement('div');      childWrapper.className = 'child';
      // 添加特殊类名用于样式控制      if (data.children.length === 1) {        childWrapper.classList.add('only-child');      } else if (index === 0) {        childWrapper.classList.add('first-child');      } else if (index === data.children.length - 1) {        childWrapper.classList.add('last-child');      }
      renderOrgChart(child, childWrapper);      childrenEl.appendChild(childWrapper);    });
    container.appendChild(childrenEl);  }
  // 默认收起部分节点  if (data.id === 2 || data.id === 4) {    nodeEl.classList.add('collapsed');  }}

节点展开/收起功能

通过给节点添加/移除 .collapsed 类来控制子节点的显示与隐藏。CSS 中利用相邻兄弟选择器来隐藏被收起节点的子级容器;同时,通过伪元素 ::after 在节点下方显示 +/- 按钮,提升用户体验。(相关事件在“节点渲染逻辑”中“为切换按钮添加点击事件”绑定)

全部展开/收起

首先判断当前是否所有节点都处于收起状态,然后统一执行相反的操作。
function toggleAll() {  var nodes = document.querySelectorAll('.node.has-children');  var isAllCollapsed = Array.from(nodes).every(node => node.classList.contains('collapsed'));
  nodes.forEach(node => {    if (isAllCollapsed) {      node.classList.remove('collapsed');    } else {      node.classList.add('collapsed');    }  });}

扩展建议

  • 节点编辑功能:支持双击节点进入编辑状态,修改人员信息

  • 拖拽排序:引入拖拽库,实现组织架构的可视化调整

  • 导出功能:支持将组织结构图导出为图片或PDF格式

  • 主题定制:提供更多颜色方案和样式选项,满足不同企业的品牌形象需求

完整代码

git地址:https://gitee.com/ironpro/hjdemo/blob/master/orgchart/index.html
<!DOCTYPE html><html lang="zh-CN"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>组织结构图</title>  <style>      * {          margin0;          padding0;          box-sizing: border-box;      }      body {          background-color#f5f5f5;          min-height100vh;          padding20px;      }      .container {          max-width980px;          margin0 auto;          background: white;          border-radius15px;          box-shadow0 20px 40px rgba(0,0,0,0.1);          overflow: hidden;      }      .header {          background#45a049;          color: white;          padding20px;          text-align: center;      }      .header h1 {          font-size24px;          font-weight500;      }      .main {          padding20px;      }      .org-chart-container {          display: flex;          justify-content: center;          overflow: auto;          padding20px 0;      }      .org-chart {          display: flex;          flex-direction: column;          align-items: center;          position: relative;      }
      .node {          background-color: white;          border2px solid #4CAF50;          border-radius8px;          padding15px;          width120px;          text-align: center;          box-shadow0 2px 5px rgba(0,0,0,0.1);          position: relative;          cursor: pointer;          transition: all 0.3s ease;          display: inline-block;      }
      .node:hover {          box-shadow0 4px 10px rgba(0,0,0,0.5);      }
      .node .title {          font-weight: bold;          color#333;          margin-bottom5px;      }
      .node .position {          color#666;          font-size14px;      }
      .children {          display: flex;          justify-content: center;          margin-top40px;          position: relative;      }
      .children::before {          content'';          position: absolute;          top: -40px;          left50%;          width2px;          height20px;          background-color#4CAF50;      }
      .child {          position: relative;          padding0 10px;          text-align: center;      }
      .child::before {          content'';          position: absolute;          top: -20px;          left50%;          width2px;          height20px;          background-color#4CAF50;      }
      .child::after {          content'';          position: absolute;          top: -20px;          left0;          width100%;          height2px;          background-color#4CAF50;      }
      .first-child::after {          width50%;          left50%;      }
      .last-child::after {          width50%;      }
      .only-child::after {          width0;      }
      .actions {          text-align: center;      }
      button {          background-color#4CAF50;          color: white;          border: none;          padding10px 15px;          border-radius4px;          cursor: pointer;          margin0 5px;          font-size14px;      }
      button:hover {          background-color#45a049;      }
      .node.collapsed + .children {          display: none;      }      .node .toggle-btn::after {          content'-';          position: absolute;          bottom: -10px;          left50%;          transformtranslateX(-50%);          width20px;          height20px;          background-color#4CAF50;          color: white;          border-radius50%;          display: flex;          align-items: center;          justify-content: center;          font-weight: bold;          font-size16px;          z-index9;          cursor: pointer;      }      .node.collapsed .toggle-btn::after {          content'+';      }      .node:not(.has-children.toggle-btn {          display: none;      }  </style></head><body><div class="container">  <div class="header">    <h1>组织结构图</h1>  </div>  <div class="main">    <div class="actions">      <button onclick="toggleAll()">展开/收起全部</button>    </div>    <div class="org-chart-container">      <div class="org-chart" id="orgChart"></div>    </div>  </div></div><script>  var orgData = {    id1,    name"张伟",    position"CEO",    children: [      {        id2,        name"李娜",        position"技术总监",        children: [          {            id4,            name"王强",            position"前端主管",            children: [              { id7name"陈晨"position"前端工程师" },              { id8name"刘芳"position"UI设计师" }            ]          },          {            id5,            name"赵磊",            position"后端主管",            children: [              { id9name"孙丽"position"Java工程师" },              { id10name"周鹏"position"数据库专家" }            ]          }        ]      },      {        id3,        name"马华",        position"市场总监",        children: [          {            id6,            name"吴敏",            position"市场经理",            children: [              { id11name"郑涛"position"市场专员" },              { id12name"黄娟"position"品牌专员" }            ]          }        ]      }    ]  };
  // 渲染组织结构图  function renderOrgChart(data, container) {    // 创建当前节点    var nodeEl = document.createElement('div');    nodeEl.className = 'node';    nodeEl.dataset.id = data.id;
    // 如果有子节点,添加标识类    if (data.children && data.children.length > 0) {      nodeEl.classList.add('has-children');    }
    // 设置节点内容    nodeEl.innerHTML = `        <div class="title">${data.name}</div>        <div class="position">${data.position}</div>        <div class="toggle-btn"></div>      `;    // 为切换按钮添加点击事件    if (data.children && data.children.length > 0) {      const toggleBtn = nodeEl.querySelector('.toggle-btn');      toggleBtn.addEventListener('click'function(e) {        e.stopPropagation();        nodeEl.classList.toggle('collapsed');      });    }
    container.appendChild(nodeEl);
    // 如果有子节点,创建子节点容器    if (data.children && data.children.length > 0) {      var childrenEl = document.createElement('div');      childrenEl.className = 'children';
      data.children.forEach((child, index) => {        var childWrapper = document.createElement('div');        childWrapper.className = 'child';
        // 添加特殊类名用于样式控制        if (data.children.length === 1) {          childWrapper.classList.add('only-child');        } else if (index === 0) {          childWrapper.classList.add('first-child');        } else if (index === data.children.length - 1) {          childWrapper.classList.add('last-child');        }
        renderOrgChart(child, childWrapper);        childrenEl.appendChild(childWrapper);      });
      container.appendChild(childrenEl);    }
    // 默认收起部分节点    if (data.id === 2 || data.id === 4) {      nodeEl.classList.add('collapsed');    }  }
  // 初始化组织结构图  function initOrgChart() {    var container = document.getElementById('orgChart');    container.innerHTML = '';    renderOrgChart(orgData, container);  }
  // 展开/收起全部节点  function toggleAll() {    var nodes = document.querySelectorAll('.node.has-children');    var isAllCollapsed = Array.from(nodes).every(node => node.classList.contains('collapsed'));
    nodes.forEach(node => {      if (isAllCollapsed) {        node.classList.remove('collapsed');      } else {        node.classList.add('collapsed');      }    });  }
  initOrgChart();</script></body></html>


阅读原文:原文链接


该文章在 2025/12/15 9:11:54 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved