![[1280X1280 (46).PNG]]
![[1280X1280 (48).PNG]]
![[1280X1280 (50).PNG]]
权限管理总结
![[download_image.jpeg]]
模拟路由信息数据
adminRouter.json
JSON
[
{
"path": "/dashboard",
"component": "/layout/index.vue",
"title": "Dashboard",
"home": "/dashboard/console",
"redirect": "/dashboard/console",
"icon": "Edit",
"name": "dashboard",
"children": [
{
"path": "console",
"component": "/dashboard/console.vue",
"title": "主控台",
"icon": "Location"
},
{
"path": "monitor",
"component": "/dashboard/monitor.vue",
"title": "监控页",
"icon": "Location"
},
{
"path": "workplace",
"component": "/dashboard/workplace.vue",
"title": "工作台",
"icon": "Location"
}
]
},
{
"path": "/student",
"component": "/layout/index.vue",
"title": "学生管理",
"home": "/student/stuAdmin",
"icon": "Edit",
"name": "student",
"children": [
{
"path": "stuAdmin",
"component": "/student/index.vue",
"title": "学生管理",
"icon": "Location",
"name":"stuAdmin"
},
{
"path": "addStu",
"component": "/student/addStu.vue",
"title": "添加学生",
"icon": "Location",
"name":"addStu"
},
{
"path": "classAdmin",
"component": "/student/classAdmin.vue",
"title": "班级管理",
"icon": "Location",
"name":"classAdmin"
}
]
},
{
"path": "/upload",
"component": "/layout/index.vue",
"title": "图片上传",
"icon": "Edit",
"name": "upload",
"children": [
{
"path": "index",
"component": "/upload/index.vue",
"title": "图片上传",
"icon": "Location",
"name":"uploadimg"
}
]
}
]
teacherRoutes.json
JSON
[
{
"path": "/dashboard",
"component": "/layout/index.vue",
"title": "Dashboard",
"home": "/dashboard/console",
"redirect": "/dashboard/console",
"icon": "Edit",
"name": "dashboard",
"children": [
{
"path": "console",
"component": "/dashboard/console.vue",
"title": "主控台",
"icon": "Location"
},
{
"path": "monitor",
"component": "/dashboard/monitor.vue",
"title": "监控页",
"icon": "Location"
},
{
"path": "workplace",
"component": "/dashboard/workplace.vue",
"title": "工作台",
"icon": "Location"
}
]
},
{
"path": "/student",
"component": "/layout/index.vue",
"title": "学生管理",
"home": "/student/stuAdmin",
"icon": "Edit",
"name": "student",
"children": [
{
"path": "stuAdmin",
"component": "/student/index.vue",
"title": "学生管理",
"icon": "Location",
"name":"stuAdmin"
},
{
"path": "addStu",
"component": "/student/addStu.vue",
"title": "添加学生",
"icon": "Location",
"name":"addStu"
},
{
"path": "classAdmin",
"component": "/student/classAdmin.vue",
"title": "班级管理",
"icon": "Location",
"name":"classAdmin"
}
]
}
]
从后端接口返回不同权限的路由数据 - user.js
JavaScript
let adminRoutes = require("../util/adminRoutes.json");
let teacherRoutes = require("../util/teacherRoutes.json");
let routes = {
admin:adminRoutes,
teacher:teacherRoutes
}
router.post('/login',async (req, res)=>{
// 1. 接收用户名和密码
let {account,pw} = req.body;
// 2. 连接数据库,查看用户名 和 密码是否存在
let sql = `select * from user where account = '${account}' and pw = '${pw}'`;
let data =await query(sql);
let token = await setToken(account);
let sendMsg = {};
if(data.data.length > 0){
let role = data.data[0].role;
sendMsg = {
msg:"登录成功",
status:200,
token,
routesData:routes[role]
}
}else{
sendMsg = {
msg:"用户名或者密码错误",
status:102
}
}
console.log(sendMsg);
res.send(sendMsg);
});
前端接受路由信息
并渲染数据【接收一个路由信息】,在login页面接受数据 - index.vue
HTML
<script setup>
import { ref, reactive } from "vue";
import { useRouter } from 'vue-router';
import { _login } from "@/api/request"
// vite工具
// 针对解析的数据,进行组件懒加载
const Modules = import.meta.glob("@/pages/**/*.vue");
// console.log(Modules);
const router = useRouter();
let formData = reactive({
account: "",
pw: ""
})
let login = async () => {
let { data } = await _login(formData);
// 接收路由信息中的值(只接受第一组路由信息,用于测试)
let { children, component, home, icon, name, path, redirect, title } = data.routesData[0];
// 一级路由渲染
router.addRoute({
name,
path,
// 注意:引入的路径中,pages后面没有 /
component: Modules[`/src/pages${component}`],
meta: {
title,
icon,
home
},
redirect
})
console.log(children[0]);
// 二级路由渲染
router.addRoute("dashboard", {
path: children[0].path,
component: Modules[`/src/pages${children[0].component}`],
meta: {
title: children[0].title,
icon: children[0].icon
}
})
// 打印路由信息
console.log(router.getRoutes());
if (data.status == 200) {
sessionStorage.setItem('token', data.token);
console.log(data);
router.push('/');
}
}
</script>
遍历二级路由,使dashboard下面的二级路由都可以使用
JavaScript
// 二级路由
children.forEach(item => {
let { path, component, title, icon } = item;
router.addRoute("dashboard", {
path,
component: Modules[`/src/pages${component}`],
meta: {
title,
icon
}
})
})
生成动态侧边栏
思路:登陆成功之后,将路由数据存储到vuex中,方便侧边栏NavMenu.vue调用数据
- 创建vuex文件
src/store/index.js
JavaScript
import { createStore } from 'vuex'
// 创建一个新的 store 实例
const store = createStore({
state () {
return {
routesData: []
}
},
mutations:{
addRoutersData:(state,payload)=>{
state.routesData = payload
}
}
})
export default store
- 在全局注册vuex
JavaScript
import store from "./store"
....
app.use(store);
- 登陆成功之后,数据存储到vuex中
JavaScript
import {useStore} from "vuex"
let store = useStore();
let login = async () => {
let { data } = await _login(formData);
// 存入store中
store.commit("addRoutersData", data.routesData);
.....
.....
}
- 在NavMenu.vue侧边栏中调用数据
JavaScript
import { useStore } from "vuex";
import { computed } from "vue";
const store = useStore();
const props = defineProps({
collapse: Boolean
})
// 获取数据
console.log("111", store.state.routesData);
let routesData = computed(() => {
return store.state.routesData;
});
// 渲染数据
<el-menu active-text-color="#ffd04b" background-color="#545c64" class="el-menu-vertical-demo" :default-active="$route.path" text-color="#fff" :default-openeds="['/dashboard']" :router="true" :collapse="props.collapse">
<h5 class="title">后台管理系统</h5>
<!--template 标签只用来渲染数据,并不会在页面中显示-->
<template v-for="(item,index) in routesData" :key="index">
<el-menu-item :index="item.path" v-if="item.children.length == 1">
<el-icon>
<component :is="item.icon"></component>
</el-icon>
<span>{{item.title}}</span>
</el-menu-item>
<el-sub-menu index="/dashboard" v-else>
<template #title>
<el-icon>
<component :is="item.icon"></component>
</el-icon>
<span>{{item.title}}</span>
</template>
<el-menu-item :index="second.path" v-for="(second,i) in item.children"
:key="i">
<el-icon>
<component :is="second.icon"></component>
</el-icon>
{{second.title}}
</el-menu-item>
</el-sub-menu>
</template>
</el-menu>
封装动态生成路由部分
- 创建文件 src/utils/generatorRoutes.js
JavaScript
// import { useRouter } from 'vue-router';
// const router = useRouter();
// 等价于下面的引入
// 引入router
import router from "@/router/index.js";
// vite工具
// 针对解析的数据,进行组件懒加载
const Modules = import.meta.glob("@/pages/**/*.vue");
export const generatorRoutes = (routesData)=>{
let { children, component, home, icon, name, path, redirect, title } = routesData[0];
// 一级路由
router.addRoute({
name,
path,
component: Modules[`/src/pages${component}`],
meta: {
title,
icon,
home
},
redirect
})
// console.log(children[0]);
// 二级路由
children.forEach(item => {
let { path, component, title, icon } = item;
router.addRoute("dashboard", {
path,
component: Modules[`/src/pages${component}`],
meta: {
title,
icon
}
})
})
}
- 登陆页面中进行引入
JavaScript
import { generatorRoutes } from '@/utils/generatorRoutes.js';
let login = async () => {
let { data } = await _login(formData);
// 存入store中
store.commit("addRoutersData", data.routesData);
// console.log(router.getRoutes());
if (data.status == 200) {
sessionStorage.setItem('token', data.token);
// -----------动态生成路由 start-----------
generatorRoutes(data.routesData);
// -----------动态生成路由 end-----------
// console.log(data);
router.push('/');
}
}
- 遍历所有的路由信息
JavaScript
// import { useRouter } from 'vue-router';
// const router = useRouter();
// 等价于下面的引入
// 引入router
import router from "@/router/index.js";
// vite工具
// 针对解析的数据,进行组件懒加载
const Modules = import.meta.glob("@/pages/**/*.vue");
export const generatorRoutes = (routesData)=>{
// 循环遍历
routesData.forEach((bigItem)=>{
let { children, component, home, icon, name, path, redirect, title } = bigItem;
// 一级路由
router.addRoute({
name,
path,
component: Modules[`/src/pages${component}`],
meta: {
title,
icon,
home
},
redirect
})
// 二级路由
children.forEach(item => {
let { path, component, title, icon } = item;
router.addRoute(bigItem.name, {
path,
component: Modules[`/src/pages${component}`],
meta: {
title,
icon
}
})
})
})
}
刷新页面内容消失的解决方案
方案:刷新的时候,判断是否存在token,如果存在,重新从vuex中读取路由数据
router/index.js
JavaScript
//路由拦截
router.beforeEach((to,from)=>{
let token = localStorage.getItem('token');
// 如果token不存在,并且要跳转的不是login页,则重定向到login
if(!token && to.fullPath !== '/login'){
return {
path:'/login'
}
}
// 解决问题的代码
if(token){
// token存在有两种情况
// 一种是没刷新时,此时动态路由已经创建,可以正常使用
// 一种是刷新后,此时动态路由已经销毁,不能进行路由跳转,需要重新加载
let routesData = store.state.routesData;
if (routesData.length === 0) {
console.log('刷新');
routesData = JSON.parse(localStorage.getItem("routesData"));
// 使用commit调用mutations下面的方法
store.commit("addRoutersData",routesData);
generatorRoutes(routesData);
// 重定向将要进入的路由
// 如果不加下面的代码,会在路由没有准备好就跳转,出现404
// 如果加,相当于延迟跳转,此时路由已经准备好
return {
path:to.fullPath
};
}
}
})
export default router
注意:当从本地存储中获取到路由数据之后,一定把把数据给vuex保存一份,否则即便有了数据,还是会一直进行 if routesData.length === 0 这一层判断 store.commit('addRoutersData', routesData);