mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-11-13 12:13:43 +08:00
feat(projects): 1.0 beta
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router';
|
||||
import { routeName } from '@/router';
|
||||
import { useRouteStore } from '@/store';
|
||||
import { localStg } from '@/utils';
|
||||
|
||||
/**
|
||||
* 动态路由
|
||||
*/
|
||||
export async function createDynamicRouteGuard(
|
||||
to: RouteLocationNormalized,
|
||||
_from: RouteLocationNormalized,
|
||||
next: NavigationGuardNext
|
||||
) {
|
||||
const route = useRouteStore();
|
||||
const isLogin = Boolean(localStg.get('token'));
|
||||
|
||||
// 初始化权限路由
|
||||
if (!route.isInitAuthRoute) {
|
||||
// 未登录情况下直接回到登录页,登录成功后再加载权限路由
|
||||
if (!isLogin) {
|
||||
const toName = to.name as AuthRoute.AllRouteKey;
|
||||
if (route.isValidConstantRoute(toName) && !to.meta.requiresAuth) {
|
||||
next();
|
||||
} else {
|
||||
const redirect = to.fullPath;
|
||||
next({ name: routeName('login'), query: { redirect } });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
await route.initAuthRoute();
|
||||
|
||||
if (to.name === routeName('not-found')) {
|
||||
// 动态路由没有加载导致被not-found路由捕获,等待权限路由加载好了,回到之前的路由
|
||||
// 若路由是从根路由重定向过来的,重新回到根路由
|
||||
const ROOT_ROUTE_NAME: AuthRoute.AllRouteKey = 'root';
|
||||
const path = to.redirectedFrom?.name === ROOT_ROUTE_NAME ? '/' : to.fullPath;
|
||||
next({ path, replace: true, query: to.query, hash: to.hash });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 权限路由已经加载,仍然未找到,重定向到404
|
||||
if (to.name === routeName('not-found')) {
|
||||
next({ name: routeName('404'), replace: true });
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,23 +1,14 @@
|
||||
import type { Router } from 'vue-router';
|
||||
import { useTitle } from '@vueuse/core';
|
||||
import { $t } from '@/locales';
|
||||
import { createProgressGuard } from './progress';
|
||||
import { createDocumentTitleGuard } from './title';
|
||||
import { createPermissionGuard } from './permission';
|
||||
|
||||
/**
|
||||
* 路由守卫函数
|
||||
* @param router - 路由实例
|
||||
* router guard
|
||||
* @param router - router instance
|
||||
*/
|
||||
export function createRouterGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 开始 loadingBar
|
||||
window.$loadingBar?.start();
|
||||
// 页面跳转权限处理
|
||||
await createPermissionGuard(to, from, next);
|
||||
});
|
||||
router.afterEach(to => {
|
||||
// 设置document title
|
||||
useTitle(to.meta.i18nTitle ? $t(to.meta.i18nTitle) : to.meta.title);
|
||||
// 结束 loadingBar
|
||||
window.$loadingBar?.finish();
|
||||
});
|
||||
createProgressGuard(router);
|
||||
createPermissionGuard(router);
|
||||
createDocumentTitleGuard(router);
|
||||
}
|
||||
|
||||
@@ -1,70 +1,141 @@
|
||||
import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router';
|
||||
import { routeName } from '@/router';
|
||||
import { useAuthStore } from '@/store';
|
||||
import { exeStrategyActions, localStg } from '@/utils';
|
||||
import { createDynamicRouteGuard } from './dynamic';
|
||||
import type { Router, NavigationGuardNext, RouteLocationNormalized } from 'vue-router';
|
||||
import type { RouteKey, RoutePath } from '@elegant-router/types';
|
||||
import { useAuthStore } from '@/store/modules/auth';
|
||||
import { useRouteStore } from '@/store/modules/route';
|
||||
import { localStg } from '@/utils/storage';
|
||||
|
||||
/** 处理路由页面的权限 */
|
||||
export async function createPermissionGuard(
|
||||
export function createPermissionGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const pass = await createAuthRouteGuard(to, from, next);
|
||||
|
||||
if (!pass) return;
|
||||
|
||||
// 1. route with href
|
||||
if (to.meta.href) {
|
||||
window.open(to.meta.href, '_blank');
|
||||
next({ path: from.fullPath, replace: true, query: from.query, hash: to.hash });
|
||||
}
|
||||
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const isLogin = Boolean(localStg.get('token'));
|
||||
const needLogin = !to.meta.constant;
|
||||
const routeRoles = to.meta.roles || [];
|
||||
const rootRoute: RouteKey = 'root';
|
||||
const loginRoute: RouteKey = 'login';
|
||||
const noPermissionRoute: RouteKey = '403';
|
||||
|
||||
const SUPER_ADMIN = 'R_SUPER';
|
||||
const hasPermission =
|
||||
!routeRoles.length ||
|
||||
authStore.userInfo.roles.includes(SUPER_ADMIN) ||
|
||||
authStore.userInfo.roles.some(role => routeRoles.includes(role));
|
||||
|
||||
const strategicPatterns: Common.StrategicPattern[] = [
|
||||
// 1. if it is login route when logged in, change to the root page
|
||||
{
|
||||
condition: isLogin && to.name === loginRoute,
|
||||
callback: () => {
|
||||
next({ name: rootRoute });
|
||||
}
|
||||
},
|
||||
// 2. if is is constant route, then it is allowed to access directly
|
||||
{
|
||||
condition: !needLogin,
|
||||
callback: () => {
|
||||
next();
|
||||
}
|
||||
},
|
||||
// 3. if the route need login but the user is not logged in, then switch to the login page
|
||||
{
|
||||
condition: !isLogin && needLogin,
|
||||
callback: () => {
|
||||
next({ name: loginRoute, query: { redirect: to.fullPath } });
|
||||
}
|
||||
},
|
||||
// 4. if the user is logged in and has permission, then it is allowed to access
|
||||
{
|
||||
condition: isLogin && needLogin && hasPermission,
|
||||
callback: () => {
|
||||
next();
|
||||
}
|
||||
},
|
||||
// 5. if the user is logged in but does not have permission, then switch to the 403 page
|
||||
{
|
||||
condition: isLogin && needLogin && !hasPermission,
|
||||
callback: () => {
|
||||
next({ name: noPermissionRoute });
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
strategicPatterns.some(({ condition, callback }) => {
|
||||
if (condition) {
|
||||
callback();
|
||||
}
|
||||
|
||||
return condition;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function createAuthRouteGuard(
|
||||
to: RouteLocationNormalized,
|
||||
from: RouteLocationNormalized,
|
||||
_from: RouteLocationNormalized,
|
||||
next: NavigationGuardNext
|
||||
) {
|
||||
// 动态路由
|
||||
const permission = await createDynamicRouteGuard(to, from, next);
|
||||
if (!permission) return;
|
||||
const notFoundRoute: RouteKey = 'not-found';
|
||||
const isNotFoundRoute = to.name === notFoundRoute;
|
||||
|
||||
// 外链路由, 从新标签打开,返回上一个路由
|
||||
if (to.meta.href) {
|
||||
window.open(to.meta.href);
|
||||
next({ path: from.fullPath, replace: true, query: from.query });
|
||||
return;
|
||||
// 1. If the route is the constant route but is not the "not-found" route, then it is allowed to access.
|
||||
if (to.meta.constant && !isNotFoundRoute) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auth = useAuthStore();
|
||||
// 2. If the auth route is initialized but is not the "not-found" route, then it is allowed to access.
|
||||
const routeStore = useRouteStore();
|
||||
if (routeStore.isInitAuthRoute && !isNotFoundRoute) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. If the route is initialized, check whether the route exists.
|
||||
if (routeStore.isInitAuthRoute && isNotFoundRoute) {
|
||||
const exist = await routeStore.getIsAuthRouteExist(to.path as RoutePath);
|
||||
|
||||
if (exist) {
|
||||
const noPermissionRoute: RouteKey = '403';
|
||||
|
||||
next({ name: noPermissionRoute });
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. If the user is not logged in, then redirect to the login page.
|
||||
const isLogin = Boolean(localStg.get('token'));
|
||||
const permissions = to.meta.permissions || [];
|
||||
const needLogin = Boolean(to.meta?.requiresAuth) || Boolean(permissions.length);
|
||||
const hasPermission = !permissions.length || permissions.includes(auth.userInfo.userRole);
|
||||
if (!isLogin) {
|
||||
const loginRoute: RouteKey = 'login';
|
||||
const redirect = to.fullPath;
|
||||
|
||||
const actions: Common.StrategyAction[] = [
|
||||
// 已登录状态跳转登录页,跳转至首页
|
||||
[
|
||||
isLogin && to.name === routeName('login'),
|
||||
() => {
|
||||
next({ name: routeName('root') });
|
||||
}
|
||||
],
|
||||
// 不需要登录权限的页面直接通行
|
||||
[
|
||||
!needLogin,
|
||||
() => {
|
||||
next();
|
||||
}
|
||||
],
|
||||
// 未登录状态进入需要登录权限的页面
|
||||
[
|
||||
!isLogin && needLogin,
|
||||
() => {
|
||||
const redirect = to.fullPath;
|
||||
next({ name: routeName('login'), query: { redirect } });
|
||||
}
|
||||
],
|
||||
// 登录状态进入需要登录权限的页面,有权限直接通行
|
||||
[
|
||||
isLogin && needLogin && hasPermission,
|
||||
() => {
|
||||
next();
|
||||
}
|
||||
],
|
||||
[
|
||||
// 登录状态进入需要登录权限的页面,无权限,重定向到无权限页面
|
||||
isLogin && needLogin && !hasPermission,
|
||||
() => {
|
||||
next({ name: routeName('403') });
|
||||
}
|
||||
]
|
||||
];
|
||||
next({ name: loginRoute, query: { redirect } });
|
||||
|
||||
exeStrategyActions(actions);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. init auth route
|
||||
await routeStore.initAuthRoute();
|
||||
|
||||
// 6. the route is caught by the "not-found" route because the auto route is not initialized. after the auto route is initialized, redirect to the original route.
|
||||
if (isNotFoundRoute) {
|
||||
const rootRoute: RouteKey = 'root';
|
||||
const path = to.redirectedFrom?.name === rootRoute ? '/' : to.fullPath;
|
||||
|
||||
next({ path, replace: true, query: to.query, hash: to.hash });
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
11
src/router/guard/progress.ts
Normal file
11
src/router/guard/progress.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { Router } from 'vue-router';
|
||||
|
||||
export function createProgressGuard(router: Router) {
|
||||
router.beforeEach((_to, _from, next) => {
|
||||
window.NProgress?.start?.();
|
||||
next();
|
||||
});
|
||||
router.afterEach(_to => {
|
||||
window.NProgress?.done?.();
|
||||
});
|
||||
}
|
||||
13
src/router/guard/title.ts
Normal file
13
src/router/guard/title.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { $t } from '@/locales';
|
||||
import { useTitle } from '@vueuse/core';
|
||||
import type { Router } from 'vue-router';
|
||||
|
||||
export function createDocumentTitleGuard(router: Router) {
|
||||
router.afterEach(to => {
|
||||
const { i18nKey, title } = to.meta;
|
||||
|
||||
const documentTitle = i18nKey ? $t(i18nKey) : title;
|
||||
|
||||
useTitle(documentTitle);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user