refactor: modularize application domains

This commit is contained in:
kkfluous
2026-08-13 12:03:34 +08:00
parent d610e4b841
commit 06fe75b4c7
142 changed files with 14645 additions and 8885 deletions
+15 -141
View File
@@ -1,142 +1,29 @@
import { lazy, Suspense, useEffect, useMemo, useState } from "react";
import { Truck, Route, Activity, Fuel, BatteryCharging, Receipt, MapPinned } from "lucide-react";
import { Shell, type ModuleConfig } from "./components/Shell";
import { Shell } from "./components/Shell";
import AuthProvider from "./auth/AuthProvider";
import { useAuth } from "./auth/useAuth";
import UnauthorizedPage from "./auth/UnauthorizedPage";
import { canAccessScheduling, canAccessEnergy } from "./shared/auth/roles";
import { canAccessEnergy } from "./shared/auth/roles";
import { LoadingState, SkeletonBlock, SurfaceCard } from "./components/ui/surface";
import { buildModules } from "./app/modules";
import {
normalizeBrowserPath,
readBrowserRoute,
} from "./app/routing";
const AssetsModule = lazy(() => import("./modules/assets/AssetsModule"));
const MileageModule = lazy(() => import("./modules/mileage/MileageModule"));
const SchedulingModule = lazy(() => import("./modules/scheduling/SchedulingModule"));
const HydrogenModule = lazy(() => import("./modules/energy/HydrogenModule"));
const ElectricModule = lazy(() => import("./modules/energy/ElectricModule"));
const EtcModule = lazy(() => import("./modules/energy/EtcModule"));
const EleImportPage = lazy(() => import("./modules/ele/EleImportPage"));
const FeedbackAdminPage = lazy(() => import("./modules/admin/FeedbackAdminPage"));
const VehicleHeatmapModule = lazy(() => import("./modules/vehicle-heatmap/VehicleHeatmapModule"));
const HydrogenHeatmapModule = lazy(() => import("./modules/hydrogen-heatmap/HydrogenHeatmapModule"));
const ASSETS_MODULE: ModuleConfig = {
id: "assets",
label: "资产管理",
icon: Truck,
component: AssetsModule,
};
const MILEAGE_MODULE: ModuleConfig = {
id: "mileage",
label: "里程管理",
icon: Route,
component: MileageModule,
};
const VEHICLE_HEATMAP_MODULE: ModuleConfig = {
id: "vehicle-heatmap",
label: "车辆",
icon: MapPinned,
component: VehicleHeatmapModule,
};
const SCHEDULING_MODULE: ModuleConfig = {
id: "scheduling",
label: "智能调度",
icon: Activity,
component: SchedulingModule,
};
const HYDROGEN_MODULE: ModuleConfig = {
id: "hydrogen",
label: "氢能",
icon: Fuel,
component: HydrogenModule,
};
const HYDROGEN_HEATMAP_MODULE: ModuleConfig = {
id: "hydrogen-heatmap",
label: "加氢",
icon: MapPinned,
component: HydrogenHeatmapModule,
};
const ELECTRIC_MODULE: ModuleConfig = {
id: "electric",
label: "电能",
icon: BatteryCharging,
component: ElectricModule,
};
const ETC_MODULE: ModuleConfig = {
id: "etc",
label: "ETC",
icon: Receipt,
component: EtcModule,
};
/**
* 把旧路径 / 根路径归一化到 `/asset` 或 `/energy` 主路径,
* 必要时携带 hash 一段定位到具体模块。已是主路径或后台管理页则不动。
*/
function normalizePath() {
if (typeof window === "undefined") return;
const { pathname, hash, search } = window.location;
// 主路径 & 隐藏后台页保持不变
if (pathname === "/asset" || pathname === "/energy") return;
if (pathname === "/ele/import" || pathname === "/admin/feedback") return;
const legacyMap: Record<string, { path: string; hash?: string }> = {
"/": { path: "/asset" },
"/vehicle": { path: "/asset", hash: "assets" },
"/assets": { path: "/asset", hash: "assets" },
"/mileage": { path: "/asset", hash: "mileage" },
"/scheduling": { path: "/asset", hash: "scheduling" },
};
// 未知路径兜底到 /asset(保留原 hash 让 Shell 内部继续解析)
const target = legacyMap[pathname] ?? { path: "/asset" };
const finalHash = target.hash ? `#${target.hash}` : hash || "";
window.history.replaceState(null, "", `${target.path}${search}${finalHash}`);
}
normalizePath();
type PathSet = "asset" | "energy";
function getPathSet(): PathSet {
return window.location.pathname === "/energy" ? "energy" : "asset";
}
function getRouteKey(): string {
if (typeof window === "undefined") return "";
const path = window.location.pathname;
const hash = window.location.hash;
if (
path === "/ele/import" ||
hash === "#/ele/import" ||
hash === "#ele/import"
)
return "ele/import";
if (
path === "/admin/feedback" ||
hash === "#/admin/feedback" ||
hash === "#admin/feedback"
)
return "admin/feedback";
return "";
}
normalizeBrowserPath();
function AuthGate() {
const { isLoading, isAuthenticated, error, user } = useAuth();
const [routeKey, setRouteKey] = useState(getRouteKey);
const [pathSet, setPathSet] = useState<PathSet>(getPathSet);
const [route, setRoute] = useState(readBrowserRoute);
const { routeKey, pathSet } = route;
// 监听 hashchange / popstate,让 a href="#/..." 跳转与浏览器前进后退能即时生效
useEffect(() => {
const update = () => {
setRouteKey(getRouteKey());
setPathSet(getPathSet());
setRoute(readBrowserRoute());
};
window.addEventListener("hashchange", update);
window.addEventListener("popstate", update);
@@ -150,23 +37,10 @@ function AuthGate() {
document.title = pathSet === "energy" ? "羚牛氢能-能源BI" : "羚牛氢能-资产BI";
}, [pathSet]);
const modules = useMemo<ModuleConfig[]>(() => {
if (pathSet === "energy") {
return [HYDROGEN_MODULE, ELECTRIC_MODULE, ETC_MODULE];
}
const heatmapChildren = canAccessEnergy(user?.roles)
? [HYDROGEN_HEATMAP_MODULE, VEHICLE_HEATMAP_MODULE]
: [VEHICLE_HEATMAP_MODULE];
const heatmapModule: ModuleConfig = {
id: "heatmap",
label: "热力图",
icon: MapPinned,
children: heatmapChildren,
};
const result: ModuleConfig[] = [ASSETS_MODULE, MILEAGE_MODULE, heatmapModule];
if (canAccessScheduling(user?.roles)) result.push(SCHEDULING_MODULE);
return result;
}, [pathSet, user?.roles]);
const modules = useMemo(
() => buildModules(pathSet, user?.roles),
[pathSet, user?.roles],
);
if (isLoading) {
return (