day06_菜單管理(查詢菜單,添加菜單,添加子菜單,修改菜單,刪除菜單,角色分配菜單,查詢菜單,保存菜單,動態菜單)

文章目錄

  • 1 菜單管理
    • 1.1 表結構介紹
    • 1.2 查詢菜單
      • 1.2.1 需求說明
      • 1.2.2 頁面制作
      • 1.2.3 后端接口
        • SysMenu
        • SysMenuController
        • SysMenuService
        • MenuHelper
        • SysMenuMapper
        • SysMenuMapper.xml
      • 1.2.4 前端對接
        • sysMenu.js
        • sysMenu.vue
    • 1.3 添加菜單
      • 1.3.1 需求說明
      • 1.3.3 頁面制作
      • 1.3.3 后端接口
        • SysMenuController
        • SysMenuService
        • SysMenuMapper
        • SysMenuMapper.xml
      • 1.3.4 前端對接
        • 實現思路
        • sysMenu.js
        • sysMenu.vue
    • 1.4 添加子菜單
      • 1.4.1 需求說明
      • 1.4.2 代碼實現
    • 1.5 修改菜單
      • 1.5.1 需求說明
      • 1.5.2 數據回顯
      • 1.5.3 提交修改
        • 后端接口
          • SysMenuController
          • SysMenuService
          • SysMenuMapper
          • SysMenuMapper.xml
        • 前端對接
          • sysMenu.js
          • sysMenu.vue
    • 1.6 刪除菜單
      • 1.6.1 需求說明
      • 1.6.2 后端接口
        • SysMenuController
        • SysMenuService
        • SysMenuMapper
        • SysMenuMapper.xml
      • 1.6.3 前端對接
        • sysMenu.js
        • sysMenu.vue
  • 2 角色分配菜單
    • 2.1 需求說明
    • 2.2 頁面制作
    • 2.3 查詢菜單
      • 2.3.1 后端接口
        • SysRoleMenuController
        • SysRoleMenuService
        • SysRoleMenuMapper
        • SysRoleMenuMapper.xml
      • 2.3.2 前端對接
        • sysRole.js
        • sysRole.vue
    • 2.4 保存菜單
      • 2.4.1 后端接口
        • AssginMenuDto
        • SysRoleMenuController
        • SysRoleMenuService
        • SysRoleMenuMapper
        • SysRoleMenuMapper.xml
      • 2.4.2 前端對接
        • sysRole.js
        • sysRole.vue
  • 3 動態菜單
    • 3.1 需求說明
    • 3.2 后端接口
      • 3.2.1 SysMenuVo
      • 3.2.1 IndexController
      • 3.2.2 SysMenuService
      • 3.2.3 SysMenuMapper
      • 3.2.4 SysMenuMapper.xml
    • 3.3 前端對接
      • 3.3.1 前端加載流程
      • 3.3.2 menu.js
      • 3.3.3 index.js
      • 3.3.4 menu.js
    • 3.4 代碼測試
      • 3.4.1 測試內容說明
      • 3.4.2 bug解決
        • SysMenuServiceImpl
        • SysMenuMapper
        • SysMenuMapper.xml
        • SysRoleMenuMapper
        • SysRoleMenuMapper.xml

1 菜單管理

菜單管理就是對系統中首頁中的左側菜單進行維護。

1.1 表結構介紹

菜單表結果如下所示:

CREATE TABLE `sys_menu` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '編號',`parent_id` bigint NOT NULL DEFAULT '0' COMMENT '所屬上級',`title` varchar(20) NOT NULL DEFAULT '' COMMENT '菜單標題',`component` varchar(100) DEFAULT NULL COMMENT '組件名稱',`sort_value` int NOT NULL DEFAULT '1' COMMENT '排序',`status` tinyint NOT NULL DEFAULT '1' COMMENT '狀態(0:禁止,1:正常)',`has_children` tinyint NOT NULL COMMENT '是否存在在子節點',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '刪除標記(0:不可用 1:可用)',PRIMARY KEY (`id`),KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='菜單表';

1.2 查詢菜單

1.2.1 需求說明

由于菜單表結構是一種樹形結構,因此在進行數據展示的時候需要按照樹形表格的方式進行數據展示。

效果圖如下所示:

在這里插入圖片描述

1.2.2 頁面制作

具體代碼如下所示:

<template><div class="tools-div"><el-button type="success" size="small">添 加</el-button></div><el-table:data="list"style="width: 100%; margin-bottom: 20px"row-key="id"borderdefault-expand-all><el-table-column prop="title" label="菜單標題" /><el-table-column prop="component" label="路由名稱" /><el-table-column prop="sortValue" label="排序" /><el-table-column prop="status" label="狀態" #default="scope">{{ scope.row.status == 1 ? '正常' : '停用' }}</el-table-column><el-table-column prop="createTime" label="創建時間" /><el-table-column label="操作" align="center" width="280" ><el-button type="success" size="small" >添加下級節點</el-button><el-button type="primary" size="small" >修改</el-button><el-button type="danger" size="small">刪除</el-button></el-table-column></el-table></template><script setup>
import { ref } from "vue"// 定義表格數據模型
const list = ref([{"id": 1,"title": "系統管理","component": "system","sortValue": 1,"status": 1,"createTime": "2023-05-04","children":[{"id":2, "title": "用戶管理" , "component":"sysUser" , "sortValue":1 ,"status":1 , "createTime":"2023-05-04"},{"id":3, "title": "角色管理" , "component":"sysRole" , "sortValue":2 ,"status":1 , "createTime":"2023-05-04"},{"id":4, "title": "菜單管理" , "component":"sysMenu" , "sortValue":3 ,"status":1 , "createTime":"2023-05-04"}]},{"id": 5,"title": "基礎數據管理","component": "base","sortValue": 2,"status": 1,"createTime": "2023-05-04","children":[{"id":6, "title": "商品單位" , "component":"productUnit" , "sortValue":2 ,"status":1 , "createTime":"2023-05-04"},{"id":7, "title": "地區管理" , "component":"region" , "sortValue":1 ,"status":1 , "createTime":"2023-05-04"}]}
])</script><style scoped>
.search-div {margin-bottom: 10px;padding: 10px;border: 1px solid #ebeef5;border-radius: 3px;background-color: #fff;
}
.tools-div {margin: 10px 0;padding: 10px;border: 1px solid #ebeef5;border-radius: 3px;background-color: #fff;
}
</style>

1.2.3 后端接口

SysMenu

創建與數據庫表相對應的實體類:

// com.atguigu.spzx.model.entity.system
@Data
public class SysMenu extends BaseEntity {private Long parentId;private String title;private String component;private Integer sortValue;private Integer status;// 下級列表private List<SysMenu> children;}
SysMenuController

表現層代碼實現

// com.atguigu.spzx.manager.controller
@RestController
@RequestMapping(value="/admin/system/sysMenu")
public class SysMenuController {@Autowiredprivate SysMenuService sysMenuService;@GetMapping("/findNodes")public Result<List<SysMenu>> findNodes() {List<SysMenu> list = sysMenuService.findNodes();return Result.build(list , ResultCodeEnum.SUCCESS) ;}}
SysMenuService

業務層代碼實現

// com.atguigu.spzx.manager.service.impl
@Service
public class SysMenuServiceImpl implements SysMenuService {@Autowiredprivate SysMenuMapper sysMenuMapper ;@Overridepublic List<SysMenu> findNodes() {List<SysMenu> sysMenuList = sysMenuMapper.selectAll() ;if (CollectionUtils.isEmpty(sysMenuList)) return null;List<SysMenu> treeList = MenuHelper.buildTree(sysMenuList); //構建樹形數據return treeList;}}
MenuHelper

構建樹形菜單的工具類:

// com.atguigu.spzx.manager.helper
public class MenuHelper {/*** 使用遞歸方法建菜單* @param sysMenuList* @return*/public static List<SysMenu> buildTree(List<SysMenu> sysMenuList) {List<SysMenu> trees = new ArrayList<>();for (SysMenu sysMenu : sysMenuList) {if (sysMenu.getParentId().longValue() == 0) {trees.add(findChildren(sysMenu,sysMenuList));}}return trees;}/*** 遞歸查找子節點* @param treeNodes* @return*/public static SysMenu findChildren(SysMenu sysMenu, List<SysMenu> treeNodes) {sysMenu.setChildren(new ArrayList<SysMenu>());for (SysMenu it : treeNodes) {if(sysMenu.getId().longValue() == it.getParentId().longValue()) {if (sysMenu.getChildren() == null) {sysMenu.setChildren(new ArrayList<>());}sysMenu.getChildren().add(findChildren(it,treeNodes));}}return sysMenu;}
}
SysMenuMapper

持久層代碼實現:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface SysMenuMapper {public abstract List<SysMenu> selectAll();
}
SysMenuMapper.xml

在映射文件中添加如下sql語句

<?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"><mapper namespace="com.atguigu.spzx.manager.mapper.SysMenuMapper"><resultMap id="sysMenuMap" type="com.atguigu.spzx.model.entity.system.SysMenu" autoMapping="true"></resultMap><!-- 用于select查詢公用抽取的列 --><sql id="columns">id,parent_id,title,component,sort_value,status,create_time,update_time,is_deleted</sql><select id="selectAll" resultMap="sysMenuMap">select <include refid="columns" />from sys_menuwhereis_deleted = 0order by sort_value</select></mapper>

1.2.4 前端對接

sysMenu.js

在api文件夾下創建一個sysMenu.js文件,在該文件中添加如下代碼:

import request from '@/utils/request'
const api_name = '/admin/system/sysMenu'// 分頁列表
export const FindNodes = () => {return request({url: `${api_name}/findNodes`,method: 'get',})
}
sysMenu.vue

更改sysMenu.vue的代碼如下所示:

<script setup>
import { ref , onMounted } from "vue"
import { FindNodes } from '@/api/sysMenu'// 定義表格數據模型
const list = ref([])// 加載數據
onMounted(() => {fetchData()
})//分頁列表
const fetchData = async () => {const { code, data, message } = await FindNodes()list.value = data
}</script>

1.3 添加菜單

1.3.1 需求說明

當用戶點擊添加按鈕的時候,那么此時就彈出對話框,在該對話框中需要展示添加菜單表單。當用戶在該表單中點擊提交按鈕的時候那么此時就需要將表單進行提交,在后端需要將提交過來的表單數據保存到數據庫中即可。頁面效果如下所示:

在這里插入圖片描述

1.3.3 頁面制作

具體代碼如下所示:

<div class="tools-div"><el-button type="success" size="small" @click="addShow">添 加</el-button>
</div><el-dialog v-model="dialogVisible" :title="dialogTitle" width="30%"><el-form label-width="120px"><el-form-item label="菜單標題"><el-input /></el-form-item><el-form-item label="路由名稱"><el-input /></el-form-item><el-form-item label="排序"><el-input /></el-form-item><el-form-item label="狀態"><el-radio-group><el-radio :label="1">正常</el-radio><el-radio :label="0">停用</el-radio></el-radio-group></el-form-item><el-form-item><el-button type="primary">提交</el-button><el-button @click="dialogVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog><script setup>// 定義添加表單菜單表單相關數據模型
const dialogTitle = ref('添加')
const dialogVisible = ref(false)//進入添加
const addShow = () => {dialogVisible.value = truedialogTitle.value = '添加'
}
</script>

1.3.3 后端接口

SysMenuController

表現層代碼實現

// com.atguigu.spzx.manager.controller.SysMenuController
@PostMapping("/save")
public Result save(@RequestBody SysMenu sysMenu) {sysMenuService.save(sysMenu);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
SysMenuService

業務層代碼實現

// com.atguigu.spzx.manager.service.impl#SysMenuServiceImpl
@Override
public void save(SysMenu sysMenu) {sysMenuMapper.insert(sysMenu) ;
}
SysMenuMapper

持久層代碼實現:

// com.atguigu.spzx.manager.mapper#SysMenuMapper
public abstract  void insert(SysMenu sysMenu);
SysMenuMapper.xml

在映射文件中添加如下sql語句

<insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into sys_menu (id,parent_id,title,component,sort_value,status) values (#{id},#{parentId},#{title},#{component},#{sortValue},#{status})
</insert>

1.3.4 前端對接

實現思路

1、給表單綁定數據模型

2、給提交按鈕綁定點擊事件

3、點擊按鈕請求后端地址

sysMenu.js

在api文件夾下創建一個sysMenu.js文件,在該文件中添加如下代碼:

// 保存信息
export const save = sysMenu => {return request({url: `${api_name}/save`,method: 'post',data: sysMenu,})
}
sysMenu.vue

更改sysMenu.vue的代碼如下所示:

<el-dialog v-model="dialogVisible" :title="dialogTitle" width="30%"><el-form label-width="120px"><el-form-item label="菜單標題"><el-input v-model="sysMenu.title"/></el-form-item><el-form-item label="路由名稱"><el-input v-model="sysMenu.component"/></el-form-item><el-form-item label="排序" ><el-input v-model="sysMenu.sortValue"/></el-form-item><el-form-item label="狀態"><el-radio-group v-model="sysMenu.status"><el-radio :label="1">正常</el-radio><el-radio :label="0">停用</el-radio></el-radio-group></el-form-item><el-form-item><el-button type="primary" @click="saveOrUpdate">提交</el-button><el-button @click="dialogVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog><script setup>
import { FindNodes , SaveMenu} from '@/api/sysMenu'
import { ElMessage, ElMessageBox } from 'element-plus'//頁面表單數據
const defaultForm = {id: '',parentId: 0,title: '',url: '',component: '',icon: '',sortValue: 1,status: 1,
}// 表單響應式數據模型對象
const sysMenu = ref(defaultForm)//提交保存與修改
const saveOrUpdate = () => {if (!sysMenu.value.id) {sysMenu.value.parentId = 0saveData()} 
}// 新增
const saveData = async () => {await SaveMenu(sysMenu.value)dialogVisible.value = falseElMessage.success('操作成功')fetchData()
}
</script>

1.4 添加子菜單

1.4.1 需求說明

當用戶點擊添加按鈕的時候,那么此時就彈出對話框,在該對話框中需要展示添加子菜單表單。當用戶在該表單中點擊提交按鈕的時候那么此時就需要將表單進行提交,在后端需要將提交過來的表單數據保存到數據庫中即可。頁面效果如下所示:

在這里插入圖片描述

可以使用添加菜單的表單。

1.4.2 代碼實現

只需要更改前端代碼即可,如下所示:

<el-table-column label="操作" align="center" width="280" #default="scope" ><el-button type="success" size="small" @click="addShow(scope.row)">添加下級節點</el-button>
</el-table-column><script setup>//進入添加
const addShow = (row) => {sysMenu.value = {}dialogVisible.value = trueif(!row.id) {dialogTitle.value = '添加'}else {dialogTitle.value = '添加下級節點'sysMenu.value.parentId = row.id}
}</script>

1.5 修改菜單

1.5.1 需求說明

當用戶點擊修改按鈕的時候,那么此時就彈出對話框,在該對話框中需要將當前行所對應的菜單數據在該表單頁面進行展示。當用戶在該表單中點擊提交按鈕的時候那么此時就需要將表單進行提交,在后端需要提交過來的表單數據修改數據庫中的即可。頁面效果如下所示:

在這里插入圖片描述

1.5.2 數據回顯

分析:

1、使用添加數據的表單即可

2、要將當前操作行的數據展示在表單中,那么此時需要用到插槽

代碼如下所示:

<el-table-column label="操作" align="center" width="280" #default="scope" ><el-button type="primary" size="small" @click="editShow(scope.row)">修改</el-button>
</el-table-column><script setup>//進入修改
const editShow = row => {sysMenu.value = rowdialogVisible.value = true
}</script>

1.5.3 提交修改

后端接口
SysMenuController

表現層代碼實現

// com.atguigu.spzx.manager.controller#SysMenuController
@PutMapping("/updateById")
public Result updateById(@RequestBody SysMenu sysMenu) {sysMenuService.updateById(sysMenu);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
SysMenuService

業務層代碼實現

// com.atguigu.spzx.manager.service.impl#SysMenuServiceImpl
@Override
public void updateById(SysMenu sysMenu) {sysMenuMapper.updateById(sysMenu) ;
}
SysMenuMapper

持久層代碼實現

// com.atguigu.spzx.manager.mapper.SysMenuMapper
public abstract void updateById(SysMenu sysMenu);
SysMenuMapper.xml

映射文件中添加如下sql語句

<update id="updateById" >update sys_menu set<if test="parentId != null and parentId != ''">parent_id = #{parentId},</if><if test="title != null and title != ''">title = #{title},</if><if test="component != null and component != ''">component = #{component},</if><if test="sortValue != null">sort_value = #{sortValue},</if><if test="status != null">status = #{status},</if>update_time =  now()whereid = #{id}
</update>
前端對接
sysMenu.js

在api目錄下創建一個sysMenu.js文件,文件的內容如下所示:

// 修改信息
export const UpdateSysMenuById = sysMenu => {return request({url: `${api_name}/updateById`,method: 'put',data: sysMenu,})
}
sysMenu.vue

更改sysMenu.vue的代碼如下所示:

<script setup>
import { FindNodes , SaveMenu , UpdateSysMenuById } from '@/api/sysMenu'//提交保存與修改
const saveOrUpdate = () => {if (!sysMenu.value.id) {saveData()} else {updateData()}
}// 修改
const updateData = async () => {await UpdateSysMenuById(sysMenu.value)dialogVisible.value = falseElMessage.success('操作成功')fetchData()
}
</script>>

1.6 刪除菜單

1.6.1 需求說明

當點擊刪除按鈕的時候此時需要彈出一個提示框,詢問是否需要刪除數據?如果用戶點擊是,那么此時向后端發送請求傳遞id參數,后端接收id參數進

行邏輯刪除。頁面效果如下所示:

在這里插入圖片描述

1.6.2 后端接口

SysMenuController

表現層代碼實現

// com.atguigu.spzx.manager.controller#SysMenuController
@DeleteMapping("/removeById/{id}")
public Result removeById(@PathVariable Long id) {sysMenuService.removeById(id);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
SysMenuService

業務層代碼實現

// com.atguigu.spzx.manager.service.impl#SysMenuServiceImpl
@Override
public void removeById(Long id) {int count = sysMenuMapper.countByParentId(id);  // 先查詢是否存在子菜單,如果存在不允許進行刪除if (count > 0) {throw new GuiguException(ResultCodeEnum.NODE_ERROR);}sysMenuMapper.deleteById(id);		// 不存在子菜單直接刪除
}
SysMenuMapper

持久層代碼實現

// com.atguigu.spzx.manager.mapper#SysMenuMapper
@Mapper
public interface SysMenuMapper {public abstract int countByParentId(Long id);public abstract void deleteById(Long id);
}
SysMenuMapper.xml

映射文件中添加如下sql語句

<select id="countByParentId" resultType="Integer">select count(id)from sys_menuwhereparent_id = #{parentId}and is_deleted = 0
</select><update id="deleteById">update sys_menu setupdate_time = now() ,is_deleted = 1whereid = #{id}
</update>

1.6.3 前端對接

sysMenu.js

在api目錄下創建一個sysMenu.js文件,文件的內容如下所示:

// 根據id刪除數據
export const RemoveSysMenuById = id => {return request({url: `${api_name}/removeById/${id}`,method: 'delete',})
}
sysMenu.vue

更改sysMenu.vue的代碼如下所示:

 <el-table-column label="操作" align="center" width="280" #default="scope" ><el-button type="danger" size="small" @click="remove(scope.row.id)">刪除</el-button>
</el-table-column><script setup>
import { RemoveSysMenuById } from '@/api/sysMenu'//刪除
const remove = async id => {console.log('removeDataById:' + id)ElMessageBox.confirm('此操作將永久刪除該記錄, 是否繼續?', 'Warning', {confirmButtonText: '確定',cancelButtonText: '取消',type: 'warning',}).then(async () => {const { code , message } = await RemoveSysMenuById(id)if(code === 200) {ElMessage.success('刪除成功')fetchData()}else {ElMessage.error(message)}}).catch(() => {ElMessage.info('取消刪除')})
}
</script>

2 角色分配菜單

2.1 需求說明

在角色列表頁面,當用戶點擊分配菜單按鈕的時候,此時就會彈出一個對話框。在該對話框中會將系統中所涉及到的所有的菜單都展示出來。并且將當

前角色所對應的菜單進行選中。效果如下圖所示:

在這里插入圖片描述

用戶選擇對應的菜單以后,點擊提交按鈕,此時請求后端接口,后端將選中的菜單數據保存到sys_role_menu表中。

2.2 頁面制作

頁面代碼如下所示:

<el-button type="warning" size="small" @click="showAssignMenu(scope.row)">分配菜單
</el-button><!-- 分配菜單的對話框  -->
<el-dialog v-model="dialogMenuVisible" title="分配菜單" width="40%"><el-form label-width="80px"><el-tree:data="sysMenuTreeList"show-checkboxdefault-expand-allnode-key="id":props="defaultProps" :check-on-click-node="true"/><el-form-item><el-button type="primary">提交</el-button><el-button @click="dialogMenuVisible = false">取消</el-button></el-form-item></el-form>
</el-dialog><script setup>//分配菜單
const defaultProps = {children: 'children',label: 'title',
}
const dialogMenuVisible = ref(false)
const sysMenuTreeList = ref([{"id": 1,"title": "系統管理","children":[{"id":2, "title": "用戶管理" },{"id":3, "title": "角色管理" },{"id":4, "title": "菜單管理" }]},{"id": 5,"title": "數據管理","component": "base","sortValue": 2,"status": 1,"createTime": "2023-05-04","children":[{"id":6, "title": "商品單位" },{"id":7, "title": "地區管理" }]}
])
const showAssignMenu = async row => { dialogMenuVisible.value = true
}</script>

2.3 查詢菜單

2.3.1 后端接口

需要:根據角色的id查詢出其對應的菜單id,并且需要將系統中所有的菜單數據查詢出來。

SysRoleMenuController

表現層代碼實現

// com.atguigu.spzx.manager.controller
@RestController
@RequestMapping(value = "/admin/system/sysRoleMenu")
public class SysRoleMenuController {@Autowiredprivate SysRoleMenuService sysRoleMenuService ;@GetMapping(value = "/findSysRoleMenuByRoleId/{roleId}")public Result<Map<String , Object>> findSysRoleMenuByRoleId(@PathVariable(value = "roleId") Long roleId) {Map<String , Object> sysRoleMenuList = sysRoleMenuService.findSysRoleMenuByRoleId(roleId) ;return Result.ok().data(sysRoleMenuList);}}
SysRoleMenuService

業務層代碼實現

// com.atguigu.spzx.manager.service.impl#SysRoleMenuServiceImpl
@Override
public Map<String, Object> findSysRoleMenuByRoleId(Long roleId) {// 查詢所有的菜單數據List<SysMenu> sysMenuList = sysMenuService.findNodes() ;// 查詢當前角色的菜單數據List<Long> roleMenuIds = sysRoleMenuMapper.findSysRoleMenuByRoleId(roleId) ;// 將數據存儲到Map中進行返回Map<String , Object> result = new HashMap<>() ;result.put("sysMenuList" , sysMenuList) ;result.put("roleMenuIds" , roleMenuIds) ;// 返回return result;
}
SysRoleMenuMapper

持久層代碼實現

@Mapper
public interface SysRoleMenuMapper {public abstract List<Long> findSysRoleMenuByRoleId(Long roleId);
}
SysRoleMenuMapper.xml

映射文件添加如下sql語句

<?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">
<mapper namespace="com.atguigu.spzx.manager.mapper.SysRoleMenuMapper"><select id="findSysRoleMenuByRoleId" resultType="long">SELECT t1.menu_idFROM sys_role_menu t1LEFT JOIN sys_menu t2ON t1.menu_id = t2.idWHERE t1.role_id = #{roleId}AND t2.parent_id !=0</select></mapper>

2.3.2 前端對接

sysRole.js

在src/api/sysRole.js文件中添加如下方法:

// 查詢指定角色所對應的菜單id
export const GetSysRoleMenuIds = (roleId) => {return request({url: "/admin/system/sysRoleMenu/findSysRoleMenuByRoleId/"+ roleId,method: 'get'})
}
sysRole.vue

修改sysRole.vue文件中的代碼,如下所示:

<!-- tree組件添加ref屬性,后期方便進行tree組件對象的獲取 -->
<el-tree:data="sysMenuTreeList"ref="tree"   show-checkboxdefault-expand-all:check-on-click-node="true"node-key="id":props="defaultProps"
/><script setup>
import { GetSysRoleMenuIds } from '@/api/sysRole';// 樹對象變量
const tree = ref() // 默認選中的菜單數據集合
const showAssignMenu = async row => { dialogMenuVisible.value = trueconst { data } = await GetSysRoleMenuIds(row.id)   // 請求后端地址獲取所有的菜單數據,以及當前角色所對應的菜單數據sysMenuTreeList.value = data.sysMenuListtree.value.setCheckedKeys(data.roleMenuIds)   // 進行數據回顯
}
</script>

2.4 保存菜單

2.4.1 后端接口

思路分析:前端請求后端接口的時候需要將角色的id和用戶所選中的菜單id傳遞到后端。后端需要先根據角色的id從sys_role_menu表中刪除其所對應的菜單數據,然后添加新的菜單數據到sys_role_menu表中。

AssginMenuDto

定義一個實體類封裝前端所傳遞過來的兩部分數據:

// com.atguigu.spzx.model.dto.system
@Data
public class AssginMenuDto {private Long roleId;							// 角色idprivate List<Long> menuIdList;	// 選中的菜單id的集合 , Map中包含了2部分的數據:全選菜單id,半選菜單id
}
SysRoleMenuController

表現層代碼實現:

// com.atguigu.spzx.manager.controller
@PostMapping("/doAssign")
public Result doAssign(@RequestBody AssginMenuDto assginMenuDto) {sysRoleMenuService.doAssign(assginMenuDto);return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
SysRoleMenuService

業務層代碼實現:

// com.atguigu.spzx.manager.service.impl#SysRoleMenuServiceImpl
@Transactional
@Override
public void doAssign(AssginMenuDto assginMenuDto) {// 根據角色的id刪除其所對應的菜單數據sysRoleMenuMapper.deleteByRoleId(assginMenuDto.getRoleId());// 獲取菜單的idList<Long> menuIds = assginMenuDto.getMenuIdList();if(CollectionUtils.isEmpty(menuIds)){//如果menuIds為空 表示刪除角色所有的菜單即可  不需要新分配return;}sysRoleMenuMapper.doAssign(assginMenuDto) ;}
SysRoleMenuMapper

持久層代碼實現:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface SysRoleMenuMapper {public abstract void deleteByRoleId(Long roleId);public abstract void doAssign(AssginMenuDto assginMenuDto);
}
SysRoleMenuMapper.xml

映射文件中添加如下sql語句:

<delete id="deleteByRoleId">delete from sys_role_menu where role_id = #{roleId}
</delete><insert id="doAssign">insert into sys_role_menu (role_id,menu_id,create_time , update_time , is_deleted , is_half) values<foreach collection="menuIdList" item="menuId" separator=",">(#{roleId} , #{menuId} , now() , now() , 0 , 0</foreach>
</insert>

2.4.2 前端對接

sysRole.js

在src/api/sysRole.js文件中添加如下方法:

// 根據角色分配菜單請求方法
export const DoAssignMenuIdToSysRole = (assignMenuDto) => {return request({url: "/admin/system/sysRoleMenu/doAssign",method: 'post',data: assignMenuDto})
}
sysRole.vue

修改sysRole.vue文件中的代碼,思路如下:

1、給提交按鈕綁定點擊事件,并提供對應的事件處理函數

2、在單機事件處理函數中執行的代碼邏輯如下所示:

  • 獲取tree組件對象中選中的節點
  • 獲取tree組件對象中半選中的節點
  • 將兩組節點數據進行組合
  • 構建請求參數
  • 發送請求

具體代碼如下所示:

const doAssign = async () => {// 將選中的節點id和半選中的節點的id進行合并const menuIds = [...tree.value.getCheckedKeys(),...tree.value.getHalfCheckedKeys()]  console.log(menuIds);// 構建請求數據const assignMenuDto = {roleId: roleId,menuIdList: menuIds}// 發送請求await DoAssignMenuIdToSysRole(assignMenuDto) ;ElMessage.success('菜單分配成功')dialogMenuVisible.value = false}   

3 動態菜單

3.1 需求說明

現在首頁的左側菜單是直接寫死在前端系統中的,這里很顯然是不合適的。不同用戶其所對應的權限是不同的,因此關于左側菜單需要根據當前登錄的用戶所對應的角色動態進行獲取。

前端所需要的菜單數據結構如下所示:

[{"title": "系統管理","name": "system","children": [{"title": "用戶管理","name": "sysUser","children": null},{"title": "角色管理","name": "sysRole","children": null},{"title": "菜單管理","name": "sysMenu","children": null}]},{"title": "數據管理","name": "base","children": [{"title": "地區管理","name": "region","children": null},{"title": "商品單位","name": "productUnit","children": null}]}
]

3.2 后端接口

3.2.1 SysMenuVo

定義一個實體類,來封裝前端所需要的菜單數據。如下所示:

// com.atguigu.spzx.model.vo.system
@Data
public class SysMenuVo {private String title;private String name;private List<SysMenuVo> children;}

3.2.1 IndexController

表現層代碼實現:

// com.atguigu.spzx.system.controller#IndexController
@GetMapping("/menus")
public Result menus() {List<SysMenuVo> sysMenuVoList =  sysMenuService.findUserMenuList() ;return Result.build(sysMenuVoList , ResultCodeEnum.SUCCESS) ;
}

3.2.2 SysMenuService

業務層代碼實現:

@Override
public List<SysMenuVo> findUserMenuList() {SysUser sysUser = AuthContextUtil.get();Long userId = sysUser.getId();          // 獲取當前登錄用戶的idList<SysMenu> sysMenuList = sysMenuMapper.selectListByUserId(userId) ;//構建樹形數據List<SysMenu> sysMenuTreeList = MenuHelper.buildTree(sysMenuList);return this.buildMenus(sysMenuTreeList);
}// 將List<SysMenu>對象轉換成List<SysMenuVo>對象
private List<SysMenuVo> buildMenus(List<SysMenu> menus) {List<SysMenuVo> sysMenuVoList = new LinkedList<SysMenuVo>();for (SysMenu sysMenu : menus) {SysMenuVo sysMenuVo = new SysMenuVo();sysMenuVo.setTitle(sysMenu.getTitle());sysMenuVo.setName(sysMenu.getComponent());List<SysMenu> children = sysMenu.getChildren();if (!CollectionUtils.isEmpty(children)) {sysMenuVo.setChildren(buildMenus(children));}sysMenuVoList.add(sysMenuVo);}return sysMenuVoList;
}

3.2.3 SysMenuMapper

持久層代碼實現:

@Mapper
public interface SysMenuMapper {public abstract List<SysMenu> selectListByUserId(Long userId);
}

3.2.4 SysMenuMapper.xml

在SysMenuMapper.xml文件中添加如下的sql語句

<select id="selectListByUserId" resultMap="sysMenuMap">select * from sys_menu sm where sm.id in (select DISTINCT srm.menu_id from sys_role_menu srm where srm.role_id in(select sr.id from sys_role sr where sr.id in(select sur.role_id from sys_user_role sur where sur.user_id = #{userId} and is_deleted = 0)and sr.is_deleted = 0)and srm.is_deleted = 0) and sm.is_deleted = 0
</select>

3.3 前端對接

3.3.1 前端加載流程

前端加載動態菜單的流程如下圖所示:

在這里插入圖片描述

通過源碼的查看得出結論:要求在添加菜單的時候,菜單的名稱和前端路由的名稱保存一致。

3.3.2 menu.js

修改/src/api/menu.js中的請求地址,如下所示:

// 獲取菜單
export const GetMenus = params => {return request({url: '/admin/system/index/menus',method: 'get',params,})
}

3.3.3 index.js

更改src/router/index.js固定菜單和異步菜單的路由加載:

// 固定菜單
export const fixedRoutes = [...home]// 動態菜單
export const asyncRoutes = [...system]

3.3.4 menu.js

修改pinia/modules/menu.js中的generateMenus方法,注釋掉方式一菜單加載,打開方式二菜單加載。

const generateMenus = async () => {// // 方式一:只有固定菜單// const menus = getFilterMenus(fixedRoutes)// setMenus(menus)// 方式二:有動態菜單// 從后臺獲取菜單const { code, data } = await GetMenus()if (+code === 200) {// 添加路由之前先刪除所有動態路由asyncRoutes.forEach(item => {router.removeRoute(item.name)})// 過濾出需要添加的動態路由const filterRoutes = getFilterRoutes(asyncRoutes, data)filterRoutes.forEach(route => router.addRoute(route))// 生成菜單const menus = getFilterMenus([...fixedRoutes, ...filterRoutes])setMenus(menus)}
}

3.4 代碼測試

3.4.1 測試內容說明

1、給管理員角色分配訪問所有的菜單權限,創建新的測試角色進行測試。

2、添加新的菜單測試角色分配菜單數據回顯

3.4.2 bug解決

測試內容二的時候會存在bug【新添加的子菜單也會被選中】,解決bug。代碼如下所示:

SysMenuServiceImpl

更改添加菜單方法

// com.atguigu.spzx.manager.service.impl.SysMenuServiceImpl
@Transactional
@Override
public void save(SysMenu sysMenu) {// 添加新的節點sysMenuMapper.insert(sysMenu) ;// 新添加一個菜單,那么此時就需要將該菜單所對應的父級菜單設置為半開updateSysRoleMenuIsHalf(sysMenu) ;
}private void updateSysRoleMenuIsHalf(SysMenu sysMenu) {// 查詢是否存在父節點SysMenu parentMenu = sysMenuMapper.selectById(sysMenu.getParentId());if(parentMenu != null) {// 將該id的菜單設置為半開sysRoleMenuMapper.updateSysRoleMenuIsHalf(parentMenu.getId()) ;// 遞歸調用updateSysRoleMenuIsHalf(parentMenu) ;}}
SysMenuMapper

持久層代碼

// com.atguigu.spzx.manager.mapper
@Mapper
public interface SysMenuMapper {public abstract SysMenu selectById(Long id) ;
}
SysMenuMapper.xml

在SysMenuMapper.xml映射文件中添加如下的sql語句

<select id="selectById" resultMap="sysMenuMap">select <include refid="columns" /> from sys_menu where id = #{id}
</select>
SysRoleMenuMapper

角色菜單持久層代碼:

// com.atguigu.spzx.manager.mapper
@Mapper
public interface SysRoleMenuMapper {public abstract  void updateSysRoleMenuIsHalf(Long menuId);
}
SysRoleMenuMapper.xml

在SysRoleMenuMapper.xml映射文件中添加如下的sql語句

<select id="updateSysRoleMenuIsHalf">update sys_role_menu srm set srm.is_half = 1 where menu_id = #{menuId}
</select>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/711044.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/711044.shtml
英文地址,請注明出處:http://en.pswp.cn/news/711044.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【git隨筆,日常積累】

Git常用基礎 branch 查看所有分支&#xff1a; git branch -a切換到分支&#xff1a;git checkout develop創建分支并切換到&#xff1a;git checkout -b develop創建一個新分支&#xff1a;git checkout --orphan new_branch --orphan 選項用于創建一個沒有歷史記錄的分支 刪…

騰訊云安裝MYSQL遠程連接不上解決方案

推薦安裝步驟博客&#xff0c;寫的很詳細&#xff0c;如果不會安裝的話&#xff0c;可以根據安裝步驟一直走。 Windows10下超詳細Mysql安裝_win10安裝mysql-CSDN博客 修改 my.cnf或者my.ini 找到里面bind-address將bind-address 127.0.0.1設置成bind-address 0.0.0.0&#x…

AI英語學習助手-幫助建立詞庫和句子-極簡安裝(python基于Django和 OpenAI GPT API的網站程序)

AI英語學習助手-幫助建立詞庫和句子-極簡安裝&#xff08;python基于Django和 OpenAI GPT API的網站程序&#xff09; 學了很久的英語&#xff0c;但是發現還是被單詞困住了&#xff0c;天天查句子查單詞太麻煩&#xff0c;現在有了Chat GPT&#xff0c;能夠很好得幫助學習英語…

CSP-202109-2-非零段劃分

CSP-202109-2-非零段劃分 【70分思路-暴力枚舉】 這段代碼的目的是在給定一個由自然數&#xff08;非負整數&#xff09;組成的數組后&#xff0c;通過選擇一個適當的正整數 p&#xff0c;將數組中所有小于 p 的數變為 0&#xff0c;從而使得數組中非零段的數量達到最大。這里…

使用 gma 繪制隋唐洛陽城

背景 最近河南文旅大伙&#xff0c;給家鄉帶了一波熱度&#xff0c;想想又是王子又是公主&#xff0c;著實羨慕。出門在外&#xff0c;還是對加很有感覺得&#xff0c;不過很遺憾&#xff0c;本人不能為家鄉做出貢獻&#xff0c;只能使用這種小伎倆&#xff0c;稍稍展示&#…

【網絡編程】理解客戶端和服務器并使用Java提供的api實現回顯服務器

目錄 一、網絡編程 二、客戶端和服務器 三、客戶端和服務器的交互模式 四、TCP 和 UDP UDP socket api 的使用 1、DatagramSoket 2、DatagramPacket TCP socket api 的使用 1、ServerSocket 2、Socket 一、網絡編程 本質上就是學習傳輸層給應用層提供的 api&#x…

Leetcode 128. 最長連續序列

最長連續序列 給定一個未排序的整數數組 nums &#xff0c;找出數字連續的最長序列&#xff08;不要求序列元素在原數組中連續&#xff09;的長度。 請你設計并實現時間復雜度為 O(n) 的算法解決此問題。 示例 1&#xff1a; 輸入&#xff1a;nums [100,4,200,1,3,2] 輸出&am…

ARM簡介

ARM&#xff1a;ARM是Advanced RISC Machine的縮寫&#xff0c;意為高級精簡指令集計算機。 英國ARM公司&#xff0c;2016年被軟銀創始人孫正義斥資320億美元收購了。現在是軟銀旗下的芯片設計公司&#xff0c;總部位于英國劍橋&#xff0c;專注于設計芯片&#xff0c;賣芯片生…

揭秘:頭部房企如何借助數據分析實現穩健發展?

房地產行業是我國國民經濟中的重要支柱產業之一&#xff0c;在房地產市場供求關系發生重大變化的當下&#xff0c;房企面臨多重挑戰。Kyligence 服務的這家頭部房企把發展的重點聚焦于內生&#xff0c;關注內生的轉化率、接管的效率以及內生毛利率的提升&#xff0c;引入 Kylig…

基于springboot實現保險信息網站系統項目【項目源碼+論文說明】計算機畢業設計

基于springboot實現保險信息網站系統演示 摘要 隨著互聯網的不斷發展&#xff0c;現在人們獲取最新資訊的主要途徑來源于網上新聞&#xff0c;當下的網上信息宣傳門戶網站的發展十分的迅速。而保險產品&#xff0c;作為當下人們非常關注的一款能夠給人們帶來醫療、生活、養老或…

面試筆記系列七之多線程+分布式系統基礎知識點整理及常見面試題

目錄 多線程 介紹一下線程的生命周期及狀態&#xff1f; 線程的sleep、wait、join、yield如何使用&#xff1f; sleep與yield方法的區別在于&#xff0c; 進程調度算法 創建線程有哪些方式&#xff1f; 什么是守護線程&#xff1f; ThreadLocal的原理是什么&#xff0c;…

當大語言模型遇到AI繪畫-google gemma與stable diffusion webui融合方法-礦卡40hx的AI一體機

你有想過建一臺主機&#xff0c;又能AI聊天又能AI繪畫&#xff0c;還可以直接把聊天內容直接畫出來的機器嗎&#xff1f; 當Google最新的大語言模型Gemma碰到stable diffusion webui會怎么樣&#xff1f; 首先我們安裝stable diffusion webui(automatic1111開源項目&#xff…

微信小程序構建npm失敗解決方式

安裝完所需要的依賴后&#xff0c;在微信開發者工具菜單欄中選擇&#xff1a;“工具” -> “構建 npm”&#xff0c;但是失敗。 解決方法&#xff1a;修改 project.config.json 開發者工具創建的項目&#xff0c;miniprogramRoot 默認為 miniprogram&#xff0c;package.js…

數據遷移DTS | 云上MySQL 數據庫遷移至達夢數據庫

引入 云上 MySQL 數據庫 —> 向達夢國產化數據庫遷移 下載&安裝 達夢客戶端工具 DM->可參考之前國產化專欄達夢文章 創建模式 在客戶端分別依次執行以下命令腳本&#xff08;這里沒有通過客戶端管理工具去創建達夢數據庫的模式&#xff0c;當然也可以通過圖形化界…

WordPress通過寶塔面板的入門安裝教程【保姆級】

WordPress安裝教程【保姆級】【寶塔面板】 前言一&#xff1a;安裝環境二&#xff1a;提前準備三&#xff1a;域名解析四&#xff1a;開始安裝五&#xff1a;安裝成功 前言 此教程適合新手&#xff0c;即使不懂代碼&#xff0c;也可輕松安裝wordpress 一&#xff1a;安裝環…

node如何解析前端傳遞過來的命令行字符串

node如何解析前端傳遞過來的命令行字符串 在Node.js中&#xff0c;如果你想處理從前端傳遞過來的命令行字符串&#xff0c;你可以根據你的應用程序的架構來決定如何接收這些字符串&#xff0c;然后進行解析。一般來說&#xff0c;命令行字符串可能會通過HTTP請求&#xff08;如…

視頻在線轉換,四種方法任你選!(建議收藏)

在當今的數字時代&#xff0c;視頻已經成為人們日常生活中不可或缺的一部分。我們通過視頻分享知識、表達創造力、觀看娛樂節目&#xff0c;甚至參與遠程學習和工作。然而&#xff0c;隨著視頻格式的多樣化和設備的激增&#xff0c;我們經常會遇到一個常見的問題&#xff1a;視…

Linux(CentOS)學習

一、認識Linux 1、如何修改Linux時區 2、配置固定IP 3、重啟網絡服務 3、小技巧快捷鍵 4、環境變量設置 5、Linux文件的上傳和下載 6、壓縮和解壓 二、基礎命令 1、目錄命令 (1、)查看目錄內容&#xff08;ls&#xff09; 1、ls //查看當前目錄內容 2、- a //顯示隱藏內容 3…

深入理解Lucene:開源全文搜索引擎的核心技術解析

1. 介紹 Lucene是什么&#xff1f; Lucene是一個開源的全文搜索引擎庫&#xff0c;提供了強大的文本搜索和檢索功能。它由Apache軟件基金會維護和開發&#xff0c;采用Java語言編寫&#xff0c;因其高性能、可擴展性和靈活性而備受歡迎。 Lucene的作用和應用場景 Lucene主要…

Linux下創建用戶并且賦root權限

背景&#xff1a;好幾次都要求自己在服務器上創建用戶&#xff0c;并且賦權限給這個用戶的root權限&#xff0c;因為生產服務器上不讓用root用戶操作&#xff0c;之前沒怎么記錄&#xff0c;因為這個操作不多&#xff0c;但是又記不住這個操作&#xff0c;一到用上&#xff0c;…