老规矩,先贴图
为什么要特意强调此图标呢?
因为该图标为中间状态。此处是我后期要优化的地方,在下面的代码中还未实现。我有了初步的思考。
我的代码中设定(以网络部为例)0 该部门成员全部未选中
1 该部门成员全部选中
-1 该部门成员至少选中一人切少于部门总人员数
好了,下面我们来实现树形结构的全选和零选中状态
index.wxml文件
树形组件主要用的就是递归。所以上面组件中我在组件中又调用组件自身。通过depth来判断层级关系,来实现数据的缩进。treeList为页面中传进来的人员数据,treeArrary为我清洗数据之后传过来的初始的人员选中状态的数组
index.json*
{ "component": true, "usingComponenuts": { "tree-select": "/components/tree-select/index" } }
此处要在usingComponenuts中引入组件自身
index.js
/** * 组件的属性列表 */ properties: { treeList: { type: Array, value: [] }, depth: { type: Number, value: 0 }, treeArray: { type: Array, value: [] } },
/** * 组件的初始数据 */ data: { realTreeMap: new Map(), },
因为小程序页面中不能直接传递map对象给组件,所以我在组件中定义了map对象。他的主要作用是用来修正人员的真实选中状态。
ready(){ const { treeList, treeArray, } = this.properties; this.setData({ treeList, treeArray, }) },
此处,获取页面中传递过来的数据
// 获取当前真实状态键值Map结构 initRealTreeMap() { // 获取真实状态键值列表 const { treeArray} = this.data // 生成真实状态键值Map结构 var mRealTreeMap = new Map() treeArray.forEach(item => { mRealTreeMap.set(item.key, item.value); }) // 给组件内部变量赋值真实状态Map结构 this.setData({ realTreeMap: mRealTreeMap }) },
这里有一个要注意的就是,你点击某一项数据时,只能获取到与他同级的所有数据。比如:点击网络部,你拿到的数据就是 杨xx,马xx,营销部,网络部这四项数据
handleClick(e){ // 获取当前真实状态键值Map结构 this.initRealTreeMap() // 获取当前的树形结构列表,真实状态键值列表,真实状态键值Map结构 const { treeList, treeArray, realTreeMap } = this.data // 获取点击项id及其前一个状态 const t = this.selectAllComponents('#treeItem'); const { id, isCheck } = e.currentTarget.dataset // 根据点击项id获取点击项及子节点id集合 const idSet = new Set() this.getData(treeList, id, idSet) // 修改TreeList点击项及其子节点选中状态,但是,平级别的其他项及子项还是原始数据 // this.setStatus(treeList, idSet, !isCheck) this.setStatus(treeList, idSet, !isCheck) // 修正TreeList除点击项及其子节点之外的其他平级及子节点项状态为真实状态 this.repairTreeList(id, treeList) treeArray.forEach(item => { item.value = realTreeMap.get(item.key) }) // 赋值给组件内部三大核心变量,树形结构列表,真实状态键值列表,真实状态键值Map结构 this.setData({ treeList, treeArray, realTreeMap }) // 触发向上通信传递事件,向父组件传递变量,主要为TreeList和TreeArray this.triggerEvent('handleClick', { id, isCheck, treeList, treeArray}); },
因为treeList 所传递的数据为所选中的部门的平级数据,所以每次点击后之前操作的数据就会被重置为初始状态。所以用 repairTreeList来修正数据的真正状态
// 【递归函数】由于TreeList递归传递渲染,导致平级非点击项数据为原始状态,此方法用于修正TreeList数据状态为真实状态,真实状态保存在treeArray及RealTreeMap中 repairTreeList(id, treeList) { treeList.forEach(item => { item.isCheck = this.data.realTreeMap.get(item.deptId) if(this.hasValidChildren(item)) { this.repairTreeList(id, item.children) } }) },
// 父组件向子组件传值 treeClick(e){ // 接收子组件向上传递的变量 const { id, isCheck, treeList, treeArray } = e.detail // 设置真实状态键值列表 this.setData({ treeArray }) // 子组件内嵌套组件,循环向上触发通信传递事件,向父组件传递变量,主要为TreeArray this.triggerEvent('handleClick', { id, isCheck, treeList, treeArray }); },
setStatus方法 将getData()方法返回的ids集合的id设为选中状态
// 【递归函数】切换选中状态 setStatus(dataTree, idSet, isCheck) { for (let i = 0; i < dataTree.length; i += 1) { // 遍历节点列表,若切换集合包含该节点,则切换选中状态 if(idSet.has(dataTree[i].deptId)) { // 修正选中项状态 this.resetCheckStatus(dataTree[i], isCheck) // 若当前节点的子节点列表不为空,则遍历子节点 if (this.hasValidChildren(dataTree[i])) { let children = dataTree[i].children children.forEach( item => { // 若切换集合包含该节点,则切换子节点选中状态 if(idSet.has(item.deptId)) { // 修正子节点选中状态 this.resetCheckStatus(item, isCheck) } // 若子节点的子节点列表不为空,则递归调用切换选中状态 if(this.hasValidChildren(item)) { this.setStatus(children, idSet, isCheck) } }) } } } },
// 修正选中项状态 resetCheckStatus(item, isCheck) { // 修改TreeList里的数据,用于渲染 item.isCheck = isCheck // 修改RealTreeMap里的数据,保存真实状态 this.data.realTreeMap.set(item.deptId, isCheck) // 优化后放到修正RepairTreeList完成之后,防止重复遍历造成性能损耗 修改treeArray里的数据,保存真实状态,用于组件间传值 // treeArray.forEach(i => { // i.value = this.data.realTreeMap.get(i.key) // }) },
getData方法实现的逻辑是当部门被选择时,他下面的部员都被选中,部门被取消选中时,部员也取消选中,并返回选中的人员的id集合
// 【递归函数】获取点击项及其子节点项id集合,即切换集合 getData(dataTree, id, idSet) { // 由于dataTree为id项的同级别列表,因此遍历同级别列表,匹配其中id项及其子节点 for (let i = 0; i < dataTree.length; i += 1) { if (dataTree[i].deptId === id) { // id匹配,加入切换集合 idSet.add(id) if (this.hasValidChildren(dataTree[i])) { // 存在子节点列表 let children = dataTree[i].children // 遍历其子节点列表,将其子节点加入切换集合 children.forEach(item=> { idSet.add(item.deptId) // 子节点存在子节点列表,则递归调用函数,获取其子节点的子节点列表及其包含项 if(this.hasValidChildren(item)) { // 注意,此处传递item及其同级别节点列表,而不是传递其子节点列表 this.getData(children, item.deptId, idSet); } }) } } } return idSet },
此方法用来判断数据的children不为空
// 判断对象是否存在有效的子节点列表 hasValidChildren(item) { return item.children != undefined && item.children.length > 0 },
下面为引入组件的页面中的代码
wxml页面
js页面
// 将树形列表结构转化为一级列表结构,传递进入组件中,便于遍历、比较和赋值 getTreeKeyValueArray(data, mTreeArray) { data.forEach(item => { mTreeArray.unshift({ 'key': item.deptId, 'value': item.isCheck }) // 若存在子节点列表,则递归处理数据 if(this.hasValidChildren(item)) { this.getTreeKeyValueArray(item.children, mTreeArray) } }) },
treeClick(e){ // 接收子组件向上传递的变量 const { id, isCheck, treeList, treeArray } = e.detail // 更新设置树形结构列表及真实状态键值列表 this.setData({ treeList, treeArray }) this.getCheckedIds(treeArray) },
到此,这个组件就全部完成了。
发表评论 取消回复