vue3.0 + TS + AntD 项目开发学习笔记:路由模块类封装+路由权限管理

208 阅读4分钟

这篇文章简单整理一下开发项目中的路由模块的封装和路由权限管理。

    1. 路由模块封装

首先新建一个 router 文件夹,在 router 文件夹下新增一个 index.ts 文件。在 index.ts 文件中敲入如下代码:

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
// 1. 定义路由组件.
// 也可以从其他文件导入
const Index = () => import('../pages/Index.vue')
const Login = () => import('../pages/common/Login.vue')

import home from './modules/home';
import user from './modules/user';
....

// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
const routes: Array<RouteRecordRaw> = [
  {
    path: '/login',
    component: Login,
    meta: {
    }
  },
  {
    path: '/',
    name: 'Index',
    component: Index,
    children: [
      ...home,
      ...user,
    ]
  }
];
// 3. 创建路由实例并传递 `routes` 配置
const router = createRouter({
  history: createWebHistory(),
  routes
});
export default router;

这是路由的入口,定义了一个 router, 这是主路径,children 可以扩展子路径,方便跳转,有多少定义多少,都可以加入。 以子路由模块 user 为例,在 router 文件夹下再新建一个文件夹 module,在 module 文件夹中新建 user.ts 文件定义子路由模块 user,代码如下:

import {RouteRecordRaw } from 'vue-router';

const User = () => import('../../pages/user/Index.vue');

const user: Array<RouteRecordRaw> = [
    {
        path: ':role/user',
        name: 'User',
        component: User,
        children: [
        ],
    }
];

export default user;

如果子路由模块还有它自己的子路由,可以继续递归下去,在 children 中加入子路由。其中 path 定义路由路径,name 是路由名称,component 是关联组件。 在 main.ts 中挂着路由实例, 通过调用 app.use(router); 我们就可以在任意组件中访问它,访问的方式为,

import router from '../router/index';
router.push('')

在有的项目中,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个 User 组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,也就是路由参数,就如上代码中定义 user 模块的路径我这里就用到了带参数的动态路由 path: ':role/user', role 是一个变量,在路由跳转是传入值,路径就会变成相应的路径,例如代码:

router.push({ name: 'User', params: { role: isMasterMode.value === true ? 'master' : 'employee' } });

根据用户 ID 身份角色的不同来传入对应的参数。

在页面显示端,我们用 router-view 来渲染组件, router-view 将显示与 url 对应的组件。你可以把它放在任何地方,以适应你的布局。

<div id="app">
  <h1>Hello App!</h1>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>
    1. 路由权限管理

这里使用的是前端控制,在 router 文件夹中再新建一个 pemission.ts 文件。写入代码如下:

import router from '../router';
import constant from '../global/constant';
import getPageTitle from '../utils/get-page-title';
import useStore from '../store';
const { user } = useStore()
// 获取用户信息,判断账号类型
const accountType = user.accountType;
const isMasterMode = Boolean(localStorage.getItem(constant.isMasterMode));
const hasToken = localStorage.getItem(constant.token);

/**
 * @description 路由白名单路径,无需 token 直接跳转
 */
const whiteList = ['/login', '/forget'];
/**
 * 全局前置守卫,进行设置网页标题,进行权限判断,路由拦截等
 * @param {Object} _to 即将要进入的目标路由对象
 * @param {Object} _from 当前导航正要离开的路由
 * @param {Function} next 用于 resolve 这个钩子,确保要调用 next 方法,否则钩子就不会被 resolved
 */
router.beforeEach(async (_to: any, _from, next) => {
    // 设置页面标题
    document.title = getPageTitle(_to.meta.title);
    if (hasToken) {
        if (_to.path === '/' || _to.path === '/login') {
            // 如果已经登录则重定向到首页
            if (accountType !== 'admin') {
                if (accountType === 'master' && isMasterMode === true) {
                    router.push({ name: 'Tracking', params: { role: 'master' } });
                } else {
                    router.push({ name: 'Tracking', params: { role: 'employee' } });
                }
            } else {
                next({ path: '/admin' });
            }
        }
        if (_to.path.includes('admin')) {
            if (accountType === 'admin') {
                // console.log('isMasterMode');
                if (_to.matched.length === 0) {
                    next({ path: '/admin' });
                } else {
                    next(); //放行
                }
            } else {
                next({ path: "/404" }) //无权限路径
            }
        }
        if (_to.path.includes('employee')) {
            if (accountType !== 'admin') {
                if (accountType === 'master') {
                    if (isMasterMode === true) {
                        next({ path: '/' });
                    } else {
                        if (_to.matched.length === 0) {
                            router.push({ name: 'Tracking', params: { role: 'employee' } });
                        } else {
                            next(); //放行
                        }
                    }
                }
            } else {
                next({ path: "/404" }) //无权限路径
            }
        }
        if (_to.path.includes('master')) {
            // console.log('master');
            if (accountType === 'master') {
                if (isMasterMode === true) {
                    if (_to.matched.length === 0) {
                        router.push({ name: 'Tracking', params: { role: 'master' } });
                    } else {
                        next(); //放行
                    }
                } else {
                    next({ path: '/' });
                }
            } else {
                next({ path: "/404" }) //无权限路径
            }
        }
    } else {
        // 如果跳转页面在白名单里面,直接跳转
        if (whiteList.indexOf(_to.path) !== -1) {
            next();
        } else {
            // 不在白名单内的页面则跳转到登录页面,且拼接跳转路径
            next('/login');
        }
    }
    next();
});

路由权限主要是考虑到用户是否登录,判断 token,来设置路由权限。如果有 token,又要考虑用户的身份,目前项目中用户有三种身份,分别是普通用户,主管用户和管理员用户。通过后端返回的账号类型 accountType 来做一个权限的管理,主管用户是可以访问主管界面和普通用户界面,不能访问管理员界面;普通用户只能访问普通用户界面,管理员用户只能访问管理员界面。由于主管用户和普通用户路由映射同一个组件,不想重复写路由表,所以这里就没用 meta.role 进行匹配。