91 lines
3.0 KiB
TypeScript
91 lines
3.0 KiB
TypeScript
|
|
import { useEffect, useMemo, useState } from "react";
|
||
|
|
import { Navigate, type RouteObject, useRoutes } from "react-router-dom";
|
||
|
|
import { fetchDynamicRoutes } from "../api/upms";
|
||
|
|
import { LoadingView } from "../components/LoadingView";
|
||
|
|
import { DefaultLayout } from "../layouts/DefaultLayout";
|
||
|
|
import { SidebarLayout } from "../layouts/SidebarLayout";
|
||
|
|
import { DashboardPage } from "../pages/DashboardPage";
|
||
|
|
import { LoginPage } from "../pages/LoginPage";
|
||
|
|
import { NotFoundPage } from "../pages/NotFoundPage";
|
||
|
|
import { RoutePlaceholderPage } from "../pages/RoutePlaceholderPage";
|
||
|
|
import type { RouteNode } from "../types/route";
|
||
|
|
|
||
|
|
const layoutRegistry = {
|
||
|
|
DEFAULT: DefaultLayout,
|
||
|
|
SIDEBAR: SidebarLayout
|
||
|
|
};
|
||
|
|
|
||
|
|
function toChildRoute(route: RouteNode): RouteObject {
|
||
|
|
const Component = route.component === "dashboard" ? DashboardPage : RoutePlaceholderPage;
|
||
|
|
|
||
|
|
return {
|
||
|
|
path: route.path === "/" ? "" : route.path.replace(/^\//, ""),
|
||
|
|
element: <Component />
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function buildRoutes(dynamicRoutes: RouteNode[]): RouteObject[] {
|
||
|
|
const grouped = dynamicRoutes.reduce<Record<string, RouteNode[]>>((acc, route) => {
|
||
|
|
acc[route.layout] ??= [];
|
||
|
|
acc[route.layout].push(route);
|
||
|
|
return acc;
|
||
|
|
}, {});
|
||
|
|
|
||
|
|
const layoutRoutes = Object.entries(grouped).map(([layout, routes]) => {
|
||
|
|
const Layout = layoutRegistry[layout as keyof typeof layoutRegistry] ?? DefaultLayout;
|
||
|
|
return {
|
||
|
|
path: "/",
|
||
|
|
element: <Layout />,
|
||
|
|
children: routes.map(toChildRoute)
|
||
|
|
} satisfies RouteObject;
|
||
|
|
});
|
||
|
|
|
||
|
|
return [
|
||
|
|
{ path: "/login", element: <LoginPage /> },
|
||
|
|
...layoutRoutes,
|
||
|
|
{ path: "*", element: <NotFoundPage /> }
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
export function AppRouter() {
|
||
|
|
const [dynamicRoutes, setDynamicRoutes] = useState<RouteNode[]>([]);
|
||
|
|
const [loading, setLoading] = useState(true);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
fetchDynamicRoutes()
|
||
|
|
.then((routes) => setDynamicRoutes(routes))
|
||
|
|
.catch(() =>
|
||
|
|
setDynamicRoutes([
|
||
|
|
{
|
||
|
|
id: "dashboard",
|
||
|
|
path: "/",
|
||
|
|
name: "dashboard",
|
||
|
|
component: "dashboard",
|
||
|
|
layout: "SIDEBAR",
|
||
|
|
meta: {
|
||
|
|
title: "控制台",
|
||
|
|
hidden: false,
|
||
|
|
permissionCodes: ["dashboard:view"]
|
||
|
|
},
|
||
|
|
children: []
|
||
|
|
}
|
||
|
|
])
|
||
|
|
)
|
||
|
|
.finally(() => setLoading(false));
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const routes = useMemo(() => {
|
||
|
|
if (loading) {
|
||
|
|
return [{ path: "*", element: <LoadingView message="正在加载路由..." /> }];
|
||
|
|
}
|
||
|
|
|
||
|
|
const nextRoutes = buildRoutes(dynamicRoutes);
|
||
|
|
if (dynamicRoutes.length === 0) {
|
||
|
|
nextRoutes.unshift({ path: "/", element: <Navigate to="/login" replace /> });
|
||
|
|
}
|
||
|
|
return nextRoutes;
|
||
|
|
}, [dynamicRoutes, loading]);
|
||
|
|
|
||
|
|
return useRoutes(routes);
|
||
|
|
}
|