菜单管理实现
程序员文章站
2022-05-06 18:20:57
...
文章目录
菜单列表(menu.html)
<!DOCTYPE html>
<html lang="zh_CN" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<meta charset="utf-8">
<head th:include="include :: header"></head>
<body class="gray-bg">
<div class="wrapper wrapper-content ">
<div class="col-sm-12">
<div class="ibox">
<div class="ibox-body">
<div id="exampleToolbar" role="group" class="t-bar">
<button shiro:hasPermission="sys:menu:add" type="button"
class="btn btn-primary" title="在根节点下添加菜单" onclick="add('0')">
<i class="fa fa-plus" aria-hidden="true"></i>添加
</button>
<button shiro:hasPermission="sys:menu:batchRemove" type="button"
class="btn btn-danger" onclick="batchRemove()">
<i class="fa fa-trash" aria-hidden="true"></i>删除
</button>
</div>
<table id="exampleTable" data-mobile-responsive="true">
</table>
</div>
</div>
</div>
<!--shiro控制bootstraptable行内按钮可见性 来自StudentBoot的创新方案 -->
<div>
<script type="text/javascript">
var s_add_h = 'hidden';
var s_edit_h = 'hidden';
var s_remove_h = 'hidden';
</script>
</div>
<div shiro:hasPermission="sys:menu:add">
<script type="text/javascript">
s_add_h = '';
</script>
</div>
<div shiro:hasPermission="sys:menu:edit">
<script type="text/javascript">
s_edit_h = '';
</script>
</div>
<div shiro:hasPermission="sys:menu:remove">
<script type="text/javascript">
var s_remove_h = '';
</script>
</div>
</div>
<div th:include="include::footer"></div>
<script src="/app/sys/menu/menu.js"></script>
</body>
</html>
菜单列表(menu.js)
var prefix = "/sys/menu"
$(document).ready(function () {
load();
});
var load = function () {
$('#exampleTable')
.bootstrapTable(
{
idField: 'menuId',
code: 'menuId',
method: "GET", // 请求数据的ajax类型
url: prefix + '/list', // 请求数据的ajax的url
queryParams: {sort:'order_num'}, // 请求数据的ajax的data属性
striped: true, // 是否各行渐变色
//cache: false,
toolbar : '#exampleToolbar',
showColumns: true, //是否显示所有的列(选择显示的列)
showRefresh: true, //是否显示刷新按钮
//clickToSelect: true, //是否启用点击选中行
columns: [
{
field: 'ck',
checkbox: true
},
{
title: '编号',
field: 'menuId',
visible: false,
align: 'center',
valign: 'center',
},
{
title: '名称',
valign: 'center',
field: 'name',
},
{
title: '图标',
field: 'icon',
align: 'center',
valign: 'center',
width : '5%',
formatter: function (value, row, index) {
return value == null ? ''
: '<i class="' + value
+ ' fa-lg"></i>';
}
},
{
title: '类型',
field: 'type',
align: 'center',
valign: 'center',
formatter: function (value, row, index) {
if (value === 0) {
return '<span class="label label-primary">目录</span>';
}
if (value === 1) {
return '<span class="label label-success">菜单</span>';
}
if (value === 2) {
return '<span class="label label-warning">按钮</span>';
}
}
},
{
title: '地址',
valign: 'center',
field: 'url'
},
{
title: '序号',
valign: 'center',
field: 'orderNum'
},
{
title: '权限标识',
valign: 'center',
field: 'perms'
},
{
title: '操作',
field: 'id',
align: 'center',
valign: 'center',
formatter: function (value, row, index) {
var e = '<a class="btn btn-primary btn-sm '
+ s_edit_h
+ '" href="#" mce_href="#" title="编辑" οnclick="edit(\''
+ row.menuId
+ '\')"><i class="fa fa-edit"></i></a> ';
var p = '<a class="btn btn-primary btn-sm '
+ s_add_h
+ '" href="#" mce_href="#" title="添加下级" οnclick="add(\''
+ row.menuId
+ '\')"><i class="fa fa-plus"></i></a> ';
var d = '<a class="btn btn-warning btn-sm '
+ s_remove_h
+ '" href="#" title="删除" mce_href="#" οnclick="remove(\''
+ row.menuId
+ '\')"><i class="fa fa-remove"></i></a> ';
return e + d + p;
}
}],
treeShowField: 'name',
parentIdField: 'parentId',
onPostBody: function() {
$('#exampleTable').treegrid({
initialState: 'collapsed',//收缩
treeColumn: 1,//指明第几列数据改为树形
expanderExpandedClass: 'glyphicon glyphicon-triangle-bottom',
expanderCollapsedClass: 'glyphicon glyphicon-triangle-right',
onChange: function() {
$('#exampleTable').bootstrapTable('resetWidth');
}
})
}
});
}
function refresh() {
$("#exampleTable").bootstrapTable("refresh");
}
function add(pId) {
layer.open({
type: 2,
title: '增加菜单',
maxmin: true,
shadeClose: false, // 点击遮罩关闭层
area: ['800px', '520px'],
content: prefix + '/add/' + pId // iframe的url
});
}
function remove(id) {
layer.confirm('确定要删除选中的记录?', {
btn: ['确定', '取消']
}, function () {
$.ajax({
url: prefix + "/remove",
type: "post",
data: {
'id': id
},
success: function (data) {
if (data.code == 0) {
layer.msg("删除成功");
refresh();
} else {
layer.msg(data.msg);
}
}
});
})
}
function edit(id) {
layer.open({
type: 2,
title: '菜单修改',
maxmin: true,
shadeClose: false, // 点击遮罩关闭层
area: ['800px', '520px'],
content: prefix + '/edit/' + id // iframe的url
});
}
function batchRemove() {
// var rows = $('#exampleTable').bootstrapTable('getSelections');
}
菜单添加(add.html)
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<meta charset="utf-8">
<head th:include="include :: header"></head>
<body class="gray-bg">
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-content">
<form class="form-horizontal m-t" id="signupForm">
<input id="parentId" name="parentId" type="hidden"
th:value="${pId}">
<div class="form-group">
<label class="col-sm-3 control-label">上级菜单:</label>
<div class="col-sm-8">
<input id="" name="" class="form-control" type="text"
th:value="${pName}" readonly>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">菜单类型:</label>
<div class="col-sm-8">
<label class="radio-inline"> <input type="radio"
name="type" value="0" /> 目录
</label> <label class="radio-inline"> <input type="radio"
name="type" value="1" /> 菜单
</label> <label class="radio-inline"> <input type="radio"
name="type" value="2" /> 按钮
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">菜单名称:</label>
<div class="col-sm-8">
<input id="name" name="name" class="form-control" type="text">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">链接地址:</label>
<div class="col-sm-8">
<input id="url" name="url" class="form-control" type="text">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">权限标识:</label>
<div class="col-sm-8">
<input id="perms" name="perms" class="form-control" type="text">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">排序号:</label>
<div class="col-sm-8">
<input id="orderNum" name="orderNum" class="form-control"
type="text">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">图标:</label>
<div class="col-sm-6">
<input id="icon" name="icon" class="form-control" type="text"
placeholder="例如:fa fa-circle-o">
</div>
<input id="ico-btn" class="btn btn-warning" type="button" value="选择图标">
</div>
<div class="form-group">
<div class="col-sm-8 col-sm-offset-3">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div th:include="include::footer"></div>
<script src="/app/sys/menu/add.js"></script>
</body>
</html>
菜单添加(add.js)
var prefix = "/sys/menu"
$(function() {
validateRule();
//打开图标列表
$("#ico-btn").click(function(){
layer.open({
type: 2,
title:'图标列表',
content: '/FontIcoList.html',
area: ['480px', '90%'],
success: function(layero, index){
//var body = layer.getChildFrame('.ico-list', index);
//console.log(layero, index);
}
});
});
});
$.validator.setDefaults({
submitHandler : function() {
save();
}
});
function save() {
$.ajax({
cache : true,
type : "POST",
url : prefix + "/save",
data : $('#signupForm').serialize(),
async : false,
error : function(request) {
layer.alert("Connection error");
},
success : function(data) {
if (data.code == 0) {
parent.layer.msg("保存成功");
parent.refresh();
var index = parent.layer.getFrameIndex(window.name); // 获取窗口索引
parent.layer.close(index);
} else {
layer.alert(data.msg)
}
}
});
}
function validateRule() {
var icon = "<i class='fa fa-times-circle'></i> ";
$("#signupForm").validate({
rules : {
name : {
required : true
},
type : {
required : true
}
},
messages : {
name : {
required : icon + "请输入菜单名"
},
type : {
required : icon + "请选择菜单类型"
}
}
})
}
菜单编辑(edit.html)
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="shortcut icon" href="favicon.ico">
<link href="/bootstrap/bootstrap.css" rel="stylesheet">
<link href="/font-awesome/font-awesome.css" rel="stylesheet">
<link href="/animate/animate.css" rel="stylesheet">
<link href="/app/style.css" rel="stylesheet">
</head>
<body class="gray-bg">
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-content">
<form class="form-horizontal m-t" id="signupForm">
<input id="parentId" name="parentId" type="hidden"
th:value="${pId}" /> <input id="menuId" name="menuId"
type="hidden" th:value="${menu.menuId}" />
<div class="form-group">
<label class="col-sm-3 control-label">上级菜单:</label>
<div class="col-sm-8">
<input id="" name="" class="form-control" type="text"
th:value="${pName}" readonly>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">菜单类型:</label>
<div class="col-sm-8">
<label class="radio-inline"> <input type="radio"
th:field="*{menu.type}" name="type" value="0" /> 目录
</label> <label class="radio-inline"> <input type="radio"
th:field="*{menu.type}" name="type" value="1" /> 菜单
</label> <label class="radio-inline"> <input type="radio"
th:field="*{menu.type}" name="type" value="2" /> 按钮
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">菜单名称:</label>
<div class="col-sm-8">
<input id="name" name="name" class="form-control" type="text"
th:value="${menu.name}" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">链接地址:</label>
<div class="col-sm-8">
<input id="url" name="url" class="form-control" type="text"
th:value="${menu.url}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">权限标识:</label>
<div class="col-sm-8">
<input id="perms" name="perms" class="form-control" type="text"
th:value="${menu.perms}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">排序号:</label>
<div class="col-sm-8">
<input id="orderNum" name="orderNum" class="form-control"
type="text" th:value="${menu.orderNum}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">图标:</label>
<div class="col-sm-5">
<input id="icon" name="icon" class="form-control" type="text"
placeholder="例如:fa fa-circle-o" th:value="${menu.icon}">
</div>
<input id="ico-btn" class="btn btn-warning" type="button" value="选择图标">
</div>
<div class="form-group">
<div class="col-sm-8 col-sm-offset-3">
<button type="submit" class="btn btn-info">提交</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div th:include="include::footer"></div>
<script src="/app/sys/menu/edit.js"></script>
</body>
</html>
菜单编辑(edit.js)
var prefix = "/sys/menu"
$(function() {
validateRule();
//打开图标列表
$("#ico-btn").click(function(){
layer.open({
type: 2,
title:'图标列表',
content: '/FontIcoList.html',
area: ['480px', '90%'],
success: function(layero, index){
//var body = layer.getChildFrame('.ico-list', index);
//console.log(layero, index);
}
});
});
});
$.validator.setDefaults({
submitHandler : function() {
update();
}
});
function update() {
$.ajax({
cache : true,
type : "POST",
url : prefix + "/update",
data : $('#signupForm').serialize(),// 你的formid
async : false,
error : function(request) {
layer.alert("Connection error");
},
success : function(data) {
if (data.code == 0) {
parent.layer.msg("保存成功");
parent.refresh();
var index = parent.layer.getFrameIndex(window.name); // 获取窗口索引
parent.layer.close(index);
} else {
layer.alert(data.msg)
}
}
});
}
function validate() {
var icon = "<i class='fa fa-times-circle'></i> ";
$("#signupForm").validate({
rules : {
name : {
required : true
},
type : {
required : true
}
},
messages : {
name : {
required : icon + "请输入菜单名"
},
type : {
required : icon + "请选择菜单类型"
}
}
})
}
function validateRule() {
var icon = "<i class='fa fa-times-circle'></i> ";
$("#signupForm").validate({
rules : {
name : {
required : true
},
type : {
required : true
}
},
messages : {
name : {
required : icon + "请输入菜单名"
},
type : {
required : icon + "请选择菜单类型"
}
}
})
}
菜单管理接口(Java)
package com.yangzc.studentboot.system.controller;
import com.yangzc.studentboot.common.config.Constant;
import com.yangzc.studentboot.common.controller.BaseController;
import com.yangzc.studentboot.common.domain.Tree;
import com.yangzc.studentboot.common.utils.R;
import com.yangzc.studentboot.system.domain.MenuDO;
import com.yangzc.studentboot.system.service.MenuService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* @author yangzc aaa@qq.com
*/
@RequestMapping("/sys/menu")
@Controller
public class MenuController extends BaseController {
String prefix = "/menu";
@Autowired
MenuService menuService;
@RequiresPermissions("sys:menu:menu")
@GetMapping()
String menu(Model model) {
return prefix+"/menu";
}
@RequiresPermissions("sys:menu:menu")
@RequestMapping("/list")
@ResponseBody
List<MenuDO> list(@RequestParam Map<String, Object> params) {
List<MenuDO> menus = menuService.list(params);
return menus;
}
@RequiresPermissions("sys:menu:add")
@GetMapping("/add/{pId}")
String add(Model model, @PathVariable("pId") Long pId) {
model.addAttribute("pId", pId);
if (pId == 0) {
model.addAttribute("pName", "根目录");
} else {
model.addAttribute("pName", menuService.get(pId).getName());
}
return prefix + "/add";
}
@RequiresPermissions("sys:menu:edit")
@GetMapping("/edit/{id}")
String edit(Model model, @PathVariable("id") Long id) {
MenuDO mdo = menuService.get(id);
Long pId = mdo.getParentId();
model.addAttribute("pId", pId);
if (pId == 0) {
model.addAttribute("pName", "根目录");
} else {
model.addAttribute("pName", menuService.get(pId).getName());
}
model.addAttribute("menu", mdo);
return prefix+"/edit";
}
@RequiresPermissions("sys:menu:add")
@PostMapping("/save")
@ResponseBody
R save(MenuDO menu) {
if (Constant.DEMO_ACCOUNT.equals(getUsername())) {
return R.error(1, "演示系统不允许修改,完整体验请部署程序");
}
if (menuService.save(menu) > 0) {
return R.ok();
} else {
return R.error(1, "保存失败");
}
}
@RequiresPermissions("sys:menu:edit")
@PostMapping("/update")
@ResponseBody
R update(MenuDO menu) {
if (Constant.DEMO_ACCOUNT.equals(getUsername())) {
return R.error(1, "演示系统不允许修改,完整体验请部署程序");
}
if (menuService.update(menu) > 0) {
return R.ok();
} else {
return R.error(1, "更新失败");
}
}
@RequiresPermissions("sys:menu:remove")
@PostMapping("/remove")
@ResponseBody
R remove(Long id) {
if (Constant.DEMO_ACCOUNT.equals(getUsername())) {
return R.error(1, "演示系统不允许修改,完整体验请部署程序");
}
if (menuService.remove(id) > 0) {
return R.ok();
} else {
return R.error(1, "删除失败");
}
}
@GetMapping("/tree")
@ResponseBody
Tree<MenuDO> tree() {
Tree<MenuDO> tree = menuService.getTree();
return tree;
}
@GetMapping("/tree/{roleId}")
@ResponseBody
Tree<MenuDO> tree(@PathVariable("roleId") Long roleId) {
Tree<MenuDO> tree = menuService.getTree(roleId);
return tree;
}
}
github项目地址
https://github.com/yangzc23/studentboot
参考资料
[01] 弹层组件文档 - layui.layer
[02] bootstrap-table-examples
[03] 简单知识点实例之四:BootstrapTable新型树表格
微信扫一扫关注公众号
点击链接加入群聊
https://jq.qq.com/?_wv=1027&k=5eVEhfN
软件测试学习交流QQ群号:511619105