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、完整代码
<!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