阅读 248

bootstrap+jquery+bootstrap-treeview实现菜单树极其父子节点的勾选功能

一、功能需求

        根据后台所给的原始数据,将其重组成bootstrap-treeview列表树插件所需要的数据结构并渲染,再根据所勾选的节点数据,重组成后台所需要的数据结构进行请求。其中在勾选节点时,如果勾选父节点,则子节点全部被勾选;如果取消勾选父节点时,则子节点全部取消勾选;如果勾选其中某个子节点,则其所对应的父节点也被勾选

二、难点分析

其中难点主要有以下三个:

1、将原始数据重组成列表树插件所需要的数据结构;

2、其中某个子节点被勾选时,其父节点也被勾选;

3、将所有被勾选的节点数据重组成后台所需要的数据结构;

三、实例

关于bootstrap-treeview插件的依赖、使用方法、事件、可用事件列表可参考此处。

1、插件依赖

本次实例使用的依赖如下:

  • bootstrap-3.4.1

  • jquery-3.1.1

2、原始数据重组为插件所需数据结构

原始数据:

    // 原始数据
    var allLevels = {
        "level1": ['level1-1', 'level1-2', 'level1-3', 'level1-4', 'level1-5'],
        "level2": ['level2-1', 'level2-2', 'level2-3', 'level2-4', 'level2-5'],
        "level3": ['level3-1', 'level3-2', 'level3-3', 'level3-4', 'level3-5'],
        "level4": ['level4-1', 'level4-2', 'level4-3', 'level4-4', 'level4-5'],
        "level5": ['level5-1', 'level5-2', 'level5-3', 'level5-4', 'level5-5'],
        "level6": ['level6-1', 'level6-2', 'level6-3', 'level6-4', 'level6-5'],
        "level7": ['level7-1', 'level7-2', 'level7-3', 'level7-4', 'level7-5'],
        "level8": ['level8-1', 'level8-2', 'level8-3', 'level8-4', 'level8-5']
    };

    // 重组过后,可在treeview上渲染的数据
    var treeData = [];复制代码

重组方法:

    // 原始数据重组
    function formatLevels(allLevels) {
        // console.log(allLevels)
        treeData = []// 清除菜单树缓存
        treeData.push({
            id: 'all',
            text: '全选',
            nodes: []
        })
        var level_number = -1
        for (var level in allLevels) {
            treeData[0].nodes.push({
                id: level,
                text: level,
                nodes: []
            })
            level_number++
            for (var index in allLevels[level]) {
                treeData[0].nodes[level_number].nodes.push({
                    id: allLevels[level][index],
                    text: allLevels[level][index]
                })
            }
        }
        // console.log(treeData)//插件渲染数据
    }复制代码

3、列表树渲染

HTML:

<div id="tree" style="height: 600px;overflow-y: auto;"></div>复制代码

js:

    // treeview初始化
    function initTreeView(treeData) {
        $('#tree').treeview({
            data: treeData,//赋值
            showIcon: true,
            showCheckbox: true,//展示checkbox
            multiSelect: true,//是否可以同时选择多个节点
            levels: 2,//设置继承树默认展开的级别。
            onNodeChecked: function (event, node) { //选中节点时触发
                var selectNodes = getChildNodeIdArr(node); //获取所有子节点
                if (selectNodes) { //子节点不为空,则选中所有子节点
                    $('#tree').treeview('checkNode', [selectNodes, { silent: true }]);
                }

                // 以下代码实现当某个子节点被选中时,其父节点被选中
                if (node.parentId >= 0) {//父节点不为空,则选中父节点
                    $("#tree").treeview("checkNode", [node.parentId, { silent: true }]);
                    var parentNode = $("#tree").treeview("getNode", node.parentId);
                    if (parentNode.parentId >= 0) {//1级父节点不为空,则选中父节点
                        $("#tree").treeview("checkNode", [parentNode.parentId, { silent: true }]);
                        var parentNode1 = $("#tree").treeview("getNode", parentNode.parentId);
                        if (parentNode1.parentId >= 0) {//2级父节点不为空,则选中父节点
                            $("#tree").treeview("checkNode", [parentNode1.parentId, { silent: true }]);
                            var parentNode2 = $("#tree").treeview("getNode", parentNode1.parentId);
                        }
                    }
                }
                setParentNodeCheck(node);
            },
            onNodeUnchecked: function (event, node) { //取消选中节点
                var selectNodes = getChildNodeIdArr(node); //获取所有子节点
                if (selectNodes) { //子节点不为空,则取消选中所有子节点
                    $('#tree').treeview('uncheckNode', [selectNodes, { silent: true }]);
                }
            },
        }).treeview('checkAll', { silent: true });//默认全选
    }

    // 选中
    function getChildNodeIdArr(node) {
        var ts = [];
        if (node.nodes) {
            for (x in node.nodes) {
                ts.push(node.nodes[x].nodeId);
                if (node.nodes[x].nodes) {
                    var getNodeDieDai = getChildNodeIdArr(node.nodes[x]);
                    for (j in getNodeDieDai) {
                        ts.push(getNodeDieDai[j]);
                    }
                }
            }
        } else {
            ts.push(node.nodeId);
        }
        return ts;
    }

    // 取消选中
    function setParentNodeCheck(node) {
        var parentNode = $("#tree").treeview("getNode", node.parentId);
        if (parentNode.nodes) {
            var checkedCount = 0;
            for (x in parentNode.nodes) {
                if (parentNode.nodes[x].state.checked) {
                    checkedCount++;
                } else {
                    break;
                }
            }
            if (checkedCount === parentNode.nodes.length) {
                $("#tree").treeview("checkNode", parentNode.nodeId);
                setParentNodeCheck(parentNode);
            }
        }
    }复制代码

4、根据所选节点数据,重组成后端所需数据结构

   function initClick() {
        // 点击按钮,获取被选中的节点,并组合成原始数据格式
        $('.selectedNodes').on('click', function () {
            var selectedNodes = $('#tree').treeview('getChecked');//获取被勾选的节点
            // console.log(selectedNodes)

            var parent_levels = {}//接收父节点
            var son_levels = {}//接收子节点
            var result_levels = {}//最终组合的数据

            for (const item in selectedNodes) {
                var name = selectedNodes[item].text
                var nodeId = selectedNodes[item].nodeId
                var parentId = selectedNodes[item].parentId

                if (selectedNodes[item].parentId === undefined) {//去掉全选节点
                    continue
                } else if (selectedNodes[item].parentId === 0) {//获取父节点key-value
                    parent_levels[name] = nodeId
                } else {//获取子节点key-value
                    son_levels[name] = parentId
                }
            }
            // console.log(parent_levels, son_levels)

            for (const key in parent_levels) {
                result_levels[key] = []
                for (const index in son_levels) {
                    if (parent_levels[key] === son_levels[index]) {
                        result_levels[key].push(index)
                    }
                }
            }
            // console.log(result_levels)
        })
    }复制代码

5、效果展示

默认节点全选,如下图所示:

当父节点level1下有子节点被勾选时,父节点仍然被勾选,效果如图所示:

点击“获取选中的节点”按钮,控制台输出最终重组后的数据,如图所示:

6、完整代码

伪原创工具 https://www.237it.com/ 

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>test</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="./assest/bootstrap-3.4.1/css/bootstrap.css">
    <style>
        .treeMenu {
            width: 600px;
            border: 1px solid #eee;
            margin: 20px auto;
        }
    </style>
</head>
<body>
    <div class="treeMenu">
        <div id="tree" style="height: 600px;overflow-y: auto;"></div>
        <button class="selectedNodes">获取选中的节点</button>
    </div>
</body>
</html>

<script type="text/javascript" src="./assest/jquery-3.1.1.min.js">
</script><script type="text/javascript" src="./assest/bootstrap-treeview/bootstrap-treeview.js">
</script><script type="text/javascript">
    // 原始数据
    var allLevels = {
        "level1": ['level1-1', 'level1-2', 'level1-3', 'level1-4', 'level1-5'],
        "level2": ['level2-1', 'level2-2', 'level2-3', 'level2-4', 'level2-5'],
        "level3": ['level3-1', 'level3-2', 'level3-3', 'level3-4', 'level3-5'],
        "level4": ['level4-1', 'level4-2', 'level4-3', 'level4-4', 'level4-5'],
        "level5": ['level5-1', 'level5-2', 'level5-3', 'level5-4', 'level5-5'],
        "level6": ['level6-1', 'level6-2', 'level6-3', 'level6-4', 'level6-5'],
        "level7": ['level7-1', 'level7-2', 'level7-3', 'level7-4', 'level7-5'],
        "level8": ['level8-1', 'level8-2', 'level8-3', 'level8-4', 'level8-5']
    };

    // 重组过后,可在treeview上渲染的数据
    var treeData = [];

    $(document).ready(function () {
        // 将原始数据重组为treeview所要求的的格式
        formatLevels(allLevels)

        // 初始化菜单树
        initTreeView(treeData)

        // 点击事件初始化
        initClick()
    })

    // 原始数据重组
    function formatLevels(allLevels) {
        // console.log(allLevels)
        treeData = []// 清除菜单树缓存
        treeData.push({
            id: 'all',
            text: '全选',
            nodes: []
        })
        var level_number = -1
        for (var level in allLevels) {
            treeData[0].nodes.push({
                id: level,
                text: level,
                nodes: []
            })
            level_number++
            for (var index in allLevels[level]) {
                treeData[0].nodes[level_number].nodes.push({
                    id: allLevels[level][index],
                    text: allLevels[level][index]
                })
            }
        }
        // console.log(treeData)
    }

    // treeview初始化
    function initTreeView(treeData) {
        $('#tree').treeview({
            data: treeData,//赋值
            showIcon: true,
            showCheckbox: true,//展示checkbox
            multiSelect: true,//是否可以同时选择多个节点
            levels: 2,//设置继承树默认展开的级别。
            onNodeChecked: function (event, node) { //选中节点时触发
                var selectNodes = getChildNodeIdArr(node); //获取所有子节点
                if (selectNodes) { //子节点不为空,则选中所有子节点
                    $('#tree').treeview('checkNode', [selectNodes, { silent: true }]);
                }

                // 以下代码实现当某个子节点被选中时,其父节点被选中
                if (node.parentId >= 0) {//父节点不为空,则选中父节点
                    $("#tree").treeview("checkNode", [node.parentId, { silent: true }]);
                    var parentNode = $("#tree").treeview("getNode", node.parentId);
                    if (parentNode.parentId >= 0) {//1级父节点不为空,则选中父节点
                        $("#tree").treeview("checkNode", [parentNode.parentId, { silent: true }]);
                        var parentNode1 = $("#tree").treeview("getNode", parentNode.parentId);
                        if (parentNode1.parentId >= 0) {//2级父节点不为空,则选中父节点
                            $("#tree").treeview("checkNode", [parentNode1.parentId, { silent: true }]);
                            var parentNode2 = $("#tree").treeview("getNode", parentNode1.parentId);
                        }
                    }
                }
                setParentNodeCheck(node);
            },

            onNodeUnchecked: function (event, node) { //取消选中节点
                var selectNodes = getChildNodeIdArr(node); //获取所有子节点
                if (selectNodes) { //子节点不为空,则取消选中所有子节点
                    $('#tree').treeview('uncheckNode', [selectNodes, { silent: true }]);
                }
            },
        }).treeview('checkAll', { silent: true });//默认全选
    }

    // 选中
    function getChildNodeIdArr(node) {
        var ts = [];
        if (node.nodes) {
            for (x in node.nodes) {
                ts.push(node.nodes[x].nodeId);
                if (node.nodes[x].nodes) {
                    var getNodeDieDai = getChildNodeIdArr(node.nodes[x]);
                    for (j in getNodeDieDai) {
                        ts.push(getNodeDieDai[j]);
                    }
                }
            }
        } else {
            ts.push(node.nodeId);
        }
        return ts;
    }

    // 取消选中
    function setParentNodeCheck(node) {
        var parentNode = $("#tree").treeview("getNode", node.parentId);
        if (parentNode.nodes) {
            var checkedCount = 0;
            for (x in parentNode.nodes) {
                if (parentNode.nodes[x].state.checked) {
                    checkedCount++;
                } else {
                    break;
                }
            }
            if (checkedCount === parentNode.nodes.length) {
                $("#tree").treeview("checkNode", parentNode.nodeId);
                setParentNodeCheck(parentNode);
            }
        }
    }

    function initClick() {
        // 点击按钮,获取被选中的节点,并组合成原始数据格式
        $('.selectedNodes').on('click', function () {
            var selectedNodes = $('#tree').treeview('getChecked');//获取被勾选的节点
            // console.log(selectedNodes)

            var parent_levels = {}//接收父节点
            var son_levels = {}//接收子节点
            var result_levels = {}//最终组合的数据

            for (const item in selectedNodes) {
                var name = selectedNodes[item].text
                var nodeId = selectedNodes[item].nodeId
                var parentId = selectedNodes[item].parentId
                if (selectedNodes[item].parentId === undefined) {//去掉全选节点
                    continue
                } else if (selectedNodes[item].parentId === 0) {//获取父节点key-value
                    parent_levels[name] = nodeId
                } else {//获取子节点key-value
                    son_levels[name] = parentId
                }
            }
            // console.log(parent_levels, son_levels)

            for (const key in parent_levels) {
                result_levels[key] = []
                for (const index in son_levels) {
                    if (parent_levels[key] === son_levels[index]) {
                        result_levels[key].push(index)
                    }
                }
            }
            console.log(result_levels)
        })
    }

</script>


作者:巨蟹座不吃鱼
链接:https://juejin.cn/post/7034068557931479048


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐