接之前的文件架构。
这次动态菜单和动态路由主要涉及到前端。
动态菜单实现
在aside.vue中修改的e-menu的代码,把之前静态的菜单代码更新为下面的代码
<div >
<div v-for="item in menuList" :key="item.id">
<div v-if="item.path">
<el-menu-item :index="item.path" >
<i :class="item.icon"></i>
{{item.name}}</el-menu-item>
</div>
<div v-if="!item.path">
<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>{{subItem.name}}</el-menu-item>
</div>
</el-submenu>
</div>
</div>
</div>
解决动态菜单刷新丢失的问题
之前考虑使用vuex.state保存菜单数据。但刷新后会导致菜单丢失,最后还是使用的localStorage的方案来解决。
data()中增加
return {
menuList:localStorage.getItem("user")?JSON.parse(localStorage.getItem("user")).menuList:null,
ops:[]
}
methods中增加
getOpS(){
const oprarr=this.menuList.filter(item=>item.children.length>0);
this.ops=oprarr.map(item=>{return item.id.toString()})
}
在mountd()中调用方法
this.getOpS()
即可实现绑定的动态菜单效果。
aside.vue的完整代码为:
<template>
<div>
<el-aside :width="sideWidth +'px'" style="background-color: rgb(238, 241, 246);height:100%" >
<el-menu :default-openeds="ops" style="min-height:100%;overflow-x:hidden;width:100%"
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;margin-right:5px">
<b style="color:white;" v-show="logotext">后台管理系统</b>
</div>
<div >
<div v-for="item in menuList" :key="item.id">
<div v-if="item.path">
<el-menu-item :index="item.path" >
<i :class="item.icon"></i>
{{item.name}}</el-menu-item>
</div>
<div v-if="!item.path">
<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>{{subItem.name}}</el-menu-item>
</div>
</el-submenu>
</div>
</div>
</div>
</el-menu>
</el-aside>
</div>
</template>
<script>
export default {
name:'AsidePage',
data(){
return {
menuList:localStorage.getItem("user")?JSON.parse(localStorage.getItem("user")).menuList:null,
ops:[]
} } ,
mounted(){
this.getOpS()
} ,
methods:{
getOpS(){
const oprarr=this.menuList.filter(item=>item.children.length>0);
this.ops=oprarr.map(item=>{return item.id.toString()})
}
},
computed:{
isCollapse(){
return this.$store.state.isCollapse
},
sideWidth(){
return this.$store.state.sideWidth
},
logotext(){
return this.$store.state.logotext
},
},
}
</script>
<style>
</style>
动态路由实现
主要就是动态增加路由,主要涉及到router/index.js的代码修改
把原来一些静态的路由代码修改为
export const setRouters = function(){
const menuList=localStorage.getItem("user")?JSON.parse(localStorage.getItem("user")).menuList:null;
if(menuList){
const man_route={path:'/',name:'框架页面',component:()=>import('../view/Main'),redirect:'/home',children:[]};
menuList.forEach(item=>{
if(item.path){
let itemMenu={path:item.path,name:item.name,component:()=>import('../components/'+item.pagePath)};
man_route.children.push(itemMenu);
}
else if(item.children.length){
item.children.forEach(child=>{
let itemMenu={path:child.path,name:child.name,component:()=>import('../components/'+child.pagePath)};
man_route.children.push(itemMenu);
})
}
})
router.addRoute(man_route);
}
}
该段代码要写到加载路由之后,否则会报错。
然后在login.vue登录成功后调用。
handleLog(){
this.Req.post('http://localhost:8181/vueDemo/login',this.user).then(res=>{
if(res.code==200){
this.$router.push("/");
localStorage.setItem("user",res.data!=null?JSON.stringify(res.data):'');
if(this.$router.app==null){
setRouters()
}
}else{
this.$message.error(res.message);
}
})
}
实现动态路由效果。
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" >
<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-input size="medium" style="margin:10px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input> -->
<el-form-item prop="password" >
<el-input size="medium" style="margin:10px 0" prefix-icon="el-icon-lock" v-model="user.password" show-password @change="handleLog"></el-input>
</el-form-item>
<div style="margin:10px 0;text-align:right">
<el-button type="primary" size="small" @click="handleLog" >登录 </el-button>
<el-button type="waring" size="small" @click="$router.push('/register')">注册 </el-button>
</div>
</el-form>
</div>
</div>
</template>
<script>
import { setRouters } from '@/router';
export default {
name:'LoginPage',
data(){
return{
user:{
username:'',
password:''
},
rules:{
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
],
} }},
methods:{
handleLog(){
this.Req.post('http://localhost:8181/vueDemo/login',this.user).then(res=>{
if(res.code==200){
this.$router.push("/");
localStorage.setItem("user",res.data!=null?JSON.stringify(res.data):'');
if(this.$router.app==null){
setRouters()
}
}else{
this.$message.error(res.message);
}
})
}
}
}
</script>
<style scoped="scoped">
.wrapper{
height: 100vh;
background-image: linear-gradient(to bottom right,#fc466b ,#3f5efb);
overflow: hidden;
}
</style>
解决刷新后路由丢失的问题
上面的效果可以出来动态路由,不刷新页面的话使用正常,但刷新页面会导致页面丢失。
查询资料,有些提到在路由中判断router.getRoutes()来判断是否存在动态路由的名称来进行是否二次加载setRoutes函数。
但我目前的版本是 “vue-router”: “^3.6.5”,
router.getRoutes返回的是函数不是数据,无法使用上面的解决方案。
解决思路是在router/index.js中的setRouters函数代码之后打印router
console.log(router)
看控制台刷新前后router值的变化,发现里面有一个app的数据刷新后变为null。
所以对上面的解决方案进行修改为判断router.app==null的时候重新加载setRouter。router/idnex.js的完整代码为:
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '@/store';
Vue.use(VueRouter)
const routes = [
{
path: '/login',
name: '登录页面',
component: ()=>import('../view/Login')
}, {
path: '/register',
name: '注册页面',
component: ()=>import ( '../view/RegiPage')
},
{
path:"/404",
name:'404',
component:()=>import('../components/NoFind')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
});
export const setRouters = function(){
const menuList=localStorage.getItem("user")?JSON.parse(localStorage.getItem("user")).menuList:null;
if(menuList){
const man_route={path:'/',name:'框架页面',component:()=>import('../view/Main'),redirect:'/home',children:[]};
menuList.forEach(item=>{
if(item.path){
let itemMenu={path:item.path,name:item.name,component:()=>import('../components/'+item.pagePath)};
man_route.children.push(itemMenu);
}
else if(item.children.length){
item.children.forEach(child=>{
let itemMenu={path:child.path,name:child.name,component:()=>import('../components/'+child.pagePath)};
man_route.children.push(itemMenu);
})
}
})
router.addRoute(man_route);
}
}
if(router.app==null){
setRouters();
}
router.beforeEach((to,from,next)=>{
store.state.currentPathName=to.name;
store.commit("setPath");
if(to.matched.length==0){
if(localStorage.getItem("user")){
next("/404");
}else{
next("/login")
}
}
next()
})
export default router
另外在header.vue中增加了在logout的时候增加清空路由的代码
handlelogout(){
this.$router.push("/login");
localStorage.removeItem("user");
this.$router.app=null;
},
在路由守卫者增加了判断,如果当前不存在去向路由,则看有没有登录登录,由登录信息意味着没有路由,跳到404页面,没有登录的话则跳到 login页面。
测试成功!!