elementUI , Vue关于路由的权限控制及实现

时间:2024-03-22 17:17:41

前提:

后台管理平台,左树菜单(后台接口返回)。前端负责写路由的对应关系(根据用户角色动态生成路由)

我并没有写的登录页面,我只是写了一个简单的菜单页及菜单内容(仅仅是为了重点介绍左树菜单的生成和动态路由的生成)

正文:

第一趴:

左树菜单生成:

思路(后台返回菜单结构),前端直接用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>

界面效果:

 

elementUI , Vue关于路由的权限控制及实现

第二趴:左树菜单有了--那我们该写对应的路由了(路由是前端本地写的)。要做成动态的,根据不同的用户,访问对应的路由才生效

思路:

先写一个空路由,然后根据权限动态的往这个空路由中追加新路由

注意:

路由的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>

文件之间关系:

elementUI , Vue关于路由的权限控制及实现

参考资料:

 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