小程序多级多选联动菜单

Home / Article 百晓生 2022-1-13 4701

本想这种功能应该比较常见,谁知搜半天没找到。只能自己造轮!

效果图:

基于微信提供的picker-view实现的

js代码

// element/pickerex/index.js
Component({
    /**
     * 组件的属性列表
     */
    properties: {
    },
    /**
     * 组件的初始数据
     */
    data: {
        value: [],
        tree: [],
        deep: 0,
        // 每列对应的数据
        columnData: [],
        // 单位索引
        idMap: undefined
    },
    observers: {
        // 在 numberA 或者 numberB 被设置时,执行这个函数
        'tree': function (tree) {
            // 建立id索引
            let map = this.getIndexMap(tree)
            let deep = this.getMaxDepth(tree)
            // 排序
            let value = [],
                data = []
            for (let i = 0; i <= deep; ++i) value.push(0)
            // 默认值
            this.getTreeData(tree, data)
            this.setData({
                value: value,
                deep: deep,
                columnData: data,
                idMap: map
            })
        }
    },
    /**
     * 组件的方法列表
     */
    methods: {
        getIndexMap: function (tree) {
            let map = new Map()
            for (let e of tree) getDepth(e)
            function getDepth(e) {
                map.set(String(e.id), e)
                if (e && e.hasOwnProperty('children')) {
                    for (let c of e.children) getDepth(c)
                }
            }
            return map
        },
        getTreeData: function (treeData, data) {
            let deep = 0
            data[deep] = treeData
            for (let e of treeData) {
                getDepth(e, deep + 1)
                break
            }
            function getDepth(e, deep) {
                if (e.hasOwnProperty('children') && e.children.length) {
                    data[deep] = e.children
                    for (let c of e.children) {
                        getDepth(c, deep + 1)
                        break
                    }
                }
            }
        },
        getMaxDepth: function (treeData, ans = {}) {
            for (let item of treeData) getDepth(item)
            function getDepth(e) {
                if (e.hasOwnProperty('children') && e.children.length) {
                    ans[String(e.id)] = 0
                    for (let c of e.children) {
                        ans[String(e.id)] = Math.max(ans[String(e.id)], getDepth(c) + 1)
                    }
                    return ans[String(e.id)]
                } else {
                    ans[String(e.id)] = 0
                    return 0
                }
            }
            let arr = Object.values(ans)
            return Math.max(...arr)
        },
        getDataByIndex: function (index, data) {
            let tree = this.data.tree
            let deep = 0
            data[deep] = tree
            getDepth(tree[index[deep]], deep + 1)
            function getDepth(e, deep) {
                if (e && e.hasOwnProperty('children') && e.children.length) {
                    data[deep] = e.children
                    getDepth(e.children[index[deep]], deep + 1)
                }
            }
        },
        // getValueByIndex: function (index) {
        //     let tmp = this.data.tree[index[0]]
        //     for (let i = 1; i <= this.data.value.length; ++i) {
        //         if (tmp && tmp.hasOwnProperty('children') && tmp.children.length > 0) {
        //             tmp = tmp.children[index[i]]
        //             if (!tmp) break
        //         }
        //     }
        //     return tmp
        // },
        bindChange: function (e) {
            let v = e.detail.value
            let val = this.data.value
            for (let i = 0; i < val.length; ++i) val[i] = 0
            for (let i = 0; i < v.length; ++i) val[i] = v[i]
            let data = []
            this.getDataByIndex(val, data)
            this.setData({
                value: val,
                columnData: data
            })
        },
        bindTap: function (e) {
            // let dataset = e.currentTarget.dataset
            // 只是一个克隆对象,并不是本体,所以修改item的属性是不会改变tree中的节点
            let tmp = e.currentTarget.dataset.item
            // console.log(dataset.column, dataset.item)
            // 此方法虽可找到对应位置值,不好找任意节点,只能找最后一个节点,可以根据点击的deep判断
            // let item = this.getValueByIndex(this.data.value)
            // 但这个值貌似好不传,在wxml文件,所以采用索引方法
            let map = this.data.idMap
            let item = map.get(String(tmp.id))
            if (!item) return
            item.checked = !item.checked
            setChecked(item, item.checked)
            // 设置子节点选中
            function setChecked(e, checked) {
                e.checked = checked
                if (e && e.hasOwnProperty('children')) {
                    for (let c of e.children) setChecked(c, checked)
                }
            }
            // 设置父节点选中
            if (item.checked) {
                item = map.get(String(item.pid))
                while (item) {
                    let full = true
                    for (let e of item.children) {
                        if (!e.checked) {
                            full = false
                            break
                        }
                    }
                    if (full) item.checked = true
                    // 如果有0节点会死循环,所以判断根节点跳出
                    if (item.pid === 0 || !full) break
                    item = map.get(String(item.pid))
                }
            } else {
                // 父节点反选
                item = map.get(String(item.pid))
                while (item) {
                    item.checked = false
                    // 如果有0节点会死循环,所以判断根节点跳出
                    if (item.pid === 0) break
                    item = map.get(String(item.pid))
                }
            }
            this.setData({
                columnData: this.data.columnData
            })
            // let val = this.data.value
            // let data = this.data.columnData
            this.triggerEvent('pickerChanged', {
                list: this.getCheckedList()
            })
        },
        getCheckedList: function () {
            let list = []
            let tree = this.data.tree
            for (let e of tree) getChecked(e)
            // 设置子节点选中
            function getChecked(e) {
                if (e.checked) list.push(e)
                if (e && e.hasOwnProperty('children')) {
                    for (let c of e.children) getChecked(c)
                }
            }
            return list
        }
    }
})

wxml(如果这个wx:for使用的item是本体就可以省掉建索引的过程,vue好像是本身,小程序是克隆体,修改dataset.item并不会修改数据源)

<!--element/pickerex/index.wxml-->
<wxs module="wx">
    var text = function (item) {
        // return item.name
        // 这里可以做一些输出字符串处理,如截断……
        return item.name
    }
    module.exports = {
        text: text
    }
</wxs>
<picker-view indicator-style="height:50px;" style="width:100%;height:200px;color:gray;" value="{{value}}" bindchange="bindChange">
    <picker-view-column wx:for="{{columnData}}" wx:key="index">
        <view class="item" wx:for="{{item}}" wx:key="index" wx:for-item="child">
            <text class="{{child.checked?'checked':''}}" bindtap="bindTap" data-item="{{child}}">{{wx.text(child)}}</text>
        </view>
    </picker-view-column>
</picker-view>

wxss

.item {
    line-height: 50px;
    text-align: center;
}
.checked {
    border-bottom: 2px #3296FA solid;
}

简短测试数据格式

[
    {
        "name":"haha",
        "id":111223,
        "pid":3,
        "children":[
            {
                "name":"还有?",
                "id":332211,
                "pid":111223
            },
            {
                "name":"真有",
                "id":332212,
                "pid":111223
            }
        ]
    }
]

使用:

 <pickerex id="area" bind:pickerChanged="pickerChanged"></pickerex>

记得json文件添加配置

    "pickerex": "../../element/pickerex/index"

test...

本文链接:https://www.it72.com/12711.htm

推荐阅读
最新回复 (1)
返回