vue2实现一个树型控件(支持展开树与checkbox勾选)

vue2实现一个树型控件(支持展开树与checkbox勾选)

TreeItem.vue

<template>
  <div class="tree-item">
    <span @click="toggleExpanded" class="icon" v-show="treeNode && treeNode.children && treeNode.children.length">
      <span
        class="triangle"
        :class="[ expanded ? 'triangle_down' : 'triangle_up']"
      ></span>
    </span>
    <span class="icon-font icon-kaiwenjianjia-shense icon-wenjianjia"></span>
    <span @click="toggleExpanded">{
    
    {
    
     treeNode.deptName }}</span>
    <input class="check-item check-style" type="checkbox" v-model="treeNode.checked" @change="handleChange(treeNode)">
    <div class="children" v-show="expanded">
      <TreeItem v-for="childNode in treeNode.children" :key="childNode.id" :tree-node="childNode" @checkItem="handleChange"></TreeItem>
    </div>
  </div>
</template>

<script>
export default {
    
    
  name: 'TreeItem',
  props: {
    
    
    // 每一项的节点数据
    treeNode: {
    
    
      type: Object,
      required: true
    }
  },
  data() {
    
    
    return {
    
    
      // 是否展开
      expanded: false,
    };
  },
  methods: {
    
    
    toggleExpanded() {
    
    
      this.expanded = !this.expanded;
    },
    handleChange(item) {
    
    
      console.log('handleChange',item, "treeNode",this.treeNode);
      this.setChecked(item,item.checked);
      this.$emit('checkItem',item)
    },
    // 递归 当父集选中或者取消 联动子集
    setChecked(node, checked) {
    
    
      node.checked = checked;
      if (node.children && node.children.length > 0) {
    
    
        for (let child of node.children) {
    
    
          this.setChecked(child, checked);
        }
      }
    }
  }
};
</script>

<style lang="less" scoped>
.tree-item {
    
    
  position: relative;
  font-size: 14px;
  .check-item {
    
    
    position: absolute;
    top: 10px;
    right: 4px;
    z-index: 111;
    cursor: pointer;
  }
}
.icon {
    
    
  width: 16px;
  display: inline-block;
  margin-right: 4px;
  line-height: 20px;
  cursor: pointer;
}
.icon-wenjianjia {
    
    
  color: #ccc;
  margin-right: 6px;
}
.children {
    
    
  margin-left: 20px;
}
input[type="checkbox"] {
    
    
  /* 未选中时的样式 */
  appearance: none;
  border: 1px solid transparent;
  width: 14px;
  height: 14px;
  display: inline-block;
  position: relative;
  vertical-align: middle;
  cursor: pointer;
  background-color: #eee;
}
 /* 选中时的样式 */
input[type="checkbox"]:checked {
    
    
  background-color: #1bc5bd;
}
  /* ✔图标 */
input[type="checkbox"]:checked:after {
    
    
  content: "✔";
  position: absolute;
  left: 1px;
  top: -11px;
  font-size: 12px;
  color: #fff;
}
.triangle {
    
    
  position: relative;
  top: -4px;
  transition: 0.5s;
}
.triangle_up {
    
    
  display: inline-block;
  margin: 0px;
  width: 0px;
  height: 0px;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-bottom: 4px solid #ccc;
}
.triangle_down {
    
    
  display: inline-block;
  margin: 0px;
  width: 0px;
  height: 0px;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 4px solid #ccc;
}
</style>

Tree.vue

<template>
  <div class="select-tree-com">
    <TreeItem
     class="tree-item" v-for="treeNode in treeData" :key="treeNode.id" :tree-node="treeNode"
    @checkItem="checkItem"
  ></TreeItem>
  </div>
</template>

<script>
import TreeItem from "./TreeItem"
export default {
    
    
  name:'SelectTreeCom',
  components:{
    
    
    TreeItem
  },
  props: {
    
    
    // 结构数据
    lists: {
    
    
      type: Array,
      default () {
    
    
        return []
      }
    },
    // 是否开启checkbox
    checkbox: {
    
    
      type: Boolean,
      default: false
    },
  },
  data() {
    
    
    return {
    
    
      treeData: [
        {
    
    
          id: 1,
          name: 'Node 1',
          deptCode:1,
          deptName:'Node-1',
          checked: false,
          children: [
            {
    
    
              id: 11,
              deptCode:11,
              deptName:'Node-11',
              parentId: 1,
              name: 'Node 11',
              checked: false,
              children: [
                {
    
    
                  id: 111,
                  deptName:'Node-111',
                  deptCode:111,
                  parentId: 11,
                  name: 'Node 111',
                  checked: false,
                  children: [
                    {
    
    
                      id: 1111,
                      deptName:'Node-1111',
                      deptCode:1111,
                      parentId: 111,
                      name: 'Node 1111',
                      checked: false,
                      children: []
                    },
                    {
    
    
                      id: 1112,
                      deptName:'Node-1112',
                      deptCode:1112,
                      parentId: 111,
                      name: 'Node 1112',
                      checked: false,
                      children: []
                    }
                  ]
                },
                {
    
    
                  id: 112,
                  deptName:'Node-112',
                  deptCode:112,
                  parentId: 11,
                  name: 'Node 112',
                  checked: false,
                  children: []
                }
              ]
            },
            {
    
    
              id: 12,
              deptName:'Node-12',
              deptCode:12,
              parentId: 1,
              name: 'Node 12',
              checked: false,
              children: []
            },
            {
    
    
              id: 13,
              deptName:'Node-13',
              deptCode:13,
              parentId: 1,
              name: 'Node 13',
              checked: false,
              children: [
                {
    
    
                  id: 131,
                  deptName:'Node-131',
                  deptCode:131,
                  parentId: 13,
                  name: 'Node 131',
                  checked: false,
                  children: [
                    {
    
    
                      id: 1311,
                      deptName:'Node-1311',
                      deptCode:1311,
                      parentId: 131,
                      name: 'Node 1311',
                      checked: false,
                      children: []
                    },
                    {
    
    
                      id: 1312,
                      deptName:'Node-1312',
                      deptCode:1312,
                      parentId: 131,
                      name: 'Node 1312',
                      checked: false,
                      children: []
                    }
                  ]
                },
                {
    
    
                  id: 132,
                  deptName:'Node-132',
                  deptCode:132,
                  parentId: 13,
                  name: 'Node 132',
                  checked: false,
                  children: []
                }
              ]
            },
          ]
        },
        {
    
    
          id:2,
          deptName:'Node-2',
          deptCode:2,
          name: 'Node 2',
          checked: false,
          children: []
        }
      ],
      // treeData: [],
      // 选中的所有项 check为true
      checkList:[],
    };
  },
  watch:{
    
    
    lists:{
    
    
      handler(newV){
    
    
        console.log('selectTreeeCom组件lists',newV);
        // this.treeData = [...newV]
      },
      // immediate: true
    }
  },
  created() {
    
    
  },
  methods: {
    
    
    // 拿到当前选中的所有item数据
    checkItem(item) {
    
    
      // console.log('selectcom-checkItem',item);
      let newArr = []
      newArr = this.flattenNodes(item)
      console.log('newArr',newArr);
      // 存储选中的!
      newArr && newArr.length && newArr.forEach(item => {
    
    
        if ( item.checked ) {
    
    
          this.checkList.push(item)
        }
      });
      console.log('存储选中的-this.checkList',this.checkList);
      // 处理再一次选中时 包含之前的选项,覆盖之前的选项 check
      this.checkList && this.checkList.length && this.checkList.forEach(itemB =>{
    
    
        newArr.some(itemA => {
    
    
          if ( itemA.id === itemB.id ) {
    
    
            itemB.checked = itemA.checked
          }
        })
      })
      console.log('处理this.checkList',this.checkList);
      // 过滤掉 check为false 得到实际选中的数据
      this.checkList = this.checkList.filter(item=>{
    
    
        if(item.checked) {
    
    
          return item;
        }
      })
      // console.log('res-this.checkList',this.checkList);
      // 去重
      let uniqueArr = []
      uniqueArr = Array.from(new Set(this.checkList.map(item => item.id))).map(id => this.checkList.find(item => item.id === id));
      console.log('uniqueArr',uniqueArr);
      this.$emit('getCheckList', uniqueArr)
    },
    // 把树对象 扁平化为父+子的数据
    flattenNodes(data) {
    
    
      let nodes = [];
      // 添加当前节点到结果数组中
      nodes.push({
    
    
        id: data.id,
        name: data.name,
        checked: data.checked,
        deptCode: data.deptCode,
        deptName: data.deptName
      });
      // 遍历子级节点并递归处理
      if (data.children && data.children.length > 0) {
    
    
        for (let child of data.children) {
    
    
          nodes = nodes.concat(this.flattenNodes(child));
        }
      }
      return nodes;
    },
    // 全选
    setCheckAll(params){
    
    
      // console.log('setCheckAll',params);
      const allTreeData = this.treeToOneArr(this.treeData)
      if ( params ) {
    
    
        this.checkList = [...allTreeData]
        return this.checkList
      } else {
    
    
        this.checkList = []
        return this.checkList
      }
    },
    // 取消全选
    cancelCheckAll(){
    
    
      this.checkList = []
    },
    // tree数据 扁平化
    treeToOneArr(arr) {
    
    
      const data = JSON.parse(JSON.stringify(arr))
      const newData = []
      const hasChildren = item => {
    
    
        (item.children || (item.children = [])).map(v => {
    
    
          hasChildren(v)
        })
        delete item.children
        newData.push(item)
      }
      data.map(v => hasChildren(v))
      return newData
    },
    oneArrToTree(data) {
    
    
    // 对源数据深度克隆
      const cloneData = JSON.parse(JSON.stringify(data))
      // filter嵌套filter相当于for循环嵌套for循环
      const result = cloneData.filter(parent => {
    
    
      // 返回每一项的子级数组
        const branchArr = cloneData.filter(child => parent.parentCode === child.parentCode)

        // 若子级存在,则给子级排序;且,赋值给父级
        if (branchArr.length > 0) {
    
    
          branchArr.sort(this.compare('order'))
          parent.children = branchArr
        }
        // 返回最高的父级,即,parent_id为0,
        return parent.parentCode === '00'
      })
      // 给最高级的父级排序
      result.sort(this.compare('order'))
      return result
    },
    // 对象数组排序
    compare(property) {
    
    
      return function(a, b) {
    
    
        const value1 = a[property]
        const value2 = b[property]
        return value1 - value2// 升序,降序为value2 - value1
      }
    },
  }

};
</script>

<style lang="less" scoped>
.select-tree-com {
    
    
  padding: 10px 0;
}
.tree-item {
    
    
    line-height: 34px;
  }
</style>

效果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43845137/article/details/132026080
今日推荐