首頁 > 軟體

微信小程式實現多層級核取方塊選單

2022-07-18 18:03:11

本文範例為大家分享了微信小程式自定義多層級核取方塊選單的具體程式碼,供大家參考,具體內容如下

一、背景

因客戶需要,有一個功能是需要註冊使用者時選擇多個【單元 - 樓層 - 裝置】的繫結,谷歌了一通,沒有找到想要的,無奈之舉只能手寫一個…

二 、效果展示

三、功能點

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如何遞迴就完美了

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com