组件源码如下
<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 v-if="treeData.image_url" :src="treeData.image_url" /><img v-else src="/static/user_default.png" /></div><div class="name">{{ treeData.name }}</div><div class="sub_name" v-if="treeData.subName">{{ treeData.subName }}</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;width: 100%}td {position: relative;vertical-align: top;padding: 0 0 50px 0;text-align: center;}.extend_handle {position: absolute;left: 50%;bottom: 30px;width: 10px;height: 10px;padding: 10px;transform: translate3d(-15px, 0, 0);cursor: pointer;}.extend_handle:before {content: "";display: block;width: 100%;height: 100%;box-sizing: border-box;border: 2px solid;border-color: #ccc #ccc transparent transparent;transform: rotateZ(135deg);transform-origin: 50% 50% 0;transition: transform ease 300ms;}.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: 15px;border-left: 2px solid #ccc;transform: translate3d(-1px, 0, 0)}.childLevel::before {content: "";position: absolute;left: 50%;bottom: 100%;height: 15px;border-left: 2px solid #ccc;transform: translate3d(-1px, 0, 0)}.childLevel::after {content: "";position: absolute;left: 0;right: 0;top: -15px;border-top: 2px solid #ccc;}.childLevel:first-child:before,.childLevel:last-child:before {display: none;}.childLevel:first-child:after {left: 50%;height: 15px;border: 2px solid;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 solid;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;cursor: pointer;}.node .person {position: relative;display: inline-block;z-index: 2;width: 6em;overflow: hidden;}.node .person .avat {display: block;width: 4em;height: 4em;margin: auto;overflow: hidden;background: #fff; /* border: 1px solid #ccc;*/border-radius: 50%;box-sizing: border-box;}.node .person .avat img {width: 100%;height: 100%;}.node .person .name {height: 2em;line-height: 2em;overflow: hidden;width: 100%;}.sub_name{}.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>
使用
<TreeChart :json="treeData" @click-node="clickNode" />
数据如下
treeData: {name: '保密中心',subName: '张三',image_url: "",class: ["rootNode"],children: [{name: '综合科室',subName: '张三',image_url: ""},{name: '保密科室',subName: '王五',image_url: "",// mate: [{// name: 'mate',// image_url: "https://static.refined-x.com/avat3.jpg"// }], children: [{name: '保密局一',subName: '张三',image_url: ""},{name: '保密局二',subName: '张三',image_url: ""},{name: '保密局三',subName: '李四',image_url: ""}]},{name: '销毁科室',subName: '王麻子',image_url: "",}]}