基于Layui实现的树形菜单页面

基于Layui实现的树形菜单页面

  • 具体方法实现
    • 方法一:针对Layui模板的前后端统一更新
      • 1. 删除
      • 2. 添加
      • 3. 后端
    • 方法二:基于Dtree实现的纯前端树形增删改
  • 文中的组件地址

具体方法实现

实现树形菜单,本文将给出两种实现方式。

  1. 针对Layui前端模板EasyWeb iframe未实现的添加\修改\删除节点的功能,这里给出与后端实时同步的前端页面刷新方式实现节点的增删改。
  2. 基于Layui扩展的Dtree包,直接实现不依赖于后端的节点增删改,即只是前端样式更改,不会更改后端存放的.json数据。

方法一:针对Layui模板的前后端统一更新

PS:我这里使用MongoDB实现json文件的存储,当然也可以将json文件直接存储在文件系统中。文中所有数据均为json格式。
实现效果图:
在这里插入图片描述
数据库存储格式(data中存放表结构,name存放用户索引):
在这里插入图片描述

当进行增删改操作时,json文件的封装在前端完成,通过Ajax请求将封装好的Json数据作为字符串数组传到后端,后端将其存入数据库。
每次增删改操作完成后,均会由insTb.reload再次调用该接口实现后端数据后,前端显示与后端数据的实时同步。

//前端通过Layui-table直接请求数据库数据
layui.use(['layer', 'form', 'admin', 'treeTable', 'util', 'xmSelect'], function () {var $ = layui.jquery;var layer = layui.layer;var form = layui.form;var admin = layui.admin;var treeTable = layui.treeTable;var util = layui.util;var xmSelect = layui.xmSelect;var tbDataList = [];// 渲染表格var insTb = treeTable.render({elem: '#authoritiesTable',// url: '/iframe/test.json',url: '/request/getMongoJson?name=user1',toolbar: ['<p>','<button lay-event="add" class="layui-btn layui-btn-sm icon-btn"><i class="layui-icon">&#xe654;</i>添加</button>&nbsp;','<button lay-event="del" class="layui-btn layui-btn-sm layui-btn-danger icon-btn"><i class="layui-icon">&#xe640;</i>删除</button>','</p>'].join(''),tree: {iconIndex: 2,idName: 'authorityId',pidName: 'parentId',isPidData: true,arrowType: 'arrow2',getIcon: 'ew-tree-icon-style2'},cols: [[// {type: 'checkbox'},{type: 'numbers'},{title: '类型', templet: function (d) {return ['<span class="layui-badge layui-badge-green">菜单</span>','<span class="layui-badge layui-badge-gray">文件</span>'][d.isMenu];}, align: 'center', width: 80},{field: 'authorityName', title: '文件名称<i class=\"layui-icon\">&#xe642;</i>', minWidth: 150,edit:'text'},{field: 'menuUrl', title: '菜单url'},{field: 'authority', title: '权限标识'},{field: 'orderNumber', title: '排序号', align: 'center', width: 80},{title: '创建时间', templet: '<p>{{layui.util.toDateString(d.createTime)}}</p>', align: 'center'},{title: '操作', toolbar: '#authoritiesTbBar', align: 'center', width: 190}]],done: function (data) {tbDataList = data;}});
	//后端根据用户name值查询并获取MongoDB中的数据,封装为Json传回前端@ResponseBody@RequestMapping(value = "/getMongoJson", method = RequestMethod.GET)public String getMongoJson(String name){Query query = new Query(Criteria.where("name").is(name));
//        query.fields().exclude("_id").include("data");List<Map> list = mongoTemplate.find(query, Map.class,  "User");Gson gson = new Gson();String res = "";if (list.size() != 0)res = gson.toJson(list.get(0));System.out.println(res);
//        FindIterable<Map> documents = mongoTemplate.getCollection("User").find(bson,Map.class);
//        for (Map document : documents) {
//            System.out.println(document);
//        }return res;}

1. 删除

删除一个节点,我们需要考虑两点:一是删除一个目录节点之后,对应子菜单以及子节点的删除;二是删除后,对后续节点ID值以及其parentID的更新。本文将通过队列进行整棵树的遍历操作,实现对应增删改操作。

        /* 删除 */function doDel(obj) {if(obj.data.authorityId != 1){layer.confirm('确定要删除选中数据吗?', {skin: 'layui-layer-admin',shade: .1}, function (i) {layer.close(i);var loadIndex = layer.load(2);var optiondata = $.extend(true, {}, insTb.options.data)var tree = insTb.options.data[0]              //insTb.options.data中存放着整个树var delNode = obj.data.authorityId              //delNode记录待删除的节点号var delParentvar delLength = 1                               //记录一共删除的节点个数var mark = false// console.log(tree)let node = []let res = []node.push(tree)while (node.length != 0) {                        //使用一个队列实现遍历整棵树var temp = node.shift()if (node.length != 0)var temp2 = node[0]elsevar temp2 = tempif (temp.authorityId != delNode) {if (mark) {temp.authorityId -= delLengthtemp.orderNumber -= delLengthif (temp.parentId > delParent)temp.parentId -= delLength}res.push(temp)}if (temp.authorityId == delNode && !mark) {           //如果当前节点是待删除根节点,判断其是否为目录,删除其所有子节点并将其标记为-2// console.log(delNode)if (temp.isMenu == 0) {                  //并计算出删除的总结点数delLength += (temp2.authorityId - temp.authorityId - 1);    //删除的节点数就是它自己加上它的子节点(下一个同级目录与它的序号差值)temp.authorityId = -2delete temp.children} else {temp.authorityId = -2}mark = truedelParent = temp.parentId}if (temp.children != undefined)                  //继续向后遍历for (var i = temp.children.length - 1; i >= 0; i--) {node.unshift(temp.children[i])}}for (var i = 0; i < res.length; i++) {delete res[i].childrendelete res[i].LAY_INDEXdelete res[i].openres[i]['open'] = trueres[i] = JSON.stringify(res[i])}console.log(res)$.ajax({url: 'request/changeMongoJson',type: 'POST',async: false,traditional: true,data: {"res": res, "name": "user1"},success: function (response) {//请求成功后执行的代码// insTb.options.data = optiondata// obj.del()res = []node = []layer.close(loadIndex);layer.msg("删除成功", {icon: 1});insTb.options.data = optiondatainsTb.refresh();},error: function (status) {//失败后执行的代码// alert("fail")// layer.close(loadIndex);// layer.msg("无法删除根目录", {icon: 2});// insTb.refresh();}})});}elselayer.msg("无法删除根目录", {icon: 2});}

2. 添加

添加操作相对简单于删除,只需将新增节点添加到其父节点的下一个,并将后续节点的ID以及parentID向后移动一位(+1)。

function doAdd(obj){mData = obj.dataif(obj.data.isMenu == 0)admin.open({type: 1,area: '600px',title: '新建文件',content: $('#authoritiesEditDialog').html(),success: function (layero, dIndex) {// 回显表单数据form.val('authoritiesEditForm', mData);// 表单提交事件form.on('submit(authoritiesEditSubmit)', function (data) {data.field.parentId = insXmSel.getValue('valueStr');var newNode = new Object();newNode["authorityId"] = parseInt(data.field.authorityId) + 1newNode["orderNumber"] = parseInt(data.field.authorityId) + 1newNode["authorityName"] = data.field.authorityNamenewNode["authority"] = data.field.authoritynewNode["isMenu"] = parseInt(data.field.isMenu)newNode["menuUrl"] = data.field.menuUrlnewNode["parentId"] = parseInt(data.field.parentId)newNode["menuIcon"] = nullDate.prototype.Format = function (fmt) { // author: meizzvar o = {"M+": this.getMonth() + 1, // 月份"d+": this.getDate(), // 日"h+": this.getHours(), // 小时"m+": this.getMinutes(), // 分"s+": this.getSeconds(), // 秒"q+": Math.floor((this.getMonth() + 3) / 3), // 季度"S": this.getMilliseconds() // 毫秒};if (/(y+)/.test(fmt))fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));for (var k in o)if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));return fmt;}newNode["createTime"] = new Date().Format("yyyy-MM-dd hh:mm:ss")newNode["updateTime"] = new Date().Format("yyyy-MM-dd hh:mm:ss")newNode["open"] = true// console.log(newNode)var tree = insTb.options.data[0]            //tt.options.data中存放着整个树var addNode = newNode.authorityId              //delNode记录待添加的节点号// console.log(insTb)let node = []let res = []var nodeNum = 0;node.push(tree)while(node.length != 0) {                        //使用一个队列实现遍历整棵树// if(addNode > )nodeNum = nodeNum + 1;var temp = node.shift()// console.log(temp)var tempcopy = new Object()if(temp.authorityId == addNode) {res.push(newNode)}if(temp.authorityId >= addNode) {       //待添加节点会增加到其父节点的第一个子节点(父节点编号10,则新增节点编号11)tempcopy["authorityId"] = temp.authorityId + 1      //所以所有编号在10之后的向后一位tempcopy["orderNumber"] = temp.authorityId + 1} else {tempcopy["authorityId"] = temp.authorityIdtempcopy["orderNumber"] = temp.authorityId}tempcopy["authorityName"] =temp.authorityNametempcopy["authority"] = temp.authoritytempcopy["isMenu"] = temp.isMenutempcopy["menuUrl"] = temp.menuUrlif(temp.parentId >= addNode) tempcopy["parentId"] = temp.parentId + 1       //同理,对应的父节点也向后一位else tempcopy["parentId"] = temp.parentIdtempcopy["menuIcon"] = nulltempcopy["createTime"] = temp.createTimetempcopy["updateTime"] = temp.updateTimetempcopy["open"] = trueres.push(tempcopy)// console.log(temp)if(temp.children != undefined)                  //继续向后遍历for(var i = temp.children.length - 1; i >= 0; i--){node.unshift(temp.children[i])}}if(nodeNum < addNode)res.push(newNode)//  console.log(tree)// console.log(res)for(var i = 0; i < res.length;i ++){res[i] = JSON.stringify(res[i])}console.log(res)var loadIndex = layer.load(2);$.ajax({url:'request/changeMongoJson',type:'POST',async: false,traditional: true,data:{"res": res, "name": "user1"},success:function(response){res = []node = []layer.close(dIndex);layer.close(loadIndex)layer.msg("添加成功", {icon: 1});insTb.refresh();},error:function(status){//失败后执行的代码// alert("fail")}})return false;});// 渲染下拉树var insXmSel = xmSelect.render({el: '#authoritiesEditParentSel',height: '0px',data: insTb.options.data,initValue: mData ? [mData.authorityId] : [],model: {label: {type: 'text'}},prop: {name: 'authorityName',value: 'authorityId'},radio: true,clickClose: true,tree: {show: true,indent: 15,strict: false,expandedKeys: true}});// 弹窗不出现滚动条$(layero).children('.layui-layer-content').css('overflow', 'visible');}});elselayer.msg("这不是一个菜单", {icon: 2});}

3. 后端

后端只需接收相应的字符串数组,将其封装为标准的Json格式并写入MongoDB数据库中。

    @RequestMapping(path = "/changeMongoJson", method = RequestMethod.POST)@ResponseBodypublic String changeMongoJson(String[] res, String name){for (String s:res) {System.out.println(s);}int len = res.length;StringBuilder sb = new StringBuilder();sb.append("{\n" +"  \"code\": 0,\n" +"  \"msg\": \"\",\n" +"  \"count\": "+ len +",\n" +"  \"name\": \""+ name +"\",\n" +"  \"data\": [");for(String temp : res){sb.append(temp + ",");}sb.deleteCharAt(sb.length() - 1);sb.append(" ]\n" +"}");Query query = new Query(Criteria.where("name").is("user1"));
//        query.fields().exclude("_id").include("data");
//        Update update = new Update();
//        update.set("count", res.length);
//        update.push("data", res);mongoTemplate.remove(query, Map.class,  "User");
//        System.out.println(1);
//        System.out.println(sb);mongoTemplate.insert(sb.toString(), "User");
//        System.out.println(sb);
//        Gson gson = new Gson();
//        TreeNode temp = gson.fromJson(res, TreeNode.class);return "iframe/index.html";}

方法二:基于Dtree实现的纯前端树形增删改

这个方法对后端访问频率低,在增删改时直接进行前端树样式的变换,即不是依靠先存入后端,再从后端读数据的模式,减少了数据库访问量。
实现效果图:
在这里插入图片描述
Dtree组件已经给出了前端修改的具体方法,只需调用对应方法,即可以实现树结构变更:

  1. 改–DTree1.changeTreeNodeEdit(true);
  2. 增–DTree1.changeTreeNodeAdd(treeNode.nodeId);
  3. 删–DTree1.changeTreeNodeDel(true);
<script type="text/javascript">layui.extend({dtree: '../assets/module/dtree/dtree'}).use(['element','layer', 'table', 'code' ,'util', 'dtree', 'form'], function(){var element = layui.element, layer = layui.layer, table = layui.table, util = layui.util, dtree = layui.dtree, form = layui.form, $ = layui.$;var olddata;// $.ajax({//     async: false,//     url: 'iframe/test2.json',//     success: function(res){//         olddata = res;//         console.log(olddata)//     }// })var DTree1 = dtree.render({async: false,elem: "#demoTree",method: "GET",url: 'iframe/test2.json',// data: data,toolbar:true,scroll:"#toolbarDiv",dataFormat: "list",  //配置data的风格为listtoolbarFun: {addTreeNode: function(treeNode, $div){console.log(treeNode)var id = treeNode.nodeId.replaceAll("_node_", "00")var title = treeNode.contextvar parentId = treeNode.parentId.replaceAll("_node_", "00")$.ajax({type: "post",traditional: true,data: {"status": "add", "filename": "test2","id": id, "title": title, "parentId": parentId},url: "request/changenewJson",success: function(result){DTree1.changeTreeNodeAdd(treeNode.nodeId); // 添加成功,返回ID// DTree1.changeTreeNodeAdd(true); // 添加成功// DTree1.changeTreeNodeAdd(result); // 添加成功,返回一个JSON对象// DTree1.changeTreeNodeAdd("refresh"); // 添加成功,局部刷新树},error: function(){DTree1.changeTreeNodeAdd(false); // 添加成功,返回ID}});},editTreeNode: function(treeNode, $div){console.log(treeNode)if (treeNode.nodeId === "000") {layer.msg("根目录无法修改" , {icon: 2})DTree1.changeTreeNodeEdit(false);//修改失败return}var id = treeNode.nodeId.replaceAll("_node_", "00")var title = treeNode.contextvar parentId = treeNode.parentId.replaceAll("_node_", "00")$.ajax({type: "post",traditional: true,data: {"status": "edit", "filename": "test2","id": id, "title": title, "parentId": parentId},url: "request/changenewJson",success: function(result){DTree1.changeTreeNodeEdit(true);// 修改成功//DTree1.changeTreeNodeEdit(result.param); // 修改成功,返回一个JSON对象},error: function(){DTree1.changeTreeNodeEdit(false);//修改失败}});},delTreeNode: function(treeNode, $div){console.log(treeNode)if (treeNode.nodeId === "000") {layer.msg("根目录无法删除" , {icon: 2})DTree1.changeTreeNodeDel(false);// 删除失败1return}var id = treeNode.nodeId.replaceAll("_node_", "00")var title = treeNode.contextvar parentId = treeNode.parentId.replaceAll("_node_", "00")$.ajax({type: "post",traditional: true,data: {"status": "del", "filename": "test2","id": id, "title": title, "parentId": parentId},url: "request/changenewJson",success: function(result){DTree1.changeTreeNodeDel(true); // 删除成功},error: function(){DTree1.changeTreeNodeDel(false);// 删除失败}});}}});dtree.on("node(demoTree)", function(obj){layer.msg(JSON.stringify(obj.param));})});
</script>

文中的组件地址

本文给出两种树形表格的实现方式,前者是基于EasyWeb-iframe框架实现;后者是基于Dtree组件实现。
EasyWeb-iframe下载地址:easyweb
Dtree组件下载地址:Dtree

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/263151.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

POJ 1723 Soldiers (中位数)

$ POJ~1723~Soldiers $ (中位数) $ solution: $ 这道题说难也不算太难&#xff0c;但是当时自己想的很矛盾。所以还是列一篇题解。 这道题首先比较容易看出来的就是&#xff1a;行和列是两个分开的问题&#xff0c;而且行的移动就是一个仓库选址的板子&#xff0c;直接求中位数…

(一)Windows环境下汇编编程读书笔记

看了一节关于80x86系列处理器简史&#xff0c;不知道云里和雾里&#xff0c;什么晶体管啊&#xff0c;什么什么之类的不知道云里和雾里&#xff0c;看了讲什么都不知道啊&#xff01; 转载于:https://www.cnblogs.com/Nuxgod/articles/692990.html

Docker知识点总结及其命令的使用

DockerDocker简介Docker与Tomcat有什么区别&#xff1f;Docker与虚拟机有什么区别&#xff1f;Docker的基本组成Docker的联合文件系统Docker基本命令Docker中的几个重要组件一、容器数据卷二、Dockerfile三、Docker网络虚拟机共享网络的三种方式Docker共享网络的四种方式Docker…

主题:Spring注解入门(转载)

原文链接&#xff1a;http://www.iteye.com/topic/295348 1. 使用Spring注解来注入属性 1.1. 使用注解以前我们是怎样注入属性的 类的实现&#xff1a; Java代码 public class UserManagerImpl implements UserManager { private UserDao userDao; public void …

XDJM的情意比山高,比海深!!

又是兄弟姐妹们帮我提前过的生日&#xff0c;我们这帮人从SC出来后还没好好聚过&#xff0c;乘这个机会把大家约了出来。星期五整整一天都很快乐&#xff0c;特别是我&#xff0c;NANA&#xff0c;小乔期待着晚上的聚餐&#xff0c;期待着金贸的蛋糕&#xff0c;嘿嘿。。他们好…

最大子矩阵和

最大子矩阵和 $ n^3 $ 算法 $ solution: $ 首先我们不难想到枚举上下左右边界&#xff0c;然后两层循环统计权值和&#xff0c;复杂度 $ O(n^6) $ 。这个我们用前缀和可以省去后面的循环&#xff0c;将复杂度降成 $ O(n^4) $ 。然后我们考虑不枚举上下左右四个边界&#xff0c;…

Springfox-swagger使用详解

Springfox-swagger使用详解什么是Swagger&#xff1f;Swagger的具体使用一、导入依赖二、建立Swagger配置类三、通过Swagger测试接口引用什么是Swagger&#xff1f; 是一个开源的API Doc的框架可以将我们的Controller中的API方法以文档的形式展现&#xff0c;并支持为其添加注…

Android中调用系统已安装的播放器来播放网络流媒体视频

2019独角兽企业重金招聘Python工程师标准>>> 实现思路比较简单几行代码就可以搞定&#xff0c;在界面放一个Button或者带有播放图标的imageview&#xff0c;点击事件中调用本地播放器来播放。 Uri uri Uri.parse("http://218.200.69.66:8302/upload/Media/20…

WireShark详解

WireShark详解Wireshark介绍Wireshark使用一、基础数据说明二、指定数据包过滤Wireshark安装Wireshark介绍 Wireshark是一款可以运行在多平台的网络抓包工具&#xff0c;可以嗅探通过本机网卡的各类网络包&#xff0c;并对它们的协议&#xff0c;源、目标地址等多种数据进行解…

多图上传 - Web Uploader

http://fex.baidu.com/webuploader/ 官方DEMO&#xff0c;我都不想说了&#xff0c;各种问题。参考ShuaiBi文章 http://www.cnblogs.com/ismars/p/4176912.html 用了bootstrap 代码百度网盘地址&#xff1a;http://pan.baidu.com/s/1pJkj9wf 自己参照改改就好了。 //所有文件上…

[开发手记] 使用.NET实现你的IP切换器

发布日期&#xff1a;2007.4.17 作者&#xff1a;Anytao ©2007 Anytao.com 转贴请注明出处&#xff0c;留此信息。 下载&#xff1a;[Anytao.IPHelper][代码下载&#xff0c;近期上传] 本文将介绍以下内容&#xff1a; • 批处理文件应用 • 调用外部应用 • 文件处理…

Linux文件读写改权限详解

Linux文件读写改权限详解文件的rwx权限 [ r ][ w ][ x ]该文件可读(read)该文件可修改(write)该文件可执行(execute) 注意&#xff1a;只有当该文件所在目录有写权限&#xff0c;并且该文件有[w]权限&#xff0c;才可以进行删除操作 目录的rwx权限 [ r ][ w ][ x ]可查看目录…

EF 从sqlserver2008 迁移到 2005出现的BUG

在VS10Server08中使用EF4.4&#xff0c;发布数据库为SQL05&#xff0c;发生错误的问题 问题描述环境描述&#xff1a;工具为VS2010SQL08 R2&#xff0c;EntityFramework版本为4.4&#xff08;在Framework4.5中为5.0&#xff09;&#xff0c;在本机完全测试完成后&#xff0c;发…

转盘不转动的问题

2019独角兽企业重金招聘Python工程师标准>>> Stack Overflow is a question and answer site for professional and enthusiast programmers. Its 100% free, no registration required. jQuery Rotate - IE7 & IE8 Issues up vote 11 down vote favorite 3 I a…

重构机房收费系统(二)

接上篇《机房收费系统重构&#xff08;一&#xff09;》 二、概要设计 完成了用例图&#xff0c;并用结合用例图完善了一下需求分析说明书&#xff0c;忘记是第几次修订需求分析说明书。有了用例图&#xff0c;很自然就进入了概要设计阶段。我认为这一阶段就是结合包图解决系统…

《Windows核心编程》学习笔记(9)– 在win7或者vista系统下提升一个进程的运行权限...

win7或者vista默认运行程序是在受限制的环境下运行的&#xff0c;以减轻病毒对于系统的破坏。那么我们怎样才能提升一个进程的权限以至让它在 管理员模式下运行。当然CreateProcess函数没有提供这个功能。相反我们需要调用的是ShellExecuteEx函数&#xff1a; BOOL ShellExecu…

2015第29周二AOP

1、问题&#xff1a;想要添加日志记录、性能监控、安全监测 2、最初解决方案2.1、最初解决方案&#xff1a;在每个需要的类函数中重复写上面处理代。 缺点&#xff1a;太多重复代码&#xff0c;且紧耦合2.2、抽象类进行共性设计&#xff0c;子类进行个性设计&#xff0c;此处不…

Docker-compose配置Mysql,Redis,MongoDB

Docker-compose下配置Mysql&#xff0c;Redis&#xff0c;MongoDB详解一、docker-compose简介二、构建一个实例项目1.前后端实现2.Dockerfile及docker-compose.yml3. 生成镜像三、实例程序源码一、docker-compose简介 Compose是用于定义和运行多容器Docker应用程序的工具。通过…

使用WebDeployment Project改善VS2005发布网站问题

VS2005发布网站时不会像VS2003一样生成规则的DLL文件、而生成的DLL文件名含有随机数且不能一个项目生成一个DLL文件、让人有一些遗憾、为了做到像vs2003一样&#xff0c;微软发布了WebDeployment Project插件可解决此问题&#xff1a;下载地址1、下载后安装、右键选择vs2005中的…

Linux、Windows、Mac下Docker的安装与使用

Linux、Windows、Mac下Docker的安装与使用一、Linux下的安装二、Windows与Mac下的安装三、Docker的使用一、Linux下的安装 这里以Centos 7为例&#xff0c;首先&#xff0c;确保系统已经正确运行了Centos 7并已经联网。yum安装gcc环境 yum -y install gcc yum -y install gcc…