为TreeView菜单增加按钮和点击事件
最近工作中在写一个后台管理系统,由于系统同种各个模块的页面样式大体一致,因此师傅决定定制一个生成页面的功能。昨天在写到菜单管理模块的时候,发现通过表格展现出来的菜单数据无法直观的体现出菜单之间的层级关系,因此就改用了Treeview树形菜单来展示。但此时碰到了一个问题,师傅想要在树形菜单的每一行上加上按钮,包括新增子菜单、修改和删除。我查了下treeview的api,并未发现有新增按钮的操作。最后通过google找到了一个解决办法,在后台接口中将按钮的代码拼接到菜单名中。下面是原网页: https://stackoverflow.com/questions/36397655/add-buttons-to-bootstrap-treeview-node
找到解决办法了,开始改代码,我的treeview数据是从后台获取来的,那么拼接数据就在后台做了:
// 给一级菜单和二级菜单使用的按钮组 String button1 = "<div style='float:right'>" + "<button type='button' id='btnEdit' class='btn-xs btn-info' onclick='beforeUpdate()'>" + "<i class='glyphicon glyphicon-edit'></i> 修改" + "</button> " + "<button type='button' id='btnEdit' class='btn-xs btn-info' onclick='beforeInsert()'>" + "<i class='glyphicon glyphicon-plus'></i> 新增子菜单" + "</button> " + "<button type='button' id='btnEdit' class='btn-xs btn-danger' onclick='beforeDelete()'>" + "<i class='fa fa-trash'></i> 删除" + "</button>" + "</div>"; // 给权限菜单使用的按钮组 String button2 = "<div style='float:right'>" + "<button type='button' id='btnEdit' class='btn-xs btn-info' onclick='beforeUpdate()'>" + "<i class='glyphicon glyphicon-edit'></i> 修改" + "</button> " + "<button type='button' id='btnEdit' class='btn-xs btn-danger' onclick='beforeDelete()'>" + "<i class='fa fa-trash'></i> 删除" + "</button>" + "</div>"; if (sysMenu.getMenuType().equals("1")) { node.setText(sysMenu.getMenuName() + button1); firstLevelList.add(node); } else if (sysMenu.getMenuType().equals("2")) { node.setText(sysMenu.getMenuName() + button1); secondLevelList.add(node); } else { node.setText(sysMenu.getMenuName() + button2); thirdLevelList.add(node); }
拼接成功,这里说明一下,树形菜单的数据需要特定格式才能展示,因此在后台接口中要自己组装出相应的格式,我使用如下的格式来传递数据:
/** * * @author sxh * @date 2019年8月20日 * @Description * */ public class TreeViewNode{ private String id; private String text; private String parentId; private List<TreeViewNode> nodes; // 用来判断相应的菜单行的复选框是否需要被勾选,格式:{"checked", true} private Map<String, Boolean> state; }
传递给前端的数据格式
按钮的问题解决了,但下面又出现了一个难题,按钮点击之后需要知道点击的是哪一条数据,然后进行相应的操作。我就想可以通过treeview的点击事件来确定具体点击了哪一行,查阅了treeview的api,并未发现点击事件。treeview只有选中事件和取消选中事件,但在选中和取消选中时都会都会触发相应的操作,并且treeview会优先执行取消选中事件再执行选中事件。假如在两个中都放上相应的方法,如果事先选中了一条A数据,那么之后再点击B数据的话就会先取消选中A数据,这时会执行取消选中事件,并且对应的数据是A,而我们想要执行的数据B却在A执行完之后才执行。没办法,只好改源码,自己加上点击事件。(源码改动参考了 https://blog.csdn.net/qq775410784/article/details/89406777#commentBox,只放上被修改过的代码)
;(function ($, window, document, undefined) { /*global jQuery, console*/ 'use strict'; var pluginName = 'treeview'; var _default = {}; _default.settings = { injectStyle: true, levels: 2, expandIcon: 'glyphicon glyphicon-plus', collapseIcon: 'glyphicon glyphicon-minus', emptyIcon: 'glyphicon', nodeIcon: '', selectedIcon: '', checkedIcon: 'glyphicon glyphicon-check', uncheckedIcon: 'glyphicon glyphicon-unchecked', color: undefined, // '#000000', backColor: undefined, // '#FFFFFF', borderColor: undefined, // '#dddddd', onhoverColor: '#F5F5F5', selectedColor: '#FFFFFF', selectedBackColor: '#428bca', searchResultColor: '#D9534F', searchResultBackColor: undefined, //'#FFFFFF', enableLinks: false, highlightSelected: true, highlightSearchResults: true, showBorder: true, showIcon: true, showCheckbox: false, showTags: false, multiSelect: false, // Event handlers onNodeChecked: undefined, onNodeCollapsed: undefined, onNodeDisabled: undefined, onNodeEnabled: undefined, onNodeExpanded: undefined, onNodeSelected: undefined, onNodeUnchecked: undefined, onNodeUnselected: undefined, onSearchComplete: undefined, onSearchCleared: undefined, /** * 给 bootstrap treeview 添加 点击事件 定义 */ onNodeClicked: undefined }; Tree.prototype.unsubscribeEvents = function () { this.$element.off('click'); this.$element.off('nodeChecked'); this.$element.off('nodeCollapsed'); this.$element.off('nodeDisabled'); this.$element.off('nodeEnabled'); this.$element.off('nodeExpanded'); this.$element.off('nodeSelected'); this.$element.off('nodeUnchecked'); this.$element.off('nodeUnselected'); this.$element.off('searchComplete'); this.$element.off('searchCleared'); /** * 给 bootstrap treeview 添加 点击事件 元素 */ this.$element.off('nodeClicked'); }; Tree.prototype.subscribeEvents = function () { this.unsubscribeEvents(); this.$element.on('click', $.proxy(this.clickHandler, this)); if (typeof (this.options.onNodeChecked) === 'function') { this.$element.on('nodeChecked', this.options.onNodeChecked); } if (typeof (this.options.onNodeCollapsed) === 'function') { this.$element.on('nodeCollapsed', this.options.onNodeCollapsed); } if (typeof (this.options.onNodeDisabled) === 'function') { this.$element.on('nodeDisabled', this.options.onNodeDisabled); } if (typeof (this.options.onNodeEnabled) === 'function') { this.$element.on('nodeEnabled', this.options.onNodeEnabled); } if (typeof (this.options.onNodeExpanded) === 'function') { this.$element.on('nodeExpanded', this.options.onNodeExpanded); } if (typeof (this.options.onNodeSelected) === 'function') { this.$element.on('nodeSelected', this.options.onNodeSelected); } if (typeof (this.options.onNodeUnchecked) === 'function') { this.$element.on('nodeUnchecked', this.options.onNodeUnchecked); } if (typeof (this.options.onNodeUnselected) === 'function') { this.$element.on('nodeUnselected', this.options.onNodeUnselected); } if (typeof (this.options.onSearchComplete) === 'function') { this.$element.on('searchComplete', this.options.onSearchComplete); } if (typeof (this.options.onSearchCleared) === 'function') { this.$element.on('searchCleared', this.options.onSearchCleared); } /** * 给 bootstrap treeview 添加 点击事件 赋值 */ if (typeof (this.options.onNodeClicked) === 'function') { this.$element.on('nodeClicked', this.options.onNodeClicked); } }; Tree.prototype.clickHandler = function (event) { if (!this.options.enableLinks) event.preventDefault(); var target = $(event.target); var node = this.findNode(target); if (!node || node.state.disabled) return; var classList = target.attr('class') ? target.attr('class').split(' ') : []; if ((classList.indexOf('expand-icon') !== -1)) { this.toggleExpandedState(node, _default.options); this.render(); } else if ((classList.indexOf('check-icon') !== -1)) { this.toggleCheckedState(node, _default.options); this.render(); } else { if (node.selectable) { this.toggleSelectedState(node, _default.options); } else { this.toggleExpandedState(node, _default.options); } this.render(); } /** * clickHandler -- 最后执行点击事件 */ this.onClicked(node, _default.options); }; /** * 给 bootstrap treeview 添加 点击事件 * 依赖于clickHandler 方法。最后执行 */ Tree.prototype.onClicked = function (node, options) { if (!node) return; if (!options.silent) { this.$element.trigger('nodeClicked', $.extend(true, {}, node)); } };
源码改动完毕,下面展示调用:
var clickEvent; $('#treeview-checkable').treeview({ data: getMenus(), // 加载的数据源 showIcon: false, showCheckbox: true, // 展示复选框 levels: 3, onNodeClicked: function(event, node) { console.log("click event"); switch(clickEvent){ case "deleteEvent": deleteMenu(node); break; case "insertEvent": insertMenu(node); break; case "updateEvent": updateMenu(node); break; default: break; } }, }); // 删除 function beforeDelete () { clickEvent = "deleteEvent"; } function beforeInsert() { clickEvent = "insertEvent"; }; // 处理修改事件 function beforeUpdate() { clickEvent = "updateEvent"; };
用户在点击按钮时,首先执行按钮的onclick事件,这时候记录一个标识,之后才会执行treeview的点击事件,根据标识执行指定的事件,并传递相应的数据。