今天的主要内容包括:新建角色数据表、使用代码生成器生成角色代码、测试生成的角色代码、新建菜单数据表、使用代码生成器生成菜单代码、测试生成的菜单代码、分配权限菜单基础功能实现、分配权限菜单图表显示与编辑功能、分配权限菜单的授权保存功能、赋予用户以角色、实现用户角色的动态菜单、实现用户菜单的动态路由、完成404提示界面、配置管理员修改用户权限后重新登录、父级菜单不显示的Bug解决、选择部分子菜单后子菜单全部选中的Bug解决、随机访问某一界面却进入404界面Bug的解决、访问修改密码界面却进入404界面Bug的解决、提交新增一级菜单信息没反应Bug的解决、新增菜单页面后授权给用户重新登录再点击新增菜单页面却出现404页面Bug的解决。可以看到今天的内容非常多,也有一定的难度,请各位读者一定要仔细跟着我做。废话不多少,下面就开始今天的学习!
<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>
<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>
<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>
<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>
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;
/**
*
* 服务实现类
*
*
* @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);
}
}
<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>
<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>
<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>
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
<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>
<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>
以上就是今天学习的全部内容了,可以看到内容非常多,而且难度确实不小,但是只要跟着我一步一步做肯定是没问题的,今天就暂时到这里。明天给大家带来关于SpringBoot实现1对1、1对多、多对多关联查询的相关内容。明天见!