树形组件,常规思想是递归实现,主要是不知道有多少层。在react中,支持jsx写法,即HTML中加入js代码,就可以使用函数递归来生成树,而小程序里面的写法,我现在还没搞太懂,个人感觉不支持。就使用组件递归来生成树,即在组件中调用自身。
WXML代码
{{item.title}}
js代码:
const tranverse=(e)=>{
for(let i in e){
if (e[i].children) {
e[i].open = false;
tranverse(e[i].children)
}
e[i].selected=false;
}
}
Component({
properties:{
treeList:{
type:Array,
value: [{
title: 'Node1',
id:0,
children: [
{
title: 'Child Node1',
id:2,
children:[{
title:'grandChild Node1',
id:4
}]
},
{
title: 'Child Node2',
id:3
},
],
},
{
title: 'Node2',
id:1
},
]},
depth:{
type:Number,
value:0
}
},
data:{
},
ready(){
const {treeList}=this.properties;
for(let i in treeList){
if(treeList[i].children){
treeList[i].open=false;
tranverse(treeList[i].children);
}
treeList[i].selected=false;
}
this.setData({
tree:treeList
})
},
methods:{
onchange(e){
const {treeList}=this.data;
const {id}=e.currentTarget.dataset;
this.changeOpen(treeList,id);
this.triggerEvent('change',id,{bubbles:true,composed:true});
this.setData({
tree:treeList
})
},
//修改折叠状态
changeOpen(tree,id){
for(let i=0;i
if(tree[i].id===id){
tree[i].open=!tree[i].open;
}
if(tree[i].children){
this.changeOpen(tree[i].children,id);
}
}
return;
},
change(e){
const id = e.detail;
const { treeList } = this.data;
this.changeOpen(treeList, id);
this.setData({
tree: treeList
})
},
click(e){
const t = this.selectAllComponents('#treeSelect');
t.forEach(item => {
item.click(e);
})
let id='';
if(e.detail){
id = e.detail.id;
}
const { treeList } = this.data;
this.setStatus(treeList, id);
this.setData({
tree: treeList
})
},
handleClick(e){
const t = this.selectAllComponents('#treeSelect');
t.forEach(item => {
item.click(e);
})
const {id}=e.currentTarget.dataset;
const {treeList}=this.data;
const value = this.getData(treeList,id)
this.setStatus(treeList,id);
this.triggerEvent('onclick',{id,value},{composed:true,bubbles:true});
this.setData({
tree:treeList
})
},
//切换选中状态
setStatus(tree,id){
for(let i=0;i
if(tree[i].id==id){
tree[i].selected=true;
}else{
tree[i].selected=false;
}
if (tree[i].children) {
this.setStatus(tree[i].children, id);
}
}
return ;
},
//获取选中项信息
getData(tree, id) {
for (let i = 0; i < tree.length; i += 1) {
if (tree[i].id === id) {
return tree[i].title;
}
if (tree[i].children) {
this.getData(tree[i].children, id);
}
}
return '';
},
}
})
调用时:
js代码:
lclick(e) {
const t = this.selectAllComponents('#treeSelect');
t.forEach(item => {
item.click(e);
})
const {id,value} = e.detail;
const { pageData } = this.data;
if(pageData){
this.setStatus(pageData, id);
}
this.setData({
tree: pageData,
choose:{id,value}
})
},
setStatus(tree, id) {
for (let i = 0; i < tree.length; i += 1) {
if (tree[i].id == id) {
tree[i].selected = true;
} else {
tree[i].selected = false;
}
if (tree[i].children) {
this.setStatus(tree[i].children, id);
}
}
return;
},
后续将继续优化
代码讲解:
上半部分就是渲染本层的树节点,其中包含对是否是父节点的判断,父节点前会加一个展开/关闭图标,若有子节点,就调用自身渲染本层节点,depth主要用来做缩进,每加一层,后面的树就会向后移动。
接下来就是重头戏,单选是怎样实现的。首先,当选中某一节点时,会拿到对应节点的id,然后拿着id去遍历该层数据将对应id的节点的selected设为true,其余设为false,并向下传递,同样更改下层的节点为false,关键到了,怎么修改更高层的状态呢,通过this.triggerEvent()将id传到高层节点,做同样的操作,配置triggerEvent时需配置{ bubbles: true, composed: true },允许冒泡,否则数据传不到上层。然后就可以实现单选了。具体见代码。我已经说不清了,就这样吧。
组件递归很简单,也很实用。但现实是残酷的,我需要实现单选功能,本来想的很简单,通过回调将选中项的selected属性设为true,其余项的selected属性设为false即可,但是事实是由于是递归生成的,每一层之间没有关联,每层的treelist只是根节点的一部分,就会有一个问题是每一层可以实现单选,但是并不满足条件。只能另想办法。
思路很简单,但点击某个节点时,一方面向下传递,将下级的节点的selected设为false,另一方面向上传递,将父节点的其余节点selected设为false,实现整体的统一,即实现单选。具体详见代码。
其实还想过用小程序的模板即template,这样就能很容易实现单选,但是小程序不允许用template递归,就只能放弃了。