横向结构的树组件(leader-line-vue)
近期做项目时需要做一个横向的树结构的图,如下所示:
本图的实现采用了leader-line-vue组件,
具体实现如下:
先npm install leader-line-vue --save,安装依赖
然后,子组件RightTree的封装,代码如下:
1 <template> 2 <div class="TreeRight" id="treeRight" v-if="showTree"> 3 <div class="childs"> 4 <div 5 class="child" 6 v-for="(item, index) in list" 7 :key="item.id + '-child-' + index" 8 > 9 <div 10 class="child-item" 11 :style="{ 12 marginRight: 13 item.children && item.children.length > 1 ? '20px' : '', 14 }" 15 > 16 <div class="childname" :id="item"> 17 <div class="content-box" :ref="item.id" :id="item.id"> 18 {{ item.id }} 19 <p 20 v-for="(itemshow, index3) in showfields" 21 :key="'itemshow' + index3" 22 > 23 {{ itemshow.name }}{{ item[itemshow.key] }} 24 </p> 25 </div> 26 <div style="width: 30px"></div> 27 <div 28 class="position-top" 29 v-if="isFirst(item.id) && domready" 30 :style="position_top(item.id, 'top')" 31 ></div> 32 <div 33 class="position-top" 34 v-if="isLast(item.id)" 35 :style="position_top(item.id, 'bottom')" 36 ></div> 37 </div> 38 <div style="width: 160px"></div> 39 </div> 40 <!-- 递归组件展示子节点 --> 41 <div 42 class="child-children" 43 v-if="item.children && item.children.length" 44 > 45 <RightTree :list="item.children" :showfields="showfields" /> 46 </div> 47 </div> 48 </div> 49 </div> 50 </template> 51 52 <script> 53 import LeaderLine from "leader-line-vue"; 54 export default { 55 name: "RightTree", 56 components: {}, 57 data() { 58 return { 59 domready: false, 60 lines: [], 61 }; 62 }, 63 created() {}, 64 props: { 65 list: { 66 type: Array, 67 default: () => [], 68 }, 69 showfields: { 70 type: Array, 71 default: () => [], 72 }, 73 }, 74 mounted() { 75 this.$nextTick(() => { 76 this.domready = true; 77 this.drawArrowLine(); 78 }); 79 }, 80 computed: { 81 /** 82 * 是否展示树计算属性 83 */ 84 showTree() { 85 return this.list && this.list.length; 86 }, 87 }, 88 beforeDestroy() { 89 /** 90 * 离开页面时销毁所有line 91 */ 92 if (this.lines && this.lines.length) { 93 this.lines.forEach((line) => { 94 line.remove(); 95 }); 96 } 97 }, 98 methods: { 99 /** 100 * 递归绘制箭头 101 */ 102 drawArrowLine() { 103 this.drawLeaderLine(this.list); 104 document.getElementById("treeRight").addEventListener("scroll", () => { 105 if (this.lines && this.lines.length) { 106 this.lines.forEach((line) => { 107 line.position(); 108 }); 109 } 110 }); 111 }, 112 /** 113 * 根据上下级关系绘制线条 114 */ 115 drawLeaderLine(list) { 116 list.forEach((element) => { 117 let start = document.getElementById(element.id); 118 if (element.children && element.children.length) { 119 element.children.forEach((child) => { 120 let line = LeaderLine.setLine( 121 start, 122 document.getElementById(child.id) 123 ); 124 line.color = "#7F7F7F"; 125 line.path = "fluid"; 126 line.size = 1; 127 line.setOptions({ 128 solid: { animation: true }, 129 }); 130 this.lines.push(line); 131 }); 132 this.drawLeaderLine(element.children); 133 } 134 }); 135 }, 136 position_top(id, position) { 137 let dom = document.getElementById(id); 138 let height; 139 if (dom) { 140 height = dom.clientHeight; 141 } 142 let rt; 143 if (position === "top") { 144 rt = { 145 height: height / 2 - 2 + "px", 146 top: 0, 147 }; 148 } 149 if (position === "bottom") { 150 rt = { 151 height: height / 2 + 1 + "px", 152 bottom: 0, 153 }; 154 } 155 return rt; 156 }, 157 isFirst(id) { 158 return ( 159 this.list.length > 1 && this.list.map((x) => x.id).indexOf(id) === 0 160 ); 161 }, 162 isLast(id) { 163 return ( 164 this.list.length > 1 && 165 this.list.map((x) => x.id).indexOf(id) === this.list.length - 1 166 ); 167 }, 168 }, 169 }; 170 </script> 171 172 <style lang="scss" scoped> 173 .TreeRight { 174 width: 100%; 175 height: 100%; 176 overflow: auto; 177 p { 178 margin: 0; 179 font-size: 13px; 180 } 181 display: flex; 182 .father { 183 width: 70px; 184 background-color: red; 185 padding: 100px 10px; 186 } 187 .childs { 188 .child { 189 display: flex; 190 background-color: #fff; 191 .child-item { 192 display: flex; 193 align-items: center; 194 margin: 10px 0; 195 .childname { 196 .content-box { 197 text-align: left; 198 border: 1px solid #e8e8e8; 199 padding: 10px; 200 height: 100px; 201 border-radius: 2px; 202 width: 100%; 203 box-shadow: 0 2px 4px 0 rgba(181, 181, 181, 0.7); 204 } 205 cursor: pointer; 206 height: 100%; 207 display: flex; 208 align-items: center; 209 width: 220px; 210 text-align: center; 211 justify-content: center; 212 position: relative; 213 padding: 10px 0; 214 .position-arrow { 215 position: absolute; 216 left: -22px; 217 } 218 .position-top { 219 position: absolute; 220 width: 3px; 221 background-color: #fff; 222 left: -23px; 223 height: 10px; 224 } 225 } 226 .childarrow { 227 height: 100%; 228 display: flex; 229 align-items: center; 230 } 231 } 232 } 233 .child-children { 234 display: flex; 235 flex-direction: column; 236 justify-content: center; 237 } 238 } 239 } 240 </style>
父亲组件引用方式如下:
1 <template> 2 <div> 3 <right-tree 4 v-if="list && list.length" 5 :list="list" 6 :showfields="showFields" 7 ></right-tree> 8 </div> 9 </template> 10 <script> 11 import RightTree from '../components/RightTree' 12 export default { 13 components:{ 14 RightTree, 15 }, 16 data(){ 17 return{ 18 list: [//datasource 19 { 20 id: '1', 21 name: 'span1', 22 serviceId: 'service1', 23 children: [ 24 { 25 id: '1-1', 26 name: 'user', 27 serviceId: 'service-user', 28 children: [ 29 { 30 id: '1-1-1', 31 name: 'shop', 32 serviceId: '18', 33 children: [ 34 { 35 id: '1-1-1-1', 36 name: 'common', 37 serviceId: 'common-service', 38 }, 39 ], 40 }, 41 { 42 id: '1-1-2', 43 name: 'account1', 44 serviceId: 'account-service', 45 }, 46 { 47 id: '1-1-3', 48 name: 'account2', 49 serviceId: 'account-service', 50 51 }, 52 { 53 id: '1-1-4', 54 name: 'account3', 55 serviceId: 'account-service', 56 }, 57 ], 58 }, 59 { 60 id: '1-2', 61 name: 'truck', 62 serviceId: 'truck-pay', 63 work: 'web', 64 }, 65 ], 66 }, 67 ], 68 showFields: [ 69 { 70 name: '服务名称:', 71 key: 'name', 72 }, 73 { 74 name: '服务编号:', 75 key: 'serviceId', 76 }, 77 ], 78 } 79 }, 80 methods:{ 81 82 }, 83 84 } 85 </script>
其实就是用leader-line-vue绘制了引导线,leader-line-vue的功能还是蛮强大的,想了解更深,可以参考官网:https://www.npmjs.com/package/leader-line-vue