前提:
后台管理平台,左树菜单(后台接口返回)。前端负责写路由的对应关系(根据用户角色动态生成路由)
我并没有写的登录页面,我只是写了一个简单的菜单页及菜单内容(仅仅是为了重点介绍左树菜单的生成和动态路由的生成)
正文:
第一趴:
左树菜单生成:
思路(后台返回菜单结构),前端直接用elementUI树组件显示即可(当然左树菜单的权限也是后台控制什么用户回显什么菜单)
后台返回的菜单结构:我做的时候自己构造的本地数据
export let mentData ={
code: 200,
data:[{
children:[],
enable: null,
icon:"el-icon-setting",
id:"0000",
level:null,
parentId:'-1',
path:"",
remark:"",
sortIndex:0,
status:1,
name: "首页",
systemId:"ops",
url:"/homePage"
},{
children:[],
enable: null,
icon:"el-icon-document",
id:"1100",
level:null,
parentId:'-1',
path:"",
remark:"",
sortIndex:0,
status:1,
name: "功能点测试",
systemId:"ops",
url:"/function"
},{
enable: null,
icon:"el-icon-menu",
id:"3100",
level:null,
parentId:'-1',
path:"",
remark:"",
sortIndex:0,
status:1,
systemId:"ops",
name: "监控管理",
url:"",
children:[{
enable: null,
icon:"xxx1",
id:"30003100",
level:null,
parentId:'3100',
path:"",
remark:"",
sortIndex:0,
status:1,
systemId:"ops",
name: "系统组信息",
url:"/systemConfig/systemInfo",
children:[]
}]
}]
}
前端elementUI展示的左树:
<template>
<div>
<div class="leftMenu">
<!-- 菜单 -->
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px;">
<el-radio-button :label="false">展开</el-radio-button>
<el-radio-button :label="true">收起</el-radio-button>
</el-radio-group>
<el-menu default-active="1"
unique-opened
class="el-menu-vertical-demo"
:collapse-transition = "transition"
:collapse="isCollapse">
<el-menu-item v-for="item in menuList" :index="item" :key="item.id"
v-if="(item.children == null || item.children.length === 0)"
@click="navigatorTo(item)">
<i :class="item.icon"></i>
<span slot="title">{{item.name}}</span>
</el-menu-item>
<el-submenu v-for="item in menuList" :index="item" :key="item.id"
v-if="item.children.length !== 0">
<template slot="title">
<i :class="item.icon"></i>
<span slot="title">{{item.name}}</span>
</template>
<el-menu-item-group>
<el-menu-item v-for="v in item.children" :index="v.id.toString()" :key="v.id" @click="navigatorTo(v)">
{{v.name}}
</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</div>
<div class="rightView">
<!-- 点击菜单切换路由 -->
<router-view/>
</div>
</div>
</template>
<script>
import {mentData} from "@/util/menu.js";
export default {
data() {
return {
isCollapse: false,
transition: false,
menuList: []/* 存储后台返回的菜单 */
};
},
methods: {
handleOpen(key, keyPath) {
console.log("open")
console.log(key, keyPath);
},
handleClose(key, keyPath) {
console.log("close")
console.log(key, keyPath);
},
navigatorTo(data){
/* 这个方法用来每次点击菜单。都会切换到具体的路由 */
this.$router.push({
"path": data.url
})
}
},
created(){
/* 这里应该调用ajax。后台返回菜单。 */
this.menuList = mentData.data;
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px;
min-height: 400px;
}
.leftMenu,.rightView{
float:left;
}
</style>
界面效果:
第二趴:左树菜单有了--那我们该写对应的路由了(路由是前端本地写的)。要做成动态的,根据不同的用户,访问对应的路由才生效
思路:
先写一个空路由,然后根据权限动态的往这个空路由中追加新路由
注意:
路由的addRoutes只能追加路由。不会删除路由。也就是说你之前注册在router实例中的路由是不会被删掉的。它会永远存在。这就为什么我们要先写一个而空路由
代码:
先构造本地路由
import Vue from 'vue'
import Router from 'vue-router'
import home from '@/components/home'
import homePage from '@/components/homePage'
import Func from '@/components/function'
import systemInfo from '@/components/systemInfo'
import page404 from '@/components/page404.vue'
Vue.use(Router)
/*
功能说明:
homeRouter 起到一个空数组的作用
{
path: '/home',
name: 'home',
component: home,
children: []
}
动态路由都会添加在上边的children中。
第一次注册路由的时候只是将homeRouter放在new Router中
然后在登录的时候(login.vue文件中)调用vuex中的某一个方法。动态筛选出某用户有权限访问的路由。再追加到homeRouter中
*/
export let homeRouter = [{
path: '/home',
name: 'home',
component: home,
children: []
},{
path: '*',
name: 'page404',
component: page404
}
];
export let otherRouter = [{
path: '/homePage',
name: 'homePage',
/* meta中没有roles属性。代表该路由所有权限都支持 */
meta: {
title:"首页"
},
component: homePage
},
{
path: '/function',
name: 'Function',
/* 在meta中添加 roles属性。说明哪个权限的用户可以访问该路由*/
meta: {
title:"功能点检测",
roles: ["admin"]
},
component: Func
},
{
path: '/systemConfig/systemInfo',
name: 'systemInfo',
meta: {
title:"系统信息",
roles: ["store"]
},
component: systemInfo
}]
export const router = new Router({
/* 1.为什么是#/query而不是/index/query,是因为你声明的路由是/query,是直接针对根路径的,如果访问时显示/index/query,需要将子路由改成上面那样,是需要手动声明的,不是说index的子路由就会自动带上index的路径前缀。
子路由的path参数如果可以写 :/父路由/子路由(即/index/query),也可以直接/子路由,不是必须带有父路由的前缀,带有父路由表示一定的从属关系,不是必须的。
2.#表示hash路由模式,hash模式会在URL后加#看上去不是那么好看,但是一般的功能上都没问题的。重要的是,另外#后面的部分服务端无法识别,即向服务器提交的只是#号前面的url地址。
history模式,另一种路由模式,如楼上那样可以转换成history模式,比hash跟美观,而且会提交完整的url,这个需要服务端来配置,不然服务器会返回404。 */
/* 子路由path属性如果是完整路径。 浏览器中http://localhost:8080/#/homePage 则可以直接找到homePage*/
/* 子路由path属性如果是完整路径。 浏览器中http://localhost:8080/#/systemConfig/systemInfo 则可以直接找到systemInfo*/
routes: homeRouter
});
router.beforeEach((to,from,next) => {
/* 验证有没有用户 -- 用户名是否存在*/
/* 用户一般登录完会存储在cookie中 */
// true的位置是判断cookie中是否有用户存在
if(true && to.path != "/login"){
/* 用户存在。并且跳转路径不是 to.path != "/login"*/
next()
}else{
/* 用户不存在 */
next("/login")
}
})
vuex文件中写一些方法,用来生成动态路由(不是非要写在 vuex中,也可以写在别的文件中)
store.js中的内容 store.js文件就是我的的vuex文件
import Vue from 'vue'
import Vuex from 'vuex'
import { homeRouter,otherRouter } from '@/router'
Vue.use(Vuex)
var tempStore ={
state: {
role: "admin",/* 这个role我设置了一个死值--这个值应该是动态的。当点击登录按钮后。后台会返回权限。然后赋值给role这个变量 */
addRouters: [],
},
getters: {
/* 权限认证 */
/* 这种写法是vue官网推荐的写法 */
hasPermisstion: (state) => (meta) =>{
let flag = false;
/* meta.roles要是undefined证明所有的用户都支持 */
if(!meta.roles){
flag = true;
}else{
flag = meta.roles.indexOf(state.role) > -1;
}
return flag;
}
},
mutations: {
/* 动态更新 addRouters状态
将动态生成的路由routers 加入到homeRouter的children路由中
{
path: '/home',
name: 'home',
component: home,
children: []
}
动态路由都会添加在上边的children中。
*/
generateAddRouter(state,routers){
homeRouter[0].children = routers;
state.addRouters = homeRouter;
}
},
actions: {
/* 再login.vue登录页面。调用该方法。动态生成路由 */
/* 对外提供生成路由接口 */
generateAddRouters({ commit, getters}){
return new Promise((resolve,reject)=>{
/* 从路由文件中拿到 otherRouter 路由集合。遍历。
let routers 根据遍历 otherRouter路由得到的新路由(该用户有权限访问的路由都在let routers中)
*/
let routers = otherRouter.filter((item)=>{
/* 每取一个路由判断meta中的roles与当前用户权限是否一致 */
let rolesFlag = getters.hasPermisstion(item.meta);
/* 如果保持一致则放在新的数组中 */
if(rolesFlag){
return true;
};
});
/* 调用mutations中方法,更新state中addRouters的值 */
commit("generateAddRouter",routers);
resolve("success");
})
}
}
}
export default new Vuex.Store(tempStore);
调用vuex的地方是app,vue文件。也就是刚进入菜单时的文件
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
import {mentData} from "@/util/menu.js";
export default {
data() {
return {
};
},
methods: {
/* 这里应该是写在login.vue中的。我没有写登录页面。所以写在这里了。 */
/*
该方法的作用是调用vuex(store.js)中的generateAddRouters 方法。根据权限生成路由。
用 this.$router.addRoutes方法将路由注册在路由实例中
*/
async redirectRounter(){
let addRouters = [];
let flag = await this.$store.dispatch("generateAddRouters");
if(flag == "success"){
addRouters = this.$store.state.addRouters;
this.$router.addRoutes(addRouters);
}
/* 页面默认登录到某一个页面中 */
this.$router.push({
"path":"/homePage"
});
}
},
created(){
this.redirectRounter();
}
}
</script>
<style lang="less">
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
margin-top: 60px;
}
</style>
文件之间关系:
参考资料:
https://blog.csdn.net/zjl199303/article/details/82655576
在vuex 内部的 mutations 里面一个方法想调用另一个方法可以吗
https://segmentfault.com/q/1010000005176931
https://vuex.vuejs.org/zh/guide/getters.html
路由间嵌套:
https://www.cnblogs.com/goloving/p/9271501.html