# 基本配置

import { createRouter, createWebHistory } from 'vue-router';
import pinia from './pinia';
import { useUserStore } from '../store/user';
const user = useUserStore(pinia);
// 不需要权限的页面
const constantRoutes = [
  {
    // 登录
    path: '/login',
    name: 'login',
    component: () => import('../views/login/index.vue')
  },
  {
    // 404
    path: '/:pathMatch(.*)',
    name: 'notFound',
    component: () => import('../views/error/notFound.vue')
  },
  {
    // 无权限
    path: '/noPermission',
    name: 'noPermission',
    component: () => import('../views/error/noPermission.vue')
  }
];
const asyncRoutes = {
  path: '/',
  name: 'main',
  component: () => import('../views/mainPage.vue'),
  children: [
    {
      // 首页
      path: '/',
      name: 'home',
      component: () => import('../views/home/index.vue')
    },
    {
      // 用户管理
      path: '/settingUser',
      name: 'settingUser',
      component: () => import('../views/setting/user.vue')
    }
  ]
};
const router = createRouter({
  history: createWebHistory('/'),
  routes: constantRoutes
});
router.addRoute(asyncRoutes);
router.beforeEach((to, from, next) => {
  // 切换 router 时,取消 pending 中的请求
  if (window.__axiosPromiseArr) {
    window.__axiosPromiseArr.forEach((ele, ind) => {
      ele.cancel();
      delete window.__axiosPromiseArr[ind];
    });
  }
  //token 过期
  if (localStorage.getItem('expires') && (new Date().getTime() - localStorage.getItem('expires')) / 1000 > 1) {
    this.$message.error('登录失效,请重新登录', () => {
      localStorage.removeItem('userInfon');
      localStorage.removeItem('token');
      localStorage.removeItem('expires');
      location.href = '/login';
    });
    return;
  }
  // 登录判断
  if (user.token) {
    if (to.path === '/login') {
      next({ path: '/' });
    } else {
      // 权限判断
      next();
    }
  } else {
    if (to.path === '/login') {
      next();
    } else {
      next({ name: 'login' });
    }
  }
});
// 跳转完成后,将滚动条位置重置
router.afterEach(to => {
  window.scrollTo(0, 0);
});
export default router;

# 1. 安装 vue-router

npm install vue-router

# 2. 路由实例

创建路由实例,顺带初始化静态路由,而动态路由需要用户登录,根据用户拥有的角色进行权限校验后进行初始化。

// src/router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
export const Layout = () => import('@/layout/index.vue');
// 静态路由
export const constantRoutes: RouteRecordRaw[] = [
  {
    path: '/redirect',
    component: Layout,
    meta: { hidden: true },
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index.vue')
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    meta: { hidden: true }
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index.vue'),
        name: 'Dashboard',
        meta: { title: 'dashboard', icon: 'homepage', affix: true }
      }
    ]
  }
];
/**
 * 创建路由
 */
const router = createRouter({
  history: createWebHashHistory(),
  routes: constantRoutes as RouteRecordRaw[],
  // 刷新时,滚动条位置还原
  scrollBehavior: () => ({ left: 0, top: 0 })
});
/**
 * 重置路由
 */
export function resetRouter() {
  router.replace({ path: '/login' });
  location.reload();
}
export default router;

# 3. 全局注册路由实例

// main.ts
import router from "@/router";
app.use(router).mount('#app')

# 4. 动态权限路由

路由守卫 src/permission.ts ,获取当前登录用户的角色信息进行动态路由的初始化

img

最终调用 permissionStore.generateRoutes(roles) 方法生成动态路由

// src/store/modules/permission.ts 
import { listRoutes } from '@/api/menu';
export const usePermissionStore = defineStore('permission', () => {
  const routes = ref<RouteRecordRaw[]>([]);
  function setRoutes(newRoutes: RouteRecordRaw[]) {
    routes.value = constantRoutes.concat(newRoutes);
  }
  /**
   * 生成动态路由
   *
   * @param roles 用户角色集合
   * @returns
   */
  function generateRoutes(roles: string[]) {
    return new Promise<RouteRecordRaw[]>((resolve, reject) => {
      // 接口获取所有路由
      listRoutes()
        .then(({ data: asyncRoutes }) => {
          // 根据角色获取有访问权限的路由
          const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
          setRoutes(accessedRoutes);
          resolve(accessedRoutes);
        })
        .catch(error => {
          reject(error);
        });
    });
  }
  // 导出 store 的动态路由数据 routes 
  return { routes, setRoutes, generateRoutes };
});

接口获取得到的路由数据

img

根据路由数据 (routes) 生成菜单的关键代码

src/layout/componets/Sidebar/index.vuesrc/layout/componets/Sidebar/SidebarItem.vue
imgimage-20230326145836872