一、功能需求
根据后台所给的原始数据,将其重组成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>
复制代码