效果图

讯享网
实现原理
1.放弃div的flex布局, 利用table可以更好地控制新增子节点的对齐,利用::before和::after实现虚线,不需要多余的div
2.使用组件递归,利用子传父 e m i t 传 值 , v − o n = " emit传值,v-on=" emit传值,v−on="listeners"实现跨层级事件监听
父组件.vue

<template> <div id="app"> <TreeChart :model="tree" @on-add="add" @on-update="update" @on-remove="remove"/> </div> </template> <script> / * 使用方式 * <TreeChart :model="data" > * data 参数格式为树形结构 * * { * resName: "xxx", * extend": true, 是否展开 * children: [{ * resName: "xxx", * children: [] * }] * } */ import TreeChart from './components/TreeChart' const dataTree= {
"resId":"root", "resName":"跟节点", "extend": true, "children": [{
"resId":"07fe2e8ce186bb6bcb8f4d6574", "resName":"跟节点1", "extend": true, "children": [] }] } export default {
data() {
return {
tree: dataTree } }, components: {
TreeChart }, methods:{
// 添加 add: function(node){
const newNode = {
resName: '222', resId: new Date().getTime(), message: '', extend: true, children: [] } node.children.push(newNode) }, // 修改 update: function(node){
node.resName = new Date().getTime() }, // 删除 remove: function(node){
if (node.resId == 'root'){
alert('跟节点不能删除') return console.log('跟节点不能删除') } const deepSearch = (tree) => {
for (let i = tree.length - 1; i >= 0; i--) {
if (tree[i].resId == node.resId){
console.log(tree[i]) tree.splice(i, 1) } else if(tree[i].children){
deepSearch(tree[i].children) } } } deepSearch(this.tree.children) } }, } </script> <style lang="scss"> *{
padding: 0; margin: 0; #app{
height: 100vh; width: 100vw; } } </style>
讯享网
递归组件TreeChart.vue
讯享网<template> <table> <tr> <td :colspan="hasChild ? model.children.length * 2 : 1" :class="{ extend: hasChild && model.extend }" > <div class="card"> <div class="title">{
{
model.resName }}</div> <div class="body">{
{
model.message }}</div> <div class="footer"> <div @click="$emit('on-add', model)">添加</div> <div @click="$emit('on-update', model)">修改</div> <div @click="$emit('on-remove', model)">删除</div> </div> </div> <div class="extend_handle" v-if="hasChild" @click="toggleExtend()">{
{
model.extend ? '展开' : '隐藏' }}</div> </td> </tr> <tr v-if="hasChild && model.extend"> <td v-for="(item, index) in model.children" :key="index" colspan="2" class="child" > <!--跨层级监听事件 v-on="$listeners"(.native原生事件无效) --> <TreeChart :model="item" v-on="$listeners"/> </td> </tr> </table> </template> <script> export default {
name: 'TreeChart', props: ['model'], computed: {
hasChild () {
return this.model.children && this.model.children.length } }, methods: {
toggleExtend () {
this.model.extend = !this.model.extend } } } </script> <style lang="scss"> .card {
background: rgb(227, 236, 247); border: 2px solid #ffffff; border-radius: 5px; margin: 0 auto; width: 200px; .title {
padding: 10px 0; font-size: 12px; } .body {
height: 100px; background: #ffffff; width: auto; margin: 0 15px; } .footer {
display: flex; margin: 5px 15px; justify-content: space-between; div {
font-size: 12px; color: rgb(20, 126, 192); cursor: pointer; } } } .extend_handle {
cursor: pointer; } table {
border-collapse: separate !important; border-spacing: 0 !important; td {
position: relative; vertical-align: top; padding: 0 0 50px 0; text-align: center; &.extend {
&::after {
content: ''; position: absolute; left: 50%; bottom: 18px; height: 30px; border-left: 2px dashed rgb(159, 186, 202); transform: translate3d(-1px, 0, 0); } } &.child {
&::before {
content: ''; position: absolute; left: 50%; bottom: 100%; height: 15px; border-left: 2px dashed rgb(159, 186, 202); transform: translate3d(-1px, 0, 0); } // 横线 &::after {
content: ''; position: absolute; left: 0; right: 0; top: -15px; border-top: 2px dashed rgb(159, 186, 202); } &:first-child:before, &:last-child:before {
display: none; } &:first-child:after {
left: 50%; height: 15px; border: 2px dashed; border-color: rgb(159, 186, 202) transparent transparent rgb(159, 186, 202); border-radius: 6px 0 0 0; transform: translate3d(1px, 0, 0); } &:last-child:after {
right: 50%; height: 15px; border: 2px dashed; border-color: rgb(159, 186, 202) rgb(159, 186, 202) transparent transparent; border-radius: 0 6px 0 0; transform: translate3d(-1px, 0, 0); } &:first-child.child:last-child::after {
left: auto; border-radius: 0; border-color: transparent rgb(159, 186, 202) transparent transparent; transform: translate3d(1px, 0, 0); } } } } </style>
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/123352.html