無限級樹結構參考網址:https://blog.csdn.net/qq_42205731/article/details/92059227
我需要達到的效果是有幾個並列的根節點,並且根據配置顯示單選框或多選框,同時子節點是否展開也要收到控制
我對上面網址裏面的方法進行了改造,第一個根節點不顯示出來,然後第二級放多個節點,就實現多個並列根節點
代碼地址:https://github.com/zhangxianbin1/tree-miniprogram
效果圖:
tree.wxml頁面
<view class="treeClass">
<view wx:if='{{model.id!=0}}'>
<radio data-itemid='{{ model.id }}' data-parentnodes='{{ model.parentnodes }}' data-childnodes='{{ model.childnodes }}' data-siblingsnodes='{{model.siblingsnodes}}' data-rootnode='{{model.rootNode}}' data-type='radio' wx:if='{{ model.selectshow==1&&model.parentchooseway==0 }}'
id="select{{model.index}}" bindtap='clickNode' checked="{{checked}}">
<text>{{model.text}}</text>
</radio>
<checkbox data-itemid='{{ model.id }}' data-parentnodes='{{ model.parentnodes }}' data-childnodes='{{ model.childnodes }}' data-siblingsnodes='{{model.siblingsnodes}}' data-rootnode='{{model.rootNode}}' data-type='checkbox' wx:elif='{{ model.selectshow==1&&model.parentchooseway==1 }}'
bindtap='clickNode' checked="{{checked}}">
<text id="select{{model.index}}">{{model.text}}</text>
</checkbox>
<text bindtap='tapItem' data-itemid='{{ model.id }}' wx:else>{{ model.text }}</text>
</view>
<view style='padding-left: 50rpx;' wx:if='{{ isBranch }}' hidden='{{ !open }}'>
<mytree wx:for='{{ model.nodes }}' wx:key='id' model='{{ item }}' id="component{{item.id}}"></mytree>
</view>
</view>
tree.js頁面(增加點擊方法和刷新方法)
// pages/components/mytree/mytree.js
Component({
properties: {
model: Object,
},
data: {
allopen: false,
open: true,
isBranch: false,
checked: false
},
methods: {
toggle: function(e) {
if (this.data.isBranch) {
this.setData({
//open: !this.data.open,
})
}
},
tapItem: function(e) {
var itemid = e.currentTarget.dataset.itemid;
console.log('組件裏點擊的id: ' + itemid);
this.triggerEvent('tapitem', {
itemid: itemid
}, {
bubbles: true,
composed: true
});
},
clickNode: function(e) {
var itemid = e.currentTarget.dataset.itemid;
var parentnodes = e.currentTarget.dataset.parentnodes;
var childnodes = e.currentTarget.dataset.childnodes;
var siblingsnodes = e.currentTarget.dataset.siblingsnodes;
var rootNode = e.currentTarget.dataset.rootnode;
var type = e.currentTarget.dataset.type;
console.log(e.currentTarget.dataset);
this.triggerEvent('clickNode', {
parentnodes: parentnodes,
itemid: itemid,
siblingsnodes: siblingsnodes,
checked: !this.data.checked,
open: !this.data.open,
childnodes: childnodes,
rootNode: rootNode,
type: type
}, {
bubbles: true,
composed: true
})
},
reInit: function() {
this.setData({
isBranch: Boolean(this.data.model.nodes && this.data.model.nodes.length),
open: Boolean(this.data.model.open),
checked: Boolean(this.data.model.checked)
});
for (let i = 0; i < this.data.model.nodes.length; i++) {
this.selectComponent('#component' + this.data.model.nodes[i].id).reInit();
}
//this.triggerEvent('reInit', { }, { bubbles: true, composed: true })
},
},
ready: function(e) {
this.setData({
isBranch: Boolean(this.data.model.nodes && this.data.model.nodes.length),
open: !Boolean(this.data.model.selectshow == 1 && this.data.model.childshow != 0),
checked: Boolean(this.data.model.checked)
});
},
})
tree.wxss頁面(稍作改變)
.treeClass
{
line-height: 60rpx;
}
tree.json頁面(未做改動)
{
"component": true,
"usingComponents": {
"mytree": "../tree/tree"
}
}
Index.WXML頁面
<!--index.wxml-->
<view class="container">
<mytree model='{{ treeData }}' bind:clickNode='clickNode' id="myComponent"></mytree>
</view>
<view style="text-align:center">選中值爲{{objectId}}</view>
Index.json頁面
{
"usingComponents": {
"mytree": "../tree/tree"
}
}
Index.js頁面
//index.js
//獲取應用實例
const app = getApp()
//treeList是原始數據,應該從接口中獲取的,在這裏就初始化好數據
var treeList = [ {
CHILDSHOW: "0",
CHOOSEWAY: "1",
MATOBJID: "6",
OBJINDEX: "1",
OBJNAME: "默認情形",
OBJPARENT: "0",
SELECTSHOW: "1"
}, {
CHILDSHOW: "0",
CHOOSEWAY: "1",
MATOBJID: "90",
OBJINDEX: "3",
OBJNAME: "測試對象",
OBJPARENT: "0",
SELECTSHOW: "1",
}, {
CHILDSHOW: "0",
CHOOSEWAY: "1",
MATOBJID: "89",
OBJINDEX: "2",
OBJNAME: "測試節點",
OBJPARENT: "1",
SELECTSHOW: "0",
}, {
CHILDSHOW: "0",
CHOOSEWAY: "0",
MATOBJID: "95",
OBJINDEX: "8",
OBJNAME: "子節點1.1",
OBJPARENT: "2",
SELECTSHOW: "1"
}, {
CHILDSHOW: "1",
CHOOSEWAY: "1",
MATOBJID: "96",
OBJINDEX: "9",
OBJNAME: "子節點1.2",
OBJPARENT: "2",
SELECTSHOW: "1"
}, {
CHILDSHOW: "1",
CHOOSEWAY: "1",
MATOBJID: "97",
OBJINDEX: "10",
OBJNAME: "子節點1.3",
OBJPARENT: "2",
SELECTSHOW: "1"
}, {
CHILDSHOW: "0",
CHOOSEWAY: "1",
MATOBJID: "91",
OBJINDEX: "4",
OBJNAME: "測試節點2",
OBJPARENT: "3",
SELECTSHOW: "1"
}, {
CHILDSHOW: "1",
CHOOSEWAY: "1",
MATOBJID: "92",
OBJINDEX: "5",
OBJNAME: "測試節點3",
OBJPARENT: "3",
SELECTSHOW: "1"
}, {
CHILDSHOW: "1",
CHOOSEWAY: "1",
MATOBJID: "93",
OBJINDEX: "6",
OBJNAME: "子節點1",
OBJPARENT: "4",
SELECTSHOW: "1"
}, {
CHILDSHOW: "1",
CHOOSEWAY: "1",
MATOBJID: "94",
OBJINDEX: "7",
OBJNAME: "子節點2",
OBJPARENT: "4",
SELECTSHOW: "1"
}]
//newTreeData用來存樹節點的數據,具體需要用到是否選中checked和是否展開open兩個字段
var newTreeData = [];
//初始化數據,刪除會影響初始化
//childshow子節點是否一直顯示,chooseway單選還是多選,selectshow子節點是否有選擇框
//下面的數據是初始數據,只用來對各個字段進行解釋
var treeData = {
text: '不顯示的根節點', //顯示文字
id: 1, //節點id
parent: 0, //父節點的index
childshow: 0, //子節點顯示方式 0是一直顯示,1是點擊父節點後子節點顯示出來
chooseway: 0, //子節點的選擇方式,0是單選 1是多選
index: 1, //父級和子級的關聯字段
selectshow: 0, //是否顯示選擇框,0不顯示,1顯示
parentchooseway: 0, //父級的子節點選擇方式,用來控制當前節點的展開方式
parentnodes: '', //所有父節點集合,一直到第二級根節點,第一級根節點不需要,所以第二級的節點作爲根節點
childnodes: '', //所有子節點集合
siblingsnodes: '', //同級節點集合
checked: false, //是否選中
open: true, //子節點列表是否展開
rootNode: 0, //根節點的index,指第二級根節點
nodes: [{
text: '根節點1',
id: 2,
parent: 1,
childshow: 0,
chooseway: 0,
index: 2,
selectshow: 0,
parentchooseway: 0,
parentnodes: '1',
childnodes: '',
siblingsnodes: '',
checked: false,
open: true,
rootNode: 2
}]
}
Page({
data: {
objectId: "", //獲取選中值
treeData: treeData, //樹數據
},
onLoad: function() {
//需要清空樹節點數據,不然會出錯
newTreeData = [];
treeData = {
id: 0,
childshow: 1,
chooseway: 0,
index: 0,
selectshow: 0,
checked: false,
open: true,
nodes: this.GetData(0, 0, treeList, "", 0, false, true, "", "", 0, "")
};
this.setData({
treeData: treeData
})
},
clickNode: function(e) {
var treeData = {
id: 0,
childshow: 1,
chooseway: 0,
index: 0,
selectshow: 0,
checked: false,
open: true,
nodes: this.GetData(0, 0, treeList, e.detail.siblingsnodes, e.detail.itemid, e.detail.checked, e.detail.open, e.detail.parentnodes, e.detail.childnodes, e.detail.rootNode, e.detail.type)
};
var checkedObjID = this.getCheckedObject();
this.setData({
isShowObject: true,
treeData: treeData,
objectId: checkedObjID
})
this.selectComponent('#myComponent').reInit();
},
//獲取被選中的情形ID集合
getCheckedObject() {
var str = "";
for (var i in newTreeData) {
//當被選中且有選擇框時
if (newTreeData[i].checked && newTreeData[i].selectshow == 1) {
str += str == "" ? newTreeData[i].id : "," + newTreeData[i].id
}
}
return str;
},
//根據菜單主鍵id獲取下級菜單
//id:菜單主鍵id
//arry:菜單數組信息
GetParentArry(id, arry) {
var newArry = new Array();
for (var i in arry) {
if (arry[i].OBJPARENT == id) newArry.push(arry[i]);
}
return newArry;
},
//獲取父級字符串
GetParentStr(id, str, arry) {
for (var i in arry) {
if (arry[i].OBJINDEX == id && arry[i].OBJPARENT != 0) {
var objparent = arry[i].OBJPARENT;
str += str == "" ? arry[i].OBJPARENT : "," + arry[i].OBJPARENT
str = this.GetParentStr(objparent, str, arry);
break;
}
}
return str;
},
//獲取第二級根節點ID
GetRootNode(id, arry) {
var str = "";
for (var i in arry) {
if (arry[i].OBJINDEX == id) {
if (arry[i].OBJPARENT == 0) {
str = arry[i].OBJINDEX
} else {
str = this.GetRootNode(arry[i].OBJPARENT, arry);
}
break;
}
}
return str;
},
//獲取子級字符串
//arr是需要查找子級的參數集合
//str是子級字符串
//tempstr是存放臨時字符串的,用來判斷每個參數是否有子級
GetChildStr(arr, str, tempstr, arry) {
for (var j in arr) {
tempstr = ""; //每個參數進行判斷子級時把該值設爲空
for (var i in arry) {
if (arry[i].OBJPARENT == arr[j]) {
str += str == "" ? arry[i].OBJINDEX : "," + arry[i].OBJINDEX;
tempstr += tempstr == "" ? arry[i].OBJINDEX : "," + arry[i].OBJINDEX;
}
}
if (tempstr != "") {
str = this.GetChildStr(tempstr.split(','), str, "", arry);
}
}
return str;
},
//id:index
//arry:節點數組
//parentchooseway 父節點的子節點顯示方式
//siblings 被選中節點的同級元素index集合
//curid 被選中節點的id
//curchecked 當前節點應該要顯示的是否選中狀態
//curopen 當前節點應該要顯示的子節點是否展開狀態
//parentnodes 當前節點的父級元素index集合,先查它的父級,再查父級的父級,直到根節點。這些父級組成的字符串
//childnodes 當前節點的所有子級元素index集合
//rootNode 當前節點的根節點的index,實際上是第二級根節點
//type 選中節點的選擇框類型
GetData(id, parentchooseway, arry, siblings, curid, curchecked, curopen, parentnodes, childnodes, rootNode, type) {
var nodeList = [];
var childArry = this.GetParentArry(id, arry);
if (childArry.length > 0) {
let siblingsNodes = "";
//獲取此父級下的所有節點
for (var i in childArry) {
siblingsNodes += siblingsNodes == "" ? childArry[i].OBJINDEX : "," + childArry[i].OBJINDEX;
}
//循環所有子節點
for (var i in childArry) {
//當前節點的根節點
var currootNode = this.GetRootNode(childArry[i].OBJINDEX, arry);
var childList = [];
//遞歸查找子節點集合
childList = this.GetData(childArry[i].OBJINDEX, childArry[i].CHOOSEWAY, arry, siblings, curid, curchecked, curopen, parentnodes, childnodes, rootNode, type);
var tempStr = "";
//獲取同一級別節點,處於同一個父級下
if (siblingsNodes != "") {
tempStr = ("," + siblingsNodes + ",").replace("," + childArry[i].OBJINDEX + ",", ",");
if (tempStr != "") {
//去掉首尾逗號
tempStr = tempStr.replace(/^,+/, "").replace(/,+$/, "")
}
}
let objindex = childArry[i].OBJINDEX;
let parentstr = this.GetParentStr(objindex, "", arry);
let childstr = this.GetChildStr([objindex], "", "", arry);
let tempNode = {};
//用來獲取這個數據原先的數據
if (newTreeData.length == arry.length) {
for (var k in newTreeData) {
if (newTreeData[k].index == childArry[i].OBJINDEX) {
tempNode = newTreeData[k];
}
}
}
//如果該節點不是選中節點的父節點,或者原先無樹數據集合,或者當前點擊後是選中,這些情況下需要走多個判斷來確定樹節點的選中及展開情況
//正常情況下newTreeData和arry的長度是一致的,如果不一致,是因爲初次加載樹時newTreeData爲空
if (("," + parentnodes + ",").indexOf("," + childArry[i].OBJINDEX + ",") == -1 || newTreeData.length < arry.length || curchecked == true) {
let check = false;
//如果這個是選中的節點就用返回回來的值
if (curid == childArry[i].MATOBJID) {
check = curchecked
} else if (("," + parentnodes + ",").indexOf("," + childArry[i].OBJINDEX + ",") > -1 && curchecked == true) {
//如果該節點是選中節點的父級,且當前節點是選中,那麼父級也都是選中
check = true;
} else if (("," + childnodes + ",").indexOf("," + childArry[i].OBJINDEX + ",") > -1 && curchecked == false) {
//如果該節點是選中節點的子級,且當前節點是未選中,那麼子級都是不選中
check = false;
} else if ((type == "checkbox" || (type == "radio" && ("," + siblings + ",").indexOf("," + childArry[i].OBJINDEX + ",") == -1)) && currootNode == rootNode) //tempNode.selectshow == 1 && tempNode.parentchooseway == 1
{
//如果勾選的是多選框,並且處於同一個根節點下就還是用原先的選中狀態
//如果勾選的是單選框,且不處於同級,並且處於同一個根節點下就還是用原先的選中狀態
check = tempNode.checked;
}
let isopen = true;
//如果有選擇框,並且子節點不是一直顯示,再加上未選中那麼不展開,當check爲false時才進方法
if (childArry[i].SELECTSHOW == 1 && childArry[i].CHILDSHOW != 0 && !check) {
isopen = false;
}
if (curid == childArry[i].MATOBJID && childArry[i].SELECTSHOW == 1 && childArry[i].CHILDSHOW != 0) {
//如果當前節點是選中的節點,並且子節點的顯示方式是點擊顯示(1),那麼值取傳過來的值
isopen = curopen
}
//nodeList存節點的所有數據且和子節點有層級關係
nodeList.push({
text: childArry[i].OBJNAME,
id: childArry[i].MATOBJID,
parent: childArry[i].OBJPARENT,
childshow: childArry[i].CHILDSHOW,
chooseway: childArry[i].CHOOSEWAY,
index: childArry[i].OBJINDEX,
selectshow: childArry[i].SELECTSHOW,
parentchooseway: parentchooseway,
parentnodes: parentstr,
childnodes: childstr,
siblingsnodes: tempStr,
checked: check,
open: isopen,
rootNode: currootNode,
nodes: childList
});
//如果是初次加載的話就把樹節點的一些基本信息存儲在newTreeData中
//如果不是初次加載的話就把樹節點的信息修改下就行,一般只需要修改是否展開和是否選中兩個字段
if (newTreeData.length < arry.length) {
newTreeData.push({
text: childArry[i].OBJNAME,
id: childArry[i].MATOBJID,
parent: childArry[i].OBJPARENT,
childshow: childArry[i].CHILDSHOW,
chooseway: childArry[i].CHOOSEWAY,
index: childArry[i].OBJINDEX,
selectshow: childArry[i].SELECTSHOW,
parentchooseway: parentchooseway,
checked: check,
open: isopen,
rootNode: currootNode
})
} else {
for (var num in newTreeData) {
if (newTreeData[num].index == childArry[i].OBJINDEX) {
newTreeData[num].checked = check;
newTreeData[num].open = isopen;
break;
}
}
}
} else {
//這種是上面三種情況都不是的時候,這時候這個節點就用它原本的數據就行,不需要改變
if (tempNode.index == childArry[i].OBJINDEX) {
nodeList.push({
text: tempNode.text,
id: tempNode.id,
parent: tempNode.parent,
childshow: tempNode.childshow,
chooseway: tempNode.chooseway,
index: tempNode.index,
selectshow: tempNode.selectshow,
parentchooseway: tempNode.parentchooseway,
parentnodes: parentstr,
childnodes: childstr,
siblingsnodes: tempStr,
checked: tempNode.checked,
open: tempNode.open,
nodes: childList,
rootNode: tempNode.rootNode
});
}
}
}
} else {
nodeList = [];
}
return nodeList;
},
})