feat(projects): 1.0 beta

This commit is contained in:
Soybean
2023-11-17 08:45:00 +08:00
parent 1ea4817f6a
commit e918a2c0f5
499 changed files with 15918 additions and 24708 deletions

View 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"),
};

View 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
}
}
];

View 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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View 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
View 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);
});
}

View File

@@ -1 +0,0 @@
export * from './scroll';

View File

@@ -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);
});
};

View File

@@ -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';

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,5 +0,0 @@
import { handleModuleRoutes } from '@/utils';
const modules = import.meta.glob('./**/*.ts', { eager: true }) as AuthRoute.RouteModule;
export const routes = handleModuleRoutes(modules);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}