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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值