微信小程序多层级复选框菜单
十三月tlz 人气:0一、背景
因客户需要,有一个功能是需要注册用户时选择多个【单元 - 楼层 - 设备】的绑定,谷歌了一通,没有找到想要的,无奈之举只能手写一个…
二 、效果展示
三、功能点
1、初始化时,默认展开选中的菜单
2、点击每一层父级菜单,会自动折叠其子菜单
3、选中子级节点会默认选中父级节点
4、子级节点都没选中默认取消选中父级节点
5、选中父级节点默认选中其所有子级节点
四、代码实现
这里没写组件,如果需要可以改为组件。
可能会有一个疑问为哈是 treeMenu2 不是 treeMenu1
因为 treeMenu1 写的low,treeMenu2 升级了js代码。
1、treeMenu2.js
js里面的各种事件均为递归操作,可根据加载的数据动态进行操作。
// pages/biz/treeMenu/treeMenu.js Page({ /** * 页面的初始数据 */ data: { menuTreeImgLeft: "../../../icon/f_left.png", menuTreeImgBottom: "../../../icon/f_bottom.png", menuTree: [{ "checked": false, "children": [{ "checked": false, "children": [{ "id": "1-1-1", "checked": true, "field": "1", "title": "设备1", }, { "id": "1-1-2", "checked": false, "field": "2", "title": "设备2" }], "id": "1-1", "field": "1-floor", "title": "1楼", "isHidden": true, "bindAll": false, }, { "checked": false, "children": [{ "checked": false, "field": "3", "title": "设备3" }], "id": "1-2", "field": "2-floor", "title": "2楼", "isHidden": true, }, { "checked": false, "children": [{ "id": "1-3-4", "checked": true, "field": "4", "title": "设备4" }], "id": "1-3", "field": "3-floor", "title": "3楼", "isHidden": true, }], "id": "1", "isHidden": true, "bindAll": false, "field": "1-unit", "title": "1单元" }, { "checked": false, "children": [{ "checked": false, "children": [{ "id": "2-1-1", "checked": false, "field": "5", "title": "设备5" }], "id": "2-1", "field": "1-floor", "title": "1楼", "isHidden": true, }, { "checked": false, "children": [{ "id": "2-2-1", "checked": false, "field": "6", "title": "设备6" }], "id": "2-2", "field": "2-floor", "title": "2楼", "isHidden": true, }], "bindAll": false, "isHidden": true, "field": "2-unit", "title": "2单元", "id": "2", }], deepList: [], deepListOne: [] }, /** * 生命周期函数--监听页面显示 */ onShow: function () { this.checkForChecked() }, /** * 默认选中是否展开 */ checkForChecked() { var data = this.data.menuTree // 获取所有被选中的节点 var checkedNodes = this.getDeep(data) // 获取所有选中节点的父节点 checkedNodes.forEach(element => { var tmp = this.getParentsById(data, element.id) if (tmp != undefined && tmp.length > 0) { // 最后一级选中,默认展开和选中父级菜单 tmp.forEach(element => { element.isHidden = false element.checked = true }) } }) this.setData({ menuTree: data }) }, // 递归 - 根据id获取所有父节点 getParentsById(list, id) { for (let i in list) { if (list[i].id == id) { return [list[i]] } if (list[i].children) { let node = this.getParentsById(list[i].children, id); if (node !== undefined) { return node.concat(list[i]) } } } }, // 递归 - 根据id获取当前节点对象 getNodeById(data, id, newNodes = []) { data.forEach(element => { // 匹配到节点 if (element.id === id) { newNodes.push(element) } if (element.children) { this.getNodeById(element.children, id, newNodes) } }) return newNodes; }, // 递归 - 根据id获取所有子节点,(其实就是先获取当前id的节点对象,然后取当前对象,注意这里返回的是数组) getChildrenById(data, id, newNodes = []) { var list = data.children if (list != undefined) { list.forEach(element => { newNodes.push(element) if (element.children) { this.getChildrenById(element, id, newNodes) } }) } return newNodes; }, // 递归 - 获取所有选中节点 getDeep(data, newCheckedNodes = []) { data.forEach(element => { if (element.checked) { newCheckedNodes.push(element) } if (element.children) { this.getDeep(element.children, newCheckedNodes) } }) return newCheckedNodes }, // 递归 - 根据节点id获取兄弟所有节点 getBrotherNodesById(list, id) { // 非顶级节点:获取节点父节点对象里的children var parentNodes = this.getParentsById(list, id) if (parentNodes && parentNodes.length >= 2) { return parentNodes[1].children } // 顶级节点:第一级是自己,从原始数组中遍历第一层即可 return list }, // 根据当前节点id,获取及所有的父级兄弟节点的所有父节点 getParentBrotherAllNodesById(list, id) { var result = [] // 1、获取当前节点id父节点的父节点 var parentNodes = this.getParentsById(list, id) // 小于3表示当前父节点是顶级节点 if (parentNodes.length < 3) { return parentNodes[parentNodes.length - 1] } var testNode = parentNodes[2]; // 2、获取父节点的父节点所有兄弟节点 var children = testNode.children children.forEach(element => { var parentNodesById = this.getParentsById(list, element.id) if (parentNodesById.length >= 2) { // js 数组中添加多个元素 简单的方法 push(...[]) result.push(...(parentNodesById.slice(0, parentNodesById.length - 1))) } }); return result; }, /** * 点击事件 - 左侧绑定复选框事件 */ checkboxChangeBindAll(e) { var index = e.currentTarget.dataset.index; var index2 = e.currentTarget.dataset.index2; var list = this.data.menuTree if (index2 == undefined) { list[index].bindAll = !list[index].bindAll } if (index2 != undefined) { list[index].children[index2].bindAll = !list[index].children[index2].bindAll } console.log(this.data.menuTree); }, checkboxChange(e) { // console.log(e); console.log('checkbox发生change事件,携带value值为:', e.detail.value) const values = e.detail.value }, /** * 点击事件 - 右侧复选框事件 */ checkboxChangeAll(e) { var id = e.currentTarget.dataset.id; var data = this.data.menuTree var node = this.getNodeById(data, id) var childrenNodes = this.getChildrenById(node[0], id) // 1、子节点点选中状态-跟随父节点移动 node[0].checked = !node[0].checked // 节点下面的所有子节点跟随父节点的选中状态 childrenNodes.forEach(element => { element.checked = node[0].checked }) // 2、父节点选中状态,子节点都没选中,父节点默认不选中,子节点有一个选中,父节点也选中 // 获取同级兄弟节点 var bortherNodes = this.getBrotherNodesById(data, id) // 3、同级都选中 var allChecked = false bortherNodes.forEach(element => { if (element.checked) { allChecked = true } }) // 获取节点id所有父节点 var parentNodes = this.getParentsById(data, id) if (parentNodes.length > 1) { if (allChecked) { // 下标index=0的节点是其本身,这里跳过 for (let index = 1; index < parentNodes.length; index++) { const element = parentNodes[index]; element.checked = true } }else{ parentNodes[1].checked =false } } // 4、同级都未选中 if (!allChecked) { var allNoChecked = false // 根据当前节点id,获取除去顶级节点的所有的父级兄弟节点的所有父节点 var parentBother = this.getParentBrotherAllNodesById(data, id) console.log(parentBother); if (parentBother.length > 1) { parentBother.forEach(element => { if (element.checked) { allNoChecked = true } }); } console.log(allNoChecked); // console.log(parentBother); if(!allNoChecked){ parentNodes.forEach(element => { element.checked=false }); } } this.setData({ menuTree: data }) // console.log(this.data.menuTree); }, /** * 点击事件 - 点击层级显示和折叠事件 */ openAndHide(e) { var id = e.currentTarget.dataset.id; var list = this.data.menuTree console.log(id); // 根据 id 获取选中节点的对象 var node = this.getNodeById(list, id) // 根据 id 获取选中节点下的所有子节点 var res = this.getChildrenById(node[0], id) // 包含当前id节点本身 res.push(node[0]) // 遍历选中节点(及自己)是否展开 res.forEach(element => { element.isHidden = !element.isHidden }) this.setData({ menuTree: list }) } })
2、treeMenu2.JSON
{ "usingComponents": {} }
3、treeMenu2.WXMl
这里其实写的有问题,我主要写java的,前端用的不多,哪个高手指点一下,wxml 文件怎么样能递归呢,
我这里直接最笨的方法,三层for…,
<view class="divcss5-max"> <view class="page-section "> <view style="padding-bottom:10px">设备选择列表:{{menuTree.length}}</view> <view class="weui-cells weui-cells_after-title "></view> <view> <!-- 第一层 --> <block class="weui-cell weui-check__label line-center" wx:for="{{menuTree}}" wx:for-index="index" wx:for-item="item" wx:key="id"> <view class="paddingBottom_10 "></view> <view class="paddingLeft_10"> <view class="treeMenuCenter"> <checkbox-group bindchange="checkboxChangeAll" data-index="{{index}}" data-id="{{item.id}}" > <checkbox value="{{item.field}}" checked="{{item.checked}}" /> </checkbox-group> <!-- 箭头图标 --> <view class="treeMenuCenter" bindtap="openAndHide" data-id="{{item.id}}" data-index="{{index}}" > <image wx:if="{{item.isHidden == true}}" class="icon-chioce" src="{{menuTreeImgLeft}}"></image> <image wx:if="{{item.isHidden == false}}" class="icon-chioce" src="{{menuTreeImgBottom}}"></image> <text style="color:black" decode="{{true}}"> {{item.title}} </text> </view> <!-- <checkbox-group bindchange="checkboxChangeBindAll" data-index="{{index}}" > <checkbox checked="{{item.bindAll}}" />绑定单元 </checkbox-group> --> </view> </view> <!-- 第二层 --> <view class="" hidden="{{item.isHidden}}" wx:for="{{item.children}}" wx:for-index="index2" wx:for-item="item2" wx:key="id"> <view class="paddingLeft_20 "> <view class="treeMenuCenter"> <!-- <view class="dashedStyleWidth_40"></view> --> <checkbox-group bindchange="checkboxChangeAll" data-id="{{item2.id}}" data-index="{{index}}" data-index2="{{index2}}" > <checkbox value="{{item2.field}}" checked="{{item2.checked}}" /> </checkbox-group> <!-- 箭头图标 --> <view class="treeMenuCenter" bindtap="openAndHide" data-id="{{item2.id}}" data-index="{{index}}" data-index2="{{index2}}" > <image wx:if="{{item2.isHidden == true}}" class="icon-chioce" src="{{menuTreeImgLeft}}"></image> <image wx:if="{{item2.isHidden == false}}" class="icon-chioce" src="{{menuTreeImgBottom}}"></image> <text style="color:#0094aa" decode="{{true}}"> {{item2.title}} </text> </view> <!-- <checkbox-group bindchange="checkboxChangeBindAll" data-index="{{index}}" data-index2="{{index2}}" > <checkbox checked="{{item2.bindAll}}" />绑定楼层 </checkbox-group> --> </view> </view> <!-- 第三层 --> <block class="" wx:for="{{item2.children}}" wx:for-item="item3" wx:for-index="index3" wx:key="id"> <view class="paddingLeft_30 " hidden="{{item2.isHidden}}"> <!-- <view class="dashedStyleWidth_80"></view> --> <checkbox-group bindchange="checkboxChangeAll" data-id="{{item3.id}}" data-index="{{index}}" data-index2="{{index2}}" data-index3="{{index3}}" > <checkbox value="{{item3.field}}" checked="{{item3.checked}}" /> {{item3.title}} </checkbox-group> </view> </block> </view> <view class="paddingBottom_10 "></view> <view class="weui-cells weui-cells_after-title "></view> </block> </view> </view> </view>
4、treeMenu2.WXSS
/* pages/biz/treeMenu/treeMenu.wxss */ @import '../../../lib/weui.wxss'; .empty{ padding: 5px 0px; } .paddingLeft_10{ padding-left: 10px; padding-bottom: 5px; } .paddingLeft_20{ padding-left: 40px; padding-bottom: 5px; } .paddingLeft_30{ padding-left: 80px; padding-bottom: 5px; } .paddingBottom_10{ padding-top: 10px; } .line-center{ display: flex; align-items: center; justify-content: center; flex-flow: column; } /** *参考:https://cloud.tencent.com/developer/article/1690869 * / /* 选中后的 背景样式 (红色背景 无边框 可根据UI需求自己修改) */ checkbox .wx-checkbox-input.wx-checkbox-input-checked { background: #0094aa; } /* 选中后的 对勾样式 (白色对勾 可根据UI需求自己修改) */ checkbox .wx-checkbox-input.wx-checkbox-input-checked::before{ color:#fff; /* 对勾颜色 白色 */ } .treeMenuCenter{ display: flex; align-items: center; } .dashedStyleWidth_80{ margin-left: 10px; margin-right: 5px; width: 80px; height: 0px; border: 0.5px dotted gray; } .dashedStyleWidth_40{ margin-left: 10px; margin-right: 5px; width: 40px; height: 0px; border: 0.5px dotted gray; } .icon-chioce { height: 15px; width: 15px; padding-top: 10px; padding-bottom: 10px; background-color: fff; }
五 、最后
哪个大侠指点一下,wxml如何递归就完美了
加载全部内容