主要思路 通过后端接口返回数据 进行判断 (通过后端实现的权限管理有许多种),在这里 我们主要通过 关键字匹配前端路由meta.menu值来实现权限管理通常会把路由权限列表存至vux中

在这里插入图片描述

箭头所指的便是 后端接口返回的权限列表

实现方法通过 通过 配置路由meta.menu来实现权限匹配 通过 router.addRoute来实现路由添加 通过meta.hidden 来决定是否在侧边栏展示当前页面

第一步 在router/index.js 中完成 无需权限路由的配置 我这里配备的便是 登录 404首页

const routes = [{
	  path: '/login',
	  component: () => import('@/views/Login.vue'),
	  meta: {
	    title: '登录'
	  }
	},
	{
	  path: '/404',
	  name: '404',
	  component: () => import('@/views/error/404'),
	  hidden: true
	},
	
	{
	  path: '',
	  redirect: '/',
	  component: Layout,
	  children: [{
	    path: '/',
	    component: () =>
	      import('../views/Home.vue'),
	    meta: {
	      title: '首页',
	      parentpath: '/home'
	    }
	  }]
	}
]


第二步 动态权限的配置存放在vueX中

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    rolelist: [], // 后端返回的路由数据
    asyncRouters: [], // 所有需要分配权限的路由
    finallyRouters: [], // 最终筛选出的路由
  },
  mutations: {
    setRoleList(state, data) {
      state.rolelist = data

    },
    setAsyncRouter(state, asyncRouters) {
      state.asyncRouters = asyncRouters
    },
    setFinallyRouters(state, data) {
    //  因为总体是用addRoute 添加 所以这儿用的concat 一个一个拼接
      state.finallyRouters = state.finallyRouters.concat(data)
      // console.log(state.finallyRouters, '最后的路由')
    }

  },
  actions: {
    async SetAsyncRouter({
      commit
    }) {
      var list = [

        // 景区
        {
          path: '/scenic',
          redirect: '/scenic/index',
          component: (resolve) => require(['@/views/layout/index.vue'], resolve),
          meta: {
            menu: "spot",
            title: '景区',
          },
          children: [{
              path: 'index',
              name: "scenicManager",
              component: (resolve) => require(['@/views/scenic/index.vue'], resolve),
              meta: {
                title: '景区管理',
                menu: "scenic"
              }
            },
            //景区
            {
              path: 'detail',
              name: 'scenicDetail',
              hidden: true,
              component: (resolve) => require(['@/views/scenic/components/detail.vue'], resolve),
              meta: {
                title: '景区详情',
                menu: "scenic",
              }
            },
            {
              path: 'manage/detail',
              name: "scenicManageDetail",
              hidden: true,
              component: (resolve) => require(['@/views/scenic/components/manageDetail.vue'], resolve),
              meta: {
                title: '管理详情',
                menu: "scenic"
              }
            },
          ]

        },
        // 用户
        {
          path: '/User',
          redirect: '/User/index',
          component: () => import('@/views/layout/index.vue'),
          meta: {
            title: '会员',
            menu: "member"
          },
          children: [
            // 用户
            {
              path: 'index',
              name: 'User',
              component: (resolve) => require(['@/views/member/user/index.vue'], resolve),
              meta: {
                title: '用户',
                menu: "user"
              }
            },
            {
              path: 'detail',
              name: 'userDetail',
              hidden: 'true',
              component: (resolve) => require(['@/views/member/user/detail.vue'], resolve),
              meta: {
                title: '用户',
                menu: "detail"
              }
            },
            {
              path: 'staff',
              name: 'UserStaff',
              component: (resolve) => require(['@/views/member/staff/index.vue'], resolve),
              meta: {
                title: '员工',
                menu: "manager"
              }
            },
            {
              path: 'supplier',
              name: 'UserSupplier',
              component: (resolve) => require(['@/views/member/supplier/index.vue'], resolve),
              meta: {
                title: '供应商',
                menu: "thirdPlat"
              }
            },
          ]
        },
      commit('setAsyncRouter', list)
    },
  },
  modules: {},
  getters: {
    asyncRouters(state) {
      return state.asyncRouters
    }
  }
})

第三步 路由守卫中拦截 和添加动态路由可在main.js 或者router/index.js中写

// 关键 添加flag防止多次获取动态路由和栈溢出
let asyncRouterFlag = 0;

router.beforeEach(async (to, from, next) => {
  const token = window.localStorage.getItem('token')
  document.title = to.meta.title
  if (to.path == '/login') {
    next()
  } else {
    if (!token) {
      next('/login')
    } else {
      if (!asyncRouterFlag) {
        // 添加flag防止多次获取动态路由和栈溢出
        asyncRouterFlag++
        //同步调用获取菜单方法
        await store.dispatch('SetAsyncRouter')   //发请求获取菜单,并将菜单设置到vuex中,
        const asyncRouters = store.getters['asyncRouters'];
        var roleList = store.state.rolelist || []
        if (roleList.length > 0) {
        //	自写方法  根据关键词匹配
          addRouter(asyncRouters, roleList)
        } else {
         await api.LoginInfo().then(res => {
            console.log(res, '数据')
            if (res.code == 200) {
              roleList = res.data.roles
              store.commit('setRoleList', roleList)
              console.log(4)
              addRouter(asyncRouters, roleList)
            } else {
              Message.error(res.data.message || res.statusText);
            }
          }).catch(err => {
            next('/login')
          })
        }
        next({ ...to, replace: true })
      } else {
        next()
      }

    }
  }
})

// 筛选出满足条件的路由    这里因人而异
function addRouter(asyncRouters, roleList) {
  asyncRouters.map(item => {
    if (roleList.indexOf(item.meta.menu) > -1) {
      let temp = cloneLoop(item)
      delete temp.children
      if (item.children.length > 0) {
        temp.children = []
        item.children.map(item1 => {
          if (roleList.indexOf(item1.meta.menu) > -1) {
      
            temp.children.push(item1)

			//  最终路由存放至vuex中
			store.commit('setFinallyRouters', temp)
			
          }
        })
      }
      router.addRoute(temp)
    }
  })

//   最后 加入* 重定向404
  router.addRoute({
    path: '*',
    redirect: "/404",
  })
}

最后在实现侧边栏

<template>
  <div>
    <el-menu :default-active="this.$route.path"   active-text-color='#13C479'  class="el-menu-vertical-demo" router>
      <!-- 首页 -->
      <el-menu-item index="/">
       // 图标  可自定义
        <i class="el-icon-s-home"></i>
        <span slot="title" style="margin-left:10px">首页</span>
      </el-menu-item>

      <el-submenu :index="item.path" v-for="(item,index) in routeList" :key="index">
        <template slot="title">
           <i class="el-icon-s-home"></i>
          <span slot="title" style="margin-left:10px"  v-if="item.meta">{{item.meta.title}}</span>
        </template>

        <template v-if="item.children">
          <div v-for="(item1,index1) in item.children" :key="index1">
            <el-menu-item :index="`${item.path}/${item1.path}`" v-if="!item1.hidden">
             <i class="el-icon-s-home"></i>
              <span slot="title" style="margin-left:10px" >{{item1.meta.title}}</span>
            </el-menu-item>
          </div>
        </template>
      </el-submenu>
    </el-menu>
  </div>
</template>

<script>
export default {
  data() {
    return {
      routeList: []
    };
  },
  computed: {},
  watch: {},
  mounted() {
  // 这里把无需权限的路由复制过来
    var tempRouter = [
      // 订单
      {
        path: "/order",
        component: () => import("@/views/layout/index.vue"),
        redirect: "/order/index",
        meta: {
          title: "订单"
        },
        children: [
          {
            path: "index",
            name: "orderTickets",
            component: () => import("@/views/order/tickets.vue"),
            meta: {
              title: "系统门票订单"
            }
          },
          {
            path: "seasonTickets",
            name: "orderSeasonTickets",
            component: () => import("@/views/order/seasonTickets.vue"),
            meta: {
              title: "套餐门票订单"
            }
          }
        ]
      },

      // 优惠券
      {
        path: "/coupon",
        component: () => import("@/views/layout/index.vue"),
        redirect: "/coupon/index",
        meta: {
          title: "优惠券"
        },
        children: [
          {
            path: "index",
            name: "coupon",
            component: () => import("@/views/coupon/index.vue"),
            meta: {
              title: "优惠券"
            }
          }
        ]
      }
    ];
	// 把筛选出来的权限列表和无需权限的列表组合起来
    this.routeList = [...this.$store.state.finallyRouters, ...tempRouter];

    console.log(this.routeList, "全部");
  },
  methods: {}
};
</script>

<style lang='scss' scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}

/deep/ .el-menu {
  background: none;
}
</style>

第四步 登录页

    login() {
      this.username = "";
      this.password = "";
      if (!this.loginForm.username) {
        this.username = "请输入账号";
      } else if (!this.loginForm.password) {
        this.password = "请输入密码";
      } else {
        this.loading = true;
        api
          .login(this.loginForm)
          .then(res => {
            // console.log(res, "登陆");
            if (res.code === 200) {
              window.localStorage.setItem("token", res.data.token);
              this.$store.commit("setRoleList", res.data.roles);
              window.localStorage.setItem(
                "buttenpremissions",
                JSON.stringify(res.data.permissions)
              );
              this.$router.push("/");
            }
          })
          .catch(err => {
            console.log(err);
          });
      }
    }

第五步 token过期后端没有返回refush-token时,退出登录切换账号我们需要清除已经注册的路由,防止其他用户获得到已经注册好了的路由。

router.replace({path: '/login'});
    window.location.reload();

service.interceptors.response.use((res) => {
  console.log(res, '我是拦截')
  // if(res.)
  nprogress.done()
  return res.data
}, err => {
  let status = err.response.status
  if (err.response && err.response.status) {
    console.log(err.response)
    if (status == 401) {
      Message.error('登录失效,请刷新后重试')
      router.replace({path: '/login'});
        window.location.reload();
    }
  }
  return Promise.reject(err.response)

可参考写法—vue 动态路由权限管理

Logo

前往低代码交流专区

更多推荐