<view class="tree">
  <block a:for="{{datas}}" a:key="index">
    <view class="tree-item">
      <label class="tree-item-left">
        <am-checkbox ctrlChecked="{{item.checked}}" onChange="onCheckChange" data-item="{{item}}" />
        <view class="tree-item-left-label">{{item.name}}</view>
      </label>
      <view class="tree-item-right" onTap="dropChange" data-item="{{item}}" a:if="{{item.organizationDTOList && item.organizationDTOList.length > 0}}">
        <am-icon type="{{item.dropStatus ? 'UpOutline' : 'DownOutline'}}" color="#979797" size="x-small" />
      </view>
    </view>
    <view class="tree-child" a:if="{{item.organizationDTOList && item.organizationDTOList.length > 0 && item.dropStatus}}">
      <tree datas="{{item.organizationDTOList}}" fuData="{{fuData}}" onDropChange="{{dropChange}}" type="{{type}}"/>
    </view>
  </block>
</view>


import {
  findNodeInTree,

} from '/utils/xym';
Component({
  data: {
    isConfirm: false, // 确认是否可点击
  },
  props: {
    datas: [],
    fuData: [],
    // onCheckChange: () => {},
    onDropChange: () => { },
    type: ""
  },
  didMount() {
    this.$page.tree = this; // 通过此操作可以将组件实例挂载到所属页面实例上
  },

  didUpdate(prevProps, prevData) {
    console.log('didUpdate', this.props.datas);
  },
  didUnmount() { },
  methods: {
    // 勾选
    onCheckChange(e) {
      console.log(111, this);
      const { item } = e.target.dataset;
      const changeData = this.changeItem(item, !item.checked);
      this.$page.onCheckChange(item, changeData, this.props.type);
    },

   
    findItemInTree(datas, item) {
      const length = datas.length;
      for (let i = 0; i < length; i++) {
        if (datas[i].id === item.id) {
          datas[i] = item;
          break;
        }
        if (datas[i].organizationDTOList && datas[i].organizationDTOList.length) {
          this.findItemInTree(datas[i].organizationDTOList, item);
        }
      }
      return datas;
    },
    changeItem(item, value) { //点击自己,做全选反选
      // 通过isChecked判断是否为选中状态:1-选中;0-未选中。这个字段是后端接口返回的
      let addData=JSON.parse(JSON.stringify(this.props.fuData))

    let objItem= this.refreshAllSonNodes(item, value);// 根据当前点击层级的状态设置自己所有子级的状态
    let newarr1=this.findItemInTree(addData,objItem)
      // 个人理解:组件每次递归调用会生成一个实例,this指向这个实例,这些实例相互独立的,所以每次的this都不一样,具体可以控制台打印看下。
      let newData=  this.refreshAllParentNodes(objItem,newarr1) // 设置父级状态
      console.log('newarr1',addData,newarr1);

      return newarr1
    
    },
    refreshAllSonNodes(item, value) {
      item.checked = value;
      if (item.organizationDTOList && item.organizationDTOList.length) {
        const child = item.organizationDTOList;
        child.forEach(ele => {
          this.refreshAllSonNodes(ele, value);
        });
      }
      return item;

    },

    refreshAllParentNodes(item,allNodes) { // 点击子节点已经更新完数据,再去操作父节点 this.props.fuData
      // item.checked = value; 
      console.log('refreshAllParentNodes',item);
      let currentnode = item

       //  return 
      if (currentnode) {
         // 该状态说明当前点击层级和兄弟层级都没选中,所以父级也是未选中状态
    
      let parentItemArr = item&&this.findParents(allNodes,item.id) // 找到当前父级赋值,
      let parentItem = parentItemArr[0] // 找到当前父级赋值,
    console.log('parentItemArr',parentItemArr,parentItem);
      let brotherNodes =parentItem&& parentItem.organizationDTOList
    
        var status = false;
        var nullCount = 0;
        var fullCount = 0;

        if (parentItem) {
          brotherNodes.map((meunItem, j) => {
            if (!meunItem.checked) {
              nullCount++
            } else {
              fullCount++
            }
          })
          // if (nullCount === brotherNodes.length) { //nullCount==4
          //   // 该状态说明当前点击层级和兄弟层级都没选中,所以父级也是未选中状态
          //   status = false;
          // } else {
          //   //该状态说明当前点击层级和兄弟层级只要有一个选中,父级也是选中状态(0<nullCount<4 ||nullCount==0 可以做区分半选中和全选中)
          //   status = true;
          // }
            if (nullCount === brotherNodes.length) { //nullCount==4
            // 该状态说明当前点击层级和兄弟层级都没选中,所以父级也是未选中状态
            status = false;
          } else {
            //该状态说明当前点击层级和兄弟层级只要有一个选中,父级也是选中状态(0<nullCount<4 ||nullCount==0 可以做区分半选中和全选中)
            // status = true;
             if (nullCount==0) {
              status = true;
             }else{
              status = false;
             }
          }
          

          this.findNodeInTree(allNodes, parentItem.id, (item, index, arr) => {
            console.log('findNodeInTree',parentItem,item);
            item['checked'] = status
          })

        }
        console.log('end....',allNodes, parentItem);

        // // 递归计算父级
        this.refreshAllParentNodes(parentItem,allNodes);

        // if (parentItem&&parentItemArr.length>1) {
        //   this.refreshAllParentNodes(parentItem);
 
        // }
      }




      
    },
     findNodeInTree (data, key, callback) {
      for (let i = 0; i < data.length; i++) {
        if (data[i].id == key) {
          return callback(data[i], i, data)
        }
        if (data[i].organizationDTOList) {
         this.findNodeInTree (data[i].organizationDTOList, key, callback)
        }
      }
    },
   
  
   findParents(treeData,id){
    if(treeData.length==0) return
    for(let i=0;i<treeData.length;i++){
      if(treeData[i].id == id){
        return []
      }else{
        if(treeData[i].organizationDTOList){
          let res = this.findParents(treeData[i].organizationDTOList,id)
          if(res !== undefined){
            return res.concat(treeData[i])
          }
        }
      }
    }  
  },
   
    // 展开 / 收起
    dropChange(e) {
      console.log('onDropChange孩子', this.$page, this.props, JSON.stringify(this.props.onDropChange));
      const { item } = e.target.dataset;
      item.dropStatus = !item.dropStatus;
      // this.props.onDropChange(item,this.props.fuData)
      this.$page.onDropChange(item, this.props.fuData, this.props.type);
    },
  }
});


/* .tree {} */
.tree-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 32rpx;
}
.tree-item-left {
  display: flex;
  align-items: center;
}
.tree-item-left-label {
  margin-left: 17rpx;
}
.tree-child {
  padding-left: 52rpx;
}

// 
      <tree datas="{{otherDatas}}" fuData="{{otherDatas}}" type="2"></tree>



父级引用
import { querySchoolOrgList } from '/utils/service/xym'
import {
  findNodeInTree,

} from '/utils/xym';
const a = [
  {
    id:1,
    value:'一年级',
    checked:false,
    children:[
      {
        id:2,
        value:'一年级1班',
        checked:false,
        children:[
        {
          id:6,
          value:'一年级1班01',
          checked:true,
          children:[
            {
              id:10,value:'一年级1班001',
              checked:true
            }
          ]
        }
    ]},
    {id:3,value:'一年级2班',checked:false},
    {id:4,value:'一年级3班',checked:false},
  ]},
  {id:5,value:'二年级',checked:false,children:[
    {id:11,value:'二年级1班',checked:false}
  ]},
  {id:7,value:'三年级',checked:false,children:[
    {id:8,value:'三年级1班',checked:false}
  ]}
]

Page({
  data: {
    datasOrization: a,
    datas: [],
    otherDatas: [],
    schoolId: ''
  },
  onLoad(option) {

    this.setData({schoolId:option.schoolId},()=>{
      this.getSchoolOrgList();
    })
  
  },

  addItemStatusTree(datas) {
    const length = datas.length;
    for (let i = 0; i < length; i++) {
      datas[i].dropStatus = false;
      datas[i].checked = false;
      if (datas[i].organizationDTOList && datas[i].organizationDTOList.length) {
        this.addItemStatusTree(datas[i].organizationDTOList);
      }
    }
    // console.log(datas, 'datasdatasdatas');
    return datas;
  },
  newTree(datas,type) {
    const length = datas.length;
    for (let i = 0; i < length; i++) {
      // datas[i].dropStatus = false;
      // datas[i].checked = false;
      if(datas[i].type==type)
      if (datas[i].organizationDTOList && datas[i].organizationDTOList.length) {
        this.addItemStatusTree(datas[i].organizationDTOList);
      }
    }
    // console.log(datas, 'datasdatasdatas');
    return datas;
  },
  async getSchoolOrgList() {
    const params = {
      "schoolId": this.data.schoolId,
      "type": ''
    }
    const result = await querySchoolOrgList(params);

   console.log('result',result);
    const new1 = []; // 班级组织
    const new2 = []; // 其它组织

    result.forEach(item => {
      if (`${item.type}` === '0') {
        new1.push(item)
      } else {
        new2.push(item)
      }
    })


    const newList1 = this.addItemStatusTree(new1); // 班级组织
    const newList2 = this.addItemStatusTree(new2); // 其它组织

   

    this.setData({
      datas: newList1,
      otherDatas: newList2
    });
  },


  // 组织:勾选
  onCheckChange(item,fuData,type) { // item.check和children下的check都改变
    // console.log('2222222222222222');
    // const { datas } = this.data;
    console.log(' 点击checkobox',item,fuData,type);

    const list =fuData
    if (type==1) {
      this.setData({
        datas: JSON.parse(JSON.stringify(list)),
      });
    }
    if (type==2) {
      this.setData({
        otherDatas: JSON.parse(JSON.stringify(list)),
      });
    }
  },

  // 组织:展开 / 收起
  onDropChange(item,fuData,type) {  // item.dropStatus改变
    console.log(' 组织:展开 / 收起父亲页面',item,fuData,type);
    // const { datas } = this.data;
    const list = this.findItemInTree(fuData, item);
 
    if (type==1) {
      this.setData({
        datas: JSON.parse(JSON.stringify(list)),
      });
      
    }
    if (type==2) {
      this.setData({
        otherDatas: JSON.parse(JSON.stringify(list)),
      });
    }
  },

  // 递归datas,改变里面的值
  findItemInTree(datas, item) {
    const length = datas.length;
    for (let i = 0; i < length; i++) {
      if (datas[i].id === item.id) {
        datas[i] = item;
        break;
      }
      if (datas[i].organizationDTOList && datas[i].organizationDTOList.length) {
        this.findItemInTree(datas[i].organizationDTOList, item);
      }
    }
    return datas;
  },
  findNodeInTree (data, result=[]) {
    for (let i = 0; i < data.length; i++) {
      if (data[i].checked) {
        result.push(data[i])
      }
      if (data[i].organizationDTOList) {
       this.findNodeInTree (data[i].organizationDTOList, result)
      }
    }
  },
  // 一年级:一班、二班
  // 二年级:一班、二班
  handleSave() {
   
    const staffPositionList = [];
    let allChildrenBox = [];
    // console.log(this.data.datas, 'datasdatasdatasdatas');
    let arr = []
    let arr1 = []
    this.findNodeInTree([... this.data.datas,...this.data.otherDatas],arr)
    this.findNodeInTree([... this.data.datas],arr1)
    console.log('handleSave',arr);
   console.log(arr, 'staffPositionList');
   getApp().globalData.staffPositionList = arr;// 包括选中的班级和其他岗位
   getApp().globalData.allClass =  [...arr1];
   my.navigateBack();
    return
   [... this.data.datas,...this.data.otherDatas].forEach(item => {
    //  type=0,班级组织 type=1其他组织。暂时只取俩级 但是多级别的全选反选已经实现
      let flag = false;
      let itemChildrenBox = [];

      if (item.checked) {
        flag = true
        item.organizationDTOList.forEach(v => {
          if (v.checked) {
            itemChildrenBox.push({
              id: v.id,
              name: v.name,
            })
            if (`${ v.type}`==='0') {
              allChildrenBox.push(v) 
            }
        
          }
        })
      }

      // console.log(itemChildrenBox, 'itemChildrenBoxitemChildrenBox');
      if (flag) {
        staffPositionList.push({
          id: item.id,
          name: item.name,
          organizationDTOList: itemChildrenBox
        })
      }
    })
    

    console.log(staffPositionList,allChildrenBox, 'staffPositionList');
    getApp().globalData.staffPositionList = staffPositionList;// 包括选中的班级和其他岗位
    getApp().globalData.allClass =  [...allChildrenBox];
    my.navigateBack();
  }

});