快照效果

vue treeselect回显后面unknown vue treeview_javascript

操作效果

vue treeselect回显后面unknown vue treeview_javascript_02

实现思路

  1. 通过递归,组件套组件,无限套娃实现无限层级
  2. 各节点增加input type=‘checkbox’,实现打勾
  3. 每次点击子节点通过最上层父组件更新数据,因为涉及到循环嵌套,不能只调用本组件的父组件操作,每个组件通过属性记录最上层父组件,这样不管哪级组件都可以调用最上层父组件的方法
  4. 通过level级别来控制各节点的padding-left,显示出层次感
  5. 组件要做成2个,一个树视图XTreeView,一个树项目XTree,具体引用的是树视图,树视图循环嵌套XTree
  6. 勾选节点时,同步更新该节点的所有子节点,因此更新也涉及到了递归

树项目Xtree代码

<template>
    <div class="ui-xtree flex flex-col cannotselect">
        <div class="item border-radius-ms flex flex-row flex-center-cz padding-left-l pointer" 
             :style="{paddingLeft: data.level * 15 + 'px'}"
             :data-level="data.level" 
             :data-id="data.id" 
             :data-name="data.name">
             <div class="flex flex-center-cz" @click="expand">
                <img v-if="hasChild && !open" class="logo-20" src="@/assets/images/add.png"/>
                <img v-if="hasChild && open" class="logo-20" src="@/assets/images/dec.png"/>
                <div v-if="!hasChild" class="logo-20 show"></div>
             </div>
             <div class="flex flex-center-cz">
                <input :value="data.id" @change="change(data.id)" v-model="checked" class="checkbox" type="checkbox"/>               
             </div>
             <div class="font-s flex flex-center-cz">{{data.name}}</div>             
        </div>
        <div v-show="open" v-if="hasChild">
            <x-tree v-for="(item, index) in data.children" :data="item" :key="index" :root="root"  ></x-tree>
        </div>
    </div>
</template>
 
<script>

export default {
    name: 'XTree',   
    props: {
        data: {},
        root: {}
    },
    watch:{
        data: {
            handler(newValue) {
                this.checked = newValue.checked
            },
            immediate: true,
            deep: true   //深度监听
        }
    },
    data() {
        return {
            select: false,
            open: false,
            checked:null
        }
    },
    computed: {
        hasChild() {
            return this.data.children && this.data.children.length //判断当前子组件有没有数据 
        }
    },

    methods: {
        expand(event) {
            if(this.hasChild) {
                this.open = !this.open
            }
            event.stopPropagation();
        },
        change(id) {
            this.root.change(id, this.checked);
        } 
    }
}
</script>

视图XTreeView代码

<template>
    <div class="full-height flex flex-col border scroll padding-l">
        <div v-for="item in treeData" >
            <x-tree :data="item" :root="root"></x-tree>
        </div>
    </div>
</template>

<script>

  import XTree from '@/components/xTree/XTree' 
  export default {
    name: 'XTreeView',
    props:{
        treeData:{}
    },
    components: {
      XTree
    },
    data() {
      return {
        root: this,
      }
    },
    methods: {
        change(id, value) {
            var find = this.getItem(this.treeData, id);
            console.log("find",find)
            if (find != null) {
                find.checked = value;
                if (find.children && find.children != null && find.children.length > 0) {
                    this.updateAll(find.children, value);
                }
            }
        },
        getItem(data, id) {
            console.log("id", id)
            console.log("len",data.length);
            for (var i= 0; i< data.length; i++) {
                console.log("data", data)
                var item = data[i];
                if (item.id == id) {
                    return item ;
                } else {
                    if (item.children && item.children != null && item.children.length > 0) {
                        let result = this.getItem(item.children, id);
                        if (result != null) {
                            return result;
                        }
                    }
                }  
            }
        },
        updateAll(children, value) {
            
            children.forEach(item => {
                item.checked = value;
                if (item.children && item.children != null && item.children.length > 0) {
                    this.updateAll(item.children, value);
                }                
            });
        }            
    }
  }
</script>

调用代码

<template>
    <div class="body">
      <div class="table">
        <div class="filter font-bold">组件库(六) 多选树形组件</div>
        <div class="flex flex-col margin-top-l margin-left-l" style="height:250px;width:350px;">
            <x-tree-view  :treeData="treeData" :root="root" @change="change"></x-tree-view>
        </div>
        <div class="margin-top-xl margin-left-l btn-blue-auto pointer" style="width:350px;" @click="confirm">确定</div>
        <div class="padding-xl">选中的数据为:{{this.treeData}}</div>
      </div>
    </div>
</template>

<script>
/*
       名称:组件库(六) 多选树形组件
       功能:vue树形结构,支持多选、不限层级,子组件调用父组件方法,遍历DOM操作
       作者:唐赢   
       时间:2023-1-14
*/

  import XTreeView from '@/components/xTree/XTreeView' 
  export default {
    name: 'XTreeDemo',
    components: {
        XTreeView
    },
    data () {
      return {

        treeData : [
          {
            "id":1,
            "name": "业务管理",
            "level": 1,
            "checked": true,
            children: [
              { "id":2,
                "name": "采购订单", 
                "level": 2, 
                "checked": true,
              },
              { "id":3,
                "name": "销售订单", 
                "level": 2, 
                "checked": true,
              }
            ],
          },
          {
            "id":4,
            "name": "系统管理",
            "level": 1,
            "checked": true,
            children: [
              { "id":5,
                "name": "权限管理", 
                "level": 2, 
                "checked": true,
                children: [
                  { "id": 7,
                    "name": "用户管理", 
                    "level": 3, 
                    "checked": true,

                  },
                  { "id": 8,
                    "name": "角色管理", 
                    "level": 3, 
                    "checked": true,
                  }
                ]

              },
              { "id":6,
                "name": "数据字典", 
                "level": 2, 
                "checked": true,
              }
            ]
          }
          
        ],
        root: this,
        
      }
    },
    methods: {
        change(data) {
            this.treeData = data;
        },
        confirm() {
            console.log(this.treeData);
        }
       
      
    }
  }
</script>

  <!-- Add "scoped" attribute to limit CSS to this component only -->
  <style scoped>
  .body {
    display: flex;
    justify-content: center;
    margin-top: 73px;
    width: 100%;    
  }
  .table {
    background-color: #fff;
    width: 1080px;
    min-height: 800px;
    box-shadow: 0px 3px 6px rgba(0, 0, 0, .1);
    margin-bottom: 10px;
  }
  .filter {
    display: flex;
    height: 60px;
    align-items:center;
    background-color: #fff;
    font-size: 18px;
    justify-content: center;
    border-bottom: 1px solid rgba(0, 0, 0, .2);;
  }


  </style>