前端vue2-org-tree实现精美组织架构图

  最近遇到开发组织架构的需求,与以往开发的组织架构不同,不光要展示人名,还要显示职务(或者子公司名称)、对应的头像等,并且要考虑,如果用户未上传头像,需使用默认头像(男、女、中性),(⊙o⊙)…要尊重尊重,不能随便喊那啥...,还要考虑子公司或者不同部门之间的员工借调问题,现简化效果图如下:

最终实现效果图如下:

纵向(默认展开前3级):

横向(默认展开前3级,因为截图无法全屏的问题,部分第3级未展开):

  废话不多说,说方法:采用vue2-org-tree组件来实现效果。

npm

1 # use npm
2 npm i vue2-org-tree
3 # use yarn 4 yarn add vue2-org-tree

安装 loader

npm install --save-dev less less-loader(不安装less-loader基本上都会报错)

Import Plugins(main.js引入)  (CDN方式请自行测试)

import Vue2OrgTree from 'vue2-org-tree'
 
Vue.use(Vue2OrgTree)
 
由于测试过程当中,发生的版本兼容问题(加载css样式丢失),故将CSS样式统一放到样式文件中引入,样式文件如下:
  1 @colors:#1FAAEB;
  2 .org-tree-container {
  3   display: inline-block;
  4   padding: 15px;
  5   background-color: #fff;
  6 }
  7 
  8 .org-tree {
  9   // display: inline-block;
 10   display: table;
 11   text-align: center;
 12 
 13   &:before, &:after {
 14     content: '';
 15     display: table;
 16   }
 17 
 18   &:after {
 19     clear: both;
 20   }
 21 }
 22 
 23 .org-tree-node,
 24 .org-tree-node-children {
 25   position: relative;
 26   margin: 0;
 27   padding: 0;
 28   list-style-type: none;
 29 
 30   &:before, &:after {
 31     transition: all .35s;
 32   }
 33 }
 34 .org-tree-node-label {
 35   position: relative;
 36   display: inline-block;
 37 
 38   .org-tree-node-label-inner {
 39     padding: 10px 15px;
 40     text-align: center;
 41     border-radius: 3px;
 42     box-shadow: 0 1px 5px rgba(0, 0, 0, .15);
 43   }
 44 }
 45 .org-tree-node-btn {
 46   position: absolute;
 47   top: 100%;
 48   left: 50%;
 49   width: 20px;
 50   height: 20px;
 51   z-index: 10;
 52   margin-left: -11px;
 53   margin-top: 9px;
 54   background-color: #fff;
 55   border: 1px solid @colors;
 56   border-radius: 50%;
 57   box-shadow: 0 0 2px rgba(0, 0, 0, .15);
 58   cursor: pointer;
 59   transition: all .35s ease;
 60 
 61   &:hover {
 62     background-color: #e7e8e9;
 63     transform: scale(1.15);
 64   }
 65 
 66   &:before, &:after {
 67     content: '';
 68     position: absolute;
 69   }
 70 
 71   &:before {
 72     top: 50%;
 73     left: 4px;
 74     right: 4px;
 75     height: 0;
 76     border-top: 1px solid @colors;
 77   }
 78 
 79   &:after {
 80     top: 4px;
 81     left: 50%;
 82     bottom: 4px;
 83     width: 0;
 84     border-left: 1px solid @colors;
 85   }
 86 
 87   &.expanded:after {
 88     border: none;
 89   }
 90 }
 91 .org-tree-node {
 92   padding-top: 20px;
 93   display: table-cell;
 94   vertical-align: top;
 95 
 96   &.is-leaf, &.collapsed {
 97     padding-left: 10px;
 98     padding-right: 10px;
 99   }
100 
101   &:before, &:after {
102     content: '';
103     position: absolute;
104     top: 0;
105     left: 0;
106     width: 50%;
107     height: 19px;
108   }
109 
110   &:after {
111     left: 50%;
112     border-left: 1px solid @colors;
113   }
114 
115   &:not(:first-child):before,
116   &:not(:last-child):after {
117     border-top: 1px solid @colors;
118   }
119 
120 }
121 .collapsable .org-tree-node.collapsed {
122   padding-bottom: 30px;
123 
124   .org-tree-node-label:after {
125     content: '';
126     position: absolute;
127     top: 100%;
128     left: 0;
129     width: 50%;
130     height: 20px;
131     border-right: 1px solid @colors;
132   }
133 }
134 .org-tree > .org-tree-node {
135   padding-top: 0;
136 
137   &:after {
138     border-left: 0;
139   }
140 }
141 .org-tree-node-children {
142   padding-top: 20px;
143   display: table;
144 
145   &:before {
146     content: '';
147     position: absolute;
148     top: 0;
149     left: 0;
150     width: 50%;
151     height: 20px;
152     border-right: 1px solid @colors;
153     border-left: none;
154   }
155 
156   &:after {
157     content: '';
158     display: table;
159     clear: both;
160   }
161 }
162 
163 .horizontal {
164   .org-tree-node {
165     // display: flex;
166     // flex-direction: row;
167     // justify-content: flex-start;
168     // align-items: center;
169     display: table-cell;
170     float: none;
171     padding-top: 0;
172     padding-left: 20px;
173 
174     &.is-leaf, &.collapsed {
175       padding-top: 10px;
176       padding-bottom: 10px;
177     }
178 
179     &:before, &:after {
180       width: 19px;
181       height: 50%;
182     }
183 
184     &:after {
185       top: 50%;
186       left: 0;
187       border-left: 0;
188     }
189 
190     &:only-child:before {
191       top: 1px;
192       border-bottom: 1px solid @colors;
193     }
194 
195     &:not(:first-child):before,
196     &:not(:last-child):after {
197       border-top: 0;
198       border-left: 1px solid @colors;
199     }
200 
201     &:not(:only-child):after {
202       border-top: 1px solid @colors;
203     }
204 
205     .org-tree-node-inner {
206       display: table;
207     }
208 
209   }
210 
211   .org-tree-node-label {
212     display: table-cell;
213     vertical-align: middle;
214   }
215 
216   &.collapsable .org-tree-node.collapsed {
217     padding-right: 30px;
218 
219     .org-tree-node-label:after {
220       top: 0;
221       left: 100%;
222       width: 20px;
223       height: 50%;
224       border-right: 0;
225       border-bottom: 1px solid @colors;
226     }
227   }
228 
229   .org-tree-node-btn {
230     top: 50%;
231     left: 100%;
232     margin-top: -11px;
233     margin-left: 9px;
234   }
235 
236   & > .org-tree-node:only-child:before {
237     border-bottom: 0;
238   }
239 
240   .org-tree-node-children {
241     // display: flex;
242     // flex-direction: column;
243     // justify-content: center;
244     // align-items: flex-start;
245     display: table-cell;
246     padding-top: 0;
247     padding-left: 20px;
248 
249     &:before {
250       top: 50%;
251       left: 0;
252       width: 20px;
253       height: 0;
254       border-left: 0;
255       border-top: 1px solid @colors;
256     }
257 
258     &:after {
259       display: none;
260     }
261 
262     & > .org-tree-node {
263       display: block;
264     }
265   }
266 }
View Code

组件中HTML代码如下:

 1 <template>
 2   <div class="org-boxs">
 3     <vue2-org-tree
 4       name="test"
 5       :data="datas"
 6       :horizontal="horizontal"
 7       :label-class-name="labelClassName"
 8       :render-content="renderContent"
 9       collapsable
10       @on-expand="onExpand"
11       @on-node-click="onNodeClick"
12     />
13   </div>
14 </template>
View Code

data中定义如下:

  1 data () {
  2       return {
  3         horizontal: false,
  4         collapsable: false,
  5         expandAll: true,
  6         labelClassName: "bg-none",
  7         datas:{
  8           id:'1',
  9           label:'老张伟',
 10           position:'董事长',
 11           img:'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4007973853,1345044449&fm=15&gp=0.jpg',
 12           relations:'1',
 13           children:[
 14             {
 15               id:'1-1',
 16               label:'大张嘎',
 17               position:'总经理',
 18               img:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=629230260,2582796696&fm=26&gp=0.jpg',
 19               relations:'1-1',
 20               children:[
 21                 {
 22                   id:'1-1-1',
 23                   label:'小张嘎',
 24                   position:'策划部',
 25                   img:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2495406741,1368429183&fm=26&gp=0.jpg',
 26                   relations:'1-1',
 27                 },
 28                 {
 29                   id:'1-1-2',
 30                   label:'中张嘎',
 31                   position:'规划部',
 32                   img:'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4125471676,2511464594&fm=26&gp=0.jpg',
 33                   relations:'1-1',
 34                 },
 35               ]
 36             },
 37             {
 38               id:'1-2',
 39               label:'大刘彪',
 40               position:'秘书长',
 41               img:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=291861640,4252957999&fm=26&gp=0.jpg',
 42               relations:'1-2',
 43               children:[
 44                 {
 45                   id:'1-2-1',
 46                   label:'中刘彪',
 47                   position:'人事部',
 48                   img:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=4122062364,793453037&fm=26&gp=0.jpg',
 49                   relations:'1-2',
 50                 },
 51                 {
 52                   id:'1-2-2',
 53                   label:'小刘彪',
 54                   position:'行政部',
 55                   img:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1018449383,841488959&fm=15&gp=0.jpg',
 56                   relations:'1-2',
 57                 },
 58               ]
 59             },
 60             {
 61               id:'1-3',
 62               label:'大美女',
 63               position:'财务总监',
 64               img:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2745812195,432411379&fm=26&gp=0.jpg',
 65               relations:'1-3',
 66               children:[
 67                 {
 68                   id:'1-3-1',
 69                   label:'中美女',
 70                   position:'财务部',
 71                   img:'',
 72                   relations:'1-3',
 73                   sex:2,
 74                   children:[
 75                     {
 76                       id:'1-3-1-1',
 77                       label:'小美女',
 78                       position:'财务科',
 79                       img:'',
 80                       sex:0,
 81                       relations:'1-3',
 82                     },
 83                   ]
 84                 },
 85               ]
 86             },
 87             {
 88               id:'1-4',
 89               label:'大霸王',
 90               position:'技术总监',
 91               img:'',
 92               sex:1,
 93               relations:'1-4',
 94               children:[
 95                 {
 96                   id:'1-4-1',
 97                   label:'小霸王',
 98                   position:'技术部',
 99                   img:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=481633131,466344723&fm=26&gp=0.jpg',
100                   relations:'1-4',
101                 },
102                 {
103                   id:'1-4-2',
104                   label:'中霸王',
105                   position:'研发部',
106                   img:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1490853638,1893546593&fm=26&gp=0.jpg',
107                   relations:'1-4',
108                 },
109               ]
110             },
111             {
112               id:'1-5',
113               label:'大赵帅',
114               position:'运营总监',
115               img:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2010835987,458488842&fm=15&gp=0.jpg',
116               relations:'1-5',
117               children:[
118                 {
119                   id:'1-5-1',
120                   label:'小赵帅',
121                   position:'市场部',
122                   img:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3576627708,888952220&fm=26&gp=0.jpg',
123                   relations:'1-5',
124                 },
125                 {
126                   id:'1-5-2',
127                   label:'中赵帅',
128                   position:'销售部',
129                   img:'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3102026869,1790261072&fm=26&gp=0.jpg',
130                   relations:'1-3',
131                 },
132                 {
133                   id:'1-5-3',
134                   label:'老赵帅',
135                   position:'执行部',
136                   img:'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3444708870,1488183897&fm=26&gp=0.jpg',
137                   relations:'1-5',
138                 },
139               ]
140             },
141           ],
142         },
143         manUrl:require('@/assets/man.jpeg'),
144         womanUrl:require('@/assets/woman.jpg'),
145         shemaleUrl:require('@/assets/shemale.jpg'),
146       }
147     },
View Code
manUrl、womanUrl、shemaleUrl为对应的男、女、中性默认头像,datas中的img为用户上传到服务器的URL,relations默认为各个公司的id(可根据实际情况自行设置),用来判断该人员劳动关系属于哪个公司/部门(尤其是子公司之间的员工借调问题,或者政府部门市县局间的人员借调问题)。
组件自带方法如下:
 1 onExpand(e, data) {
 2         if ("expand" in data) {
 3           data.expand = !data.expand;
 4           if (!data.expand && data.children) {
 5             this.collapse(data.children);
 6           }
 7         } else {
 8           this.$set(data, "expand", true);
 9         }
10       },
11       //点击选项执行的方法,可以用于跳转到其他链接,注意一定要写协议头
12       onNodeClick(e, data) {
13         //console.log(data.label);
14         if(data.url==null){
15           return false
16         }else{
17           window.open(data.url)
18         }
19       },
20       collapse(list) {
21         var _this = this;
22         list.forEach(function(child) {
23           if (child.expand) {
24             child.expand = false;
25           }
26           child.children && _this.collapse(child.children);
27         });
28       },
29       expandChange() {
30         this.toggleExpand(this.data, this.expandAll);
31       },
32       toggleExpand(data, val) {
33         var _this = this;
34         if (Array.isArray(data)) {
35           data.forEach(function(item) {
36             _this.$set(item, "expand", val);
37             if (item.children) {
38               _this.toggleExpand(item.children, val);
39             }
40           });
41         } else {
42           this.$set(data, "expand", val);
43           if (data.children) {
44             _this.toggleExpand(data.children, val);
45           }
46         }
47       }
View Code

组件加载时的初始化方法如下(默认展开3级):

1 initOrg(){
2         this.$set(this.datas,'expand',true);
3         if(this.datas.children){
4           this.datas.children.forEach((item,index)=>{
5             this.$set(item,'expand',true);
6           })
7         }
8       },
View Code

重点来了,每个节点内容的渲染方法如下:

1 renderContent(h, data) {
2         return (
3           <span style="100%;height:100%;display:block;padding:10px 15px;border-radius:3px;" class={data.relations === '1-1'?'bg-tomato':(data.relations === '1-2'?'bg-gold':(data.relations === '1-3'?'bg-gray':(data.relations === '1-4'?'bg-lightpink':(data.relations === '1-5'?'bg-blue':'bg-green'))))}>
4             <dd style="height:6vh;border-radius:50%;padding:0;margin-bottom:1vh;"><img style="6vh;height:6vh;border-radius:50%;" src={data.img?data.img:(data.sex === 1?this.manUrl:(data.sex === 2?this.womanUrl:this.shemaleUrl))}/></dd>
5             <dd style="font-size:1.6vh;">{data.label}</dd>
6             <dd style="font-size:1vh;">{data.position}</dd>
7           </span>
8         )
9       },
View Code

CSS样式如下:

 1 <style lang="less">
 2   @import "~@assets/less/org-tree.less";
 3   .org-boxs{
 4     width:100%;
 5     height:100%;
 6     text-align: center;
 7     /*background: #030C24;*/
 8     background-image: -webkit-radial-gradient(ellipse farthest-corner at center 40%, #000d4d 0%, #000105 100%);
 9     background-image: radial-gradient(ellipse farthest-corner at center 40%, #000d4d 0%, #000105 100%);
10     overflow-y: scroll;
11   }
12   .org-tree-container{
13     background:none!important;
14   }
15   .org-tree-node-label {
16     white-space: nowrap;
17   }
18   .bg-none{
19     background-color:#030C24;
20     color:#ffffed;
21   }
22   .bg-white {
23      background-color: #ECF5FF;
24    }
25   .org-tree-node-label .org-tree-node-label-inner {
26     width:6vw;
27     padding: 0px 0px;
28     text-align: center;
29     border-radius: 3px;
30     box-shadow: 0 1px 5px rgba(0, 0, 0, 0.15);
31    /* border: 1px solid @colors;*/
32     overflow: hidden;
33     box-sizing: border-box;
34   }
35   .bg-tomato {
36     background-color: #9E4A1C;
37   }
38   .bg-gold {
39     background-color: #ECA150;
40   }
41   .bg-gray {
42     background-color: #DECEAA;
43   }
44   .bg-lightpink {
45     background-color: lightpink;
46   }
47   .bg-blue {
48     background-color: #057D9F;
49   }
50   .bg-green {
51     background-color: #50CB90;
52   }
53 </style>
View Code

最终效果如文章开始效果图所示,renderContent方法中渲染节点背景颜色class的判断,有更好的方式的,请联系我,多谢!

PS:组件封装至此已基本完毕,转载请注明出处

原文地址:https://www.cnblogs.com/trampeagle/p/13432335.html