vue+html编写组件elment-ui 的el-tree,完整版,

本文介绍了一种基于Vue的树形组件实现方案,通过优化父子级关联,实现了更灵活的业务需求处理。该组件支持子级点击时父级状态更新,提供全选与默认选择功能,并详细展示了组件的代码结构和使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可进行修改,此版本是父级关联子级,点击子级不需要选择父级的业务需求

完整关联的可在下面修改 

treeList.vue

<template>
  <div>
    <my-trees :list="list" @getResult="getResult" ref="treeMenu"></my-trees>
  </div>
</template>
<script>
let vue=this;
import myTrees from './treeMenus'
export default {
  components: {
    myTrees
  },
  props:{
    data: {
      type: Array,
      required: true
    },
    props:{
      type:Object
    }
  },
 
  data () {
    return {
      //  list: [//实例
      //   {
      //     name: '一级',checked:false,expendclick:false,id:'1',parentId:'0',
      //     cList: [
      //       { name: '二级一',checked:false,expendclick:false,id:'21',parentId:'1' },
      //       {
      //         name: '二级二',checked:false,expendclick:false,id:'22',parentId:'1',
      //         cList: [
      //           { name: '三级',checked:false,expendclick:false,id:'31',parentId:'22', cList: [{ id:'41',parentId:'31',name: '四级' ,checked:false,expendclick:false,}] }
      //         ]
      //       }
      //     ]
      //   },]
      list:[],
      isTreeRoot:true,//验证真实根组件
      checkList:[],//选中值
      checkData:[],//返回选择值
    }
  },
  mounted(){
  
  },
   watch:{
      data(n,o){ // 监听收到转换前数据
          // handler:(n,o) => {
          if(n){
            this.list=this.ruleList(JSON.parse(JSON.stringify(n)))
          
            
          }
        },
  },
  methods: {
      async getResult(val){//接收子元素的,修改父元素
            await this.searchList(this.list,val)
            await this.getChecked()
      },
      async searchList(arr,val){
       await arr.map(item=>{
          if(item.id==val){//不管子元素如何变化,只要是点击子元素就要把父级元素checked改为false
             this.$set(item,'checked',false)
            
             let code=item.cList.every(vall=>{
              return vall.checked==false&&vall.indeterminate==false
            })
            if(!code){
               this.$set(item,'indeterminate',true) //只要子级元素checked与indeterminate有true,则父级的indeterminate比为true
               if(item.parentId){//只要有父级元素就进行上级递归
                 this.searchList(this.list,item.parentId)
               }
               
            }else{
              this.$set(item,'indeterminate',false)//只要子级元素checked与indeterminate全部为false,则父级的indeterminate比为false
               if(item.parentId){//只要有父级元素就进行上级递归
                 this.searchList(this.list,item.parentId)
               }
            }
            //  if(item.parentId&&item.parentId!=='0'){//没有父级时进行上级递归
            //    this.getResult(item.parentId)
            //  }
          }else{
            if(item.cList&&item.cList.length){
              return this.searchList(item.cList,val) //下级递归
            }
          }

        })
      },
      setCheckedKeys(arr){ //设置选中
        
        let datas=JSON.parse(JSON.stringify(this.data))
        this.list=this.ruleList(datas)
        
        if(arr.length){
          arr.map(item=>{
            this.setcheckList(this.list,item[this.props.value])
          })
        
        }
        
      },
      setcheckList(arr,id){
        arr.map(item=>{
          if(item.id==id){
            
            this.$set(item,'checked',true)
            this.$refs.treeMenu.changValue(item)//将设置选中的值进行触发点击事件
          }else{
            if(item.cList&&item.cList.length){
              this.setcheckList(item.cList,id)
            }
          }
        })
      },
      async getChecked(){//获取选中元素并传递给父级组件
        this.checkList=[]
        this.checkData=[]
        await this.searchCheck(this.list) // 获取转换后选中元素
       
        this.checkList.map(item=>{
          this.seaarchData(this.data,item.id) //获取转换前选中元素
        })
       
        this.$emit('check',this.checkData)//将转换前选中元素传递给父元素
      },
      searchCheck(arr){// 获取转换后选中元素
         arr.map(item=>{
          if(item.checked){
            this.checkList.push(item)
          }else{
            if(item.cList&&item.cList.length){
             return this.searchCheck(item.cList)
            }
          }
        })
      },
      seaarchData(arr,id){//获取转换前选中元素
        arr.map(item=>{
          if(item[this.props.value]==id){
            let ids=[]
            if(this.checkData.length){
              this.checkData.map(val=>{
                return ids.push(val[this.props.value])
              })
            }
         
             if(ids.indexOf(id)==-1){
                this.checkData.push(item)
              }
            
          }else{
            if(item[this.props.children]&&item[this.props.children].length){
             return this.seaarchData(item[this.props.children],id)
            }
          }
        })
      },
      ruleList(arr){//将接受到的值转换为需要样式
        
      let arrs=arr.map(item=>{
       
          // this.$set(item,"checked",false)
          // this.$set(item,"expendclick",false)
          item.checked=false
          item.expendclick=false
          item.indeterminate=false
          if(this.props.label){//元素属性替换
              let regExp = new RegExp(this.props.label, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExp,"name"));
          }
            if(this.props.children){
              let regExps = new RegExp(this.props.children, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExps,"cList"));
          }
            if(this.props.value){
              let regExpid = new RegExp(this.props.value, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExpid,"id"));
          }
            if(this.props.parentId){
              let regExpparentId=new RegExp(this.props.parentId, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExpparentId,"parentId"));
          }
            if(this.props.disabled){
              let regExpdisabled=new RegExp(this.props.disabled, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExpdisabled,"disabled"));
          }
          if(item.cList&&item.cList.length){
            item.cList=this.ruleList(item.cList)
          }
          return item
        })
        
        return arrs
        
      }
      

  }
  
}
</script>

treeMenus.vue 

<template>
 
  <ul class="treeList">
    <li v-for="(item,index) in list" :key="index">
      <p @click="changeStatus(index,item)" :class="{'spanColor':true,'el-icon-caret-right':(!item.expendclick&&item.cList),'el-icon-caret-bottom':(item.expendclick&&item.cList)}">
          <span v-if="!(item.cList&&item.cList.length)" style="padding-left:13px;">
          </span>
         <!-- <el-checkbox v-model="item.checked" @change="changValue(item)" :disabled="item.disabled">
            </el-checkbox> -->
          <el-checkbox v-if="item.checked" v-model="item.checked" @change="changValue(item)" :disabled="item.disabled">
            </el-checkbox>
             <el-checkbox v-else :indeterminate="item.indeterminate" v-model="item.checked" @change="changValue(item)" :disabled="item.disabled"></el-checkbox>
            {{item.name}}
          </p>
      <tree-menus v-if="scopesDefault[index]" :list="item.cList"></tree-menus>
    </li>
  </ul>
 
</template>
 <style scoped lang="scss">
.treeList {
  padding-left: 20px !important;
  li{
    padding: 5px 0;
  }
  .spanColor{
    cursor: pointer;
  }
}

</style>
<script>
// import treeMenus from './treeMenu2.vue'
export default {
  name: 'treeMenus',
  props: {
    list: Array
  },
  data() {
    return {
      scopesDefault: [],
      scopes: [],
      tree: null,
    }
  },
 
  methods: {
    changeStatus(index,item) {//切换展开
      this.$set(item,'expendclick',!item.expendclick)
      if (this.scopesDefault[index] == true) {
        this.$set(this.scopesDefault, index, false)
      } else {
        
        if(this.scopes[index]){
          this.$set(this.scopesDefault, index, this.scopes[index])
        }else{
          if(this.scopes[index]===undefined){
              this.$set(this.scopesDefault, index, true)
          }else{
            this.$set(this.scopesDefault, index, false)
          }
        }
        
      }
    },
    scope() {//是否展开下一级
      this.list.forEach((item, index) => {
        this.scopesDefault[index] = false
        if ('cList' in item) {
          this.scopes[index] = true
         
        } else {
          this.scopes[index] = false
        }
      })
     
     
    },
    changValue(item){ //checkbox的选中与取消
        
        // if(!item.checked){
          this.$set(item,'indeterminate',item.checked) //切换indeterminate 状态,只负责样式控制
        // }
        console.log(item,'1111111111111111111111')
        if(item.cList&&item.cList.length){
          this.searchList(item.cList,item.checked)
        }
         this.tree.getResult(item.parentId)//在终极父组件中修改父级显示样式
    },
    searchList(arr,check){//对所有子集进行统一控制
      arr.map(item=>{
        this.$set(item,'checked',check)
        this.$set(item,'indeterminate',check)
        if(item.cList&&item.cList.length){
         return this.searchList(item.cList,check)
        }
      })
    }
  },
  created() {
    
    let parent = this.$parent
       
        while (parent && !parent.isTreeRoot) {
            parent = parent.$parent
        }
        this.tree = parent //获取终极父组件
       
  },
  mounted(){
    this.scope()
  }
}
</script>

 

 应用init.vue

</template>
       <div class="eboss-col col-flex">
                        <label for="" class="form-label">业务区域<i>*</i>:</label>
                        <div class="form-control">
                            <div style="width: 100%">
                               <el-popover ref="business" placement="bottom-start" v-model="showSelect">
                                    <div style="width: 300px; max-height: 200px; overflow-y: scroll">
                                        <treeList :data="treeData" @check="getCheck" ref="trees" :props="{
                                                    label: 'areaName',
                                                    value:'id',
                                                    parentId:'parentId',
                                                    children: 'childList' 
                                                 }" ></treeList>
                                              
                                    </div>
                                </el-popover>
                                <div class="simulation-tags" @click.stop="showSelect = true">
                                    <el-tag type="danger" size="small" closable
                                            v-for="(item, index) in tags" @close="closeHandle(item)" :key="index"
                                    >{{item.areaName}}</el-tag>
                                </div>
                                <div  v-popover:business style="height:0"></div>
                                
                                <!-- <el-popover ref="business" placement="bottom-start" v-model="showSelect">
                                    <div style="width: 220px; max-height: 200px; overflow-y: scroll">
                                        <el-tree ref="tree"
                                                 :data="treeData"
                                                 :props="{
                                                    label: 'areaName',
                                                    children: 'childList'
                                                 }"
                                                 node-key="originalId"
                                                 show-checkbox @check="checkTreeHandle"></el-tree>
                                              
                                    </div>
                                </el-popover>
                                <div class="simulation-tags" @click.stop="showSelect = true">
                                    <el-tag type="danger" size="small" closable
                                            v-for="(item, index) in tags" @close="closeHandle(item)" :key="index"
                                    >{{item.areaName}}</el-tag>
                                </div>
                                <div  v-popover:business style="height:0"></div> -->
                                
                            </div>
                        </div>
                    </div>
</template>
<script>

import treeList from 'module/newCkeckTree/treeList';
 export default {
 components:{
      treeList,
    },
    data:{
    },
    methods:{
    closeHandle (item) {
       
        this.tags = this.tags.filter(tag => tag.id != item.id)
       
        this.$refs.trees.setCheckedKeys(this.tags);
        this.$set(this.query, 'businessAreaList', this.tags.map(item => item.id))
       
      },
      getCheck(item){
        console.log(item,'huoqu')
        this.tags=item
        this.$set(this.query, 'businessAreaList', this.tags.map(item => item.id))
      },


    }
}



</script>

进行子集全选,父级默认选则

treeList.vue

<template>
  <div>
    <my-trees :list="list" @getResult="getResult" ref="treeMenu"></my-trees>
  </div>
</template>
<script>
let vue=this;
import myTrees from './treeMenus'
export default {
  components: {
    myTrees
  },
  props:{
    data: {//传入递归组件的原始数据
      type: Array,
      required: true
    },
    props:{//label,value,children,disabled,parentId(父子集关联数据)
      type:Object
    },
    awlAll:{//如果传值为true,则子集全选,默认父级选择,如果不传值或者传值为false,子集全选,父级不选择
      type:Boolean
    }
  },
 
  data () {
    return {
      //  list: [//实例
      //   {
      //     name: '一级',checked:false,expendclick:false,id:'1',parentId:'0',
      //     cList: [
      //       { name: '二级一',checked:false,expendclick:false,id:'21',parentId:'1' },
      //       {
      //         name: '二级二',checked:false,expendclick:false,id:'22',parentId:'1',
      //         cList: [
      //           { name: '三级',checked:false,expendclick:false,id:'31',parentId:'22', cList: [{ id:'41',parentId:'31',name: '四级' ,checked:false,expendclick:false,}] }
      //         ]
      //       }
      //     ]
      //   },]
      list:[],
      isTreeRoot:true,//验证真实根组件
      checkList:[],//选中值
      checkData:[],//返回选择值
    }
  },
  mounted(){
  
  },
   watch:{
      data(n,o){ // 监听收到转换前数据
          // handler:(n,o) => {
          if(n){
            this.list=this.ruleList(JSON.parse(JSON.stringify(n)))
          
            
          }
        },
  },
  methods: {
      async getResult(val){//接收子元素的,修改父元素
            await this.searchList(this.list,val)
            await this.getChecked()
      },
      async searchList(arr,val){
       await arr.map(item=>{
          if(item.id==val){//不管子元素如何变化,只要是点击子元素就要把父级元素checked改为false
            
            if(this.awlAll){
                let codes=item.cList.every(vall=>{
                  return vall.checked==true
                })
                if(codes){
                  this.$set(item,'checked',true)
                  if(item.parentId){//只要有父级元素就进行上级递归
                    this.searchList(this.list,item.parentId)
                  }
                }else{
                  this.$set(item,'checked',false)
                }
            }else{
              this.$set(item,'checked',false)
            }
           
             let code=item.cList.every(vall=>{
              return vall.checked==false&&vall.indeterminate==false
            })
            if(!code){
               this.$set(item,'indeterminate',true) //只要子级元素checked与indeterminate有true,则父级的indeterminate比为true
               if(item.parentId){//只要有父级元素就进行上级递归
                 this.searchList(this.list,item.parentId)
               }
               
            }else{
              this.$set(item,'indeterminate',false)//只要子级元素checked与indeterminate全部为false,则父级的indeterminate比为false
               if(item.parentId){//只要有父级元素就进行上级递归
                 this.searchList(this.list,item.parentId)
               }
            }
            //  if(item.parentId&&item.parentId!=='0'){//没有父级时进行上级递归
            //    this.getResult(item.parentId)
            //  }
          }else{
            if(item.cList&&item.cList.length){
              return this.searchList(item.cList,val) //下级递归
            }
          }

        })
      },
      setCheckedKeys(arr){ //设置选中
        
        let datas=JSON.parse(JSON.stringify(this.data))
        this.list=this.ruleList(datas)
        
        if(arr.length){
          arr.map(item=>{
            this.setcheckList(this.list,item[this.props.value])
          })
        
        }
        
      },
      setcheckList(arr,id){
        arr.map(item=>{
          if(item.id==id){
            
            this.$set(item,'checked',true)
            this.$refs.treeMenu.changValue(item)//将设置选中的值进行触发点击事件
          }else{
            if(item.cList&&item.cList.length){
              this.setcheckList(item.cList,id)
            }
          }
        })
      },
      async getChecked(){//获取选中元素并传递给父级组件
        this.checkList=[]
        this.checkData=[]
        await this.searchCheck(this.list) // 获取转换后选中元素
       
        this.checkList.map(item=>{
          this.seaarchData(this.data,item.id) //获取转换前选中元素
        })
       
        this.$emit('check',this.checkData)//将转换前选中元素传递给父元素
      },
      searchCheck(arr){// 获取转换后选中元素
         arr.map(item=>{
          if(item.checked){
            this.checkList.push(item)
          }else{
            if(item.cList&&item.cList.length){
             return this.searchCheck(item.cList)
            }
          }
        })
      },
      seaarchData(arr,id){//获取转换前选中元素
        arr.map(item=>{
          if(item[this.props.value]==id){
            let ids=[]
            if(this.checkData.length){
              this.checkData.map(val=>{
                return ids.push(val[this.props.value])
              })
            }
         
             if(ids.indexOf(id)==-1){
                this.checkData.push(item)
              }
            
          }else{
            if(item[this.props.children]&&item[this.props.children].length){
             return this.seaarchData(item[this.props.children],id)
            }
          }
        })
      },
      ruleList(arr){//将接受到的值转换为需要样式
        
      let arrs=arr.map(item=>{
       
          // this.$set(item,"checked",false)
          // this.$set(item,"expendclick",false)
          item.checked=false
          item.expendclick=false
          item.indeterminate=false
          if(this.props.label){//元素属性替换
              let regExp = new RegExp(this.props.label, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExp,"name"));
          }
            if(this.props.children){
              let regExps = new RegExp(this.props.children, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExps,"cList"));
          }
            if(this.props.value){
              let regExpid = new RegExp(this.props.value, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExpid,"id"));
          }
            if(this.props.parentId){
              let regExpparentId=new RegExp(this.props.parentId, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExpparentId,"parentId"));
          }
            if(this.props.disabled){
              let regExpdisabled=new RegExp(this.props.disabled, 'g');
              item=JSON.parse(JSON.stringify(item).replace(regExpdisabled,"disabled"));
          }
          if(item.cList&&item.cList.length){
            item.cList=this.ruleList(item.cList)
          }
          return item
        })
        
        return arrs
        
      }
      

  }
  
}
</script>

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值