springboot2.x+security+vue2.x后台管理框架---菜单管理(五)
菜单管理
一、数据库构建
-- longzy.sys_menu definition
CREATE TABLE `sys_menu` (
`menuid` int(20) NOT NULL AUTO_INCREMENT COMMENT '菜单id',
`name` varchar(200) NOT NULL COMMENT '菜单名称',
`parentid` int(20) NOT NULL COMMENT '父菜单id',
`path` varchar(255) DEFAULT NULL COMMENT '菜单url',
`perms` longtext COMMENT '权限',
`menutype` varchar(1) NOT NULL COMMENT '菜单类型(0目录,1菜单,2按钮)',
`icon` varchar(100) DEFAULT NULL COMMENT '菜单图标',
`orderno` int(20) DEFAULT NULL COMMENT '排序号',
`createuser` int(20) NOT NULL COMMENT '创建人',
`createtime` date NOT NULL COMMENT '创建时间',
`effective` varchar(1) NOT NULL COMMENT '有效状态',
`destory` varchar(1) NOT NULL COMMENT '注销状态',
PRIMARY KEY (`menuid`)
) ENGINE=InnoDB AUTO_INCREMENT=1000000007 DEFAULT CHARSET=utf8mb4 COMMENT='菜单表';
二、前端与后台实现
1、前端效果
2、自定义tag
效果:
自定义tag:
<template>
<div class="tag">
<span class="title">{{title}}</span>
<el-tag :size="size"
v-for="tag in tagData"
:key="tag.code"
:class="{active: tag.code == active}"
@click="handleTag(tag)"
>
{{tag.label}}
</el-tag>
</div>
</template>
<script>
export default {
name: 'myTag',
props: {
title: {
type: String,
default: ''
},
size: {
type: String,
default: ''
},
tagData: {
type: Array,
default: () => []
}
},
data(){
return{
active: ''
}
},
methods: {
handleTag(tag){
if (this.active != tag.code){
this.active = tag.code
this.$emit('handleTag', tag)
}else {
this.active = ''
this.$emit('handleTag')
}
}
}
}
</script>
<style lang="less" scoped>
.tag{
font-size: 12px;
.title {
margin-right: 10px;
}
.el-tag{
margin-right: 8px;
}
.active {
background-color: #66b1ff;
color: #fff;
}
}
</style>
调用代码:
<my-tag title="有效状态: " size="small"
:tagData="$store.getters.getDictList('EFFECTIVE')"
@handleTag="handleTag">
</my-tag>
3、页面实现代码
<template>
<div class="app-content">
<div class="search">
<el-input v-model="searchInfo" placeholder="请输入菜单名称"/>
<el-button type="primary" icon="el-icon-search" @click="queryMenuByCondition">搜索</el-button>
</div>
<div class="op-btn">
<div class="op-btn-left">
<my-tag title="有效状态: " size="small"
:tagData="$store.getters.getDictList('EFFECTIVE')"
@handleTag="handleTag">
</my-tag>
</div>
</div>
<my-table :data="menuTreeData"
:columns="columns"
height="600px"
:selection="true"
tooltipEffect="dark"
:highlightCurrentRow="true"
rowKey="menuId"
>
<!-- 菜单类型 -->
<template slot="menuType" slot-scope="{scope}">
<el-tag
:type="scope.row.menuType === '0' ? '' : (scope.row.menuType == '1' ? 'success' : 'danger')"
disable-transitions>{{$store.getters.getDictLabelByType('MENUTYPE', scope.row.menuType)}}</el-tag>
</template>
<!-- 有效状态 -->
<template slot="effective" slot-scope="{scope}">
<el-tag
:type="scope.row.effective === '1' ? 'success' : 'danger'"
disable-transitions>{{$store.getters.getDictLabelByType('EFFECTIVE', scope.row.effective)}}</el-tag>
</template>
<!-- 操作 -->
<template slot="operate" slot-scope="{scope}">
<el-button type="text" size="small"
@click="handleAddMenu(scope.row)" :disabled="scope.row.effective == 0 ? true : false">添加下级</el-button>
<el-button type="text" size="small"
@click="handleEditMenu(scope.row)" :disabled="scope.row.effective == 0 ? true : false">编辑</el-button>
<el-dropdown>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="handleEffective('1', scope.row)" >启用</el-dropdown-item>
<el-dropdown-item @click.native="handleEffective('2', scope.row)" divided >禁用</el-dropdown-item>
<el-dropdown-item @click.native="handleDelete(scope.row)" divided>删除</el-dropdown-item>
</el-dropdown-menu>
<span class="el-dropdown-link">
更多<i class="el-icon-arrow-down el-icon--right"></i>
</span>
</el-dropdown>
</template>
</my-table>
<!-- 编辑/新增菜单 -->
<edit-menu :visible="editVisible" :rowData="rowData" @close="editVisible = false" @queryMenuByCondition="queryMenuByCondition"></edit-menu>
</div>
</template>
<script>
import myTag from '@/components/tag/myTag'
import $api from './api/index'
import editMenu from './part/editMenu';
const columns = [
{label: '菜单名称', prop: 'name'},
{label: '菜单路径', prop: 'path', align: 'center'},
{label: '菜单类型', prop: 'menuType', align: 'center', slot: 'menuType'},
{label: '有效状态', prop: 'effective', align: 'center', slot: 'effective'},
{label: '操作', prop: 'operate', align: 'center', slot: 'operate'}
]
export default{
components: { myTag, editMenu },
name: 'menuManagement',
data() {
return{
editVisible: false,
columns,
searchInfo: '',
menuTreeData: [],
rowData: {},
effectiveTag: '',
}
},
mounted() {
this.queryMenuByCondition()
},
methods: {
// 添加下级菜单
handleAddMenu(val){
this.rowData = {}
this.editVisible = true
this.rowData.parentName = val.name
this.rowData.editType = '1'
this.rowData.parentId = val.menuId
},
// 编辑菜单
handleEditMenu(val){
this.rowData = {}
this.editVisible = true
this.rowData = val
this.rowData.editType = '2'
},
// 有效状态条件
handleTag(val){
this.effectiveTag = ''
if (val != undefined){
this.effectiveTag = val.code
}
this.queryMenuByCondition()
},
// 填充表格数据
queryMenuByCondition(){
let param = {
searchInfo: this.searchInfo,
effective: this.effectiveTag
}
$api.queryMenuByCondition(param).then(res => {
if (res.data.code == 200){
this.menuTreeData = res.data.data
}
})
},
// 更新有效状态: 1启用 2禁用
handleEffective(type, row){
if (type == "1"){
if (row.effective == "1"){
this.$message({
showClose: true,
message: '该记录已启用,请勿重复操作.',
type: 'error',
})
return
}
}else {
if (row.effective != "1"){
this.$message({
showClose: true,
message: '该记录已禁用,请勿重复操作.',
type: 'error',
})
return
}
}
$api.updateEffective({type: type, menuId: row.menuId}).then(res => {
if (res.data.code == 200){
this.queryMenuByCondition()
this.$message({
showClose: true,
message: type == '1' ? '启用成功.' : '禁用成功.',
type: 'success',
})
}
})
},
// 删除
handleDelete(row){
this.$confirm('确认删除该记录?',{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
$api.deleteMenuByMenuId({menuId: row.menuId}).then(res => {
if (res.data.code == 200){
this.queryMenuByCondition()
this.$message({
showClose: true,
message: '删除成功.',
type: 'success',
})
}
})
})
}
}
}
</script>
<style lang="less" scoped>
.search {
text-align: center;
margin-bottom: 40px;
.el-input{
width: 300px;
}
}
.op-btn{
display: flex;
align-content: center;
justify-content: space-between;
padding: 2px 5px;
}
.el-button{
margin-right: 10px;
}
.el-dropdown-link {
cursor: pointer;
color: #409EFF;
}
.el-icon-arrow-down {
font-size: 12px;
}
</style>
4、编辑实现效果及代码
<template>
<el-drawer
:title="rowData.editType == '2' ? '编辑菜单' : '添加下级菜单'"
:visible="visible"
size="25%"
:before-close="closeDrawer"
:destroy-on-close="true"
>
<el-form ref="formData" :rules="formRules" :model="formData" class="drawer__content" label-width="120px">
<el-form-item label="上级菜单" prop="parentName">
<el-input v-model="formData.parentName" autocomplete="off" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="菜单名称" prop="name">
<el-input v-model="formData.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="菜单路径" prop="path">
<el-input v-model="formData.path" autocomplete="off" placeholder="如:/home"></el-input>
</el-form-item>
<el-form-item label="菜单权限" prop="perms">
<el-input v-model="formData.perms" autocomplete="off" placeholder="如:sys:system"></el-input>
</el-form-item>
<el-form-item label="菜单图标" prop="icon">
<icon-select :iconName="formData.icon" @selected="selected"></icon-select>
</el-form-item>
<el-form-item label="菜单类型" prop="menuType">
<el-radio-group v-model="formData.menuType">
<el-radio
v-for="item in $store.getters.getDictList('MENUTYPE')"
:key="item.code"
:label="item.code">
{{item.label}}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序号" prop="orderNo">
<el-input-number v-model="formData.orderNo" controls-position="right" :min="1"></el-input-number>
</el-form-item>
<el-form-item label="有效状态" prop="effective" >
<el-switch v-model="formData.effective"></el-switch>
</el-form-item>
</el-form>
<div class="drawer__footer">
<el-button @click="resetForm('formData')">重 置</el-button>
<el-button type="primary" @click="submitForm('formData')">确 定</el-button>
</div>
</el-drawer>
</template>
<script>
import iconSelect from '@/components/iconSelect/iconSelect'
import $api from '../api/index'
export default {
components: { iconSelect },
name: 'editMenu',
props: ['visible', 'rowData'],
data(){
return {
drawer: false,
formData: {},
formRules: {
parentName: [
{ required: true, message: '请输入上级菜单', trigger: 'blur'}
],
name: [
{ required: true, message: '请输入菜单名称', trigger: 'blur'}
],
menuType: [
{ required: true, message: '请输入菜单类型', trigger: 'blur'}
]
}
}
},
watch: {
visible(val){
if (val){
this.setFormValue()
}
}
},
methods: {
// 表单数据
setFormValue(){
const {menuId, menuType, name, path, perms, component, icon, editType, orderNo, effective, parentName, parentId} = this.rowData
if(editType == '1'){
this.formData = {
parentName: parentName,
parentId: parentId,
effective: true,
orderNo: orderNo ? orderNo : 1
}
}else {
this.formData = {
menuId: menuId,
menuType: menuType,
name: name,
path: path,
perms: perms,
component: component,
icon: icon,
orderNo: orderNo,
parentName: parentName,
effective: effective == '1' ? true : false
}
}
},
// 图标选择
selected(icon){
this.formData.icon = icon
},
// 编辑/新增保存
submitForm(formName){
this.$refs[formName].validate((valid) => {
if (valid){
this.formData.effective = this.formData.effective ? '1' : '0'
// 新增
if(this.rowData.editType == '1'){
$api.addMenu(this.formData).then(res => {
if (res.data.code == 200){
this.closeDrawer()
this.$emit('queryMenuByCondition')
this.$message({
showClose: true,
message: '新增菜单成功.',
type: 'success',
})
}
})
}else { // 编辑
$api.editMenu(this.formData).then(res => {
if (res.data.code == 200){
this.$emit('queryMenuByCondition')
this.closeDrawer()
this.$message({
showClose: true,
message: '编辑菜单成功..',
type: 'success',
})
}
})
}
}
})
},
// 重置表单
resetForm(formName){
this.$refs[formName].resetFields();
this.setFormValue()
},
// 关闭
closeDrawer(){
this.formData = {}
this.resetForm('formData')
this.$emit('close')
}
}
}
</script>
<style lang="less" scoped>
/deep/ .el-drawer__header{
padding: 20px;
border-bottom: 1px solid rgba(0,21,41,0.08);
}
.drawer__content {
width: 90%;
}
.drawer__footer{
position: absolute;
bottom: 20px;
left: 25px;
.el-button{
width: 150px;
margin-right: 15px;
}
}
</style>
5、调用js方法
import request from '@/axios/request';
const api = {
// 填充树形数据
queryMenuByCondition(data){
return request.post("/menu/queryMenuByCondition", data, true)
},
// 新增菜单
addMenu(data){
return request.post('/menu/addMenu', data)
},
// 编辑菜单
editMenu(data){
return request.post('/menu/editMenu', data)
},
// 更新菜单有效状态
updateEffective(data){
return request.post("/menu/updateEffectiveByMenuId", data)
},
// 删除菜单
deleteMenuByMenuId(data){
return request.post("/menu/deleteMenuByMenuId", data)
}
}
export default api
6、后台实现
(1)、结构
(2)、自定义构建树形结构工具类
package com.longzy.common.bean;
import java.util.List;
/**
* @Desc: 构建树形结构接口
* @Packge: com.longzy.common.bean
* @Author: longzy
* @Date: 2022/9/24 22:41
*/
public interface TreeBean<T> {
public Object getId();
public Object getParentId();
// 子节点
public void setChildren(List<T> children);
}
package com.longzy.common.utils;
import com.longzy.common.bean.TreeBean;
import com.longzy.component.menu.vo.SysMenuVo;
import org.apache.commons.lang3.ObjectUtils;
import java.util.*;
/**
* @Desc: 树形数据结构工具类
* @Packge: com.longzy.common.utils
* @Author: longzy
* @Date: 2022/9/24 21:31
*/
public class TreeUtils {
/**
* 构建树形结构数据
* @param lists 数据
* @param topId 顶级id
* @return
*/
public static <T extends TreeBean<T>> List<T> buildTree(List<T> lists, Object topId) {
List<T> finalNodes = new ArrayList();
if (ObjectUtils.isEmpty(topId) && ObjectUtils.isEmpty(lists)){
return finalNodes;
}
// 获取顶级节点集合
for (T bean: lists) {
Object parentId = bean.getParentId();
if (parentId == null || topId.equals(parentId)){
finalNodes.add(bean);
}
}
// 获取顶级节点的子节点集合
for (T bean : finalNodes){
bean.setChildren(getChildren(bean.getId(), lists));
}
// 没有顶级元素择返回搜索结果(0为顶级元素)
if (finalNodes.size() < 1){
return lists;
}
return finalNodes;
}
private static <T extends TreeBean<T>> List<T> getChildren(Object id, List<T> nodeList){
List<T> childrenNodes = new ArrayList();
if (ObjectUtils.isEmpty(id)){
return childrenNodes;
}
// 获取顶级节点集合
for (T bean: nodeList) {
Object parentId = bean.getParentId();
if (id.equals(parentId)){
childrenNodes.add(bean);
}
}
// 获取顶级节点的子节点集合
for (T bean : childrenNodes){
bean.setChildren(getChildren(bean.getId(), nodeList));
}
return childrenNodes;
}
public static void main(String[] args) {
List list = new ArrayList();
SysMenuVo map = new SysMenuVo();
map.setMenuId(1);
map.setName("系統管理");
map.setParentId(0);
map.setOrderNo(10);
SysMenuVo map1 = new SysMenuVo();
map1.setMenuId(2);
map1.setName("用户管理");
map1.setParentId(1);
map1.setOrderNo(10);
SysMenuVo map3 = new SysMenuVo();
map3.setMenuId(3);
map3.setName("菜单管理");
map3.setParentId(1);
map3.setOrderNo(5);
SysMenuVo map2 = new SysMenuVo();
map2.setMenuId(4);
map2.setName("资源管理");
map2.setParentId(0);
map2.setOrderNo(5);
SysMenuVo map4 = new SysMenuVo();
map4.setMenuId(5);
map4.setName("字典管理");
map4.setParentId(4);
map4.setOrderNo(10);
list.add(map);
list.add(map1);
list.add(map2);
list.add(map3);
// list.add(map4);
System.out.println(buildTree(list, 0));
List<SysMenuVo> menuList = buildTree(list, 0);
Collections.sort(menuList, new Comparator<SysMenuVo>() {
@Override
public int compare(SysMenuVo o1, SysMenuVo o2) {
return o1.getOrderNo() - o2.getOrderNo();
}
});
System.out.println(menuList);
}
}
(3)、实体类po与vo
package com.longzy.component.menu.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* @Desc: 菜单实体类
* @Packge: com.longzy.component.menu.entity
* @Author: longzy
* @Date: 2022/9/24 21:05
*/
@ApiModel
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SysMenuPo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "菜单id")
private Integer menuId;
@ApiModelProperty(value = "菜单名称")
private String name;
@ApiModelProperty(value = "父菜单id")
private Integer parentId;
@ApiModelProperty(value = "菜单url")
private String path;
@ApiModelProperty(value = "权限")
private String perms;
@ApiModelProperty(value = "菜单类型")
private String menuType;
@ApiModelProperty(value = "菜单图标")
private String icon;
@ApiModelProperty(value = "排序号")
private Integer orderNo;
@ApiModelProperty(value = "创建人")
private Integer createUser;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "有效状态")
private String effective;
@ApiModelProperty(value = "销毁状态")
private String destory;
}
package com.longzy.component.menu.vo;
import com.longzy.common.bean.TreeBean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* @Desc: 菜单实体类
* @Packge: com.longzy.component.menu.entity
* @Author: longzy
* @Date: 2022/9/24 21:05
*/
@ApiModel
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SysMenuVo implements Serializable, TreeBean<SysMenuVo> {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "菜单id")
private Integer menuId;
@ApiModelProperty(value = "菜单名称")
private String name;
@ApiModelProperty(value = "父菜单id")
private Integer parentId;
@ApiModelProperty(value = "父菜单名称")
private String parentName;
@ApiModelProperty(value = "菜单url")
private String path;
@ApiModelProperty(value = "权限")
private String perms;
@ApiModelProperty(value = "菜单类型")
private String menuType;
@ApiModelProperty(value = "菜单图标")
private String icon;
@ApiModelProperty(value = "排序号")
private Integer orderNo;
@ApiModelProperty(value = "创建人")
private Integer createUser;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "有效状态")
private String effective;
@ApiModelProperty(value = "销毁状态")
private String destory;
@ApiModelProperty(value = "子节点")
private List<SysMenuVo> children;
@Override
public Integer getId() {
return this.menuId;
}
}
(4)、控制层(controller)实现
package com.longzy.component.menu.controller;
import com.longzy.common.response.CommonReturnType;
import com.longzy.component.menu.service.read.SysMenuReadService;
import com.longzy.component.menu.service.write.SysMenuWriteService;
import com.longzy.component.menu.vo.SysMenuVo;
import com.longzy.component.user.service.read.SysUserReadService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
import java.util.List;
/**
* @Desc: 菜单
* @Packge: com.longzy.component.menu.controller
* @Author: longzy
* @Date: 2022/9/24 21:03
*/
@Api(value = "菜单模块")
@RestController
@RequestMapping("menu")
public class SysMenuController {
@Autowired
private SysMenuReadService sysMenuReadService;
@Autowired
private SysMenuWriteService sysMenuWriteService;
@Autowired
private SysUserReadService sysUserReadService;
@ApiOperation(value = "菜单查询", notes = "根据条件查询树形菜单列表")
@PostMapping("queryMenuByCondition")
public CommonReturnType queryMenuByCondition(String effective, String searchInfo){
List<SysMenuVo> treeMenu = sysMenuReadService.queryMenuByCondition(effective, searchInfo);
return CommonReturnType.success("操作成功", treeMenu);
}
@ApiOperation(value = "添加菜单")
@PostMapping("addMenu")
public CommonReturnType addMenu(Principal principal, SysMenuVo sysMenuVo){
// 获取当前操作人员id
int currentUserId = sysUserReadService.getUserIdByLoginId(principal.getName());
sysMenuWriteService.addMenu(currentUserId, sysMenuVo);
return CommonReturnType.success("操作成功.");
}
@ApiOperation(value = "编辑菜单")
@PostMapping("editMenu")
public CommonReturnType editMenu(SysMenuVo menuVo){
sysMenuWriteService.editMenu(menuVo);
return CommonReturnType.success("操作成功.");
}
@ApiOperation(value = "更新菜单状态")
@PostMapping("updateEffectiveByMenuId")
public CommonReturnType updateEffectiveByMenuId(String type, int menuId){
sysMenuWriteService.updateEffective(type, menuId);
return CommonReturnType.success("操作成功.");
}
@ApiOperation(value = "删除菜单", notes = "根据菜单id删除菜单项")
@PostMapping("deleteMenuByMenuId")
public CommonReturnType deleteMenuByMenuId(int menuId){
sysMenuWriteService.deleteMenuByMenuId(menuId);
return CommonReturnType.success("操作成功.");
}
}
(5)、服务层(service)实现
read:
package com.longzy.component.menu.service.read;
import com.longzy.component.menu.vo.SysMenuVo;
import java.util.List;
/**
* @Desc:
* @Packge: com.longzy.component.menu.service.read
* @Author: longzy
* @Date: 2022/9/24 21:19
*/
public interface SysMenuReadService {
List<SysMenuVo> queryMenuByCondition(String effective, String searchInfo);
}
package com.longzy.component.menu.service.read.impl;
import com.longzy.common.utils.TreeUtils;
import com.longzy.component.menu.mapper.read.SysMenuReadMapper;
import com.longzy.component.menu.service.read.SysMenuReadService;
import com.longzy.component.menu.vo.SysMenuVo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @Desc:
* @Packge: com.longzy.component.menu.service.read.impl
* @Author: longzy
* @Date: 2022/9/24 21:19
*/
@Service
public class SysMenuReadServiceImpl implements SysMenuReadService {
@Resource
private SysMenuReadMapper sysMenuReadMapper;
@Override
public List<SysMenuVo> queryMenuByCondition(String effective, String searchInfo) {
List<SysMenuVo> menuList = sysMenuReadMapper.queryMenuByCondition(effective, searchInfo, "0");
// 构建树形结构数据
List<SysMenuVo> menuTree = TreeUtils.buildTree(menuList, 0);
// 节点排序(升序)
compareTo(menuTree);
return menuTree;
}
/**
* 排序
* @param menuList 需排序的集合
*/
private static void compareTo(List<SysMenuVo> menuList) {
Collections.sort(menuList, new Comparator<SysMenuVo>() {
@Override
public int compare(SysMenuVo o1, SysMenuVo o2) {
return o1.getOrderNo() - o2.getOrderNo();
}
});
for (SysMenuVo menuVo : menuList){
if (menuVo.getChildren() != null && menuVo.getChildren().size() > 0){
compareTo(menuVo.getChildren());
}
}
}
}
write:
package com.longzy.component.menu.service.write;
import com.longzy.component.menu.vo.SysMenuVo;
/**
* @Desc:
* @Packge: com.longzy.component.menu.service.write
* @Author: longzy
* @Date: 2022/9/26 16:27
*/
public interface SysMenuWriteService {
void addMenu(int currentUserId, SysMenuVo sysMenuVo);
void editMenu(SysMenuVo menuVo);
void updateEffective(String type, int menuId);
void deleteMenuByMenuId(int menuId);
}
package com.longzy.component.menu.service.write.impl;
import com.longzy.common.utils.ConvertUtils;
import com.longzy.component.menu.entity.SysMenuPo;
import com.longzy.component.menu.mapper.read.SysMenuReadMapper;
import com.longzy.component.menu.mapper.write.SysMenuWriteMapper;
import com.longzy.component.menu.service.write.SysMenuWriteService;
import com.longzy.component.menu.vo.SysMenuVo;
import com.longzy.error.BusinessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Date;
/**
* @Desc:
* @Packge: com.longzy.component.menu.service.write.impl
* @Author: longzy
* @Date: 2022/9/26 16:27
*/
@Service
@Transactional
public class SysMenuWriteServiceImpl implements SysMenuWriteService {
@Resource
private SysMenuWriteMapper sysMenuWriteMapper;
@Resource
private SysMenuReadMapper sysMenuReadMapper;
@Override
public void addMenu(int currentUser, SysMenuVo sysMenuVo) {
SysMenuPo menuPo = ConvertUtils.convert(sysMenuVo, new SysMenuPo());
menuPo.setCreateUser(currentUser);
menuPo.setCreateTime(new Date());
menuPo.setDestory("0");
int count = sysMenuWriteMapper.addMenu(menuPo);
if (count < 1){
throw new BusinessException("新增菜单失败.");
}
}
@Override
public void editMenu(SysMenuVo menuVo) {
SysMenuPo menuPo = ConvertUtils.convert(menuVo, new SysMenuPo());
int count = sysMenuWriteMapper.editMenu(menuPo);
if (count < 1){
throw new BusinessException("编辑菜单失败.");
}
}
/**
* 更新菜单状态
* @param type 1启用 2禁用
* @param menuId 菜单id
*/
@Override
public void updateEffective(String type, int menuId) {
String effective = null;
if ("1".equals(type)){
effective = "1";
// 查询该记录是否还有父记录为禁用状态
int countMenu = sysMenuReadMapper.countEnableByMenuId(menuId, "0");
if (countMenu > 0){
throw new BusinessException("该记录还有父记录的状态为无效,不可以操作该记录.");
}
}else {
effective = "0";
// 查询该记录是否还有子记录为启用状态
int countMenu = sysMenuReadMapper.countDisableByParentId(menuId, "0");
if (countMenu > 0){
throw new BusinessException("该记录还有子记录的状态为有效,不可以操作该记录.");
}
}
int count = sysMenuWriteMapper.updateEffective(menuId, effective);
if (count < 1){
throw new BusinessException(type== "1" ? "该记录启用失败." : "该记录禁用失败.");
}
}
@Override
public void deleteMenuByMenuId(int menuId) {
// 统计删除菜单是否还有子元素
int countMenu = sysMenuReadMapper.countDeleteByChildren(menuId, "0");
if (countMenu > 0){
throw new BusinessException("该记录还有子记录,不可以操作该记录.");
}
int count = sysMenuWriteMapper.deleteMenuByMenuId(menuId, "1");
if (count < 1){
throw new BusinessException("改记录删除失败.");
}
}
}
(6)、持久层(mapper)实现
read:
package com.longzy.component.menu.mapper.read;
import com.longzy.component.menu.vo.SysMenuVo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Desc:
* @Packge: com.longzy.component.menu.mapper.read
* @Author: longzy
* @Date: 2022/9/24 21:22
*/
public interface SysMenuReadMapper {
/**
* 查询菜单列表
* @param effective 有效状态
* @param searchInfo 菜单名称
* @param destory 销毁状态
* @return
*/
List<SysMenuVo> queryMenuByCondition(@Param("effective") String effective, @Param("searchInfo") String searchInfo, @Param("destory") String destory);
/**
* 启用:根据父菜单id统计
* @param menuId 菜单id
* @param destory 销毁状态
* @return
*/
int countEnableByMenuId(@Param("menuId")int menuId, @Param("destory") String destory);
/**
* 禁用: 根据菜单id统计
* @param parentId 父菜单id
* @param destory 销毁状态
* @return
*/
int countDisableByParentId(@Param("parentId") int parentId, @Param("destory") String destory);
/**
* 删除: 根据父菜单id统计
* @param parentId
* @param destory
* @return
*/
int countDeleteByChildren(@Param("parentId") int parentId, @Param("destory") String destory);
}
write:
package com.longzy.component.menu.mapper.write;
import com.longzy.component.menu.entity.SysMenuPo;
import org.apache.ibatis.annotations.Param;
/**
* @Desc:
* @Packge: com.longzy.component.menu.mapper.write
* @Author: longzy
* @Date: 2022/9/26 16:29
*/
public interface SysMenuWriteMapper {
/**
* 新增菜单
* @param menuPo 入参
* @return
*/
int addMenu(SysMenuPo menuPo);
/**
* 编辑菜单
* @param menuPo 入参
* @return
*/
int editMenu(SysMenuPo menuPo);
/**
* 更新状态
* @param menuId 菜单id
* @param effective 状态值
* @return
*/
int updateEffective(@Param("menuId") int menuId, @Param("effective") String effective);
/**
* 逻辑删除菜单
* @param menuId 菜单id
* @param destory 1销毁 0正常
* @return
*/
int deleteMenuByMenuId(@Param("menuId") int menuId, @Param("destory") String destory);
}
(7)、sql实现
read:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--suppress ALL -->
<mapper namespace="com.longzy.component.menu.mapper.read.SysMenuReadMapper">
<sql id="Base_Column_List">
m.menuid,
m.name,
m.parentid,
m.path,
m.perms,
m.menutype,
m.icon,
m.orderno,
m.createuser,
m.createtime,
m.effective,
m.destory
</sql>
<select id="queryMenuByCondition" resultType="com.longzy.component.menu.vo.SysMenuVo">
SELECT <include refid="Base_Column_List"></include>,
ifnull((SELECT n.name from sys_menu n where n.menuid = m.parentid), '0') as parentname
FROM sys_menu m
WHERE m.destory = #{destory,jdbcType=VARCHAR}
<if test="searchInfo != '' and searchInfo != null">
AND m.name like concat('%', #{searchInfo,jdbcType=VARCHAR}, '%')
</if>
<if test="effective != '' and effective != null">
AND m.effective = #{effective,jdbcType=VARCHAR}
</if>
</select>
<select id="countEnableByMenuId" resultType="int">
SELECT count(1)
FROM sys_menu
WHERE destory = #{destory,jdbcType=VARCHAR}
AND effective = '0'
AND menuid = (select parentid from sys_menu where menuid = #{menuId,jdbcType=INTEGER})
</select>
<select id="countDisableByParentId" resultType="int">
SELECT count(1)
FROM sys_menu
WHERE destory = #{destory,jdbcType=VARCHAR}
AND effective = '1'
AND parentid = #{parentId,jdbcType=INTEGER}
</select>
<select id="countDeleteByChildren" resultType="int">
SELECT count(1)
FROM sys_menu
WHERE destory = #{destory,jdbcType=VARCHAR}
AND parentid = #{parentId,jdbcType=INTEGER}
</select>
</mapper>
write:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--suppress ALL -->
<mapper namespace="com.longzy.component.menu.mapper.write.SysMenuWriteMapper">
<insert id="addMenu" parameterType="com.longzy.component.menu.entity.SysMenuPo">
INSERT INTO sys_menu
(name, parentid, `path`, perms, menutype, icon, orderno, createuser, createtime, effective, destory)
VALUES(#{name,jdbcType=VARCHAR},
#{parentId,jdbcType=INTEGER},
#{path,jdbcType=VARCHAR},
#{perms,jdbcType=VARCHAR},
#{menuType,jdbcType=VARCHAR},
#{icon,jdbcType=VARCHAR},
#{orderNo,jdbcType=INTEGER},
#{createUser,jdbcType=INTEGER},
#{createTime,jdbcType=DATE},
#{effective,jdbcType=VARCHAR},
#{destory,jdbcType=VARCHAR})
</insert>
<update id="editMenu" parameterType="com.longzy.component.menu.entity.SysMenuPo">
UPDATE sys_menu
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
<if test="path != null">
path = #{path,jdbcType=VARCHAR},
</if>
<if test="perms != null">
perms = #{perms,jdbcType=VARCHAR},
</if>
<if test="menuType != null">
menutype = #{menuType,jdbcType=VARCHAR},
</if>
<if test="icon != null">
icon = #{icon,jdbcType=VARCHAR},
</if>
<if test="orderNo != null">
orderno = #{orderNo,jdbcType=INTEGER},
</if>
<if test="effective != null">
effective = #{effective,jdbcType=VARCHAR}
</if>
</set>
WHERE menuid = #{menuId,jdbcType=INTEGER}
</update>
<update id="updateEffective" >
UPDATE sys_menu SET effective = #{effective,jdbcType=VARCHAR} WHERE menuid = #{menuId,jdbcType=INTEGER}
</update>
<update id="deleteMenuByMenuId">
UPDATE sys_menu SET destory = #{destory,jdbcType=VARCHAR} WHERE menuid = #{menuId,jdbcType=INTEGER}
</update>
</mapper>
代码地址:
后端: https://gitee.com/longzyl/longzy-admin
前端: https://gitee.com/longzyl/longzy-vue2