This commit is contained in:
2025-12-16 20:20:53 +07:00
commit 2e651f1c89
315 changed files with 33529 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
/* 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"),
"iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
login: () => import("@/views/_builtin/login/index.vue"),
deposit_fiat: () => import("@/views/deposit/fiat/index.vue"),
home: () => import("@/views/home/index.vue"),
withdraw_fiat: () => import("@/views/withdraw/fiat/index.vue"),
};

View File

@@ -0,0 +1,120 @@
/* 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,
hideInMenu: true
}
},
{
name: '404',
path: '/404',
component: 'layout.blank$view.404',
meta: {
title: '404',
i18nKey: 'route.404',
constant: true,
hideInMenu: true
}
},
{
name: '500',
path: '/500',
component: 'layout.blank$view.500',
meta: {
title: '500',
i18nKey: 'route.500',
constant: true,
hideInMenu: true
}
},
{
name: 'deposit',
path: '/deposit',
component: 'layout.base',
meta: {
title: 'deposit',
i18nKey: 'route.deposit',
order: 2
},
children: [
{
name: 'deposit_fiat',
path: '/deposit/fiat',
component: 'view.deposit_fiat',
meta: {
title: 'deposit_fiat',
i18nKey: 'route.deposit_fiat'
}
}
]
},
{
name: 'home',
path: '/home',
component: 'layout.base$view.home',
meta: {
title: 'home',
i18nKey: 'route.home',
icon: 'mdi:monitor-dashboard',
order: 1
}
},
{
name: 'iframe-page',
path: '/iframe-page/:url',
component: 'layout.base$view.iframe-page',
props: true,
meta: {
title: 'iframe-page',
i18nKey: 'route.iframe-page',
constant: true,
hideInMenu: true,
keepAlive: true
}
},
{
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,
hideInMenu: true
}
},
{
name: 'withdraw',
path: '/withdraw',
component: 'layout.base',
meta: {
title: 'withdraw',
i18nKey: 'route.withdraw',
order: 3
},
children: [
{
name: 'withdraw_fiat',
path: '/withdraw/fiat',
component: 'view.withdraw_fiat',
meta: {
title: 'withdraw_fiat',
i18nKey: 'route.withdraw_fiat'
}
}
]
}
];

View File

@@ -0,0 +1,196 @@
/* 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) {
const layout = component.replace(LAYOUT_PREFIX, '');
if(!layouts[layout]) {
throw new Error(`Layout component "${layout}" not found`);
}
return layout;
}
function isView(component: string) {
return component.startsWith(VIEW_PREFIX);
}
function getViewName(component: string) {
const view = component.replace(VIEW_PREFIX, '');
if(!views[view]) {
throw new Error(`View component "${view}" not found`);
}
return view;
}
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;
try {
if (component) {
if (isSingleLevelRoute(route)) {
const { layout, view } = getSingleLevelRouteComponent(component);
const singleLevelRoute: RouteRecordRaw = {
path,
component: layouts[layout],
meta: {
title: route.meta?.title || ''
},
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];
}
}
} catch (error: any) {
console.error(`Error transforming route "${route.name}": ${error.toString()}`);
return [];
}
// 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",
"deposit": "/deposit",
"deposit_fiat": "/deposit/fiat",
"home": "/home",
"iframe-page": "/iframe-page/:url",
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
"withdraw": "/withdraw",
"withdraw_fiat": "/withdraw/fiat"
};
/**
* 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;
}

15
src/router/guard/index.ts Normal file
View File

@@ -0,0 +1,15 @@
import type { Router } from 'vue-router';
import { createRouteGuard } from './route';
import { createProgressGuard } from './progress';
import { createDocumentTitleGuard } from './title';
/**
* Router guard
*
* @param router - Router instance
*/
export function createRouterGuard(router: Router) {
createProgressGuard(router);
createRouteGuard(router);
createDocumentTitleGuard(router);
}

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?.();
});
}

192
src/router/guard/route.ts Normal file
View File

@@ -0,0 +1,192 @@
import type {
LocationQueryRaw,
NavigationGuardNext,
RouteLocationNormalized,
RouteLocationRaw,
Router
} 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';
import { getRouteName } from '@/router/elegant/transform';
/**
* create route guard
*
* @param router router instance
*/
export function createRouteGuard(router: Router) {
router.beforeEach(async (to, from, next) => {
const location = await initRoute(to);
if (location) {
next(location);
return;
}
const authStore = useAuthStore();
const rootRoute: RouteKey = 'root';
const loginRoute: RouteKey = 'login';
const noAuthorizationRoute: RouteKey = '403';
const isLogin = Boolean(localStg.get('token'));
const needLogin = !to.meta.constant;
const routeRoles = to.meta.roles || [];
const hasRole = authStore.userInfo.roles.some(role => routeRoles.includes(role));
const hasAuth = authStore.isStaticSuper || !routeRoles.length || hasRole;
// if it is login route when logged in, then switch to the root page
if (to.name === loginRoute && isLogin) {
next({ name: rootRoute });
return;
}
// if the route does not need login, then it is allowed to access directly
if (!needLogin) {
handleRouteSwitch(to, from, next);
return;
}
// the route need login but the user is not logged in, then switch to the login page
if (!isLogin) {
next({ name: loginRoute, query: { redirect: to.fullPath } });
return;
}
// if the user is logged in but does not have authorization, then switch to the 403 page
if (!hasAuth) {
next({ name: noAuthorizationRoute });
return;
}
// switch route normally
handleRouteSwitch(to, from, next);
});
}
/**
* initialize route
*
* @param to to route
*/
async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw | null> {
const routeStore = useRouteStore();
const notFoundRoute: RouteKey = 'not-found';
const isNotFoundRoute = to.name === notFoundRoute;
// if the constant route is not initialized, then initialize the constant route
if (!routeStore.isInitConstantRoute) {
await routeStore.initConstantRoute();
// the route is captured by the "not-found" route because the constant route is not initialized
// after the constant route is initialized, redirect to the original route
const path = to.fullPath;
const location: RouteLocationRaw = {
path,
replace: true,
query: to.query,
hash: to.hash
};
return location;
}
const isLogin = Boolean(localStg.get('token'));
if (!isLogin) {
// if the user is not logged in and the route is a constant route but not the "not-found" route, then it is allowed to access.
if (to.meta.constant && !isNotFoundRoute) {
routeStore.onRouteSwitchWhenNotLoggedIn();
return null;
}
// if the user is not logged in, then switch to the login page
const loginRoute: RouteKey = 'login';
const query = getRouteQueryOfLoginRoute(to, routeStore.routeHome);
const location: RouteLocationRaw = {
name: loginRoute,
query
};
return location;
}
if (!routeStore.isInitAuthRoute) {
// initialize the auth route
await routeStore.initAuthRoute();
// the route is captured by the "not-found" route because the auth route is not initialized
// after the auth route is initialized, redirect to the original route
if (isNotFoundRoute) {
const rootRoute: RouteKey = 'root';
const path = to.redirectedFrom?.name === rootRoute ? '/' : to.fullPath;
const location: RouteLocationRaw = {
path,
replace: true,
query: to.query,
hash: to.hash
};
return location;
}
}
routeStore.onRouteSwitchWhenLoggedIn();
// the auth route is initialized
// it is not the "not-found" route, then it is allowed to access
if (!isNotFoundRoute) {
return null;
}
// it is captured by the "not-found" route, then check whether the route exists
const exist = await routeStore.getIsAuthRouteExist(to.path as RoutePath);
const noPermissionRoute: RouteKey = '403';
if (exist) {
const location: RouteLocationRaw = {
name: noPermissionRoute
};
return location;
}
return null;
}
function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
// 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 });
return;
}
next();
}
function getRouteQueryOfLoginRoute(to: RouteLocationNormalized, routeHome: RouteKey) {
const loginRoute: RouteKey = 'login';
const redirect = to.fullPath;
const [redirectPath, redirectQuery] = redirect.split('?');
const redirectName = getRouteName(redirectPath as RoutePath);
const isRedirectHome = routeHome === redirectName;
const query: LocationQueryRaw = to.name !== loginRoute && !isRedirectHome ? { redirect } : {};
if (isRedirectHome && redirectQuery) {
query.redirect = `/?${redirectQuery}`;
}
return query;
}

13
src/router/guard/title.ts Normal file
View File

@@ -0,0 +1,13 @@
import type { Router } from 'vue-router';
import { useTitle } from '@vueuse/core';
import { $t } from '@/locales';
export function createDocumentTitleGuard(router: Router) {
router.afterEach(to => {
const { i18nKey, title } = to.meta;
const documentTitle = i18nKey ? $t(i18nKey) : title;
useTitle(documentTitle);
});
}

30
src/router/index.ts Normal file
View File

@@ -0,0 +1,30 @@
import type { App } from 'vue';
import {
type RouterHistory,
createMemoryHistory,
createRouter,
createWebHashHistory,
createWebHistory
} from 'vue-router';
import { createBuiltinVueRoutes } from './routes/builtin';
import { createRouterGuard } from './guard';
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
};
export const router = createRouter({
history: historyCreatorMap[VITE_ROUTER_HISTORY_MODE](VITE_BASE_URL),
routes: createBuiltinVueRoutes()
});
/** Setup Vue Router */
export async function setupRouter(app: App) {
app.use(router);
createRouterGuard(router);
await router.isReady();
}

View File

@@ -0,0 +1,31 @@
import type { CustomRoute } from '@elegant-router/types';
import { layouts, views } from '../elegant/imports';
import { getRoutePath, transformElegantRoutesToVueRoutes } from '../elegant/transform';
export const ROOT_ROUTE: CustomRoute = {
name: 'root',
path: '/',
redirect: getRoutePath(import.meta.env.VITE_ROUTE_HOME) || '/home',
meta: {
title: 'root',
constant: true
}
};
const NOT_FOUND_ROUTE: CustomRoute = {
name: 'not-found',
path: '/:pathMatch(.*)*',
component: 'layout.blank$view.404',
meta: {
title: 'not-found',
constant: true
}
};
/** builtin routes, it must be constant and setup in vue-router */
const builtinRoutes: CustomRoute[] = [ROOT_ROUTE, NOT_FOUND_ROUTE];
/** create builtin vue routes */
export function createBuiltinVueRoutes() {
return transformElegantRoutesToVueRoutes(builtinRoutes, layouts, views);
}

View File

@@ -0,0 +1,40 @@
import type { CustomRoute, ElegantConstRoute, ElegantRoute } from '@elegant-router/types';
import { generatedRoutes } from '../elegant/routes';
import { layouts, views } from '../elegant/imports';
import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
/**
* custom routes
*
* @link https://github.com/soybeanjs/elegant-router?tab=readme-ov-file#custom-route
*/
const customRoutes: CustomRoute[] = [];
/** create routes when the auth route mode is static */
export function createStaticRoutes() {
const constantRoutes: ElegantRoute[] = [];
const authRoutes: ElegantRoute[] = [];
[...customRoutes, ...generatedRoutes].forEach(item => {
if (item.meta?.constant) {
constantRoutes.push(item);
} else {
authRoutes.push(item);
}
});
return {
constantRoutes,
authRoutes
};
}
/**
* Get auth vue routes
*
* @param routes Elegant routes
*/
export function getAuthVueRoutes(routes: ElegantConstRoute[]) {
return transformElegantRoutesToVueRoutes(routes, layouts, views);
}