项目实战第十五记
- 写在前面
- 1.后端接口实现
- 1.1 用户表添加角色字段
- 1.2 角色表增加唯一标识字段
- 1.3 UserDTO
- 1.4 UserServiceImpl
- 1.5 MenuServiceImpl
- 2. 前端实现
- 2.1 User.vue
- 2.2 动态菜单设计
- 2.2.1 Login.vue
- 2.2.2 Aside.vue
- 2.3 动态路由设计
- 2.3.1 菜单表新增字段page_path
- 2.3.2 路由设计
- 2.3.3 登录界面Login.vue设置路由
- 总结
- 写在最后
写在前面
- 动态菜单设计
- 动态路由设计
1.后端接口实现
1.1 用户表添加角色字段
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`username` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户名',
`password` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码',
`nickname` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '昵称',
`email` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '电话',
`address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '地址',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`avatar_url` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT 'https://himg.bdimg.com/sys/portraitn/item/public.1.23bd3c8c.ANoeKxl_gef9fnrikdXOYA' COMMENT '头像',
`role` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色',
`deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除0(未删除),1(删除)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC
1.2 角色表增加唯一标识字段
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`role_key` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '唯一标识',
`name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',
`description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '描述',
`deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC
1.3 UserDTO
@Data
public class UserDTO {
private String username;
private String password;
private String nickname;
private String avatarUrl;
private String token;
private String role;
private List<Menu> menus;
}
1.4 UserServiceImpl
package com.ppj.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ppj.constants.Constants;
import com.ppj.entity.Menu;
import com.ppj.entity.Role;
import com.ppj.entity.RoleMenu;
import com.ppj.entity.User;
import com.ppj.entity.dto.UserDTO;
import com.ppj.exception.ServiceException;
import com.ppj.mapper.MenuMapper;
import com.ppj.mapper.RoleMapper;
import com.ppj.mapper.RoleMenuMapper;
import com.ppj.mapper.UserMapper;
import com.ppj.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ppj.utils.TokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.sql.rowset.serial.SerialException;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
* @author ppj
* @since 2024-04-20
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
private static final Log LOG = Log.get();
@Autowired
private RoleMapper roleMapper;
@Autowired
private RoleMenuMapper roleMenuMapper;
@Autowired
private MenuServiceImpl menuService;
@Override
public UserDTO login(UserDTO userDTO) {
if(StrUtil.isBlank(userDTO.getUsername()) || StrUtil.isBlank(userDTO.getPassword())){
return null;
}
User user = getUserInfo(userDTO);
if(user != null){
String token = TokenUtils.genToken(user.getId().toString(), userDTO.getPassword());
userDTO.setToken(token);
// 把user相应的值传递给userDTO
BeanUtil.copyProperties(user,userDTO,true);
List<Menu> roleMenus = getRoleMenus(user.getRole());
userDTO.setMenus(roleMenus);
return userDTO;
}else{ // 数据库查不到
throw new ServiceException(Constants.CODE_600,"用户名或密码错误");
}
}
@Override
public Boolean register(UserDTO userDTO) {
if(StrUtil.isBlank(userDTO.getUsername()) || StrUtil.isBlank(userDTO.getPassword())){
return false;
}
User user = getUserInfo(userDTO);
if(user == null){
User newUser = new User();
// 值传递
BeanUtil.copyProperties(userDTO,newUser,true);
// 保存至数据库
save(newUser);
}else{
throw new ServiceException(Constants.CODE_600,"用户已存在");
}
return true;
}
public User getUserInfo(UserDTO userDTO){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",userDTO.getUsername())
.eq("password",userDTO.getPassword());
User user;
try {
// 可能查到多条记录,后台报异常,写个异常类(主动捕获异常)
user = getOne(queryWrapper);
}catch (Exception e){ // 可能查出多个符合条件的记录
LOG.error(e);
throw new ServiceException(Constants.CODE_500,"系统错误");
}
return user;
}
/**
* 根据角色名称获取菜单列表
* @param roleName
* @return
*/
public List<Menu> getRoleMenus(String roleName){
Integer roleId = roleMapper.getRoleIdByName(roleName);
List<Integer> menuIds = roleMenuMapper.getMenuIdsByRoleId(roleId);
// 查询系统中所有菜单,树结构
List<Menu> menus = menuService.findMenus("");
// new一个最后筛选完成之后的list
ArrayList<Menu> roleMenus = new ArrayList<>();
for (Menu menu : menus) {
if(menuIds.contains(menu.getId())){
roleMenus.add(menu);
}
List<Menu> children = menu.getChildren();
// 移除children中不在menuIds集合中的元素
if (children != null) {
children.removeIf(child -> !menuIds.contains(child.getId()));
}
}
return roleMenus;
}
}
1.5 MenuServiceImpl
将写在MenuController中的方法提取到service中(更符合现实中的开发)
如下图所示:controller不含业务代码
package com.ppj.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ppj.entity.Menu;
import com.ppj.mapper.MenuMapper;
import com.ppj.service.IMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>
* 服务实现类
* </p>
*
* @author ppj
* @since 2024-05-29
*/
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {
public List<Menu> findMenus(String name) {
QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
if(StrUtil.isNotBlank(name)){
queryWrapper.like("name",name);
}
List<Menu> list = list(queryWrapper);
//找出一级菜单
List<Menu> parentNodes = list.stream().filter(menu -> menu.getPid() == null).collect(Collectors.toList());
//找出一级菜单的子菜单
for (Menu menu : parentNodes) {
menu.setChildren(list.stream().filter(m -> menu.getId().equals(m.getPid())).collect(Collectors.toList()));
}
return parentNodes;
}
}
2. 前端实现
2.1 User.vue
主要是表格多添加role字段,添加框增加角色选择框
<template>
<div>
<div style="margin: 10px 0">
<el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="username"></el-input>
<el-input style="width: 200px" placeholder="请输入地址" suffix-icon="el-icon-position" class="ml-5" v-model="address"></el-input>
<el-button class="ml-5" type="primary" @click="getList">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @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>
<!-- <el-upload action="http://localhost:9000/user/import" :show-file-list="false" accept="xlsx" :on-success="handleImport" style="display: inline-block">-->
<!-- <el-button type="primary" class="ml-5">导入 <i class="el-icon-bottom"></i></el-button>-->
<!-- </el-upload>-->
<el-button type="success" @click="handleImport" class="ml-5">导入 <i class="el-icon-bottom"></i></el-button>
<el-button type="warning" @click="handleExport" class="ml-5">导出 <i class="el-icon-top"></i></el-button>
</div>
<el-table v-loading="loading" :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="序号" width="140"></el-table-column>
<el-table-column prop="username" label="用户名" width="140"></el-table-column>
<el-table-column prop="nickname" label="昵称" width="140"></el-table-column>
<el-table-column prop="role" label="角色" width="140">
<template v-slot="scope">
<el-tag>{{ scope.row.role}}</el-tag>
</template>
</el-table-column>
<el-table-column prop="email" label="邮箱" width="200"></el-table-column>
<el-table-column prop="address" label="地址" width="140"></el-table-column>
<el-table-column prop="createTime" label="创建时间" width="140"></el-table-column>
<el-table-column label="操作" align="center">
<template v-slot="scope">
<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
class="page"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-sizes="[5, 10]"
: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.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="角色" >
<el-select v-model="form.role" placeholder="请选择" style="width: 100%">
<el-option
v-for="item in roles"
:key="item.name"
:label="item.name"
:value="item.roleKey">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="form.address" 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="upload.title" :visible.sync="upload.open" width="400px">
<el-upload
ref="upload"
:limit="1"
accept=".xlsx, .xls"
:action="upload.url"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或
<em>点击上传</em>
</div>
<div class="el-upload__tip" slot="tip">
<el-link type="info" style="font-size: 16px;color:green" @click="importTemplate">下载模板</el-link>
</div>
<div class="el-upload__tip" style="color: red" slot="tip">
提示:仅允许导入“xls”或“xlsx”格式文件!
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm">确 定</el-button>
<el-button @click="upload.open = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "User",
data(){
return {
tableData: [],
pageSize: 5,
total: 0,
pageNum: 1,
username: '',
address: '',
collapseBtnClass: 'el-icon-s-fold',
isCollapse: false,
sideWidth: 200,
logoTextShow: true,
headerBg: 'headerBg',
dialogFormVisible: false,
form: {},
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
//用户导入参数
upload: {
//是否显示弹出层(用户导入)
open: false,
//弹出层标题(用户导入)
title: "",
//是否禁用上传
// isUploading: false,
//是否更新已经存在的用户数据
//updateSupport: 0,
//设置上传的请求头部
//headers: "",
//上传的地址
url: "http://localhost:9000/user/import",
},
roles: [],
}
},
created() {
this.getList();
},
methods: {
getList(){
this.loading = true;
this.request.get('/user/page',
{
params: {
pageNum: this.pageNum,
pageSize: this.pageSize,
username: this.username,
address: this.address
}
}
).then(res => {
if(res.code === '200'){
this.tableData = res.data.records;
this.total = res.data.total;
this.loading = false;
}else{
this.$message.error(res.msg)
}
})
this.request.get('/role').then(res => {
if(res.code === '200'){
this.roles = res.data;
}else{
this.$message.error(res.msg)
}
})
},
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;
},
// 重置按钮
resetQuery(){
this.username = undefined;
this.address = undefined;
this.getList();
},
// 新增
handleAdd(){
this.dialogFormVisible = true;
this.form = {};
},
save(){
this.request.post("/user",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 userId = row.id || this.ids;
this.request.get('/user/'+userId).then(response => {
this.form = response.data;
this.dialogFormVisible = true;
});
},
reset(){
this.form.username = undefined;
this.form.nickname = undefined;
this.form.email = undefined;
this.form.phone = undefined;
this.form.address = undefined;
},
// 删除
handleDelete(row){
let _this = this;
const userIds = row.id || this.ids;
this.$confirm('是否确认删除用户编号为"' + userIds + '"的数据项?', '删除用户', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
_this.request.delete("/user/"+userIds).then(res=>{
if(res.code === "200" || res.code === 200){
_this.$message.success("删除成功")
}else {
_this.$message.error("删除失败")
}
this.getList();
})
}).catch(() => {
});
},
// 导出
handleExport(){
window.open('http://localhost:9000/user/export');
this.$message.success("导出成功");
},
// handleImport(){
// this.$message.success('导入成功')
// this.getList()
// },
handleImport(){
this.upload.title = '用户导入'
this.upload.open = true
},
importTemplate(){
this.$message.success("正在下载模版");
window.open('http://localhost:9000/user/download')
},
//文件上传处理
handleFileUploadProgress(event,file,fileList){
//this.upload.isUploading = true;
this.loading = true;
},
//文件上传成功处理
handleFileSuccess(response,file,fileList){
this.loading = false;
this.upload.open = false;
// this.upload.isUploading = false;
this.$refs.upload.clearFiles();
this.$message.success("导入成功");
this.getList()
},
//提交上传文件
submitFileForm(){
this.$refs.upload.submit();
}
}
}
</script>
<style scoped>
</style>
2.2 动态菜单设计
2.2.1 Login.vue
在登录成功的时候将菜单存到浏览器中
<template>
<div class="wrapper">
<div style="margin: 200px auto; background-color: #fff; width: 350px; height: 300px; padding: 20px; border-radius: 10px">
<div style="margin: 20px 0; text-align: center; font-size: 24px"><b>登 录</b></div>
<el-form :model="user" :rules="rules" ref="userForm">
<el-form-item prop="username">
<el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-lock" show-password v-model="user.password"></el-input>
</el-form-item>
<el-form-item style="margin: 10px 0; text-align: right">
<el-button type="primary" size="small" autocomplete="off" @click="login">登录</el-button>
<el-button type="warning" size="small" autocomplete="off" @click="$router.push('/register')">注册</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
user: {},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 10, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
],
}
}
},
methods: {
login() {
this.$refs['userForm'].validate((valid) => {
if (valid) { // 表单校验合法
this.request.post("/user/login", this.user).then(res => {
if(res.code === 200 || res.code === '200') {
localStorage.setItem('loginUser',JSON.stringify(res.data));
localStorage.setItem("menus",JSON.stringify(res.data.menus));
this.$router.push("/")
this.$message.success("登录成功")
} else {
this.$message.error(res.msg)
}
})
} else {
return false;
}
});
}
}
}
</script>
<style>
.wrapper {
height: 100vh;
background-image: linear-gradient(to bottom right, #FC466B , #3F5EFB);
overflow: hidden;
}
</style>
2.2.2 Aside.vue
侧边栏动态显示菜单
<template>
<el-menu :default-openeds="['1', '3']" 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">
<template slot="title">
<i :class="item.icon"></i>
<span slot="title">{{ item.name }}</span>
</template>
</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">
<template slot="title">
<i :class="subItem.icon"></i>
<span slot="title">{{ subItem.name }}</span>
</template>
</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')) : []
}
},
}
</script>
<style scoped>
</style>
2.3 动态路由设计
为什么设置动态路由,这是因为没有其他页面权限的用户也是可以访问其他页面
如下图所示:
安其拉是普通用户,只有主页的菜单权限(自己设置的角色所拥有的菜单)
2.3.1 菜单表新增字段page_path
对应每个页面组件名称
CREATE TABLE `sys_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',
`path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '路径',
`page_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '页面路径',
`pid` int(11) DEFAULT NULL COMMENT '父级id',
`icon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '图标',
`description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '描述',
`deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC
2.3.2 路由设计
import Vue from 'vue'
import VueRouter from 'vue-router'
import Manage from '../views/Manage.vue'
import store from "@/store";
Vue.use(VueRouter)
//定义一个路由对象数组
const routes = [
{
path: '/login',
name: '登录',
component: () => import('../views/Login.vue')
},
{
path: '/register',
name: '注册',
component: () => import('../views/Register.vue')
}
]
//使用路由对象数组创建路由实例,供main.js引用
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
// 注意:刷新页面会导致页面路由重置
export const setRoutes = () => {
const storeMenus = localStorage.getItem("menus");
if (storeMenus) {
// 获取当前的路由对象名称数组
const currentRouteNames = router.getRoutes().map(v => v.name)
if (!currentRouteNames.includes('Manage')) {
// 拼装动态路由
const manageRoute = { path: '/', name: 'Manage', component: () => import('../views/Manage.vue'), redirect: "/home", children: [
{ path: 'person', name: '个人信息', component: () => import('../views/Person.vue')},
// { path: 'password', name: '修改密码', component: () => import('../views/Password.vue')}
] }
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'),meta: { title: item.name }}
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'),meta: { title: item.name }}
manageRoute.children.push(itemMenu)
}
})
}
})
// 动态添加到现在的路由对象中去
router.addRoute(manageRoute)
}
}
}
// 重置我就再set一次路由
setRoutes()
// 路由守卫
// router.beforeEach((to, from, next) => {
// localStorage.setItem('currentPathName',to.name); // 设置当前的路由名称,为了在Header组件中去使用
// store.commit('setPath') // 触发store的数据更新
// next() // 放行路由
// })
export default router
2.3.3 登录界面Login.vue设置路由
改动登录页面在成功登录时,设置路由
// 导入
import {setRoutes} from "@/router";
// 当登录成功时,同时设置路由
localStorage.setItem("user",JSON.stringify(res.data)); //存储用户信息到浏览器
localStorage.setItem("menus",JSON.stringify(res.data.menus)); //存储菜单到浏览器
setRoutes();
this.$router.push("/");
this.$message.success("登录成功");
总结
- 本篇主要讲解动态菜单和动态路由的设计与实现
写在最后
如果此文对您有所帮助,请帅戈靓女们务必不要吝啬你们的Zan,感谢!!不懂的可以在评论区评论,有空会及时回复。
文章会一直更新