全新的 ui,soybean admin
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/* 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"),
|
||||
headphone_brand: () => import("@/views/headphone/brand/index.vue"),
|
||||
headphone_model: () => import("@/views/headphone/model/index.vue"),
|
||||
home: () => import("@/views/home/index.vue"),
|
||||
"share-code_log": () => import("@/views/share-code/log/index.vue"),
|
||||
system_users: () => import("@/views/system/users/index.vue"),
|
||||
"toolbox_luxsin-controller": () => import("@/views/toolbox/luxsin-controller/index.vue"),
|
||||
upgrade_blacklist: () => import("@/views/upgrade/blacklist/index.vue"),
|
||||
"upgrade_ota-target-device": () => import("@/views/upgrade/ota-target-device/index.vue"),
|
||||
upgrade_ota: () => import("@/views/upgrade/ota/index.vue"),
|
||||
};
|
||||
@@ -0,0 +1,233 @@
|
||||
/* 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: 'headphone',
|
||||
path: '/headphone',
|
||||
component: 'layout.base',
|
||||
meta: {
|
||||
title: 'headphone',
|
||||
i18nKey: 'route.headphone',
|
||||
icon: 'mdi:headphones',
|
||||
order: 2
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'headphone_brand',
|
||||
path: '/headphone/brand',
|
||||
component: 'view.headphone_brand',
|
||||
meta: {
|
||||
title: 'headphone_brand',
|
||||
i18nKey: 'route.headphone_brand',
|
||||
icon: 'mdi:tag-multiple-outline',
|
||||
order: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'headphone_model',
|
||||
path: '/headphone/model',
|
||||
component: 'view.headphone_model',
|
||||
meta: {
|
||||
title: 'headphone_model',
|
||||
i18nKey: 'route.headphone_model',
|
||||
icon: 'mdi:earbuds',
|
||||
order: 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
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: 'share-code',
|
||||
path: '/share-code',
|
||||
component: 'layout.base',
|
||||
meta: {
|
||||
title: 'share-code',
|
||||
i18nKey: 'route.share-code',
|
||||
icon: 'mdi:share-variant-outline',
|
||||
order: 4
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'share-code_log',
|
||||
path: '/share-code/log',
|
||||
component: 'view.share-code_log',
|
||||
meta: {
|
||||
title: 'share-code_log',
|
||||
i18nKey: 'route.share-code_log',
|
||||
icon: 'mdi:file-document-outline',
|
||||
order: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'system',
|
||||
path: '/system',
|
||||
component: 'layout.base',
|
||||
meta: {
|
||||
title: 'system',
|
||||
i18nKey: 'route.system',
|
||||
icon: 'mdi:cog-outline',
|
||||
order: 5,
|
||||
hideInMenu: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'system_users',
|
||||
path: '/system/users',
|
||||
component: 'view.system_users',
|
||||
meta: {
|
||||
title: 'system_users',
|
||||
i18nKey: 'route.system_users',
|
||||
icon: 'mdi:account-cog-outline',
|
||||
order: 1,
|
||||
roles: ['R_SUPER']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'toolbox',
|
||||
path: '/toolbox',
|
||||
component: 'layout.base',
|
||||
meta: {
|
||||
title: 'toolbox',
|
||||
i18nKey: 'route.toolbox',
|
||||
icon: 'mdi:toolbox-outline',
|
||||
order: 6
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'toolbox_luxsin-controller',
|
||||
path: '/toolbox/luxsin-controller',
|
||||
component: 'view.toolbox_luxsin-controller',
|
||||
meta: {
|
||||
title: 'toolbox_luxsin-controller',
|
||||
i18nKey: 'route.toolbox_luxsin-controller',
|
||||
icon: 'mdi:tune-vertical',
|
||||
order: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'upgrade',
|
||||
path: '/upgrade',
|
||||
component: 'layout.base',
|
||||
meta: {
|
||||
title: 'upgrade',
|
||||
i18nKey: 'route.upgrade',
|
||||
icon: 'mdi:package-up',
|
||||
order: 3
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'upgrade_blacklist',
|
||||
path: '/upgrade/blacklist',
|
||||
component: 'view.upgrade_blacklist',
|
||||
meta: {
|
||||
title: 'upgrade_blacklist',
|
||||
i18nKey: 'route.upgrade_blacklist',
|
||||
icon: 'mdi:account-cancel-outline',
|
||||
order: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'upgrade_ota',
|
||||
path: '/upgrade/ota',
|
||||
component: 'view.upgrade_ota',
|
||||
meta: {
|
||||
title: 'upgrade_ota',
|
||||
i18nKey: 'route.upgrade_ota',
|
||||
icon: 'mdi:cellphone-arrow-down',
|
||||
order: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'upgrade_ota-target-device',
|
||||
path: '/upgrade/ota-target-device',
|
||||
component: 'view.upgrade_ota-target-device',
|
||||
meta: {
|
||||
title: 'upgrade_ota-target-device',
|
||||
i18nKey: 'route.upgrade_ota-target-device',
|
||||
icon: 'mdi:crosshairs-gps',
|
||||
order: 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,205 @@
|
||||
/* 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",
|
||||
"headphone": "/headphone",
|
||||
"headphone_brand": "/headphone/brand",
|
||||
"headphone_model": "/headphone/model",
|
||||
"home": "/home",
|
||||
"iframe-page": "/iframe-page/:url",
|
||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
|
||||
"share-code": "/share-code",
|
||||
"share-code_log": "/share-code/log",
|
||||
"system": "/system",
|
||||
"system_users": "/system/users",
|
||||
"toolbox": "/toolbox",
|
||||
"toolbox_luxsin-controller": "/toolbox/luxsin-controller",
|
||||
"upgrade": "/upgrade",
|
||||
"upgrade_blacklist": "/upgrade/blacklist",
|
||||
"upgrade_ota": "/upgrade/ota",
|
||||
"upgrade_ota-target-device": "/upgrade/ota-target-device"
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { Router } from 'vue-router';
|
||||
|
||||
export function createProgressGuard(router: Router) {
|
||||
router.beforeEach(() => {
|
||||
window.NProgress?.start?.();
|
||||
return;
|
||||
});
|
||||
router.afterEach(() => {
|
||||
window.NProgress?.done?.();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
import type { LocationQueryRaw, 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) => {
|
||||
const location = await initRoute(to);
|
||||
|
||||
if (location) {
|
||||
return location;
|
||||
}
|
||||
|
||||
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) {
|
||||
return { name: rootRoute };
|
||||
}
|
||||
|
||||
// if the route does not need login, then it is allowed to access directly
|
||||
if (!needLogin) {
|
||||
return handleRouteSwitch(to, from);
|
||||
}
|
||||
|
||||
// the route need login but the user is not logged in, then switch to the login page
|
||||
if (!isLogin) {
|
||||
return { name: loginRoute, query: { redirect: to.fullPath } };
|
||||
}
|
||||
|
||||
// if the user is logged in but does not have authorization, then switch to the 403 page
|
||||
if (!hasAuth) {
|
||||
return { name: noAuthorizationRoute };
|
||||
}
|
||||
|
||||
// switch route normally
|
||||
return handleRouteSwitch(to, from);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// route with href
|
||||
if (to.meta.href) {
|
||||
window.open(to.meta.href, '_blank');
|
||||
|
||||
return { path: from.fullPath, replace: true, query: from.query, hash: to.hash };
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Layout from '@/layout/index.vue'
|
||||
import { getToken, clearAuth, isTokenExpired, isSuperAdmin } from '@/utils/auth'
|
||||
import { HOME_PATH } from '@/utils/tabs'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: () => import('@/views/login/index.vue'),
|
||||
meta: { requiresAuth: false }
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: Layout,
|
||||
meta: { requiresAuth: true },
|
||||
redirect: HOME_PATH,
|
||||
children: [
|
||||
{
|
||||
path: HOME_PATH,
|
||||
name: 'Home',
|
||||
component: () => import('@/views/home/index.vue'),
|
||||
meta: { title: '首页', icon: 'home' }
|
||||
},
|
||||
{
|
||||
path: '/brand',
|
||||
name: 'Brand',
|
||||
component: () => import('@/views/brand/index.vue'),
|
||||
meta: { title: '品牌管理', icon: 'collection' }
|
||||
},
|
||||
{
|
||||
path: '/model',
|
||||
name: 'Model',
|
||||
component: () => import('@/views/model/index.vue'),
|
||||
meta: { title: '型号管理', icon: 'list' }
|
||||
},
|
||||
{
|
||||
path: '/ota',
|
||||
name: 'Ota',
|
||||
component: () => import('@/views/ota/index.vue'),
|
||||
meta: { title: 'OTA 管理', icon: 'file-text' }
|
||||
},
|
||||
{
|
||||
path: '/blacklist',
|
||||
name: 'BlackList',
|
||||
component: () => import('@/views/blacklist/index.vue'),
|
||||
meta: { title: '黑名单管理', icon: 'user-x' }
|
||||
},
|
||||
{
|
||||
path: '/ota-target-device',
|
||||
name: 'OtaTargetDevice',
|
||||
component: () => import('@/views/ota-target-device/index.vue'),
|
||||
meta: { title: '定向升级', icon: 'crosshair' }
|
||||
},
|
||||
{
|
||||
path: '/share-code/log',
|
||||
name: 'ShareCodeLog',
|
||||
component: () => import('@/views/share-code/log.vue'),
|
||||
meta: { title: '分享日志', icon: 'document' }
|
||||
},
|
||||
{
|
||||
path: '/system/users',
|
||||
name: 'SystemUsers',
|
||||
component: () => import('@/views/system/users/index.vue'),
|
||||
meta: { title: '账号管理', icon: 'user', requiresSuperAdmin: true }
|
||||
},
|
||||
{
|
||||
path: '/toolbox/luxsin-controller',
|
||||
name: 'LuxsinController',
|
||||
component: () => import('@/views/toolbox/luxsin-controller/index.vue'),
|
||||
meta: { title: 'luxsin 控制器', icon: 'sliders-horizontal' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
router.beforeEach((to, _from, next) => {
|
||||
const needAuth = to.matched.some((r) => r.meta && r.meta.requiresAuth === true)
|
||||
const token = getToken()
|
||||
|
||||
if (needAuth) {
|
||||
if (!token || isTokenExpired(token)) {
|
||||
if (token) clearAuth()
|
||||
next({ path: '/login', query: { redirect: to.fullPath } })
|
||||
return
|
||||
}
|
||||
|
||||
const needSuperAdmin = to.matched.some((r) => r.meta && r.meta.requiresSuperAdmin === true)
|
||||
if (needSuperAdmin && !isSuperAdmin()) {
|
||||
next({ path: HOME_PATH })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (to.path === '/login' && token && !isTokenExpired(token)) {
|
||||
next({ path: HOME_PATH })
|
||||
return
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user