vue实现element可选择树形控件

854 阅读2分钟

仿element实现可选择的树形控件

最终界面

组件套组件的方式实现循环嵌套

html:

<div class="box">
    <Tree :data="list" />
</div>

js:

import Tree from '@/components/tree.vue'//树形控件组件
export default {
  name: 'treet',
  data() {
    return {
    //循环列表
      list: [
        {
          id: 1,
          label: '一级 1',
          children: [
            {
              id: 4,
              label: '二级 1-1',
              children: [
                {
                  id: 9,
                  label: '三级 1-1-1'
                },
                {
                  id: 10,
                  label: '三级 1-1-2'
                }
              ]
            },
            {
              id: 5,
              label: '二级 1-2',
              children: [
                {
                  id: 9,
                  label: '三级 1-2-1'
                },
                {
                  id: 10,
                  label: '三级 1-2-2'
                }
              ]
            }
          ]
        },
        {
          id: 2,
          label: '一级 2',
          children: [
            {
              id: 5,
              label: '二级 2-1',
              children: [
                {
                  id: 11,
                  label: '三级 2-1-1'
                },
                {
                  id: 12,
                  label: '三级 2-1-2'
                }
              ]
            },
            {
              id: 6,
              label: '二级 2-2'
            }
          ]
        },
        {
          id: 3,
          label: '一级 3',
          children: [
            {
              id: 7,
              label: '二级 3-1'
            },
            {
              id: 8,
              label: '二级 3-2'
            }
          ]
        }
      ]
    }
  },
  components: {
    Tree
  },
  mounted() {},
  methods: {}
}

css:

.box{
    padding-top: 20px;
}

以上是引入树形控件的文件内容


以下是树形控件本身

html:

 <div>
    <div v-for="item in data" :key="item.id">
      <div class="flex">
        <a v-if='item.children' :class="item.open?'transicon':''" @click="open(item)"></a>
        <button @click="select(item)" :class="item.select=='1'?'active':item.select=='2'?'actives':''"></button>
        <div>{{item.label}}</div>
      </div>
      <div class="children" v-show="item.children&&item.open">
      	<!-- 就是这个标签名 -->
        <tree :data='item.children' />
      </div>
    </div>
  </div>

这里划重点呐 集美们!!html引入的组件是本身这个vue文件!所以!vue文件的name要和引入组件的标签名保持一致啊,要不报错了可别怪我没提醒你!!

js:

export default {
  name: 'tree',//就是这个name
  props: {
    data: {
      type: Array //上边那个父组件传过来的循环列表
    }
  },
  data() {
    return {}
  },
  mounted() {},
  computed: {},
  methods: {
    select(item) {
    //==1是全选了显示对号 ==2是没全选显示横岗 剩下的就是没选的状态
      if (item.select == '1' || item.select == '2') {
        this.$set(item, 'select', '0')
      } else {
        this.$set(item, 'select', '1')
      }
      this.forLiat(item)//这个是你点击了的框的孩子们要做的事
      this.parentList(this.$parent)//这是他的祖宗们要做的
    },
    parentList(item) {
      if (!item.data) return//如果没上级了 那就啥也不干
      if (item.$children) {
        item.$children.map((element, index) => {
          let a = 0//选中状态有几个
          let b = 0//没被选择的有几个
          if (element.data) {
            element.data.map(elements => {
              if (elements.select == '1') {
                a++//子集有被选中的就++
              }
              if (elements.select != '1' && elements.select != '2') {
                b++//同理
              }
            })
            if (element.data.length == a) {//子集全部被选中了 这个父级也标记为选中状态
              this.$set(item.data[index], 'select', '1')
            } else if (element.data.length == b) {//子集全部没被选 这个父级被标记为没选状态
              this.$set(item.data[index], 'select', '0')
            } else {//啥也不是的就是横岗状态
              this.$set(item.data[index], 'select', '2')
            }
          }
        })
        this.parentList(item.$parent)//一直找上级 直到没上级了
      }
    },
    forLiat(item) {//item的子元素继承父元素的选中状态
      if (item.children) {
        item.children.forEach(element => {
          this.$set(element, 'select', item.select)
          if (element.children) {
            this.forLiat(element)//反复循环直到没子元素了
          }
        })
      }
    },
    open(item) {
      this.$set(item, 'open', !item.open)
    }
  }
}

css:

.children {
  padding-left: 15px;
}
.childrenhide {
  display: none;
}
.flex {
  display: flex;
  align-items: center;
  padding-left: 20px;
  position: relative;
  margin: 5px 0;
}
.flex > a {
  position: absolute;
  left: 0px;
  width: 0;
  height: 0;
  border-left: 6px solid black;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  margin-left: 10px;
  margin-right: 10px;
  transform: rotate(0deg);
  transition: transform 0.3s ease-in-out;
}
.transicon {
  transform: rotate(90deg) !important;
}
button {
  margin: 0 5px;
  outline: 0;
  line-height: 1;
  display: inline-block;
  position: relative;
  border: 2px solid #212121;
  border-radius: 2px;
  width: 16px;
  height: 16px;
  background-color: #fff;
  z-index: 1;
  -webkit-transition: border-color 0.15s ease-in, background-color 0.15s ease-in;
  transition: border-color 0.15s ease-in, background-color 0.15s ease-in;
}
/* 对号 */
.active {
  background-color: #2196f3;
  border-color: #2196f3;
}
.active::after {
  -webkit-box-sizing: content-box;
  box-sizing: content-box;
  content: '';
  border: 2px solid #fff;
  border-left: 0;
  border-top: 0;
  height: 6px;
  left: 4px;
  position: absolute;
  top: 1px;
  width: 3px;
  -webkit-transition: -webkit-transform 0.15s
    cubic-bezier(0.71, -0.46, 0.88, 0.6) 0.05s;
  transition: -webkit-transform 0.15s cubic-bezier(0.71, -0.46, 0.88, 0.6) 0.05s;
  transition: transform 0.15s cubic-bezier(0.71, -0.46, 0.88, 0.6) 0.05s;
  transition: transform 0.15s cubic-bezier(0.71, -0.46, 0.88, 0.6) 0.05s,
    -webkit-transform 0.15s cubic-bezier(0.71, -0.46, 0.88, 0.6) 0.05s;
  -webkit-transform-origin: center;
  transform-origin: center;
  -webkit-transform: rotate(45deg) scaleY(1);
  transform: rotate(45deg) scaleY(1);
}
/* 横杠 */
.actives {
  background-color: #2196f3;
  border-color: #2196f3;
}
.actives::before {
  content: '';
  position: absolute;
  display: block;
  border-top: 2px solid #fff;
  left: 3px;
  right: 3px;
  top: 50%;
  -webkit-transform: translateY(-1px);
  transform: translateY(-1px);
}