项目中需要动态展示组织结构,这里只是展示简单的实现,如果需要更复杂的就需要去深入去实现。
插件安装:
npm i vue-tree-chart --save
组件内容:vart.vue
<template>
<table v-if="treeData.name">
<tr>
<td :colspan="Array.isArray(treeData.children) ? treeData.children.length * 2 : 1"
:class="{parentLevel: Array.isArray(treeData.children) && treeData.children.length,
extend: Array.isArray(treeData.children) && treeData.children.length && treeData.extend}">
<div :class="{node: true, hasMate: treeData.mate}">
<div class="person" :class="Array.isArray(treeData.class) ? treeData.class : []"
@click="$emit('click-node', treeData)">
<div class="avat">
<img :src="treeData.image_url" />
</div>
<div class="right">
<div class="name1">META PRINCIPAL</div>
<div class="name">{{treeData.name}}</div>
<div class="name2">{{treeData.n}}</div>
</div>
</div>
<template v-if="Array.isArray(treeData.mate) && treeData.mate.length">
<!-- <div class="person" v-for="(mate, mateIndex) in treeData.mate" :key="treeData.name+mateIndex"
:class="Array.isArray(mate.class) ? mate.class : []"
@click="$emit('click-node', mate)" >
<div class="avat">
<img :src="mate.image_url" />
</div>
<div class="name">{{mate.name}}</div>
</div> -->
</template>
</div>
<div class="extend_handle" v-if="Array.isArray(treeData.children) && treeData.children.length" @click="toggleExtend(treeData)"></div>
</td>
</tr>
<!-- 这是一个递归组件,注意,这里还要调用,需要传递的数据这里也要传递,否则操作时拿不到子级的数据 -->
<tr v-if="Array.isArray(treeData.children) && treeData.children.length && treeData.extend">
<td v-for="(children, index) in treeData.children" :key="index" colspan="2" class="childLevel">
<TreeChart :json="children" @click-node="$emit('click-node', $event)"/>
</td>
</tr>
</table>
</template>
<script>
export default {
name: "TreeChart",
props: ["json"],
data() {
return {
treeData: {}
}
},
watch: {
// 遍历当前的数据
json: {
handler: function(Props){
let extendKey = function(jsonData){
jsonData.extend = (jsonData.extend===void 0 ? true: !!jsonData.extend);
if(Array.isArray(jsonData.children)){
jsonData.children.forEach(c => {
extendKey(c)
})
}
return jsonData;
}
if(Props){
this.treeData = extendKey(Props);
}
},
immediate: true
}
},
methods: {
toggleExtend: function(treeData){
treeData.extend = !treeData.extend;
this.$forceUpdate();
}
}
}
</script>
<style scoped>
table{
border-collapse: separate!important;
border-spacing: 0!important;
}
td{
position: relative;
vertical-align: top;
padding:0 0 40px 0;
text-align: center;
}
.extend_handle:hover:before{
border-color:#333 #333 transparent transparent;
}
.extend .extend_handle:before{
transform: rotateZ(-45deg);
}
.extend::after{
content: "";
position: absolute;
left:50%;
bottom:15px;
height:20px;
border-left:2px dashed #ccc;
transform: translate3d(-1px,0,0)
}
.childLevel::before{
content: "";
position: absolute;
left:50%
;bottom:100%;
height:15px;
border-left:2px dashed #ccc;
transform: translate3d(-1px,0,0)}
.childLevel::after{
content: "";
position: absolute;
left:0;
right:0;
top:-15px;
border-top:2px dashed #ccc;
}
.childLevel:first-child:before, .childLevel:last-child:before{
display: none;
}
.childLevel:first-child:after{
left:50%;height:15px;
border:2px dashed;
border-color:#ccc transparent transparent #ccc;
border-radius: 6px 0 0 0;transform: translate3d(1px,0,0);
}
.childLevel:last-child:after{
right:50%;
height:15px;
border:2px dashed;
border-color:#ccc #ccc transparent transparent;
border-radius: 0 6px 0 0;
transform: translate3d(-1px,0,0);
}
.childLevel:first-child.childLevel:last-child::after{
left:auto;
border-radius: 0;
border-color:transparent #ccc transparent transparent;
transform: translate3d(1px,0,0)
}
.node{
position: relative;
display: inline-block;
margin: 0 1em;
box-sizing: border-box;
text-align: center;
}
.node .person{
position: relative;
display: inline-block;
z-index: 2;
width:16em;
height: 8em;
overflow: hidden;
border-radius: 4px;
display: flex;
padding:17px 0px 20px 10px;
}
.node .person:nth-child(1){
border:1px solid #409EFF;
background: #409EFF;
}
.node .person .avat{
display: block;
width:4em;
height: 60px;
overflow:hidden;
background:#fff;
border-radius: 8px;
border:1px solid #ccc;
box-sizing: border-box;
}
.right{
text-align: left;
padding-left: 10px;
color: #fff;
}
.name1{
font-weight:bold;
}
.name{
border-bottom: 1px solid #ffffff;
}
.name2{
padding-top:8px;
}
.node .person .avat img{
width:100%;
height: 60px;
}
.node .person .name{
height:2em;
line-height: 2em;
overflow: hidden;
color: #fff;
width:100%;
}
.node.hasMate::after{
content: "";
position: absolute;
left:2em;
right:2em;
top:2em;
border-top:2px solid #ccc;
z-index: 1;
}
/* 横板 */
.landscape{
transform:translate(-100%,0) rotate(-90deg);
transform-origin: 100% 0;
}
.landscape .node{
text-align: left;
height: 8em;
width:8em;
}
.landscape .person{
position: relative;
transform: rotate(90deg);
padding-left: 4.5em;
height: 4em;
top:4em;
left: -1em;
}
.landscape .person .avat{
position: absolute;
left: 0;
}
.landscape .person .name{
height: 4em;
line-height: 4em;
}
.landscape .hasMate{
position: relative;
}
.landscape .hasMate .person{
position: absolute;
}
.landscape .hasMate .person:first-child{
left:auto;
right:-4em;
}
.landscape .hasMate .person:last-child{
left: -4em;
margin-left:0;
}
</style>
页面中使用:
<div class="app" style="height:560px;width:100%;position:relative;margin-left:20%;">
<TreeChart :json="dataList" :class="{landscape: landscape.length}" @click-node="clickNode" />
</div>
<script>
import TreeChart from "./vart";
export default {
components: {TreeChart},
data() {
return {
dataList: {
name: '何xx',
n:'xxxx',
image_url: "https://static.refined-x.com/static/avatar.jpg",
class: ["rootNode"],
children: [
{
name: '张xxx',
n:'xxxx',
image_url: "https://static.refined-x.com/static/avatar.jpg",
children:[
{
name: '代x',
n:'xxx',
image_url: "https://static.refined-x.com/static/avatar.jpg",
children: [
{
name: '李x', n:'xxx',
image_url: "https://static.refined-x.com/static/avatar.jpg"
},
{
name: '李x', n:'xxx,
image_url: "https://static.refined-x.com/static/avatar.jpg"
},
{
name: '赵x', n:'xxxx',
image_url: "https://static.refined-x.com/static/avatar.jpg"
}
]
},
]
},
]
},
}
},
methods: {
clickNode: function(node){
console.log(node)
},
}
}
</script>
<style lang="scss" scoped>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#app .avat{
border-radius: 2em;
border-width:2px;
}
#app .name{
font-weight:700;
}
#app .rootNode .name{
color: red;
}
</style>
最后展示简单结构图
|