項目實戰第十三記
- 寫在前面
- 1.建立角色表
- 2. 后端代碼生成
- 2.1 RoleController
- 3. 前端頁面的搭建
- 3.1 Role.vue
- 3.2 路由
- 3.3 Aside.vue
- 3.4 頁面效果
- 4.建立菜單表
- 5.后端代碼編寫
- 5.1 Menu
- 5.2 MenuController
- 6.前端頁面的搭建
- 6.1 Menu.vue
- 6.2 路由
- 6.3 Aside.vue
- 6.4 頁面效果
- 總結
- 寫在最后
寫在前面
本篇主要講解動態分配菜單第一章節,每個角色應該具備不一樣的菜單權限
1.建立角色表
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',`role_key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '唯一標識',`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名稱',`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '描述',`is_delete` tinyint(1) NULL DEFAULT 0 COMMENT '是否刪除',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC;
2. 后端代碼生成
2.1 RoleController
package com.ppj.controller;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Arrays;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ppj.common.Result;import com.ppj.service.IRoleService;
import com.ppj.entity.Role;import org.springframework.web.bind.annotation.RestController;/*** <p>* 前端控制器* </p>** @author ppj* @since 2024-05-29*/
@RestController
@RequestMapping("/role")
public class RoleController {@Resourceprivate IRoleService roleService;// 新增或者更新@PostMappingpublic Result save(@RequestBody Role role) {roleService.saveOrUpdate(role);return Result.success();}@DeleteMapping("/{roleIds}")public Result delete(@PathVariable Integer[] roleIds) {roleService.removeByIds(Arrays.asList(roleIds));return Result.success();}@GetMappingpublic Result findAll() {return Result.success(roleService.list());}@GetMapping("/{id}")public Result findOne(@PathVariable Integer id) {return Result.success(roleService.getById(id));}@GetMapping("/page")public Result findPage(@RequestParam Integer pageNum,@RequestParam Integer pageSize,@RequestParam(defaultValue = "") String name) {QueryWrapper<Role> queryWrapper = new QueryWrapper<>();queryWrapper.like("name",name);return Result.success(roleService.page(new Page<>(pageNum, pageSize), queryWrapper));}}
3. 前端頁面的搭建
3.1 Role.vue
<template><div><!-- 設計的查詢 --><div style="margin: 10px 0"><el-inputstyle="width: 200px"placeholder="請輸入名稱"suffix-icon="el-icon-search"v-model="name"/><el-button type="primary" icon="el-icon-search" class="ml-5" @click="getList">搜索</el-button><el-button type="warning" icon="el-icon-reset" @click="resetQuery">重置</el-button></div><div style="margin: 10px 0"><el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i></el-button><el-button type="warning" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate">修改</el-button><el-button type="danger" :disabled="multiple" @click="handleDelete">刪除 <i class="el-icon-remove-outline"></i></el-button></div><el-table :data="tableData" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" /><el-table-column prop="id" label="角色ID" width="80"></el-table-column><el-table-column prop="roleKey" label="唯一標識"></el-table-column><el-table-column prop="name" label="角色名稱"></el-table-column><el-table-column prop="description" label="角色描述"></el-table-column><el-table-column label="操作"><template v-slot="scope"><el-buttontype="info"icon="el-icon-menu"@click="openMenuAllocDialog(scope.row.id)">分配菜單</el-button><el-button type="success" @click="handleUpdate(scope.row)">編輯 <i class="el-icon-edit"></i></el-button><el-button type="danger" @click="handleDelete(scope.row)">刪除 <i class="el-icon-remove-outline"></i></el-button></template></el-table-column></el-table><div style="padding: 10px 0"><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="pageNum":page-sizes="[5, 10, 15]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div><!-- 角色添加對話框 --><el-dialog title="角色信息" :visible.sync="dialogFormVisible" width="30%"><el-form :model="form"><el-form-item label="唯一標識" :label-width="formLabelWidth"><el-input v-model="form.roleKey" autocomplete="off"></el-input></el-form-item><el-form-item label="角色名稱" :label-width="formLabelWidth"><el-input v-model="form.name" autocomplete="off"></el-input></el-form-item><el-form-item label="描述" :label-width="formLabelWidth"><el-input v-model="form.description" autocomplete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogFormVisible = false">取 消</el-button><el-button type="primary" @click="save">確 定</el-button></div></el-dialog><!-- 分配菜單 --><el-dialog title="菜單分配" :visible.sync="menuDialogVis" width="30%"><el-tree:props="props":data="menuData"show-checkboxnode-key="id"ref="tree":default-expanded-keys="expends":default-checked-keys="checks"><span class="custom-tree-node" slot-scope="{ node, data }"><span><i :class="data.icon"></i> {{ data.name }}</span></span></el-tree><div slot="footer" class="dialog-footer"><el-button @click="menuDialogVis = false">取 消</el-button><el-button type="primary" @click="saveRoleMenu">確 定</el-button></div></el-dialog></div>
</template>
<script>
export default {name: "Role",data() {return {name: "",tableData: [],total: 0,pageSize: 5,pageNum: 1,dialogFormVisible: false,menuDialogVis: false,formLabelWidth: "80px",ids: [],// 非單個禁用single: true,// 非多個禁用multiple: true,form: {id: "",name: "",description: "",},menuData: [],props: {label: 'name',},expends: [],checks: [],roleId: 0,};},//頁面一創建成功created() {//請求分頁查詢數據this.getList();},methods: {getList() {this.request.get("/role/page", {params: {pageNum: this.pageNum,pageSize: this.pageSize,name: this.name,},}).then((res) => {if(res.code === "200"){this.tableData = res.data.records;this.total = res.data.total;}else{this.$message.error(res.msg);}});},//分配菜單openMenuAllocDialog(){this.menuDialogVis = true;//請求菜單數據this.request.get("/menu",{params: {name: ""}}).then(res => {this.menuData = res.data;//展開菜單數據this.expends = this.menuData.map(v => v.id);})},//保存角色下的菜單saveRoleMenu(){},// 重置按鈕resetQuery(){this.username = "";this.pageNum = 1;this.pageSize = 5;this.getList();},handleSizeChange(val) {this.pageSize = val;},handleCurrentChange(val) {this.pageNum = val;this.getList();},// 多選框選中數據handleSelectionChange(selection) {this.ids = selection.map(item => item.id);this.single = selection.length != 1;this.multiple = !selection.length;},// 新增handleAdd(){this.dialogFormVisible = true;this.form = {};},save(){this.request.post("/role",this.form).then(res => {if(res.code === "200" || res.code === 200){this.$message.success("操作成功")}else {this.$message.error("操作失敗")}this.dialogFormVisible = false;this.getList();})},// 修改handleUpdate(row){// 表單置空this.reset();// 重新查詢數據const roleId = row.id || this.ids;this.request.get('/role/'+roleId).then(response => {this.form = response.data;this.dialogFormVisible = true;});},reset(){this.form.roleKey = undefined;this.form.name = undefined;this.form.description = undefined;},// 刪除handleDelete(row){let _this = this;const roleIds = row.id || this.ids;this.$confirm('是否確認刪除角色編號為"' + roleIds + '"的數據項?', '刪除角色', {confirmButtonText: '確定',cancelButtonText: '取消',type: 'warning'}).then(() => {_this.request.delete("/role/"+roleIds).then(res=>{if(res.code === "200" || res.code === 200){_this.$message.success("刪除成功")}else {_this.$message.error("刪除失敗")}this.getList();})}).catch(() => {});}},
};
</script>
3.2 路由
{path: 'role',name: '角色管理',component: () => import('../views/Role.vue'),meta: {title: '角色管理'}
},
3.3 Aside.vue
<el-menu-item index="/role"><i class="el-icon-s-custom"></i><span slot="title">角色管理</span></el-menu-item>
3.4 頁面效果
4.建立菜單表
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '名稱',`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '路徑',`pid` int(11) NULL DEFAULT NULL COMMENT '父級id',`icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '圖標',`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '描述',`is_delete` tinyint(1) NULL DEFAULT 0 COMMENT '邏輯刪除',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC;
5.后端代碼編寫
5.1 Menu
package com.ppj.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.List;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;/*** <p>* * </p>** @author ppj* @since 2024-05-29*/
@Getter
@Setter@TableName("sys_menu")
@ApiModel(value = "Menu對象", description = "")
public class Menu implements Serializable {private static final long serialVersionUID = 1L;@ApiModelProperty("id")@TableId(value = "id", type = IdType.AUTO)private Integer id;@ApiModelProperty("名稱")private String name;@ApiModelProperty("路徑")private String path;@ApiModelProperty("父級id")private Integer pid;@ApiModelProperty("圖標")private String icon;@ApiModelProperty("描述")private String description;@ApiModelProperty("邏輯刪除")private Boolean isDelete;// 構造樹形數據@TableField(exist = false)private List<Menu> children;}
5.2 MenuController
package com.ppj.controller;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ppj.common.Result;import com.ppj.service.IMenuService;
import com.ppj.entity.Menu;import org.springframework.web.bind.annotation.RestController;/*** <p>* 前端控制器* </p>** @author ppj* @since 2024-05-29*/
@RestController
@RequestMapping("/menu")
public class MenuController {@Resourceprivate IMenuService menuService;// 新增或者更新@PostMappingpublic Result save(@RequestBody Menu menu) {menuService.saveOrUpdate(menu);return Result.success();}@DeleteMapping("/{menuIds}")public Result delete(@PathVariable Integer[] menuIds) {menuService.removeByIds(Arrays.asList(menuIds));return Result.success();}@GetMapping("/{id}")public Result findOne(@PathVariable Integer id) {return Result.success(menuService.getById(id));}// 樹形數據@GetMappingpublic Result findAll(@RequestParam(defaultValue = "") String name) {QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();queryWrapper.like("name",name);List<Menu> list = menuService.list(queryWrapper);// 找出一級菜單List<Menu> parentNode = list.stream().filter(m -> m.getPid() == null).collect(Collectors.toList());// 找出一級菜單的子菜單for (Menu menu : parentNode) {menu.setChildren(list.stream().filter(m -> menu.getId().equals(m.getPid())).collect(Collectors.toList()));}return Result.success(parentNode);}@GetMapping("/page")public Result findPage(@RequestParam Integer pageNum,@RequestParam Integer pageSize) {QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();return Result.success(menuService.page(new Page<>(pageNum, pageSize), queryWrapper));}}
6.前端頁面的搭建
6.1 Menu.vue
<template><div><!-- 設計的查詢 --><div style="margin: 10px 0"><el-inputstyle="width: 200px"placeholder="請輸入名稱"suffix-icon="el-icon-search"v-model="name"/><el-button type="primary" icon="el-icon-search" class="ml-5" @click="getList">搜索</el-button><el-button type="warning" icon="el-icon-reset" @click="resetQuery">重置</el-button></div><div style="margin: 10px 0"><el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i></el-button><el-button type="warning" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate">修改</el-button></div><el-table :data="tableData"row-key="id"@selection-change="handleSelectionChange"><el-table-column type="selection" width="55" /><el-table-column prop="id" label="菜單ID"></el-table-column><el-table-columnprop="name"label="菜單名稱"></el-table-column><el-table-columnprop="path"label="菜單路徑"></el-table-column><el-table-columnprop="icon"label="菜單圖標"></el-table-column><el-table-column prop="description" label="描述"></el-table-column><el-table-column label="操作" width="300px"><template v-slot="scope"><el-buttontype="primary"@click="handleAdd(scope.row.id)"v-if="!scope.row.pid && !scope.row.path">新增子菜單</el-button><el-button type="success" @click="handleUpdate(scope.row)">編輯 <i class="el-icon-edit"></i></el-button><el-button type="danger" @click="handleDelete(scope.row)">刪除 <i class="el-icon-remove-outline"></i></el-button></template></el-table-column></el-table><!-- <div style="padding: 10px 0"><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="pageNum":page-sizes="[5, 10, 15]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div> --><!-- 菜單添加對話框 --><el-dialog title="菜單信息" :visible.sync="dialogFormVisible" width="30%"><el-form :model="form"><el-form-item label="菜單名稱" :label-width="formLabelWidth"><el-input v-model="form.name" autocomplete="off"></el-input></el-form-item><el-form-item label="菜單路徑" :label-width="formLabelWidth"><el-input v-model="form.path" autocomplete="off"></el-input></el-form-item><el-form-item label="圖標" :label-width="formLabelWidth"><el-input v-model="form.icon" autocomplete="off"></el-input></el-form-item><el-form-item label="描述" :label-width="formLabelWidth"><el-input v-model="form.description" autocomplete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogFormVisible = false">取 消</el-button><el-button type="primary" @click="save">確 定</el-button></div></el-dialog></div>
</template>
<script>
export default {name: "Menu",data() {return {name: "",tableData: [],total: 0,pageSize: 5,pageNum: 1,dialogFormVisible: false,formLabelWidth: "80px",ids: [],// 非單個禁用single: true,// 非多個禁用multiple: true,form: {id: '',name: "",path: "",icon: "",description: "",},};},//頁面一創建成功created() {//請求分頁查詢數據this.getList();},methods: {getList() {this.request.get("/menu", {params: {name: this.name,},}).then((res) => {if(res.code === "200"){this.tableData = res.data;}else{this.$message.error(res.msg);}});},resetQuery() {this.name = "";this.pageNum = 1;this.pageSize = 5;this.getList();},handleSizeChange(val) {this.pageSize = val;},handleCurrentChange(val) {this.pageNum = val;this.getList();},// 多選框選中數據handleSelectionChange(selection) {this.ids = selection.map(item => item.id);this.single = selection.length != 1;this.multiple = !selection.length;},// 新增handleAdd(id){this.reset();this.dialogFormVisible = true;if(id){ // 新建子菜單時,設置父idthis.form.pid = id;}},save(){this.request.post("/menu",this.form).then(res => {if(res.code === "200" || res.code === 200){this.$message.success("操作成功")}else {this.$message.error("操作失敗")}this.dialogFormVisible = false;this.getList();})},// 修改handleUpdate(row){// 表單置空this.reset();// 重新查詢數據const menuId = row.id || this.ids;this.request.get('/menu/'+menuId).then(response => {this.form = response.data;this.dialogFormVisible = true;});},reset(){this.form.path = undefined;this.form.name = undefined;this.form.icon = undefined;this.form.description = undefined;},// 刪除handleDelete(row){let _this = this;const menuIds = row.id || this.ids;this.$confirm('是否確認刪除菜單編號為"' + menuIds + '"的數據項?', '刪除菜單', {confirmButtonText: '確定',cancelButtonText: '取消',type: 'warning'}).then(() => {_this.request.delete("/menu/"+menuIds).then(res=>{if(res.code === "200" || res.code === 200){_this.$message.success("刪除成功")}else {_this.$message.error("刪除失敗")}this.getList();})}).catch(() => {});}},
};
</script>
6.2 路由
{path: 'menu',name: '菜單管理',component: () => import('../views/Menu.vue'),meta: {title: '菜單管理'}
},
6.3 Aside.vue
<el-menu-item index="/menu"><i class="el-icon-menu"></i><span slot="title">菜單管理</span>
</el-menu-item>
6.4 頁面效果
總結
- 本篇后端難點是構造樹形數據,前端主要是樹形數據菜單和表格展示,細節部分代碼有解釋
寫在最后
如果此文對您有所幫助,請帥戈靚女們務必不要吝嗇你們的Zan,感謝!!不懂的可以在評論區評論,有空會及時回復。
文章會一直更新