The use of vue2-org-tree tree structure

vue2-org-tree is used to create and display organizational structure tree diagrams, helping developers easily visualize organizational structures, such as company levels, relationships between departments, team members, etc. Its main functions include: custom nodes, collapsible nodes, support for drag-and-drop, search, navigation and other functions.
Here we mainly use vue2-org-tree to price product information for multiple times and categories, and display it in a visual structure chart to make it easier to understand and browse.

1. Install dependencies

# use npm
npm i vue2-org-tree

# use yarn
yarn add vue2-org-tree

2. Introducing the platform

import Vue from 'vue'
import Vue2OrgTree from 'vue2-org-tree'

Vue.use(Vue2OrgTree)

3. Implement renderings

3. Code implementation

<vue2-org-tree
  :data="data"
  :horizontal="true"
  :collapsable="false"
  :label-class-name="labelClassName"
  :render-content="renderContent"
/>
3.1 Style configuration
<style lang="less">
.org-tree-node,
.org-tree-node-children {<!-- -->
  position: relative;
  margin: 0;
  padding: 0;
  list-style-type: none;

   & amp;:before, & amp;:after {<!-- -->
    transition: all .35s;
  }
}
.org-tree-node-label {<!-- -->
  position: relative;
  display: inline-block;

  .org-tree-node-label-inner {<!-- -->
    padding: 10px 15px;
    text-align: center;
    border-radius: 3px;
    box-shadow: 0 1px 5px rgba(0, 0, 0, .15);
  }
}
.org-tree-node-btn {<!-- -->
  position: absolute;
  top: 100%;
  left: 50%;
  width: 20px;
  height: 20px;
  z-index: 10;
  margin-left: -11px;
  margin-top: 9px;
  background-color: #fff;
  border: 1px dashed @colors;
  border-radius: 50%;
  box-shadow: 0 0 2px rgba(0, 0, 0, .15);
  cursor: pointer;
  transition: all .35s ease;

   & amp;:hover {<!-- -->
    background-color: #e7e8e9;
    transform: scale(1.15);
  }

   & amp;:before, & amp;:after {<!-- -->
    content: '';
    position: absolute;
  }

   & amp;:before {<!-- -->
    top: 50%;
    left: 4px;
    right: 4px;
    height: 0;
    border-top: 1px dashed @colors;
  }

   & amp;:after {<!-- -->
    top: 4px;
    left: 50%;
    bottom: 4px;
    width: 0;
    // border-left: 1px dashed @colors;
  }

   & amp;.expanded:after {<!-- -->
    border: none;
  }
}
.org-tree-node {<!-- -->
  padding-top: 20px;
  display: table-cell;
  vertical-align: top;

   & amp;.is-leaf, & amp;.collapsed {<!-- -->
    padding-left: 10px;
    padding-right: 10px;
  }

   & amp;:before, & amp;:after {<!-- -->
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 50%;
    height: 19px;
  }

   & amp;:after {<!-- -->
    left: 50%;
    border-left: 1px dashed @colors;
  }

   & amp;:not(:first-child):before,
   & amp;:not(:last-child):after {<!-- -->
    border-top: 1px dashed @colors;
  }

}
.collapsable .org-tree-node.collapsed {<!-- -->
  padding-bottom: 30px;

  .org-tree-node-label:after {<!-- -->
    content: '';
    position: absolute;
    top: 100%;
    left: 0;
    width: 50%;
    height: 20px;
    border-right: 1px dashed @colors;
  }
}
.org-tree > .org-tree-node {<!-- -->
  padding-top: 0;

   & amp;:after {<!-- -->
    border-left: 0;
  }
}
.org-tree-node-children {<!-- -->
  padding-top: 20px;
  display: table;

   & amp;:before {<!-- -->
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 50%;
    height: 20px;
    border-right: 1px dashed @colors;
    border-left: none;
  }

   & amp;:after {<!-- -->
    content: '';
    display: table;
    clear: both;
  }
}

.horizontal {<!-- -->
  .org-tree-node {<!-- -->
    display: inline-flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    // display: table-cell;
    float: none;
    padding-top: 0;
    padding-left: 20px;

     & amp;.is-leaf, & amp;.collapsed {<!-- -->
      padding-top: 10px;
      padding-bottom: 10px;
    }

     & amp;:before, & amp;:after {<!-- -->
      width: 19px;
      height: 50%;
    }

     & amp;:after {<!-- -->
      top: 50%;
      left: 0;
      border-left: 0;
    }

     & amp;:only-child:before {<!-- -->
      top: 1px;
      border-bottom: 1px dashed @colors;
    }

     & amp;:not(:first-child):before,
     & amp;:not(:last-child):after {<!-- -->
      border-top: 0;
      border-left: 1px dashed @colors;
    }

     & amp;:not(:only-child):after {<!-- -->
      border-top: 1px dashed @colors;
    }

    .org-tree-node-inner {<!-- -->
      display: table;
    }

  }

  .org-tree-node-label {<!-- -->
    display: table-cell;
    vertical-align: middle;
  }

   & amp;.collapsable .org-tree-node.collapsed {<!-- -->
    padding-right: 30px;

    .org-tree-node-label:after {<!-- -->
      top: 0;
      left: 100%;
      width: 20px;
      height: 50%;
      border-right: 0;
      border-bottom: 1px dashed @colors;
    }
  }

  .org-tree-node-btn {<!-- -->
    top: 50%;
    left: 100%;
    margin-top: -11px;
    margin-left: 9px;
  }

   & amp; > .org-tree-node:only-child:before {<!-- -->
    border-bottom: 0;
  }

  .org-tree-node-children {<!-- -->
    display: table-cell;
    padding-top: 0;
    padding-left: 20px;

     & amp;:before {<!-- -->
      top: 50%;
      left: 0;
      width: 20px;
      height: 0;
      border-left: 0;
      border-top: 1px dashed @colors;
    }

     & amp;:after {<!-- -->
      display: none;
    }

     & amp; > .org-tree-node {<!-- -->
      display: block;
    }
  }
}
</style>
3.2 Define label style

Use the labelClassName API to give the class on the label to define the label style.

const colorObj = {<!-- -->
  'DEPOSIT': 'bg-blue',
  'ESTIMATE': 'bg-green',
  'PAYMENTS': 'bg-orange',
}
//Define label style
labelClassName (item) {<!-- -->
  if (item.pricingType) {<!-- -->
    return colorObj[item.pricingType]
  }
},

We can see the constant colorObj. The key value in its object is the pricing type, and the value value is the corresponding class name.

<style lang="less">
.bg-green {<!-- -->
  color: #fff;
  background-color: #87d068;
}
.bg-blue {<!-- -->
  color: #fff;
  background-color: #2db7f5;
}
.bg-orange {<!-- -->
  color: #fff;
  background-color: #FF913A;
}
</style>
3.3 Rendering Node

Use the renderContent API to render child nodes.

renderContent (h, item) {<!-- -->
  return (
    <div>
      <span class="item_name">
        {<!-- -->this.getTitle(item)}
      </span>
      {<!-- -->!item.name & amp; & amp; !item.skuName & amp; & amp; !item.pricingType & amp; & amp; ['add'].includes(this.mode) & amp; & amp; <a class="m-l-10" onClick={<!-- -->() => this.$emit('open', item)}>Pricing</a>}
    </div>
  )
},

Remove the product and priced data, and add pricing buttons for pricing operations. Since the data data is a tree structure and the rendering logic at different levels is different, we extract the rendering logic into the getTitle method. code show as below:

getTitle (item) {<!-- -->
  const max = this.getPricingQtyMax(item)
  if (item.name) {<!-- --> // Root node
    return item.name
  } else if (item.skuName) {<!-- --> // Product information
    return `${<!-- -->item.skuName} (${<!-- -->item.planMainQty}t)`
  } else if (item.pricingType) {<!-- --> // Priced
    return <span>
      {<!-- -->pricingType._find(item.pricingType).name}: {<!-- -->item.pricingQty}t {<!-- -->item.price} yuan
      {<!-- -->(max > 0) & amp; & amp; ['add'].includes(this.mode) & amp; & amp; <a class="c-red m-l-10" onClick ={<!-- -->() => this.$emit('cancel', {<!-- --> ...item, pricingQtyMax: max, pricingQty: max })}>Cancel pricing</ a>}
    </span>
  } else {<!-- --> // Unpriced
    return `${<!-- -->item.pricingQty} tons unpriced`
  }
},

The display logic of the cancel pricing button is handled by the getPricingQtyMax method. The specific code is as follows:

getPricingQtyMax () {<!-- -->
  return function (item) {<!-- -->
    /* Payment: Not applied for payment | Deposit/provisional payment: Unpriced portion */
    if (['PAYMENTS'].includes(item.pricingType)) {<!-- -->
      return NP.minus(item.pricingQty, item.applyQty || 0)
    } else if (['DEPOSIT', 'ESTIMATE'].includes(item.pricingType)) {<!-- -->
      return NP.minus(item.pricingQty, handleTableTotal('pricingQty', item.children.filter(item => item.pricingType)))
    }
  }
},