微信小程序实现多层级复选框菜单

目录
  • 一、背景
  • 二 、效果展示
  • 三、功能点
  • 四、代码实现
  • 五 、最后

本文实例为大家分享了微信小程序自定义多层级复选框菜单的具体代码,供大家参考,具体内容如下

一、背景

因客户需要,有一个功能是需要注册用户时选择多个【单元 - 楼层 - 设备】的绑定,谷歌了一通,没有找到想要的,无奈之举只能手写一个…

二 、效果展示

三、功能点

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}}">&nbsp;{{item.title}} &nbsp;</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}}">&nbsp; {{item2.title}}&nbsp; &nbsp;</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如何递归就完美了

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 微信小程序实现的点击按钮 弹出底部上拉菜单功能示例

    本文实例讲述了微信小程序实现的点击按钮 弹出底部上拉菜单功能.分享给大家供大家参考,具体如下: index.wxml <button type="default" bindtap="actionSheetTap">弹出action sheet</button> <action-sheet hidden="{{actionSheetHidden}}" bindchange="actionSheetbindch

  • 微信小程序实现复选框效果

    本文实例为大家分享了微信小程序实现复选框片展示的具体代码,供大家参考,具体内容如下 样式部分你们自由发挥,反正这里是什么都没写的,选中和没选中直接用这个this.data.arrStatus[checkIndex]去判断就行了,之后你们都懂的. 效果预览: js部分 // page/index/index.js Page({ /** * 页面的初始数据 */ data: { items: [ { name: 'USA', value: '美国' }, { name: 'CHN', value:

  • 微信小程序获取复选框全选反选选中的值(实例代码)

    wxml文件 <view class="tr"> <view class="th"> <checkbox bindtap="selectall" />全选 </view> <view class="th">id</view> <view class="th">名称</view> </view> <

  • 微信小程序自定义复选框

    本文实例为大家分享了微信小程序自定义复选框的具体代码,供大家参考,具体内容如下 1.效果 2.wxml <checkbox-group bindchange="checkboxChange" class="checkbox-group">      <view wx:for="{{checkboxArr}}" wx:key="index" class="padding-xs">   

  • 微信小程序 下拉菜单的实现

    微信小程序 下拉菜单 看下实现效果图: 实例: //wcss /**DropDownMenu**/ /*总菜单容器*/ .menu { display: block; height: 28px; position: relative; } /*一级菜单*/ .menu dt { font-size: 15px; float: left; /*hack*/ width: 33%; height: 38px; border-right: 1px solid #d2d2d2; border-botto

  • 微信小程序实现单列下拉菜单效果

    接下来我想做的是一个下拉菜单用来分类的,但是在网上搜了一下,基本上全都是比较复杂的分类菜单,最简单的也是分三列的下拉菜单,但是并不想要这么复杂的,最后找了一个三列的,改成了单列.也把代码尽可能的简单化了. 实现的效果图: 合并状态: 首先下面是目录结构:  下面是实现的具体代码: .wxml <!--选择框--> <view class="product-list"> <!--条件选择--> <view class="choice-b

  • 微信小程序复选框实现多选一功能过程解析

    这篇文章主要介绍了微信小程序复选框实现多选一功能过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 功能实现界面 data: { checkboxItems: [ { name: '全天(1-8节)', value: 'allday' }, { name: '上午(1-4节)', value: 'am' }, { name: '下午(5-8节)', value: 'pm' }, { name: '晚上(晚自习)', value: 'nigh

  • 微信小程序 自定义复选框实现代码实例

    功能: 1.需要多选复选框,并且可以上拉滚动: 2.需要通过名称手写字母排序的,并且可以上拉滚动: 常规的小程序自带的原生picker无法满足我们的要求,因此一些特殊效果需要我们自定义来实现,废话不多说,我们直接看效果: index.wxml布局 <view class="container"> <view class='class bgFFF' bindtap='isDep'> <view class='class-text'> <text

  • 微信公众号菜单配置微信小程序实例详解

    先提条件是你的公众号版定了小程序 第一种:直接配置     第二张:代码配置 1.进入在线接口调试工具        2.选择自定义菜单 菜单body为json格式: { "button": [ { "type": "miniprogram", "name": "合作", "url": "http://form.mikecrm.com/2xb9aT", "a

  • 微信小程序实现多层级复选框菜单

    目录 一.背景 二 .效果展示 三.功能点 四.代码实现 五 .最后 本文实例为大家分享了微信小程序自定义多层级复选框菜单的具体代码,供大家参考,具体内容如下 一.背景 因客户需要,有一个功能是需要注册用户时选择多个[单元 - 楼层 - 设备]的绑定,谷歌了一通,没有找到想要的,无奈之举只能手写一个… 二 .效果展示 三.功能点 1.初始化时,默认展开选中的菜单2.点击每一层父级菜单,会自动折叠其子菜单3.选中子级节点会默认选中父级节点4.子级节点都没选中默认取消选中父级节点5.选中父级节点默认

  • 微信小程序单选radio及多选checkbox按钮用法示例

    本文实例讲述了微信小程序单选radio及多选checkbox按钮用法.分享给大家供大家参考,具体如下: 1.单选:radio 实例: js: Page({ data : { radioId:"", loves:[ {id:1, name:"跑步" ,checked : 'true'}, {id:2, name:"篮球" }, {id:3, name:"足球" }, ] }, updataRadio:function(e){ v

  • 微信小程序自定义组件实现多选功能

    本文实例为大家分享了微信小程序自定义组件实现多选的具体代码,供大家参考,具体内容如下 效果图: 调用部分(例如在index页面) index.wxml <view catchtap="showMultiple">   多选按钮   <multiple id="multiple" bind:multipleCancel="_multipleCancel" bind:multipleConfirm="_multipleCo

  • 微信小程序使用input组件实现密码框功能【附源码下载】

    本文实例讲述了微信小程序使用input组件实现密码框功能.分享给大家供大家参考,具体如下: 1.效果展示 2.关键代码 index.wxml <view style="width:80%; display:flex;"> <view>用户名:</view> <input style="border: 1px solid;border-color: lightblue;width:60%;" placeholder="

  • 微信小程序自定义select下拉选项框组件的实现代码

    知识点:组件,animation,获取当前点击元素的索引与内容 微信小程序中没有select下拉选项框,所以只有自定义.自定义的话,可以选择模板的方式,也可以选择组件的方式来创建. 这次我选择了组件,这样只需引入组件和添加数据,其它的不用管,就能实现多个地方重复使用了. 第一步:创建组件所需的文件 我喜欢把共用的内容都放在和pages文件同级的地方,所以有了下面的目录结构 (1) 先创建一个自定义名字的文件夹,例如我上面的Componet (2) 再创建一个select文件夹,然后:右键这个文件

  • 微信小程序登录时如何获取input框中的内容

    这篇文章主要介绍了微信小程序登录时如何获取input框中的内容,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 最近写小程序项目遇到一些问题,今天整理下这些问题的解决方法,希望对用户有帮助.下面是登录页,点击登录时获取input框中的值, 效果如下: wxml布局如下: <view > <input type="text" placeholder-style="color:#fff;" bindin

  • 微信小程序实现底部弹出模态框

    本文实例为大家分享了微信小程序实现底部弹出模态框的具体代码,供大家参考,具体内容如下 代码: <view class="modals modals-bottom-dialog" hidden="{{hideModal}}" style="width:100%;height:{{widheight}}px"> <view class="modals-cancel" bindtap="hideModal

  • 微信小程序自定义select下拉选项框的方法

    本文实例为大家分享了微信小程序自定义select下拉选项框的具体代码,供大家参考,具体内容如下 第一步:创建组件所需的文件 第二步:开始配置组件 select.json {   "component": true,   "usingComponents": {     "select": "./select"   } } 第三步:自定义组件样式及js select.wxml <view class='com-select

  • 微信小程序使用uni-app实现首页搜索框导航栏功能详解

    目录 前言 一.兼容APP与H5的方式 二.兼容小程序 三.实现同时兼容 前言 首页都会提供一个搜索框给到客户,让客户自己去搜索自己想要的内容,这里就需要导航栏,来实现搜索页面的跳转,效果如下 App.H5效果 小程序效果 一.兼容APP与H5的方式 在常见titleNView配置代码示例中可以看到基本样式的代码如下 { "pages": [{ "path": "pages/index/index", //首页 "style"

  • 微信小程序开发之toast等弹框提示使用教程

    介绍 微信小程序中toast消息提示框只有两种显示的效果,就是成功和加载,使用wx.showToast(OBJECT) . 看下有关参数说明: 代码很简单: wx.showToast({ title: '成功', icon: 'succes', duration: 1000, mask:true }) mask属性好像并没有起作用.有一点值得注意的是提示的延迟时间是有限制的,最大10000毫秒. 还有一个函数是wx.hideToast() ,这个是隐藏toast,主要用于显示加载提示的时候用到,

随机推荐