mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-10-16 06:33:43 +08:00
feat(projects): 1.0 beta
This commit is contained in:
30
src/router/elegant/imports.ts
Normal file
30
src/router/elegant/imports.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// Generated by elegant-router
|
||||
// Read more: https://github.com/soybeanjs/elegant-router
|
||||
|
||||
import type { RouteComponent } from "vue-router";
|
||||
import type { LastLevelRouteKey, RouteLayout } from "@elegant-router/types";
|
||||
|
||||
import BaseLayout from "@/layouts/base-layout/index.vue";
|
||||
import BlankLayout from "@/layouts/blank-layout/index.vue";
|
||||
|
||||
export const layouts: Record<RouteLayout, RouteComponent | (() => Promise<RouteComponent>)> = {
|
||||
base: BaseLayout,
|
||||
blank: BlankLayout,
|
||||
};
|
||||
|
||||
export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<RouteComponent>)> = {
|
||||
403: () => import("@/views/_builtin/403/index.vue"),
|
||||
404: () => import("@/views/_builtin/404/index.vue"),
|
||||
500: () => import("@/views/_builtin/500/index.vue"),
|
||||
login: () => import("@/views/_builtin/login/index.vue"),
|
||||
home: () => import("@/views/home/index.vue"),
|
||||
manage_role: () => import("@/views/manage/role/index.vue"),
|
||||
manage_route: () => import("@/views/manage/route/index.vue"),
|
||||
"manage_user-detail": () => import("@/views/manage/user-detail/[id].vue"),
|
||||
manage_user: () => import("@/views/manage/user/index.vue"),
|
||||
"multi-menu_first_child": () => import("@/views/multi-menu/first_child/index.vue"),
|
||||
"multi-menu_second_child_home": () => import("@/views/multi-menu/second_child_home/index.vue"),
|
||||
"user-center": () => import("@/views/user-center/index.vue"),
|
||||
};
|
197
src/router/elegant/routes.ts
Normal file
197
src/router/elegant/routes.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// Generated by elegant-router
|
||||
// Read more: https://github.com/soybeanjs/elegant-router
|
||||
|
||||
import type { GeneratedRoute } from '@elegant-router/types';
|
||||
|
||||
export const generatedRoutes: GeneratedRoute[] = [
|
||||
{
|
||||
name: '403',
|
||||
path: '/403',
|
||||
component: 'layout.blank$view.403',
|
||||
meta: {
|
||||
title: '403',
|
||||
i18nKey: 'route.403',
|
||||
constant: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '404',
|
||||
path: '/404',
|
||||
component: 'layout.blank$view.404',
|
||||
meta: {
|
||||
title: '404',
|
||||
i18nKey: 'route.404',
|
||||
constant: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '500',
|
||||
path: '/500',
|
||||
component: 'layout.blank$view.500',
|
||||
meta: {
|
||||
title: '500',
|
||||
i18nKey: 'route.500',
|
||||
constant: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'home',
|
||||
path: '/home',
|
||||
component: 'layout.base$view.home',
|
||||
meta: {
|
||||
title: 'home',
|
||||
i18nKey: 'route.home',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'login',
|
||||
path: '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?',
|
||||
component: 'layout.blank$view.login',
|
||||
props: true,
|
||||
meta: {
|
||||
title: 'login',
|
||||
i18nKey: 'route.login',
|
||||
constant: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'manage',
|
||||
path: '/manage',
|
||||
component: 'layout.base',
|
||||
meta: {
|
||||
title: 'manage',
|
||||
i18nKey: 'route.manage',
|
||||
icon: 'carbon:cloud-service-management',
|
||||
order: 9,
|
||||
roles: ['R_ADMIN']
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'manage_role',
|
||||
path: '/manage/role',
|
||||
component: 'view.manage_role',
|
||||
meta: {
|
||||
title: 'manage_role',
|
||||
i18nKey: 'route.manage_role',
|
||||
icon: 'carbon:user-role',
|
||||
order: 2,
|
||||
roles: ['R_ADMIN']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'manage_route',
|
||||
path: '/manage/route',
|
||||
component: 'view.manage_route',
|
||||
meta: {
|
||||
title: 'manage_route',
|
||||
i18nKey: 'route.manage_route',
|
||||
icon: 'material-symbols:route',
|
||||
order: 3,
|
||||
roles: ['R_ADMIN'],
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'manage_user',
|
||||
path: '/manage/user',
|
||||
component: 'view.manage_user',
|
||||
meta: {
|
||||
title: 'manage_user',
|
||||
i18nKey: 'route.manage_user',
|
||||
icon: 'ic:round-manage-accounts',
|
||||
order: 1,
|
||||
roles: ['R_ADMIN']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'manage_user-detail',
|
||||
path: '/manage/user-detail/:id',
|
||||
component: 'view.manage_user-detail',
|
||||
props: true,
|
||||
meta: {
|
||||
title: 'manage_user-detail',
|
||||
i18nKey: 'route.manage_user-detail',
|
||||
hideInMenu: true,
|
||||
roles: ['R_ADMIN'],
|
||||
activeMenu: 'manage_user'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'multi-menu',
|
||||
path: '/multi-menu',
|
||||
component: 'layout.base',
|
||||
meta: {
|
||||
title: 'multi-menu',
|
||||
i18nKey: 'route.multi-menu',
|
||||
order: 8
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'multi-menu_first',
|
||||
path: '/multi-menu/first',
|
||||
meta: {
|
||||
title: 'multi-menu_first',
|
||||
i18nKey: 'route.multi-menu_first',
|
||||
order: 1
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'multi-menu_first_child',
|
||||
path: '/multi-menu/first/child',
|
||||
component: 'view.multi-menu_first_child',
|
||||
meta: {
|
||||
title: 'multi-menu_first_child',
|
||||
i18nKey: 'route.multi-menu_first_child'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'multi-menu_second',
|
||||
path: '/multi-menu/second',
|
||||
meta: {
|
||||
title: 'multi-menu_second',
|
||||
i18nKey: 'route.multi-menu_second',
|
||||
order: 2
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'multi-menu_second_child',
|
||||
path: '/multi-menu/second/child',
|
||||
meta: {
|
||||
title: 'multi-menu_second_child',
|
||||
i18nKey: 'route.multi-menu_second_child'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'multi-menu_second_child_home',
|
||||
path: '/multi-menu/second/child/home',
|
||||
component: 'view.multi-menu_second_child_home',
|
||||
meta: {
|
||||
title: 'multi-menu_second_child_home',
|
||||
i18nKey: 'route.multi-menu_second_child_home'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'user-center',
|
||||
path: '/user-center',
|
||||
component: 'layout.base$view.user-center',
|
||||
meta: {
|
||||
title: 'user-center',
|
||||
i18nKey: 'route.user-center',
|
||||
hideInMenu: true
|
||||
}
|
||||
}
|
||||
];
|
183
src/router/elegant/transform.ts
Normal file
183
src/router/elegant/transform.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// Generated by elegant-router
|
||||
// Read more: https://github.com/soybeanjs/elegant-router
|
||||
|
||||
import type { RouteRecordRaw, RouteComponent } from 'vue-router';
|
||||
import type { ElegantConstRoute } from '@elegant-router/vue';
|
||||
import type { RouteMap, RouteKey, RoutePath } from '@elegant-router/types';
|
||||
|
||||
/**
|
||||
* transform elegant const routes to vue routes
|
||||
* @param routes elegant const routes
|
||||
* @param layouts layout components
|
||||
* @param views view components
|
||||
*/
|
||||
export function transformElegantRoutesToVueRoutes(
|
||||
routes: ElegantConstRoute[],
|
||||
layouts: Record<string, RouteComponent | (() => Promise<RouteComponent>)>,
|
||||
views: Record<string, RouteComponent | (() => Promise<RouteComponent>)>
|
||||
) {
|
||||
return routes.flatMap(route => transformElegantRouteToVueRoute(route, layouts, views));
|
||||
}
|
||||
|
||||
/**
|
||||
* transform elegant route to vue route
|
||||
* @param route elegant const route
|
||||
* @param layouts layout components
|
||||
* @param views view components
|
||||
*/
|
||||
function transformElegantRouteToVueRoute(
|
||||
route: ElegantConstRoute,
|
||||
layouts: Record<string, RouteComponent | (() => Promise<RouteComponent>)>,
|
||||
views: Record<string, RouteComponent | (() => Promise<RouteComponent>)>
|
||||
) {
|
||||
const LAYOUT_PREFIX = 'layout.';
|
||||
const VIEW_PREFIX = 'view.';
|
||||
const ROUTE_DEGREE_SPLITTER = '_';
|
||||
const FIRST_LEVEL_ROUTE_COMPONENT_SPLIT = '$';
|
||||
|
||||
function isLayout(component: string) {
|
||||
return component.startsWith(LAYOUT_PREFIX);
|
||||
}
|
||||
|
||||
function getLayoutName(component: string) {
|
||||
return component.replace(LAYOUT_PREFIX, '');
|
||||
}
|
||||
|
||||
function isView(component: string) {
|
||||
return component.startsWith(VIEW_PREFIX);
|
||||
}
|
||||
|
||||
function getViewName(component: string) {
|
||||
return component.replace(VIEW_PREFIX, '');
|
||||
}
|
||||
|
||||
function isFirstLevelRoute(item: ElegantConstRoute) {
|
||||
return !item.name.includes(ROUTE_DEGREE_SPLITTER);
|
||||
}
|
||||
|
||||
function isSingleLevelRoute(item: ElegantConstRoute) {
|
||||
return isFirstLevelRoute(item) && !item.children?.length;
|
||||
}
|
||||
|
||||
function getSingleLevelRouteComponent(component: string) {
|
||||
const [layout, view] = component.split(FIRST_LEVEL_ROUTE_COMPONENT_SPLIT);
|
||||
|
||||
return {
|
||||
layout: getLayoutName(layout),
|
||||
view: getViewName(view)
|
||||
};
|
||||
}
|
||||
|
||||
const vueRoutes: RouteRecordRaw[] = [];
|
||||
|
||||
// add props: true to route
|
||||
if (route.path.includes(':') && !route.props) {
|
||||
route.props = true;
|
||||
}
|
||||
|
||||
const { name, path, component, children, ...rest } = route;
|
||||
|
||||
const vueRoute = { name, path, ...rest } as RouteRecordRaw;
|
||||
|
||||
if (component) {
|
||||
if (isSingleLevelRoute(route)) {
|
||||
const { layout, view } = getSingleLevelRouteComponent(component);
|
||||
|
||||
const singleLevelRoute: RouteRecordRaw = {
|
||||
path,
|
||||
component: layouts[layout],
|
||||
children: [
|
||||
{
|
||||
name,
|
||||
path: '',
|
||||
component: views[view],
|
||||
...rest
|
||||
} as RouteRecordRaw
|
||||
]
|
||||
};
|
||||
|
||||
return [singleLevelRoute];
|
||||
}
|
||||
|
||||
if (isLayout(component)) {
|
||||
const layoutName = getLayoutName(component);
|
||||
|
||||
vueRoute.component = layouts[layoutName];
|
||||
}
|
||||
|
||||
if (isView(component)) {
|
||||
const viewName = getViewName(component);
|
||||
|
||||
vueRoute.component = views[viewName];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// add redirect to child
|
||||
if (children?.length && !vueRoute.redirect) {
|
||||
vueRoute.redirect = {
|
||||
name: children[0].name
|
||||
};
|
||||
}
|
||||
|
||||
if (children?.length) {
|
||||
const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views));
|
||||
|
||||
if(isFirstLevelRoute(route)) {
|
||||
vueRoute.children = childRoutes;
|
||||
} else {
|
||||
vueRoutes.push(...childRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
vueRoutes.unshift(vueRoute);
|
||||
|
||||
return vueRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* map of route name and route path
|
||||
*/
|
||||
const routeMap: RouteMap = {
|
||||
"root": "/",
|
||||
"not-found": "/:pathMatch(.*)*",
|
||||
"403": "/403",
|
||||
"404": "/404",
|
||||
"500": "/500",
|
||||
"home": "/home",
|
||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
|
||||
"manage": "/manage",
|
||||
"manage_role": "/manage/role",
|
||||
"manage_route": "/manage/route",
|
||||
"manage_user": "/manage/user",
|
||||
"manage_user-detail": "/manage/user-detail/:id",
|
||||
"multi-menu": "/multi-menu",
|
||||
"multi-menu_first": "/multi-menu/first",
|
||||
"multi-menu_first_child": "/multi-menu/first/child",
|
||||
"multi-menu_second": "/multi-menu/second",
|
||||
"multi-menu_second_child": "/multi-menu/second/child",
|
||||
"multi-menu_second_child_home": "/multi-menu/second/child/home",
|
||||
"user-center": "/user-center"
|
||||
};
|
||||
|
||||
/**
|
||||
* get route path by route name
|
||||
* @param name route name
|
||||
*/
|
||||
export function getRoutePath<T extends RouteKey>(name: T) {
|
||||
return routeMap[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* get route name by route path
|
||||
* @param path route path
|
||||
*/
|
||||
export function getRouteName(path: RoutePath) {
|
||||
const routeEntries = Object.entries(routeMap) as [RouteKey, RoutePath][];
|
||||
|
||||
const routeName: RouteKey | null = routeEntries.find(([, routePath]) => routePath === path)?.[0] || null;
|
||||
|
||||
return routeName;
|
||||
}
|
@@ -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);
|
||||
});
|
||||
}
|
@@ -1 +0,0 @@
|
||||
export * from './scroll';
|
@@ -1,38 +0,0 @@
|
||||
import type { RouterScrollBehavior } from 'vue-router';
|
||||
import { useAppStore, useTabStore } from '@/store';
|
||||
|
||||
export const scrollBehavior: RouterScrollBehavior = (to, from) => {
|
||||
return new Promise(resolve => {
|
||||
const app = useAppStore();
|
||||
const tab = useTabStore();
|
||||
|
||||
if (to.hash) {
|
||||
const el = document.querySelector(to.hash);
|
||||
if (el) {
|
||||
resolve({
|
||||
el,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { left, top } = tab.getTabScrollPosition(to.path);
|
||||
const scrollPosition = {
|
||||
left,
|
||||
top
|
||||
};
|
||||
const { scrollEl, scrollLeft, scrollTop } = app.getScrollConfig();
|
||||
|
||||
const isFromCached = Boolean(from.meta.keepAlive);
|
||||
if (isFromCached) {
|
||||
tab.recordTabScrollPosition(from.path, { left: scrollLeft, top: scrollTop });
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (scrollEl) {
|
||||
scrollEl.scrollLeft = scrollPosition.left;
|
||||
scrollEl.scrollTop = scrollPosition.top;
|
||||
}
|
||||
}, 400);
|
||||
});
|
||||
};
|
@@ -1,30 +1,34 @@
|
||||
import type { App } from 'vue';
|
||||
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
|
||||
import { transformRouteNameToRoutePath } from '@/utils';
|
||||
import { transformAuthRouteToVueRoutes } from '@/utils/router/transform';
|
||||
import { constantRoutes } from './routes';
|
||||
import { scrollBehavior } from './helpers';
|
||||
import {
|
||||
createRouter,
|
||||
createWebHistory,
|
||||
createWebHashHistory,
|
||||
createMemoryHistory,
|
||||
type RouterHistory
|
||||
} from 'vue-router';
|
||||
import { createRoutes } from './routes';
|
||||
import { createRouterGuard } from './guard';
|
||||
|
||||
const { VITE_HASH_ROUTE = 'N', VITE_BASE_URL } = import.meta.env;
|
||||
const { VITE_ROUTER_HISTORY_MODE = 'history', VITE_BASE_URL } = import.meta.env;
|
||||
|
||||
const historyCreatorMap: Record<Env.RouterHistoryMode, (base?: string) => RouterHistory> = {
|
||||
hash: createWebHashHistory,
|
||||
history: createWebHistory,
|
||||
memory: createMemoryHistory
|
||||
};
|
||||
|
||||
const { constantVueRoutes } = createRoutes();
|
||||
|
||||
export const router = createRouter({
|
||||
history: VITE_HASH_ROUTE === 'Y' ? createWebHashHistory(VITE_BASE_URL) : createWebHistory(VITE_BASE_URL),
|
||||
routes: transformAuthRouteToVueRoutes(constantRoutes),
|
||||
scrollBehavior
|
||||
history: historyCreatorMap[VITE_ROUTER_HISTORY_MODE](VITE_BASE_URL),
|
||||
routes: constantVueRoutes
|
||||
});
|
||||
|
||||
/** setup vue router. - [安装vue路由] */
|
||||
/**
|
||||
* setup Vue Router
|
||||
*/
|
||||
export async function setupRouter(app: App) {
|
||||
app.use(router);
|
||||
createRouterGuard(router);
|
||||
await router.isReady();
|
||||
}
|
||||
|
||||
/** 路由名称 */
|
||||
export const routeName = (key: AuthRoute.AllRouteKey) => key;
|
||||
/** 路由路径 */
|
||||
export const routePath = (key: Exclude<AuthRoute.AllRouteKey, 'not-found'>) => transformRouteNameToRoutePath(key);
|
||||
|
||||
export * from './routes';
|
||||
export * from './modules';
|
||||
|
@@ -1,17 +0,0 @@
|
||||
const about: AuthRoute.Route = {
|
||||
name: 'about',
|
||||
path: '/about',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '关于',
|
||||
i18nTitle: 'routes.about',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
singleLayout: 'basic',
|
||||
permissions: ['super', 'admin', 'user'],
|
||||
icon: 'fluent:book-information-24-regular',
|
||||
order: 10
|
||||
}
|
||||
};
|
||||
|
||||
export default about;
|
@@ -1,38 +0,0 @@
|
||||
const authDemo: AuthRoute.Route = {
|
||||
name: 'auth-demo',
|
||||
path: '/auth-demo',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'auth-demo_permission',
|
||||
path: '/auth-demo/permission',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
i18nTitle: 'routes.auth-demo.permission',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'auth-demo_super',
|
||||
path: '/auth-demo/super',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '超级管理员可见',
|
||||
i18nTitle: 'routes.auth-demo.super',
|
||||
requiresAuth: true,
|
||||
permissions: ['super'],
|
||||
icon: 'ic:round-supervisor-account'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
i18nTitle: 'routes.auth-demo._value',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
};
|
||||
|
||||
export default authDemo;
|
@@ -1,48 +0,0 @@
|
||||
const component: AuthRoute.Route = {
|
||||
name: 'component',
|
||||
path: '/component',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'component_button',
|
||||
path: '/component/button',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '按钮',
|
||||
i18nTitle: 'routes.component.button',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:button-cursor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component_card',
|
||||
path: '/component/card',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '卡片',
|
||||
i18nTitle: 'routes.component.card',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:card-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component_table',
|
||||
path: '/component/table',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '表格',
|
||||
i18nTitle: 'routes.component.table',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:table-large'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '组件示例',
|
||||
i18nTitle: 'routes.component._value',
|
||||
icon: 'cib:app-store',
|
||||
order: 3
|
||||
}
|
||||
};
|
||||
|
||||
export default component;
|
@@ -1,37 +0,0 @@
|
||||
const dashboard: AuthRoute.Route = {
|
||||
name: 'dashboard',
|
||||
path: '/dashboard',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'dashboard_analysis',
|
||||
path: '/dashboard/analysis',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '分析页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:analysis',
|
||||
i18nTitle: 'routes.dashboard.analysis'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'dashboard_workbench',
|
||||
path: '/dashboard/workbench',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '工作台',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:workbench',
|
||||
i18nTitle: 'routes.dashboard.workbench'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1,
|
||||
i18nTitle: 'routes.dashboard._value'
|
||||
}
|
||||
};
|
||||
|
||||
export default dashboard;
|
@@ -1,70 +0,0 @@
|
||||
const document: AuthRoute.Route = {
|
||||
name: 'document',
|
||||
path: '/document',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'document_vue',
|
||||
path: '/document/vue',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vue文档',
|
||||
i18nTitle: 'routes.document.vue',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vue'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_vite',
|
||||
path: '/document/vite',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vite文档',
|
||||
i18nTitle: 'routes.document.vite',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vitejs'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_naive',
|
||||
path: '/document/naive',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'naive文档',
|
||||
i18nTitle: 'routes.document.naive',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:naiveui'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_project',
|
||||
path: '/document/project',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '项目文档',
|
||||
i18nTitle: 'routes.document.project',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_project-link',
|
||||
path: '/document/project-link',
|
||||
meta: {
|
||||
title: '项目文档(外链)',
|
||||
i18nTitle: 'routes.document.project-link',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo',
|
||||
href: 'https://admin-docs.soybeanjs.cn/'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '文档',
|
||||
i18nTitle: 'routes.document._value',
|
||||
icon: 'mdi:file-document-multiple-outline',
|
||||
order: 2
|
||||
}
|
||||
};
|
||||
|
||||
export default document;
|
@@ -1,48 +0,0 @@
|
||||
const exception: AuthRoute.Route = {
|
||||
name: 'exception',
|
||||
path: '/exception',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'exception_403',
|
||||
path: '/exception/403',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页403',
|
||||
i18nTitle: 'routes.exception.403',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-block'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception_404',
|
||||
path: '/exception/404',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页404',
|
||||
i18nTitle: 'routes.exception.404',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-web-asset-off'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception_500',
|
||||
path: '/exception/500',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页500',
|
||||
i18nTitle: 'routes.exception.500',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-wifi-off'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
i18nTitle: 'routes.exception._value',
|
||||
title: '异常页',
|
||||
icon: 'ant-design:exception-outlined',
|
||||
order: 7
|
||||
}
|
||||
};
|
||||
|
||||
export default exception;
|
@@ -1,51 +0,0 @@
|
||||
const functionRoute: AuthRoute.Route = {
|
||||
name: 'function',
|
||||
path: '/function',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'function_tab',
|
||||
path: '/function/tab',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab',
|
||||
i18nTitle: 'routes.function.tab',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function_tab-detail',
|
||||
path: '/function/tab-detail',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab Detail',
|
||||
requiresAuth: true,
|
||||
hide: true,
|
||||
activeMenu: 'function_tab',
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function_tab-multi-detail',
|
||||
path: '/function/tab-multi-detail',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab Multi Detail',
|
||||
requiresAuth: true,
|
||||
hide: true,
|
||||
multiTab: true,
|
||||
activeMenu: 'function_tab',
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '功能',
|
||||
i18nTitle: 'routes.function._value',
|
||||
icon: 'icon-park-outline:all-application',
|
||||
order: 6
|
||||
}
|
||||
};
|
||||
|
||||
export default functionRoute;
|
@@ -1,5 +0,0 @@
|
||||
import { handleModuleRoutes } from '@/utils';
|
||||
|
||||
const modules = import.meta.glob('./**/*.ts', { eager: true }) as AuthRoute.RouteModule;
|
||||
|
||||
export const routes = handleModuleRoutes(modules);
|
@@ -1,63 +0,0 @@
|
||||
const management: AuthRoute.Route = {
|
||||
name: 'management',
|
||||
path: '/management',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'management_auth',
|
||||
path: '/management/auth',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
i18nTitle: 'routes.management.auth',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
icon: 'ic:baseline-security'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_role',
|
||||
path: '/management/role',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
i18nTitle: 'routes.management.role',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
icon: 'carbon:user-role'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_user',
|
||||
path: '/management/user',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
i18nTitle: 'routes.management.user',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
icon: 'ic:round-manage-accounts'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_route',
|
||||
path: '/management/route',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '路由管理',
|
||||
i18nTitle: 'routes.management.route',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
icon: 'material-symbols:route'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
i18nTitle: 'routes.management._value',
|
||||
icon: 'carbon:cloud-service-management',
|
||||
order: 9
|
||||
}
|
||||
};
|
||||
|
||||
export default management;
|
@@ -1,61 +0,0 @@
|
||||
const multiMenu: AuthRoute.Route = {
|
||||
name: 'multi-menu',
|
||||
path: '/multi-menu',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'multi-menu_first',
|
||||
path: '/multi-menu/first',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'multi-menu_first_second',
|
||||
path: '/multi-menu/first/second',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '二级菜单',
|
||||
i18nTitle: 'routes.multi-menu.first.second',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'multi-menu_first_second-new',
|
||||
path: '/multi-menu/first/second-new',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'multi-menu_first_second-new_third',
|
||||
path: '/multi-menu/first/second-new/third',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '三级菜单',
|
||||
i18nTitle: 'routes.multi-menu.first.second-new.third',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '二级菜单(有子菜单)',
|
||||
i18nTitle: 'routes.multi-menu.first.second-new._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '一级菜单',
|
||||
i18nTitle: 'routes.multi-menu.first._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '多级菜单',
|
||||
i18nTitle: 'routes.multi-menu._value',
|
||||
icon: 'carbon:menu',
|
||||
order: 8
|
||||
}
|
||||
};
|
||||
|
||||
export default multiMenu;
|
@@ -1,149 +0,0 @@
|
||||
const plugin: AuthRoute.Route = {
|
||||
name: 'plugin',
|
||||
path: '/plugin',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_charts',
|
||||
path: '/plugin/charts',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_charts_echarts',
|
||||
path: '/plugin/charts/echarts',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'ECharts',
|
||||
i18nTitle: 'routes.plugin.charts.echarts',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:apacheecharts'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_charts_antv',
|
||||
path: '/plugin/charts/antv',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'AntV',
|
||||
i18nTitle: 'routes.plugin.charts.antv',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:antdesign'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '图表',
|
||||
i18nTitle: 'routes.plugin.charts._value',
|
||||
icon: 'mdi:chart-areaspline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_map',
|
||||
path: '/plugin/map',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '地图',
|
||||
i18nTitle: 'routes.plugin.map',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:map'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_video',
|
||||
path: '/plugin/video',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '视频',
|
||||
i18nTitle: 'routes.plugin.video',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:video'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_editor',
|
||||
path: '/plugin/editor',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_editor_quill',
|
||||
path: '/plugin/editor/quill',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
i18nTitle: 'routes.plugin.editor.quill',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:file-document-edit-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_editor_markdown',
|
||||
path: '/plugin/editor/markdown',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'markdown编辑器',
|
||||
i18nTitle: 'routes.plugin.editor.markdown',
|
||||
requiresAuth: true,
|
||||
icon: 'ri:markdown-line'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '编辑器',
|
||||
i18nTitle: 'routes.plugin.editor._value',
|
||||
icon: 'icon-park-outline:editor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_swiper',
|
||||
path: '/plugin/swiper',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Swiper插件',
|
||||
i18nTitle: 'routes.plugin.swiper',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:swiper'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_copy',
|
||||
path: '/plugin/copy',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '剪贴板',
|
||||
i18nTitle: 'routes.plugin.copy',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:clipboard-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_icon',
|
||||
path: '/plugin/icon',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '图标',
|
||||
i18nTitle: 'routes.plugin.icon',
|
||||
requiresAuth: true,
|
||||
localIcon: 'custom-icon'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_print',
|
||||
path: '/plugin/print',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '打印',
|
||||
i18nTitle: 'routes.plugin.print',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:printer'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '插件示例',
|
||||
i18nTitle: 'routes.plugin._value',
|
||||
icon: 'clarity:plugin-line',
|
||||
order: 4
|
||||
}
|
||||
};
|
||||
|
||||
export default plugin;
|
@@ -1,78 +1,59 @@
|
||||
import { getLoginModuleRegExp } from '@/utils';
|
||||
import type { ElegantConstRoute, ElegantRoute, CustomRoute } from '@elegant-router/types';
|
||||
import { generatedRoutes } from '../elegant/routes';
|
||||
import { layouts, views } from '../elegant/imports';
|
||||
import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
|
||||
|
||||
/** 根路由: / */
|
||||
export const ROOT_ROUTE: AuthRoute.Route = {
|
||||
export const ROOT_ROUTE: CustomRoute = {
|
||||
name: 'root',
|
||||
path: '/',
|
||||
redirect: import.meta.env.VITE_ROUTE_HOME_PATH,
|
||||
redirect: '/home',
|
||||
meta: {
|
||||
title: 'Root'
|
||||
title: 'root',
|
||||
constant: true
|
||||
}
|
||||
};
|
||||
|
||||
/** 固定的路由 */
|
||||
export const constantRoutes: AuthRoute.Route[] = [
|
||||
const customRoutes: CustomRoute[] = [
|
||||
ROOT_ROUTE,
|
||||
{
|
||||
name: 'login',
|
||||
path: '/login',
|
||||
component: 'self',
|
||||
props: route => {
|
||||
const moduleType = (route.params.module as UnionKey.LoginModule) || 'pwd-login';
|
||||
return {
|
||||
module: moduleType
|
||||
};
|
||||
},
|
||||
meta: {
|
||||
title: '登录',
|
||||
dynamicPath: `/login/:module(${getLoginModuleRegExp()})?`,
|
||||
singleLayout: 'blank'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'constant-page',
|
||||
path: '/constant-page',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '固定页面',
|
||||
singleLayout: 'blank'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '403',
|
||||
path: '/403',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '无权限',
|
||||
singleLayout: 'blank'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '404',
|
||||
path: '/404',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '未找到',
|
||||
singleLayout: 'blank'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '500',
|
||||
path: '/500',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '服务器错误',
|
||||
singleLayout: 'blank'
|
||||
}
|
||||
},
|
||||
// 匹配无效路径的路由
|
||||
{
|
||||
name: 'not-found',
|
||||
path: '/:pathMatch(.*)*',
|
||||
component: 'blank',
|
||||
component: 'layout.blank$view.404',
|
||||
meta: {
|
||||
title: '未找到',
|
||||
singleLayout: 'blank'
|
||||
title: 'not-found',
|
||||
constant: true
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* create routes
|
||||
*/
|
||||
export function createRoutes() {
|
||||
const constantRoutes: ElegantRoute[] = [];
|
||||
|
||||
const authRoutes: ElegantRoute[] = [];
|
||||
|
||||
[...customRoutes, ...generatedRoutes].forEach(item => {
|
||||
if (item.meta?.constant) {
|
||||
constantRoutes.push(item);
|
||||
} else {
|
||||
authRoutes.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
const constantVueRoutes = transformElegantRoutesToVueRoutes(constantRoutes, layouts, views);
|
||||
|
||||
return {
|
||||
constantVueRoutes,
|
||||
authRoutes
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* get auth vue routes
|
||||
* @param routes elegant routes
|
||||
*/
|
||||
export function getAuthVueRoutes(routes: ElegantConstRoute[]) {
|
||||
return transformElegantRoutesToVueRoutes(routes, layouts, views);
|
||||
}
|
||||
|
Reference in New Issue
Block a user