vue + element 实现 tree 树形控件

工作中遇到的需求,记录一下,效果图如下:
vue + element 实现 tree 树形控件_第1张图片
贴上代码,直接复制就可以看到效果

// 组织架构
<template>
  <div id='newTreeView'>
    <div class="ntv-company">
      <div class="ntvc-child">
        <span>芒果财税一分公司</span>
        <span>(699)</span>
      </div>
    </div>
    <div class="ntv-line1">
      <div></div>
    </div>
    <div class="ntv-list" v-for='(el, inx) in departmentList' :key='el.id' :style='{"margin-top": inx === 0 ? "114px" : "0"}'> <!-- 循环显示每行 -->
      <div :id='"ntvList" + inx'></div> <!-- 用来撑开每一行与左侧的距离 -->
      <div class="ntv-item" v-for='(item, index) in el' :key='item.id' :style='{"flex": inx === 0 && el.length < 6 ? "1" : ""}'> <!-- 循环显示每个部门 -->
        <div class='ntv-line2'> <!-- 部门上方的线条 -->
          <div class="ntvl2-left" :style='{"border-top": index === 0 ? "0" : ""}'></div>
          <div class="ntvl2-right" :style='{"border-top": index === el.length - 1 ? "0" : ""}'></div>
        </div>
        <div @click='showChildDepartment(item, inx, index)' v-if='item.areaName !== "新增部门"' class="ntv-content"> <!-- 部门详情 -->
          <span class='ntv-span1'>{{item.areaName}}</span>
          <span class='ntv-span2'>(699)</span>
        </div>
        <div v-else class="ntv-addcontent"> <!-- 新增部门 -->
          <span>{{item.areaName}}</span>
        </div>
        <div v-if='curDepartmentId[inx] === item.id && item.children' class="ntv-line3"> <!-- 部门下方的线条 -->
          <div></div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'newTreeView',
  methods: {
    showChildDepartment (item, inx, index) { // 当前部门对象, 父部门下标, 当前部门对象下标
      /* 封装数据结构 */
      if (this.curDepartmentId[inx]) {
        this.departmentList = this.departmentList.splice(0, inx + 1) // 如果点击过该行 则删除该行下方所有行
        for (let i in this.curDepartmentId) {
          if (i > inx) {
            delete this.curDepartmentId[i] // 删除行点击过的部门的记录
          }
        }
        this.curDepartmentId[inx] = item.id
      } else { // 如果没有点击过该行 则添加该行 以该行下标为key 当前点击部门id为value  用来控制点击过的部门下方的竖线的显示
        this.curDepartmentId[inx] = item.id
      }
      if (!item.children) return // 如果点击的该部门没有子部门  则结束方法
      let flag = true
      item.children.forEach(item => { // 如果子部门中没有新增部门则添加  有则跳过
        if (item.areaName === '新增部门') {
          flag = false
        }
      })
      if (flag) {
        item.children.push({id: new Date().valueOf(), areaName: '新增部门', children: []})
      }
      this.departmentList.push(item.children)
      /* 设置每一行的左边距 */
      let curId = 'ntvList' + (inx + 1) // 每一行list的id
      if (inx === 0) { // 当点击第一行部门时 设置子部门的左边距
        if (item.children.length / 2 > index + 1) { // 如果子部门长度 / 2 超出了当前点击的部门到最左侧的宽度 则子部门左边距为0
          this.listMarginLeft = 0
        } else { // 如果子部门的长度 / 2 不大于当前点击部门到最左侧的距离  则设置子部门在当前部门下居中显示
          this.listMarginLeft = ((index + 0.5) * 128) - (item.children.length / 2 * 128)
        }
      }
      let marginLeft = document.getElementById('ntvList' + inx).style.marginLeft.replace('px', '') // 获取当前点击部门所在行的左边距 用来计算下一行(子部门的左边距)
      if (inx !== 0 && item.children) { // 设置点击第二行及后面的行时 子部门的左边距
        // 子部门的左边距 = 上一列(父部门)左边距 + (当前点击部门的下标 + 0.5) * 128(单个部门的长度) - 子部门行一般的长度 (结合树状图理解)
        this.listMarginLeft = Number(marginLeft) + ((index + 0.5) * 128) - (item.children.length / 2 * 128)
        this.listMarginLeft = this.listMarginLeft >= 0 ? this.listMarginLeft : 0
      }
      setTimeout(() => {
        document.getElementById(curId).style.marginLeft = this.listMarginLeft + 'px'
      }, 50)
    }
  },
  data () {
    return {
      listMarginLeft: 0, // 子部门左边距
      curDepartmentId: {}, // 存放所有点击过的部门id  控制点击过的部门下方的 竖线的显示
      departmentList: [], // 存放每一行数据的数组,结构为 [[{第一行部门}], [{第二行部门}], [{第三行部门}]...]
      treeData: [] // 总数据 一般是从后台请求接口得到
    }
  },
  created () {
    /* 数据结构为对象数组,每个对象中也有子对象数组,以此类推 */
    this.treeData = [
      {id: '124wre', areaName: '部门一', children: [
        {id: '3245e2', areaName: '部门1', children: [
          {id: '124esfd', areaName: '双方都', children: [
            {id: '35etgfdg', areaName: '我确认', children: [
              {id: '124rftgr', areaName: '无法改'},
              {id: '234etrd', areaName: '无法改'}
            ]},
            {id: '24resrf', areaName: '我确认'},
            {id: '23retgdf', areaName: '我确认'},
            {id: 'g45ytfh', areaName: '我确认'},
          ]},
          {id: '214re', areaName: '双方都'},
          {id: '2retgr', areaName: '双方都'},
          {id: 'rhfgjh66', areaName: '双方都'},
          {id: 'dfhert4', areaName: '双方都'},
          {id: 'rh6yj', areaName: '双方都'},
          {id: 'rh5ujyg', areaName: '双方都'},
          {id: '34ytrtyhfg', areaName: '双方都'},
          {id: '437yrtjhyhg', areaName: '双方都'},
          {id: '24etr', areaName: '双方都'},
          {id: 'fgth56ujt', areaName: '双方都'}
        ]},
        {id: '346t5e', areaName: '部门2'},
        {id: 'ryhfgs', areaName: '部门3'}
      ]},
      {id: '365', areaName: '部门二', children: [
        {id: '24erttg', areaName: '部门1'},
        {id: 'dfhtrjh', areaName: '部门2', children: [
          {id: '24rewtg54', areaName: '二分干'},
          {id: '24rwetr', areaName: '的服务', children: [
            {id: '245etrd', areaName: '按我发'},
            {id: '124wr', areaName: '按我发'},
            {id: '346rtyt', areaName: '按我发'},
            {id: '346yrt', areaName: '按我发', children: [
              {id: '235retre', areaName: '受访人'},
              {id: '235erytr', areaName: '受访人'},
              {id: '346rtyhyt', areaName: '受访人', children: [
                {id: '25rwetre', areaName: '时发热'},
                {id: 'ytuyt', areaName: '时发热'},
                {id: 'uyjyj', areaName: '时发热'},
              ]},
            ]},
            {id: '4yrtyt', areaName: '按我发'},
            {id: '457tyjiu', areaName: '按我发'}
          ]}
        ]},
        {id: '24wetr', areaName: '部门3'},
        {id: '548utyjty', areaName: '部门4'},
        {id: '457ytujgh', areaName: '部门5'}
      ]},
      {id: '326tr', areaName: '部门三', children: [
        {id: '23546tyf', areaName: '部门1'},
        {id: 'dgdgfdsf', areaName: '部门2'},
        {id: 'sdfegfdg', areaName: '部门3'},
        {id: 'etg5u6jj', areaName: '部门4'},
      ]},
      {id: '325trdy', areaName: '部门四', children: [
        {id: '35rytf', areaName: '部门1'},
      ]},
      {id: '254et', areaName: '部门五', children: [
        {id: '23433wtrdg', areaName: '部门1'},
        {id: '2354eryhgh', areaName: '部门2'},
      ]},
      {id: '324etrd', areaName: '部门六', children: [
        {id: '3tergfd', areaName: '部门1'}
      ]},
      {id: '214wre', areaName: '部门四', children: [
        {id: '35rytf', areaName: '部门1'},
        {id: '25rwetre', areaName: '部门2'},
        {id: '325tert', areaName: '部门3'},
        {id: '36tryrgf', areaName: '部门4', children: [
          {id: '1243wer', areaName: '部门3', children: [
            {id: '23re', areaName: '部门5'},
            {id: '23ter', areaName: '部门5'},
            {id: '346trtu', areaName: '部门5'},
            {id: '34trth', areaName: '部门5'}
          ]},
          {id: '235ertr', areaName: '部门3', children: [
            {id: '45ythft', areaName: '部门5'},
            {id: '346thg', areaName: '部门5'},
            {id: '45utyj', areaName: '部门5'}
          ]},
          {id: '25tr', areaName: '部门3', children: [
            {id: '235e4tr', areaName: '部门5', children: [
              {id: '346ertge', areaName: '部门6'},
              {id: '23trdhg', areaName: '部门6', children: [
                {id: 'htrh34yr', areaName: '部门7'},
                {id: '436yrthtf', areaName: '部门7'},
                {id: '43yrdgtfg', areaName: '部门7'},
                {id: '57ty', areaName: '部门7'}
              ]},
              {id: '54utyjtg', areaName: '部门6'},
              {id: '43yrtfhg', areaName: '部门6'}
            ]},
            {id: '4576tuy', areaName: '部门5'}
          ]},
          {id: '235tr', areaName: '部门3', children: [
            {id: '235yt', areaName: '部门5'},
            {id: '67iyjg', areaName: '部门5', children: [
              {id: '24etr', areaName: '部门7'},
              {id: '436yrt', areaName: '部门7'},
            ]},
            {id: '43ythg', areaName: '部门5'}
          ]},
          {id: '3476uyg', areaName: '部门3'}
        ]}
      ]},
      {id: '25124tgf4et', areaName: '部门五', children: [
        {id: '23433wtrdg', areaName: '部门1'},
        {id: '2354eryhgh', areaName: '部门2'},
      ]},
      {id: '214etry', areaName: '部门六', children: [
        {id: '3tergfd', areaName: '部门1'}
      ]},
      {id: '213retgtf', areaName: '新增部门', children: []},
    ]
    this.departmentList.push(this.treeData)
  }
}
</script>

<style lang="less">
#newTreeView {
  overflow: auto;
  padding-bottom: 20px;
  .ntv-line3 {
    width: 100%;
    height: 30px;
    margin-top: 10px;
    > div {
      width: 50%;
      height: 100%;
      border-right: 2px solid #ACB7C3;
    }
  }
  .ntv-line2 {
    margin-bottom: 10px;
    width: 100%;
    height: 33px;
    display: flex;
    .ntvl2-left {
      width: 50%;
      height: 100%;
      border-right: 2px solid #ACB7C3;
      border-top: 2px solid #ACB7C3;
    }
    .ntvl2-right {
      width: calc(100% - 50% - 2px);
      height: 100%;
      border-top: 2px solid #ACB7C3;
    }
  }
  .ntv-line1 {
    margin-top: 8px;
    position: absolute;
    top: 117px;
    left: 399px;
    > div {
      height: 30px;
      width: 2px;
      background-color: #ACB7C3;
    }
  }
  .ntvc-child {
    width: 132px;
    height: 66px;
    background: rgba(22,119,255,1);
    border-radius: 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .ntv-company {
    width: 142px;
    height: 76px;
    background: rgba(22,119,255,0.4);
    box-shadow: 0px 2px 8px 0px rgba(0,0,0,0.1);
    border-radius: 8px;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #FFFFFF;
    position: absolute;
    left: 327px;
    top: 42px;
  }
  .ntv-span1 {
    color: #4A4A4A;
    font-size: 14px;
    font-weight: 600;
  }
  .ntv-span2 {
    color: #ACB7C3;
    font-size: 12px;
  }
  .ntv-addcontent {
    width: 108px;
    height: 52px;
    background: rgba(255,255,255,1);
    box-shadow: 0px 2px 8px 0px rgba(0,0,0,0.1);
    border-radius: 4px;
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    color: #9B9B9B;
    font-size: 14px;
  }
  .ntv-content {
    width: 108px;
    height: 52px;
    background: rgba(255,255,255,1);
    box-shadow: 0px 2px 8px 0px rgba(0,0,0,0.1);
    border-radius: 4px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    cursor: pointer;
  }
  .ntv-item {
    min-width: 128px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .ntv-list {
    display: flex;
    margin-top: 114px;
    position: relative;
  }
}
</style>

你可能感兴趣的:(前端-vue)