vue横向组织结构图实现

实现效果图如下:


image.png

数据格式 tree

let  dataInfo = [
        {
          name: "root",
          value: "root",
          children: [
            {
              name: "child1",
              value: "一级1",
              children: [
                {
                  name: "child2",
                  value: "二级1",
                  children: []
                },
                {
                  name: "child2",
                  value: "二级2",
                  children: []
                }
              ]
            },
            {
              name: "child1",
              value: "一级2",
              children: [
                {
                  name: "child2",
                  value: "二级3",
                  children: [
                  ]
                }
              ]
            }
          ]
        }
      ]

实现代码 递归调用最小实现单元 组件NodeTree.vue

<!--
 * @Descripttion: 横向树实现demo
 * @version: 
 * @Author: year
--> 
<template>
  <div class="item-content" ref="cardItemDiv">
    <div class="info-card">
      <div class="card-item" v-for="(item, index) in data" :key="index">
        <span
          class="vertical-line"
          :style="computedHeight(item.height, data.length, index)"
          v-if="item.name !== 'root'"
        ></span>
        <span class="horizontal-line" v-if="item.name !== 'root'"></span>
        <div class="div-card">
          <div>{{item.value}}</div>
        </div>
        <span class="horizontal-line" v-if="item.children&&item.children.length !== 0"></span>
        <equip-list :data="item.children" v-if="item.children&&item.children.length !== 0"></equip-list>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "equipList",
  props: {
    data: Array
  },
  data() {
    return {
    };
  },
  methods: {
    computedHeight(pheight, length, index) {
      if (length == 1 || length == 0) {
        return {
          height: "0px",
          display: "none"
        };
      } else {
        let height = 0;
        let marginTop = 0;
        let marginB = 0;
        if (index == 0) {
          height = pheight / 2;
          marginTop = height;
          return {
            height: height + "px",
            "margin-top": marginTop + "px"
          };
        }
        if (index == length - 1) {
          height = pheight / 2;
          marginB = height;
          return {
            height: height + "px",
            "margin-bottom": marginB + "px"
          };
        } else {
          height = pheight;
          return {
            height: height + "px"
          };
        }
      }
    }
  },
  components: {},
  mounted() {
  }
};
</script>

<style lang='scss' scoped>
.item-content {
  height: 100%;
  width: 100%;
  display: flex;
  .vertical-line {
    display: inline-block;
    width: 2px;
    background: #eaeaea;
  }

  .card-item {
    display: flex;
    align-items: center;
    .horizontal-line {
      width: 40px;
      display: inline-block;
      height: 2px;
      background: #eaeaea;
    }
  }
}

.div-card {
  height: 77px;
  & > div {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 220px;
    height: 67px;
    background: inherit;
    background-color: rgba(253, 253, 253, 1);
    border: none;
    border-radius: 4px;
    -moz-box-shadow: 1px 3px 5px rgba(0, 0, 0, 0.2);
    -webkit-box-shadow: 1px 3px 5px rgba(0, 0, 0, 0.2);
    box-shadow: 1px 3px 5px rgba(0, 0, 0, 0.2);
    color: #8d8d8e;
  }
}
</style>

父组件调用 Parent.vue

引入NodeTree.vue

<!--
 * @Descripttion: 横向组织架构图实现
 * @version: 
 * @Author: year

--> 
<template>
  <div class="shopping-table-info">
    <NodeTree :data="dataInfo" />
  </div>
</template>

<script>
import NodeTree from "./ShoppingEquipList";
export default {
  data() {
    return {
      dataInfo: []
    };
  },
  methods: {
    getDataInfo() {
      let dataInfo = [
        {
          name: "root",
          value: "root",
          children: [
            {
              name: "child1",
              value: "一级1",
              children: [
                {
                  name: "child2",
                  value: "二级1",
                  children: []
                },
                {
                  name: "child2",
                  value: "二级2",
                  children: []
                }
              ]
            },
            {
              name: "child1",
              value: "一级2",
              children: [
                {
                  name: "child2",
                  value: "二级3",
                  children: []
                }
              ]
            }
          ]
        }
      ];

      let fixedData = dataInfo.map(item => {
        return this.traveTree(item);
      });

      this.dataInfo = fixedData;
    },
    traveTree(nodeInfo) {
      let childrenInfo = nodeInfo.children;
      if (!childrenInfo || childrenInfo.length == 0) {
        nodeInfo.height = 77;
      } else {
        childrenInfo.map(item => {
          this.traveTree(item);
        });

        nodeInfo.height = childrenInfo.reduce((preV, n) => {
          return preV + n.height;
        }, 0);
      }
      return nodeInfo;
    }
  },
  components: {
    NodeTree
  },
  mounted() {
    this.getDataInfo();
  }
};
</script>



实现思路

抽取最小实现单元,然后循环递归即可


image.png

(1)左右两端水平线 实现:


image.png
 <span class="horizontal-line" v-if="item.name !== 'root'"></span>
<span class="horizontal-line"  v-if="item.children&&item.children.length !== 0"></span>

span标签画两条水平线,然后利用flex布局,居中布局即可。最小单元后面的水平线判断没有后代的时候 不显示即可
(2)连接后代的纵轴部分实现:


image.png

需要知道每个后代的高度,然后第一个后代的高度/2 + 中间所有后代的高度和+ 最后一个后代的高度/2 就是所需要纵轴的高度
高度的具体实现代码: 假设最后一层后代的高度为70
a.初始数据处理: 利用二叉树的 后序遍历方法先给每一个节点添加上高度属性

     traveTree(nodeInfo) {
      let childrenInfo = nodeInfo.children;
      if (!childrenInfo || childrenInfo.length == 0) {
        nodeInfo.height = 77;
      } else {
        childrenInfo.map(item => {
          this.traveTree(item);
        });

        nodeInfo.height = childrenInfo.reduce((preV, n) => {
          return preV + n.height;
        }, 0);
      }
      return nodeInfo;
    }

b.计算链接线高度

 computedHeight(pheight, length, index) {
      if (length == 1 || length == 0) {
        return {
          height: "0px",
          display: "none"
        };
      } else {
        let height = 0;
        let marginTop = 0;
        let marginB = 0;
        if (index == 0) {
          height = pheight / 2;
          marginTop = height;
          return {
            height: height + "px",
            "margin-top": marginTop + "px"
          };
        }
        if (index == length - 1) {
          height = pheight / 2;
          marginB = height;
          return {
            height: height + "px",
            "margin-bottom": marginB + "px"
          };
        } else {
          height = pheight;
          return {
            height: height + "px"
          };
        }
      }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351