SpringBoot和Vue实现权限菜单功能——基于SpringBoot和Vue的后台管理系统项目系列博客(十七)
今天的主要内容包括:新建角色数据表、使用代码生成器生成角色代码、测试生成的角色代码、新建菜单数据表、使用代码生成器生成菜单代码、测试生成的菜单代码、分配权限菜单基础功能实现、分配权限菜单图表显示与编辑功能、分配权限菜单的授权保存功能、赋予用户以角色、实现用户角色的动态菜单、实现用户菜单的动态路由、完成404提示界面、配置管理员修改用户权限后重新登录、父级菜单不显示的Bug解决、选择部分子菜单后子菜
·
系列文章目录
- 系统功能演示——基于SpringBoot和Vue的后台管理系统项目系列博客(一)
- Vue2安装并集成ElementUI——基于SpringBoot和Vue的后台管理系统项目系列博客(二)
- Vue2前端主体框架搭建——基于SpringBoot和Vue的后台管理系统项目系列博客(三)
- SpringBoot后端初始框架搭建——基于SpringBoot和Vue的后台管理系统项目系列博客(四)
- SpringBoot集成Mybatis——基于SpringBoot和Vue的后台管理系统项目系列博客(五)
- SpringBoot实现增删改查——基于SpringBoot和Vue的后台管理系统项目系列博客(六)
- SpringBoot实现分页查询——基于SpringBoot和Vue的后台管理系统项目系列博客(七)
- SpringBoot实现集成Mybatis-Plus和SwaggerUI——基于SpringBoot和Vue的后台管理系统项目系列博客(八)
- Vue实现增删改查——基于SpringBoot和Vue的后台管理系统项目系列博客(九)
- SpringBoot实现代码生成器——基于SpringBoot和Vue的后台管理系统项目系列博客(十)
- Vue使用路由——基于SpringBoot和Vue的后台管理系统项目系列博客(十一)
- SpringBoot和Vue实现导入导出——基于SpringBoot和Vue的后台管理系统项目系列博客(十二)
- SpringBoot和Vue实现用户登录注册与异常处理——基于SpringBoot和Vue的后台管理系统项目系列博客(十三)
- SpringBoot和Vue实现用户个人信息展示与保存与集成JWT——基于SpringBoot和Vue的后台管理系统项目系列博客(十四)
- SpringBoot和Vue实现文件上传与下载——基于SpringBoot和Vue的后台管理系统项目系列博客(十五)
- SpringBoot和Vue整合ECharts——基于SpringBoot和Vue的后台管理系统项目系列博客(十六)
- SpringBoot和Vue实现权限菜单功能——基于SpringBoot和Vue的后台管理系统项目系列博客(十七)
- SpringBoot实现1对1、1对多、多对多关联查询——基于SpringBoot和Vue的后台管理系统项目系列博客(十八)
- 用户前台页面设计与实现——基于SpringBoot和Vue的后台管理系统项目系列博客(十九)
- SpringBoot集成Redis实现缓存——基于SpringBoot和Vue的后台管理系统项目系列博客(二十)
- SpringBoot和Vue集成高德地图——基于SpringBoot和Vue的后台管理系统项目系列博客(二十一)
- SpringBoot和Vue集成视频播放组件——基于SpringBoot和Vue的后台管理系统项目系列博客(二十二)
- SpringBoot和Vue集成Markdown和多级评论——基于SpringBoot和Vue的后台管理系统项目系列博客(二十三)
项目资源下载
文章目录
- 系列文章目录
- 项目资源下载
- 前言
- 一、新建角色数据表
- 二、使用代码生成器生成角色代码
- 三、测试生成的角色代码
- 四、新建菜单数据表
- 五、使用代码生成器生成菜单代码
- 六、测试生成的菜单代码
- 七、分配权限菜单基础功能实现
- 八、分配权限菜单图标显示与编辑功能
- 九、分配权限菜单的授权保存功能
- 十、赋予用户以角色
- 十一、实现用户角色的动态菜单
- 十二、实现用户菜单的动态路由
- 十三、完成404提示界面
- 十四、配置管理员修改用户权限后重新登陆
- 十五、父级菜单不显示的Bug解决
- 十六、选择部分子菜单后子菜单全部选中的Bug解决
- 十七、随机访问某一界面却进入404界面Bug的解决
- 十八、访问修改密码界面却进入404界面Bug的解决
- 十九、提交新增一级菜单信息没反应Bug的解决
- 二十、新增菜单页面后授权给用户重新登陆再点击新增的菜单页面却出现404页面Bug的解决
- 总结
前言
今天的主要内容包括:新建角色数据表、使用代码生成器生成角色代码、测试生成的角色代码、新建菜单数据表、使用代码生成器生成菜单代码、测试生成的菜单代码、分配权限菜单基础功能实现、分配权限菜单图表显示与编辑功能、分配权限菜单的授权保存功能、赋予用户以角色、实现用户角色的动态菜单、实现用户菜单的动态路由、完成404提示界面、配置管理员修改用户权限后重新登录、父级菜单不显示的Bug解决、选择部分子菜单后子菜单全部选中的Bug解决、随机访问某一界面却进入404界面Bug的解决、访问修改密码界面却进入404界面Bug的解决、提交新增一级菜单信息没反应Bug的解决、新增菜单页面后授权给用户重新登录再点击新增菜单页面却出现404页面Bug的解决。可以看到今天的内容非常多,也有一定的难度,请各位读者一定要仔细跟着我做。废话不多少,下面就开始今天的学习!
一、新建角色数据表
- 在Mysql中新建数据表,添加如下字段
- 表名如下
二、使用代码生成器生成角色代码
- 在CodeGenerator.java中生成加入刚创建的表名
- 在controller.java.vm中修改此部分为entity参数
- 然后右键运行CodeGenerator.java,生成代码即可,最后我们发现已经成功生成了所需代码,非常方便
- 然后加入如下两处代码,为后面的搜索做准备
三、测试生成的角色代码
- 然后在前端中新建Role.vue,在其中加入如下代码
<template>
<!-- 页面主体 -->
<div>
<!-- 搜索部分 -->
<div style="margin: 10px 0">
<el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search"
v-model="name"></el-input>
<el-button class="ml-5" type="primary" @click="load">搜索</el-button>
<el-button type="warning" @click="reset">重置</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-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='我再想想'
icon="el-icon-info"
icon-color="red"
title="您确定批量删除这些数据吗?"
@confirm="delBatch"
>
<el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</div>
<!-- 表格内部操作部分 -->
<el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="ID" width="80"></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="操作" width="200" align="center">
<template slot-scope="scope">
<el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
<el-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='我再想想'
icon="el-icon-info"
icon-color="red"
title="您确定删除吗?"
@confirm="del(scope.row.id)"
>
<el-button type="danger" slot="reference">删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- 翻页与页码部分 -->
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[2, 5, 10, 20]"
: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 label-width="80px" size="small">
<el-form-item label="名称">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="描述">
<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>
<!--页面数据与动作Js代码-->
<script>
export default {
name: "Role",
data() {
return {
tableData: [],
total: 0,
pageNum: 1,
pageSize: 10,
name: "",
form: {},
dialogFormVisible: false,
multipleSelection: []
}
},
// 请求分页查询数据
created() {
this.load()
},
methods: {
// 将数据库查询操作封装
load() {
this.request.get("/role/page", {
params: {
pageNum: this.pageNum,
pageSize: this.pageSize,
name: this.name,
}
}).then(res => {
console.log(res)
this.tableData = res.records
this.total = res.total
})
},
save() {
this.request.post("/role", this.form).then(res => {
if (res) {
this.$message.success("保存成功")
this.dialogFormVisible = false
this.load()
} else {
this.$message.error("保存失败")
}
})
},
handleAdd() {
this.dialogFormVisible = true
this.form = {}
},
handleEdit(row) {
this.form = row
this.dialogFormVisible = true
},
del(id) {
this.request.delete("/role/" + id).then(res => {
if (res) {
this.$message.success("删除成功")
this.load()
} else {
this.$message.error("删除失败")
}
})
},
handleSelectionChange(val) {
console.log(val)
this.multipleSelection = val
},
delBatch() {
let ids = this.multipleSelection.map(v => v.id) // [{}, {}, {}] => [1,2,3]
this.request.post("/role/del/batch", ids).then(res => {
if (res) {
this.$message.success("批量删除成功")
this.load()
} else {
this.$message.error("批量删除失败")
}
})
},
reset() {
this.name = ""
this.load()
},
// 动态分页请求
handleSizeChange(pageSize) {
console.log(pageSize)
this.pageSize = pageSize
this.load()
},
handleCurrentChange(pageNum) {
console.log(pageNum)
this.pageNum = pageNum
this.load()
},
exp() {
window.open("http://localhost:9090/role/export")
},
handleExcelImportSuccess(){
this.$message.success("文件导入成功!")
this.load()
}
}
}
</script>
<!--表格头部样式-->
<style>
.headerBg {
background: #eee !important;
}
</style>
- 然后在index.js中加入路由
- 然后在Aside.vue中加入角色管理侧边栏
- 最后在前端测试一下,发现功能均完好
四、新建菜单数据表
- 在Mysql中新建数据表,添加如下字段
- 表名如下
五、使用代码生成器生成菜单代码
- 在CodeGenerator.java中设置刚才的数据库名称
- 然后运行代码生成器,我们发现已经成功生成所需代码
- 在生成的MenuController.java中加入如下两行代码,为了实现后面的搜索功能
六、测试生成的菜单代码
- 在Menu.vue中加入如下代码
<template>
<!-- 页面主体 -->
<div>
<!-- 搜索部分 -->
<div style="margin: 10px 0">
<el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search"
v-model="name"></el-input>
<el-button class="ml-5" type="primary" @click="load">搜索</el-button>
<el-button type="warning" @click="reset">重置</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-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='我再想想'
icon="el-icon-info"
icon-color="red"
title="您确定批量删除这些数据吗?"
@confirm="delBatch"
>
<el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</div>
<!-- 表格内部操作部分 -->
<el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="name" label="名称"></el-table-column>
<el-table-column prop="path" label="路径"></el-table-column>
<el-table-column prop="icon" label="图标"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
<el-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='我再想想'
icon="el-icon-info"
icon-color="red"
title="您确定删除吗?"
@confirm="del(scope.row.id)"
>
<el-button type="danger" slot="reference">删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- 翻页与页码部分 -->
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[2, 5, 10, 20]"
: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 label-width="80px" size="small">
<el-form-item label="名称">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="路径">
<el-input v-model="form.path" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="图标">
<el-input v-model="form.icon" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="描述">
<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>
<!--页面数据与动作Js代码-->
<script>
export default {
name: "Role",
data() {
return {
tableData: [],
total: 0,
pageNum: 1,
pageSize: 10,
name: "",
form: {},
dialogFormVisible: false,
multipleSelection: []
}
},
// 请求分页查询数据
created() {
this.load()
},
methods: {
// 将数据库查询操作封装
load() {
this.request.get("/menu/page", {
params: {
pageNum: this.pageNum,
pageSize: this.pageSize,
name: this.name,
}
}).then(res => {
console.log(res)
this.tableData = res.records
this.total = res.total
})
},
save() {
this.request.post("/menu", this.form).then(res => {
if (res) {
this.$message.success("保存成功")
this.dialogFormVisible = false
this.load()
} else {
this.$message.error("保存失败")
}
})
},
handleAdd() {
this.dialogFormVisible = true
this.form = {}
},
handleEdit(row) {
this.form = row
this.dialogFormVisible = true
},
del(id) {
this.request.delete("/menu/" + id).then(res => {
if (res) {
this.$message.success("删除成功")
this.load()
} else {
this.$message.error("删除失败")
}
})
},
handleSelectionChange(val) {
console.log(val)
this.multipleSelection = val
},
delBatch() {
let ids = this.multipleSelection.map(v => v.id) // [{}, {}, {}] => [1,2,3]
this.request.post("/menu/del/batch", ids).then(res => {
if (res) {
this.$message.success("批量删除成功")
this.load()
} else {
this.$message.error("批量删除失败")
}
})
},
reset() {
this.name = ""
this.load()
},
// 动态分页请求
handleSizeChange(pageSize) {
console.log(pageSize)
this.pageSize = pageSize
this.load()
},
handleCurrentChange(pageNum) {
console.log(pageNum)
this.pageNum = pageNum
this.load()
},
exp() {
window.open("http://localhost:9090/menu/export")
},
handleExcelImportSuccess(){
this.$message.success("文件导入成功!")
this.load()
}
}
}
</script>
<!--表格头部样式-->
<style>
.headerBg {
background: #eee !important;
}
</style>
- 在index.js中设置路由
- 在Aside.vue中加入菜单管理侧边栏
- 最后测试一下,发现所有功能均完好
七、分配权限菜单基础功能实现
- 在Role.vue中加入分配权限菜单功能的按钮
- 效果如下图所示
- 在Menu.vue中加入如下代码,方便后面的权限分配
- 在Menu.java中加入如下字段
- 在sys_menu表中加入如下父级字段
- 并加入内容
- 修改MenuController.java中的内容,为了后面的父子级菜单功能
- 来到前端测试,发现成功显示父子目录
- 加入新增子菜单按钮
- 然后完善此函数功能
- 最终效果如下图所示
- 最后将其修改为如下形式,需要注意的是以上操作都在Menu.vue中进行
- 然后在Role.vue中加入菜单分配输入框
- 然后加入相关信息
- 然后新增请求菜单数据的函数
- 最终效果如下图所示,已经成功请求到动态数据了,需要注意以上操作都在Role.vue中完成
八、分配权限菜单图标显示与编辑功能
- 新建数据表
- 设置表名为sys_dict
- 在其中输入如下内容
- 在entity下新建Dict实体类
- 在Dict.java中输入如下内容
- 在mapper下新建DictMapper,方便后面的增删改查
- 在其中输入如下内容
- 在MenuController.java中引入DictMapper,方便后续操作
- 然后在MenuController.java中增加查找图标的函数
- 在Constants.java中加入图标类型变量
- 在MenuController.java中加入查询图标类型的代码
- 在Menu.vue中加入如下三处代码,为了选择图标
- 然后我们来到前台测试,发现已经成功显示图标下拉菜单了
- 然后点击保存,发现已经成功保存图标的代码
- 但是现在只能显示图标代码,为了显示真正的图标,我们在Menu.vue中加入如下两处代码
- 然后来到前端测试,我们发现已经成功显示图标样式了
- 但是角色管理那里还没有显示图标也没有默认展开,为了显示图标和默认展开我们同样在Role.vue中修改如下三处代码
- 然后我们来到角色管理页面,我们发现已经成功显示图标了,而且默认展开
九、分配权限菜单的授权保存功能
- 新建表输入如下字段内容
- 将此表命名为sys_role_menu
- 在entity中新建RoleMenu.java
- 在RoleMenu.java中输入如下内容
- 然后在mapper中新建RoleMenuMapper.java
- 在RoleMenuMapper.java中输入如下代码
- 在RoleMenuMapper.java中输入如下内容
- 将IRoleService.java替换为如下内容,注意自己的路径
package com.ironmanjay.springboot.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ironmanjay.springboot.entity.Menu;
import com.ironmanjay.springboot.entity.Role;
import com.ironmanjay.springboot.entity.RoleMenu;
import com.ironmanjay.springboot.mapper.RoleMapper;
import com.ironmanjay.springboot.mapper.RoleMenuMapper;
import com.ironmanjay.springboot.service.IMenuService;
import com.ironmanjay.springboot.service.IRoleService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
* @author IronmanJay
* @since 2022-09-14
*/
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {
@Resource
private RoleMenuMapper roleMenuMapper;
@Resource
private IMenuService menuService;
@Override
public void setRoleMenu(Integer roleId, List<Integer> menuIds) {
// 先删除当前角色id所有的绑定关系
roleMenuMapper.deleteByRoleId(roleId);
// 再把前端传过来的菜单id数组绑定到当前的这个角色id上去
for (Integer menuId : menuIds) {
RoleMenu roleMenu = new RoleMenu();
roleMenu.setRoleId(roleId);
roleMenu.setMenuId(menuId);
roleMenuMapper.insert(roleMenu);
}
}
@Override
public List<Integer> getRoleMenu(Integer roleId) {
return roleMenuMapper.selectByRoleId(roleId);
}
}
- 在RoleController.java中输入如下内容
- 然后将Role.vue全部替换为如下内容
<template>
<!-- 页面主体 -->
<div>
<!-- 搜索部分 -->
<div style="margin: 10px 0">
<el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search"
v-model="name"></el-input>
<el-button class="ml-5" type="primary" @click="load">搜索</el-button>
<el-button type="warning" @click="reset">重置</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-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='我再想想'
icon="el-icon-info"
icon-color="red"
title="您确定批量删除这些数据吗?"
@confirm="delBatch"
>
<el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</div>
<!-- 表格内部操作部分 -->
<el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="ID" width="80"></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="操作" width="280" align="center">
<template slot-scope="scope">
<el-button type="info" @click="selectMenu(scope.row.id)">分配菜单 <i class="el-icon-menu"></i>
</el-button>
<el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
<el-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='我再想想'
icon="el-icon-info"
icon-color="red"
title="您确定删除吗?"
@confirm="del(scope.row.id)"
>
<el-button type="danger" slot="reference">删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- 翻页与页码部分 -->
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[2, 5, 10, 20]"
: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 label-width="80px" size="small">
<el-form-item label="名称">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="描述">
<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-checkbox
node-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="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="saveRoleMenu">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<!--页面数据与动作Js代码-->
<script>
export default {
name: "Role",
data() {
return {
tableData: [],
total: 0,
pageNum: 1,
pageSize: 10,
name: "",
form: {},
dialogFormVisible: false,
menuDialogVis: false,
multipleSelection: [],
menuData: [],
props: {
label: 'name',
},
expends: [],
checks: [],
roleId: 0
}
},
// 请求分页查询数据
created() {
this.load()
},
methods: {
// 将数据库查询操作封装
load() {
this.request.get("/role/page", {
params: {
pageNum: this.pageNum,
pageSize: this.pageSize,
name: this.name,
}
}).then(res => {
console.log(res)
this.tableData = res.records
this.total = res.total
});
},
save() {
this.request.post("/role", this.form).then(res => {
if (res) {
this.$message.success("保存成功")
this.dialogFormVisible = false
this.load()
} else {
this.$message.error("保存失败")
}
})
},
saveRoleMenu() {
this.request.post("/role/roleMenu/" + this.roleId, this.$refs.tree.getCheckedKeys()).then(res => {
if (res.code === '200') {
this.$message.success("绑定成功")
this.menuDialogVis = false
} else {
this.$message.error(res.msg)
}
})
},
handleAdd() {
this.dialogFormVisible = true
this.form = {}
},
handleEdit(row) {
this.form = row
this.dialogFormVisible = true
},
del(id) {
this.request.delete("/role/" + id).then(res => {
if (res) {
this.$message.success("删除成功")
this.load()
} else {
this.$message.error("删除失败")
}
})
},
handleSelectionChange(val) {
console.log(val)
this.multipleSelection = val
},
delBatch() {
let ids = this.multipleSelection.map(v => v.id) // [{}, {}, {}] => [1,2,3]
this.request.post("/role/del/batch", ids).then(res => {
if (res) {
this.$message.success("批量删除成功")
this.load()
} else {
this.$message.error("批量删除失败")
}
})
},
reset() {
this.name = ""
this.load()
},
// 动态分页请求
handleSizeChange(pageSize) {
console.log(pageSize)
this.pageSize = pageSize
this.load()
},
handleCurrentChange(pageNum) {
console.log(pageNum)
this.pageNum = pageNum
this.load()
},
selectMenu(roleId) {
this.menuDialogVis = true;
this.roleId = roleId;
// 请求菜单数据
this.request.get("/menu", {}).then(res => {
console.log(res);
this.menuData = res.data;
// 把后台返回的菜单数据处理成id数组
this.expends = this.menuData.map(v => v.id)
})
this.request.get("/role/roleMenu/" + this.roleId).then(res => {
this.checks = res.data;
})
},
}
}
</script>
<!--表格头部样式-->
<style>
.headerBg {
background: #eee !important;
}
</style>
- 来到前端测试授权功能
- 最后发现已经将授权信息成功保存到数据库中,这样我们现阶段任务已经完成了
十、赋予用户以角色
- 在sys_user中新增role字段
- 在sys_role中新增flag字段
- 并在sys_role中输入数据
- 在User.java中加入角色字段
- 在Role.java中加入唯一标识字段
- 在Role.vue中添加如下两处代码
- 在User.vue中添加如下四处代码
- 然后来到前端测试
- 发现已经成功保存了用户的角色信息
十一、实现用户角色的动态菜单
- 在UserDTO中加入两个新的字段
- 在common中新建枚举类型RoleEnum.java
- 在其中输入如下内容
- 在RoleMapper.java中加入如下内容
- 在MenuServiceImpl.java中输入如下内容
- 在IMenuService.java中输入以下内容
- 然后在MenuController.java中将此方法封装
- 在UserServiceImpl.java中引入IMenuService
- 在UserServiceImpl.java中加入如下函数,获取当前角色的菜单信息
- 在UserServiceImpl.java中修改login函数,在登陆的时候设置用户的菜单列表
- 在UserController.java中的login方法中增加如下代码
- 然后来到前端测试,发现可以针对不同的用户显示不同的菜单
- 在Login.vue中加入如下内容
- 然后将Aside.vue全部替换为以下内容
<!-- 侧边栏内容 -->
<template>
<el-menu :default-openeds="opens" style="min-height: 100%; overflow-x: hidden"
background-color="rgb(48, 65, 86)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"
router
>
<div style="height: 60px; line-height: 60px; text-align: center">
<img src="../assets/logo.png" alt="" style="width: 20px; position: relative; top: 5px; right: 5px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<div v-for="item in menus" :key="item.id">
<div v-if="item.path">
<el-menu-item :index="item.path">
<i :class="item.icon"></i>
<span slot="title">{{ item.name }}</span>
</el-menu-item>
</div>
<div v-else>
<el-submenu :index="item.id + ''">
<template slot="title">
<i :class="item.icon"></i>
<span slot="title">{{ item.name }}</span>
</template>
<div v-for="subItem in item.children" :key="subItem.id">
<el-menu-item :index="subItem.path">
<i :class="subItem.icon"></i>
<span slot="title">{{ subItem.name }}</span>
</el-menu-item>
</div>
</el-submenu>
</div>
</div>
</el-menu>
</template>
<script>
export default {
name: "Aside",
props: {
isCollapse: Boolean,
logoTextShow: Boolean,
},
data() {
return {
menus: localStorage.getItem("menus") ? JSON.parse(localStorage.getItem("menus")) : [],
opens: localStorage.getItem("menus") ? JSON.parse(localStorage.getItem("menus")).map(v => v.id + '') : [],
}
}
}
</script>
<style scoped>
</style>
- 然后来到前端发现,不同的用户角色会显示不同的菜单,这样就实现了我们的功能
①:普通用户
②:超级用户
十二、实现用户菜单的动态路由
- 在sys_menu中加入新的字段,用来保存页面路径
- 在Menu.java中加入新的对应字段
- 在Menu.vue中加入两处对应字段
- 然后来到前端测试,给每个页面输入对应的页面路径
- 然后将其保存成如下形式即可
- 将router/index.js中的全部内容替换为如下内容
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from "@/store/store";
Vue.use(VueRouter)
const routes = [
// 用户登陆页面
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue')
},
// 用户注册页面
{
path: '/register',
name: 'Register',
component: () => import('../views/Register.vue')
},
{
path: '/404',
name: '404',
component: () => import('../views/404.vue')
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
// 注意:刷新页面会导致页面路由重置
export const setRoutes = () => {
const storeMenus = localStorage.getItem("menus");
if (storeMenus) {
// 拼装动态路由
const manageRoute = {
path: '/',
name: 'Manage',
component: () => import('../views/Manage.vue'),
redirect: "/home",
children: []
}
const menus = JSON.parse(storeMenus)
menus.forEach(item => {
if (item.path) { // 当且仅当path不为空的时候才去设置路由
let itemMenu = {
path: item.path.replace("/", ""),
name: item.name,
component: () => import('../views/' + item.pagePath + '.vue')
}
manageRoute.children.push(itemMenu)
} else if (item.children.length) {
item.children.forEach(item => {
if (item.path) {
let itemMenu = {
path: item.path.replace("/", ""),
name: item.name,
component: () => import('../views/' + item.pagePath + '.vue')
}
manageRoute.children.push(itemMenu)
}
})
}
})
// 获取当前的路由对象名称数组
const currentRouteNames = router.getRoutes().map(v => v.name)
if (!currentRouteNames.includes("Manage")) {
// 动态添加到现在的路由对象中去
router.addRoute(manageRoute)
}
}
}
// 重置我就再set一次路由
setRoutes()
// 路由守卫
router.beforeEach((to, from, next) => {
localStorage.setItem("currentPathName", to.name) // 设置当前的路由名称,为了在Header组件中去使用
store.commit("setPath") // 触发store的数据更新
// 未找到路由的情况
if (!to.matched.length) {
const storeMenus = localStorage.getItem("menus")
if (storeMenus) {
next("/404")
} else {
// 跳回登录页面
next("/login")
}
}
next() // 放行路由
})
export default router
- 然后在Login.vue中添加如下两处代码
- 然后来到前端发现,已经可以针对不同的用户角色,产生不同的动态菜单路由了
十三、完成404提示界面
- 在此目录下新建404.vue
- 在router/index.js中写入固定路由404
- 然后上网上随便找一张404的图片,放在此目录下
- 然后将404.vue中全部替换为如下内容
<template>
<div style="overflow: hidden; height: 100vh">
<img src="../assets/404.png" alt="" style="width: 100%; height: 100%">
</div>
</template>
<script>
export default {
name: "NotFound"
}
</script>
<style>
.bgImg {
background: url("../assets/404.png") no-repeat;
background-size: 100% 100vh;
}
</style>
- 然后来到前端发现,访问其他非法界面已经可以显示404的提示了,说明成功了
十四、配置管理员修改用户权限后重新登陆
- 在store/store.js中加入如下两处代码
- 然后在Header.vue中加入如下代码
- 在Role.vue中修改如下四处代码
- 然后来到前端测试,发现权限修改后已经成功登录了
十五、父级菜单不显示的Bug解决
- 当我们给普通用户分配子菜单后
- 但是当我们登录普通用户时,明明已经给这个用户分配了子菜单,但是并没有显示,如下图所示,我们要解决这个问题
- 我们只需要在RoleServiceImpl.java中加入如下代码即可
- 然后我们来到前端测试,发现已经成功针对不同的用户显示不同的菜单了
十六、选择部分子菜单后子菜单全部选中的Bug解决
- 当我们只给某个角色赋予部分子菜单后,发现显示的时候展示了全部子菜单内容,这不是我们想要的,我们就要解决这个问题
- 首先在MenuController.java中加入如下内容
- 然后在Role.vue中加入如下内容
- 当我们来到前端测试后发现,当我们选中子菜单后,全部的子菜单也并没有选中,而且侧边栏也没有选中,这样就解决了我们的Bug
十七、随机访问某一界面却进入404界面Bug的解决
- 目前有一个Bug,就是当我们点击某个界面的时候,却是打开了404的界面,我们要解决这个问题
- 首先在index.js中修改如下一处代码
- 然后再在index.js中修改如下内容
- 然后来到前端测试发现问题已经解决
十八、访问修改密码界面却进入404界面Bug的解决
- 首先在如下位置新建UserPasswordDTO.java,并在其中输入如下内容
- 然后在UserMapper.java中输入如下内容
- 然后在mapper中新建UserMapper.java,然后在其中输入如下内容
- 然后在impl中的UserServiceImpl.java中引入此mapper
- 然后还是在UserServiceImpl.java中新建修改密码的函数
- 然后在IUserService.java中新建修改密码的接口
- 然后在UserController.java中新建修改密码的函数
- 然后在views中新建Password.vue
- 然后将Password.vue全部替换为如下内容
<template>
<el-card style="width: 500px;">
<el-form label-width="120px" size="small" :model="form" :rules="rules" ref="pass">
<el-form-item label="原密码" prop="password">
<el-input v-model="form.password" autocomplete="off" show-password></el-input>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input v-model="form.newPassword" autocomplete="off" show-password></el-input>
</el-form-item>
<el-form-item label="确认新密码" prop="confirmPassword">
<el-input v-model="form.confirmPassword" autocomplete="off" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="save">确 定</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
export default {
name: "Password",
data() {
return {
form: {},
user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {},
rules: {
password: [
{ required: true, message: '请输入原密码', trigger: 'blur' },
{ min: 3, message: '长度不少于3位', trigger: 'blur' }
],
newPassword: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 3, message: '长度不少于3位', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 3, message: '长度不少于3位', trigger: 'blur' }
],
}
}
},
created() {
this.form.username = this.user.username
},
methods: {
save() {
this.$refs.pass.validate((valid) => {
if (valid) {
if (this.form.newPassword !== this.form.confirmPassword) {
this.$message.error("2次输入的新密码不相同")
return false
}
this.request.post("/user/password", this.form).then(res => {
if (res.code === '200') {
this.$message.success("修改成功")
this.$store.commit("logout")
} else {
this.$message.error(res.msg)
}
})
}
})
},
}
}
</script>
<style>
.avatar-uploader {
text-align: center;
padding-bottom: 10px;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 138px;
height: 138px;
line-height: 138px;
text-align: center;
}
.avatar {
width: 138px;
height: 138px;
display: block;
}
</style>
- 然后在index.js中加入如下路由内容
- 然后来到前端测试发现已经可以成功修改密码
- 修改密码后回要求重新登陆
十九、提交新增一级菜单信息没反应Bug的解决
- 当我们在菜单管理中,新增一级菜单,点击提交后发现没反应
- 此时我们修改Menu.vue中此部分内容即可
- 然后来到前端测试发现已经成功了
二十、新增菜单页面后授权给用户重新登陆再点击新增的菜单页面却出现404页面Bug的解决
- 当我们新增菜单后,将此菜单的权限授予用户,重新登陆后,点击新增的菜单页面却产生了404错误,我们要解决这个问题
- 为了解决这个问题,我们首先在index.js中新建一个重置路由的方法
- 然后在store.js中引用这个方法用来重置路由
- 再来到前端用同样的方法测试,发现问题已经成功解决了,不会出现404异常界面了(Ps:我使用的文件管理的界面进行测试的,读者也可以新建测试页面)
总结
以上就是今天学习的全部内容了,可以看到内容非常多,而且难度确实不小,但是只要跟着我一步一步做肯定是没问题的,今天就暂时到这里。明天给大家带来关于SpringBoot实现1对1、1对多、多对多关联查询的相关内容。明天见!
更多推荐
已为社区贡献5条内容
所有评论(0)