路由权限
1、先登录后 将用户的 uid 传给 后端,请求后端api接口 得到路由权限的列表;
2、后端 给到用户对应的路由权限列表 返回给前端;
3、数据是 json 格式 ,树形结构化路由列表,id对应的pid值 放到对应的children中 ,如下:
4、树形结构化数据 —— vue路由结构;
5、将路由的结构动态 添加到 静态路由中;
6、树形结构化的数据 —— 菜单组件;
let json = [
{
id: 2,
pid: '',
path: '',
name: '',
link: '',
title: ''
},
{
id: 3,
pid: '',
path: '',
name: '',
link: '',
title: ''
},
{
id: 1,
pid: 3, // 代表是 parentId 说明这个路由是id 为3 子路由
path: '',
name: '',
link: '',
title: ''
}
]
有一种写法:先把路由写好,然后放到路由表中作为变量,然后从后端请求回来数据,然后对比静态的表,删减合并到原本的路由中去。注意:实际这种做法不正确;
应该写法:从后端表里面取出来,然后在前端形成树形,然后转为路由,这种做法较正确;
koa搭建后端
以koa为后端开始编写路有权限需要的路由等数据
部分 koa 代码请看博客 使用koa-generator快速搭建 Koa项目
得到返回的如下路由数据
转为树形结构的数据如下:
// 格式化树形结构
function formateRouterTree(data) {
let parents = data.filter(p => p.pid === 0),
children = data.filter(c => c.pid !== 0);
dataToTree(parents, children)
function dataToTree(parents, children) {
parents.map(p => {
children.map((c, i) => {
if (c.pid === p.id) {
let _c = JSON.parse(JSON.stringify(children))
_c.splice(i, 1);
dataToTree([c], _c)
if (p.children) {
p.children.push(c)
} else {
p.children = [c]
}
}
})
})
}
return parents
}
// 转为vue 路由格式
function generateRouter(userRouters){
let newRouters = userRouters.map(r => {
let routes = {
path:r.path,
name:r.name,
component:() => import(`@/views/${r.name}`)
}
if(r.children){
routes.children = generateRouter(r.children)
}
return routes
})
return newRouters
}
export {
formateRouterTree,
generateRouter
}
对数据的处理和路由的处理
数据的处理
在store中新建state.js、mutations.js、actions.js;
state.js
export default {
// 注意 uid 是请求
uid:3,
hasAuth:false,
userRouters:[],
}
mutations.js
export default {
setAuth(state,auth){
state.hasAuth = auth
},
setUserRouters(state,userRouters){
state.userRouters = userRouters
}
}
actions.js
import { getUserRouters } from '@/server4';
import {formateRouterTree,generateRouter} from '@/utils'
export default {
async setUserRouters({commit,state}){
const userRouters = await getUserRouters(state.uid),
payload = formateRouterTree(userRouters);
// console.log(userRouters);
// console.log(payload);
// console.log(generateRouter(payload));
commit('setUserRouters',payload)
commit('setAuth',true)
}
}
路由处理
main.js
中添加路由拦截
import {generateRouter} from '@/utils';
router.beforeEach(async (to, form, next)=>{
if(!store.state.hasAuth){
await store.dispatch('setUserRouters')
const newRouters = generateRouter(store.state.userRouters)
router.addRoutes(newRouters)
next({path:to.path})
}else{
next()
}
})
router里面的index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'hash',
// base: process.env.BASE_URL,
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
} else {
return {
x: 0,
y: 0
};
}
}
})
router.beforeEach((to, from, next) => {
next();
});
router.afterEach((to, from, next) => {
window.scrollTo(0, 0);
});
const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(to) {
return VueRouterPush.call(this, to).catch(err => err)
}
export default router
routes.js
// function load(component) {
// return resolve => require([`../views/${component}/${component}`], resolve)
// }
const routes = [
{
path: '/',
component: resolve => require(['@/views/myhome.vue'], resolve),
},
{
path: '/home',
name: 'home',
component: resolve => require(['@/views/myhome.vue'], resolve),
meta: {
title: '首页'
},
{
path: '*',
name:'NotFound',
component:()=>import ('@/views/NotFound.vue'),
}
];
export default routes;
src中views文件里面的部分代码
MHeader.vue
<template>
<div class="header-container">头部</div>
</template>
<script>
export default {
name: "MHeader",
data() {
return {};
},
created() {},
computed: {},
methods: {},
};
</script>
<style lang="scss" scoped>
.header-container {
width: 100%;
height: 60px;
position: fixed;
top: 0;
left: 0;
background: #000;
color: #fff;
z-index: 2;
}
</style>
PageBoard.vue
<template>
<div class="page-board">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "PageBoard",
data() {
return {};
},
created() {},
computed: {},
methods: {},
};
</script>
<style lang="scss" scoped>
.page-board {
width: 100%;
height: 100%;
padding: 90px 30px 30px 230px;
background: cadetblue;
}
</style>
SildBar.vue
<template>
<div class="side-bar">
<ul>
<li>
<router-link to="/">首页</router-link>
</li>
</ul>
<template v-for="(item, index) in $store.state.userRouters">
<MenuItem :key="index" :item="item" />
</template>
</div>
</template>
<script>
import MenuItem from "@/components/MenuItem";
export default {
name: "SildBar",
components: {
MenuItem,
},
data() {
return {};
},
created() {},
computed: {},
methods: {},
};
</script>
<style lang="scss" scoped>
.side-bar {
position: fixed;
top: 0;
left: 0;
z-index: 1;
width: 200px;
height: 100%;
padding-top: 60px;
box-sizing: border-box;
background: #ccc;
}
</style>
MenuItem.vue
<template>
<div>
<ul v-if="item.children && item.children.length > 0">
<li>
<router-link :to="item.link || item.path">{{ item.title }}</router-link>
<template v-for="(c, i) in item.children">
<MenuItem :key="i" :item="c" />
</template>
</li>
</ul>
<ul v-else>
<li>
<router-link :to="item.link || item.path">{{ item.title }}</router-link>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "MenuItem",
props: {
item: {
type: Object,
default: () => {
return {};
},
},
},
data() {
return {};
},
created() {},
computed: {},
methods: {},
};
</script>
<style lang="scss" scoped>
</style>
后端的权限列表和前端的路由表匹配的另一中写法
注意:前端路由表不是一开始就加到路由中的,而是与后端返回的数据匹配到最后有权限的才加到路由中;
/**
* 参数解析:
* route: 前端写好的路由表中的每一项
* menuList:权限列表 后端根据用户角色生成的权限列表
* 其中menuLis中的code是后端固定写死数据,而route中的name是是路由表中的,要与后端的code匹配是否有权限展示当前路由信息
*/
function hasPermission(route, menuList) {
const seq = menuList.findIndex(menu => menu.code === route.name)
if (seq >= 0) {
route.seq = seq
return true
}
return false
}
/**
* 参数解析:
* asyncRoutes:动态路由 前端写好的路由表
* menuList:权限列表 后端根据用户角色生成的权限列表
*/
function filterAsyncRoutes(asyncRoutes, menuList) {
let res = []
asyncRoutes.forEach((route) => {
if (route.name && hasPermission(route, menuList)) {
let curRoute = {
...route,
children: []
}
if (route.children && route.children.length) {
curRoute.children = filterAsyncRoutes(route.children, menuList)
} else {
delete curRoute.children
}
res.push(curRoute)
}
})
return res.sort((a, b) => a.seq - b.seq)
}
// 参数如下
let menuList = [{
"id": "1414409930173083706",
"code": "PAGE_EXECUTE",
"name": "EXECUTE",
"parentId": "254069752945840128",
"url": ""
},
{
"id": "1425351825283924055",
"code": "PAGE_LOOK",
"name": "LOOK",
"parentId": "254069802262466560",
"url": ""
},
{
"id": "1428620149098856496",
"code": "PAGE_NEWTASK",
"name": "NEWTASK",
"parentId": "1428567167967739913",
"url": ""
},
{
"id": "254069654174175232",
"code": "MENU_INFO",
"name": "INFO",
"parentId": "",
"url": ""
},
...
]
const asyncRoutes = [{
name: 'MENU_INFO',
path: '/taskInfo',
// component: Layout,
redirect: '/taskInfo/list',
meta: {
icon: 'icon-task-system',
title: "信息"
},
children: [{
name: 'MENU_LOGGING',
path: 'Logging',
// component: () => import('@/views/taskInfo/executiveLogging'),
meta: {
title: '记录'
}
},
{
name: 'MENU_TASK',
path: 'list',
// component: () => import('@/views/taskInfo/list'),
meta: {
title: '任务',
}
},
{
name: 'MENU_APPROVAL',
path: 'Approval',
// component: () => import('@/views/taskInfo/pendingApproval'),
meta: {
title: '审批',
}
}
]
},
...
]