vue前端动态路由实现用户权限过滤

5,568 阅读3分钟

在后台系统这类的项目中,用户角色和权限的区分是不可分割的一部分需求。常见的用户权限区分形式有两种,一是前端请求接口拿到后台配置的用户权限信息;另一种是直接由前端来定义每种用户角色的权限页面,然后在生成对应的路由。今天我们来了解前端配置路由权限的方式。

角色权限页面配置

在配置角色对应的路由页面时,可以参考项目所用的前端框架导航组件的参数,我们以elment uimenu组件为例:可对审核方(Auditor)、供应商(Supplier)和需求方(Business)做如下配置:

const appMenuitem = {
// 审核方下拉选项(路由)菜单
Auditor: [
  {
    // icon: "el-icon-view", // operationLog  userConfiguration
    title: "基本配置",
    index: "1",
    subs: [
      {
        index: "dataOverview",
        title: "数据概览"
      }
    ]
  },
  {
    // icon: "el-icon-view",
    title: "需求审核",
    index: "2",
    subs: [
      {
        index: "demendManage",
        title: "需求管理"
      }
    ]
  }
],
// 业务方下拉选项和路由
Business: [
  {
    title: "基本配置",
    index: "1",
    subs: [
      {
        index: "demandList",
        title: "需求列表"
      }
    ]
  }
],
// 供应商
Supplier: [
  {
    title: "需求制作",
    index: "1",
    subs: [
      {
        index: "needList",
        title: "需求列表"
      }
    ]
  }
]
};

这样配置后,所有用户角色的权限信息都在appMenuitem中,在项目的开发,我们可以按照配置的对象属性index(这里是可以由开发按自己的习惯约定)的值,如dataOverviewdemendManage等来建立页面。如图:

image

生成侧边菜单逻辑(需配置好路由router才能运行)

    <el-menu
      background-color="#fff"
      text-color="#222222"
      theme="dark"
      :default-active="onRoute"
      :collapse="false"
      router
    >
     <template v-for="(item, index) in items">
        <!-- 有子选项 -->
        <template v-if="item.subs && item.subs.length > 0">
          <el-submenu :index="item.index" :key="index">
            <template slot="title">
              <i :class="item.icon"
              ></i>
              <span>{{ item.title }}</span>
            </template>
            <el-menu-item-group>
              <el-menu-item
                v-for="(subItem, index2) in item.subs"
                :key="index2"
                :index="subItem.index"
              >
                <span>{{ subItem.title }}</span>
              </el-menu-item>
            </el-menu-item-group>
          </el-submenu>
        </template>
        <!-- 无子选项 -->
        <template v-else>
          <el-menu-item :index="item.index" :key="item.index">
            <i :class="item.icon"></i>
            {{ item.title }}
          </el-menu-item>
        </template>
      </template>
    </el-menu>
    this.items = appMenuitem[userMap[this.userInfo.role]];

手动配置上的工作就完成了。后面就是完成路由的动态生成了。

路由生成

首页配置在router.js中配置项目通用路由:

let router = new Router({
routes: [
  {
    path: "/",
    redirect: "/home",
  },
  {
    path: "/login",
    name: "login",
    component: resolve => require(["../views/Login.vue"], resolve)
  }
]
});

home是主路由,前面配置的路由页面均嵌套在其中。下面来看看生成其他路由的方法

function initRouter() {
// 登录用户 角色  审核方 业务方 供应商
const userType = userMap[store.state.userInfo.role] || 'Auditor';
let subs = appMenuitem[userType],
  children = [];
subs = subs
  .map(c => c["subs"])
  .forEach(sub => {
    children = children.concat(sub);
  });
children = children.map((child, index) => {
  if (index == 0) {
    return {
      path: `/${child.index}`,
      component: resolve =>
        require([`@/views/${userType}/${child.index}.vue`], resolve)
    };
  }
  return {
    path: `/${child.index}`,
    name: `${child.index}`,
    component: resolve =>
      require([`@/views/${userType}/${child.index}.vue`], resolve)
  };
});
// 为了配置 菜单导航
children.unshift({
  path: "/",
  redirect: children[0].path
});
// console.log(children)
return children;

}

只要调用这个方法就可以生成,home的子级路由了。接下来我们可以通过router的APIaddRoutes完成路由的添加

// 在页面不刷新的前提 避免重复生产动态路由
window.hasRoutes = false;

router.beforeEach((to, from, next) => {
// 路由卫士
if (to.path === "/login") {
  // 请除登录信息
  localStorage.removeItem("hasLogin");
}
let user = localStorage.getItem("hasLogin") == null;
// 这里store.state.userInfo.role 是后台返回来的 用户角色信息 role 取值 为 1 2 3 分别 对应前面的键值 Author...
if ((user|| !store.state.userInfo.role) && to.path !== "/login" ) {
  // 登录信息失效 直接去登录页面
  next({ path: "/login" });
} else {
  // 获取动态路由
  if (store.state.userInfo.role && !window.hasRoutes) {
    // window.hasRoutes 为了简单,使用的全局变量 进入登录login.vue 页面也可以重置为false即可

    // router 创建新的路由对象
    let newRouter = new Router({
      routes: [
        {
          path: "/",
          redirect: "/home",
        },
        {
          path: "/login",
          name: "login",
          component: resolve => require(["../views/Login.vue"], resolve)
        }
      ]
    })
    let children = initRouter();
    router.matcher = newRouter.matcher;
    // 添加路由
    router.addRoutes([
      {
        path: "/home",
        // name: "home",
        component: resolve => require(["@/views/Home.vue"], resolve),
        children: children
      }
    ]);
    // 避免重复
    window.hasRoutes = true;
    // 这里 时在实际情况存在 切换登录对象的时候, 首次路由加载不成功,需要刷新, 我们 手动跳到 home 重新走下刚刚的逻辑
    if(from.path == '/login') {
      router.push({path:'/home'})
    }else{
      router.push({ path: to.path })
    }
  } 
  else  {
    next();
  }
}
});

最后,前端配置生成动态路由的方法基本就是这样啦,这是我在项目中遇到的坑,希望有所帮助.