小程序无限级树结构菜单扩展
程序员文章站
2022-07-02 22:38:10
...
无限级树结构参考网址: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;
},
})
Index.wxss无内容
上一篇: React脚手架
下一篇: React学习-脚手架中组件和样式的使用