文件
hyb-oa/client/src/router/index.tsx
T
wangsli 58dcee08f7
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s
fix: 用专用权限收窄经理 AP 访问(09-07-manager-ap-access-scope)
2026-09-08 07:21:37 +08:00

744 行
22 KiB
TypeScript

import { Suspense, type ReactNode } from "react";
import { createBrowserRouter, createRoutesFromElements, Navigate, Outlet, Route } from "react-router-dom";
import { AuthProvider } from "../auth/AuthContext";
import { getManagementRoutePermissions } from "../features/navigation/managementRoutes";
import { RoutePageFallback } from "../components/common/RoutePageFallback";
import { UnsavedChangesProvider } from "../components/common/UnsavedChangesGuard";
import { AppShellLayout } from "../layouts/AppShellLayout";
import { AuthLayout } from "../layouts/AuthLayout";
import { BudgetsPage } from "../pages/BudgetsPage";
import { ChangePasswordPage } from "../pages/ChangePasswordPage";
import { DashboardPage } from "../pages/DashboardPage";
import { ForbiddenPage } from "../pages/ForbiddenPage";
import { ForgotPasswordPage } from "../pages/ForgotPasswordPage";
import { LoginPage } from "../pages/LoginPage";
import { NotFoundPage } from "../pages/NotFoundPage";
import { ProfilePage } from "../pages/ProfilePage";
import {
ApOperationsPage,
ApprovalRulesPage,
AnnouncementsPage,
AttendancePage,
AssetCreatePage,
AssetDetailPage,
AssetRegistryPage,
AuditLogsPage,
CutoverConsolePage,
EmployeeProfilePage,
DelegationsPage,
DepartmentsPage,
ExpenseStandardsPage,
FeatureFlagsPage,
InventoryChecksPage,
InvoicePoolPage,
LeaveRequestPage,
MasterDataPage,
MobileApprovalsPage,
MobileWorkflowTaskRoutePage,
MobileAssetScanPage,
MobileCapturePage,
MobileLeavePage,
MobileMePage,
MobileNotificationsPage,
MobilePortalPage,
MobileProfilePage,
MobilePurchaseRequestDetailPage,
MobilePurchaseRequestsPage,
MobileReimbursementDetailPage,
MobileReimbursementsPage,
NotificationChannelsPage,
NotificationsPage,
OrganizationPage,
PayrollPage,
PerformancePage,
PaymentBatchesPage,
PaymentAccountsPage,
PeoplePage,
PeopleStatsPage,
PortalAnnouncementsPage,
PortalHomePage,
ProjectsPage,
ProjectDetailPage,
ContractsRegistryPage,
ContractRegistryCreatePage,
ContractRegistryDetailPage,
SystemSettingsPage,
TeamPresencePage,
TodosPage,
PurchaseRequestsPage,
PurchaseRequestDetailPage,
ReimbursementDetailPage,
ReimbursementDashboardPage,
ReimbursementPrintPage,
ReimbursementTemplatesPage,
ReimbursementsPage,
TravelRequestsPage,
UserManagementPage,
WecomApprovalArchivePage,
WeeklyReportsPage,
WorkspaceSetupPage,
WorkflowDefinitionsPage,
WorkflowTaskRoutePage,
WorkflowTasksPage,
} from "./lazy";
import { GuestOnly, RequireAuth, RequireBusinessApprover, RequirePermission } from "./guards";
function withRouteFallback(node: ReactNode) {
return <Suspense fallback={<RoutePageFallback />}>{node}</Suspense>;
}
function RouterRoot() {
return (
<UnsavedChangesProvider>
<AuthProvider>
<Outlet />
</AuthProvider>
</UnsavedChangesProvider>
);
}
const routeElements = (
<Route element={<RouterRoot />}>
<Route
path="/login"
element={
<GuestOnly>
<AuthLayout variant="login" />
</GuestOnly>
}
>
<Route index element={<LoginPage />} />
</Route>
<Route
path="/forgot-password"
element={
<GuestOnly>
<AuthLayout />
</GuestOnly>
}
>
<Route index element={<ForgotPasswordPage />} />
</Route>
<Route
path="/change-password"
element={
<RequireAuth>
<AuthLayout />
</RequireAuth>
}
>
<Route index element={<ChangePasswordPage />} />
</Route>
<Route
path="/"
element={
<RequireAuth>
<AppShellLayout />
</RequireAuth>
}
>
<Route index element={<Navigate to="/portal" replace />} />
<Route path="portal" element={withRouteFallback(<PortalHomePage />)} />
<Route path="announcements" element={withRouteFallback(<AnnouncementsPage />)} />
<Route path="team" element={withRouteFallback(<TeamPresencePage />)} />
<Route
path="projects"
element={
<RequirePermission anyOf={["project.view_own", "project.view_all", "project.manage", "system.settings"]}>
{withRouteFallback(<ProjectsPage />)}
</RequirePermission>
}
/>
<Route
path="projects/:projectId"
element={
<RequirePermission anyOf={["project.view_own", "project.view_all", "project.manage", "system.settings"]}>
{withRouteFallback(<ProjectDetailPage />)}
</RequirePermission>
}
/>
<Route
path="contracts-registry"
element={
<RequirePermission anyOf={["project.view_own", "project.view_all", "project.manage", "system.settings"]}>
{withRouteFallback(<ContractsRegistryPage />)}
</RequirePermission>
}
/>
<Route
path="contracts-registry/new"
element={
<RequirePermission anyOf={["project.view_all", "project.manage", "system.settings"]}>
{withRouteFallback(<ContractRegistryCreatePage />)}
</RequirePermission>
}
/>
<Route
path="contracts-registry/:contractId"
element={
<RequirePermission anyOf={["project.view_own", "project.view_all", "project.manage", "system.settings"]}>
{withRouteFallback(<ContractRegistryDetailPage />)}
</RequirePermission>
}
/>
<Route path="weekly-reports" element={withRouteFallback(<WeeklyReportsPage />)} />
<Route path="todos" element={withRouteFallback(<TodosPage />)} />
<Route path="dashboard" element={<DashboardPage />} />
<Route
path="budgets"
element={
<RequirePermission anyOf={["budget.view", "budget.manage"]}>
<BudgetsPage />
</RequirePermission>
}
/>
<Route
path="attendance"
element={withRouteFallback(<AttendancePage />)}
/>
<Route
path="wecom-approval-archive"
element={
<RequirePermission anyOf={["leave.view_own", "attendance.view_own", "leave.view_all", "attendance.view_all", "*"]}>
{withRouteFallback(<WecomApprovalArchivePage />)}
</RequirePermission>
}
/>
<Route
path="reimbursements"
element={
<RequirePermission anyOf={["expense.view_own", "expense.view_all"]}>
{withRouteFallback(<ReimbursementsPage />)}
</RequirePermission>
}
/>
<Route
path="reimbursements/dashboard"
element={
<RequirePermission anyOf={["expense.view_all"]}>
{withRouteFallback(<ReimbursementDashboardPage />)}
</RequirePermission>
}
/>
<Route
path="system/reimbursement-templates"
element={
<RequirePermission anyOf={getManagementRoutePermissions("reimbursement_templates")}>
{withRouteFallback(<ReimbursementTemplatesPage />)}
</RequirePermission>
}
/>
<Route
path="reimbursements/:reimbursementId"
element={
<RequirePermission anyOf={["expense.view_own", "expense.view_all"]}>
{withRouteFallback(<ReimbursementDetailPage />)}
</RequirePermission>
}
/>
<Route
path="reimbursements/:reimbursementId/print"
element={
<RequirePermission anyOf={["expense.view_own", "expense.view_all"]}>
{withRouteFallback(<ReimbursementPrintPage />)}
</RequirePermission>
}
/>
<Route
path="leave-requests"
element={
<RequirePermission anyOf={["leave.view_own", "leave.view_all"]}>
{withRouteFallback(<LeaveRequestPage />)}
</RequirePermission>
}
/>
<Route
path="purchase-requests"
element={
<RequirePermission
anyOf={["procurement.create", "procurement.view_own", "procurement.view_all", "procurement.manage"]}
>
{withRouteFallback(<PurchaseRequestsPage />)}
</RequirePermission>
}
/>
<Route
path="purchase-requests/:purchaseRequestId"
element={
<RequirePermission anyOf={["procurement.create", "procurement.view_own", "procurement.view_all", "procurement.manage"]}>
{withRouteFallback(<PurchaseRequestDetailPage />)}
</RequirePermission>
}
/>
<Route
path="travel-requests"
element={
<RequirePermission
anyOf={[
"travel.create",
"travel.view_own",
"travel.view_all",
"travel.manage",
"travel.payment.manage",
"integration.manage",
]}
>
{withRouteFallback(<TravelRequestsPage />)}
</RequirePermission>
}
/>
<Route
path="payment-batches"
element={
<RequirePermission anyOf={["payment.batch.manage", "risk.review.manage", "payment.batch.review"]}>
{withRouteFallback(<PaymentBatchesPage />)}
</RequirePermission>
}
/>
<Route
path="ap-operations"
element={
<RequirePermission anyOf={["finance.ledger.view"]}>
{withRouteFallback(<ApOperationsPage />)}
</RequirePermission>
}
/>
<Route
path="assets"
element={
<RequirePermission
anyOf={["asset.view_own", "asset.view_all", "asset.manage", "asset.transfer", "asset.inventory"]}
>
{withRouteFallback(<AssetRegistryPage />)}
</RequirePermission>
}
/>
<Route
path="assets/new"
element={
<RequirePermission anyOf={["asset.manage"]}>
{withRouteFallback(<AssetCreatePage />)}
</RequirePermission>
}
/>
<Route
path="assets/:assetId"
element={
<RequirePermission
anyOf={["asset.view_own", "asset.view_all", "asset.manage", "asset.transfer", "asset.inventory"]}
>
{withRouteFallback(<AssetDetailPage />)}
</RequirePermission>
}
/>
<Route
path="inventory-checks"
element={
<RequirePermission anyOf={["asset.inventory"]}>
{withRouteFallback(<InventoryChecksPage />)}
</RequirePermission>
}
/>
<Route
path="workflow/tasks"
element={
<RequireBusinessApprover>
{withRouteFallback(<WorkflowTasksPage />)}
</RequireBusinessApprover>
}
/>
<Route
path="workflow/tasks/:taskId"
element={
<RequireBusinessApprover>
{withRouteFallback(<WorkflowTaskRoutePage />)}
</RequireBusinessApprover>
}
/>
<Route
path="invoice-pool"
element={
<RequirePermission anyOf={["invoice.upload", "invoice.view_own", "invoice.manage"]}>
{withRouteFallback(<InvoicePoolPage />)}
</RequirePermission>
}
/>
<Route path="mobile" element={withRouteFallback(<MobilePortalPage />)} />
<Route
path="mobile/attendance"
element={
<RequirePermission anyOf={["attendance.view_own", "attendance.view_all", "*"]}>
{withRouteFallback(<AttendancePage />)}
</RequirePermission>
}
/>
<Route path="mobile/me" element={withRouteFallback(<MobileMePage />)} />
<Route path="mobile/profile" element={withRouteFallback(<MobileProfilePage />)} />
<Route path="mobile/notifications" element={withRouteFallback(<MobileNotificationsPage />)} />
<Route
path="mobile/payment-batches"
element={
<RequirePermission anyOf={["payment.batch.manage", "risk.review.manage", "payment.batch.review"]}>
{withRouteFallback(<PaymentBatchesPage />)}
</RequirePermission>
}
/>
<Route
path="mobile/wecom-approval-archive"
element={
<RequirePermission anyOf={["leave.view_own", "attendance.view_own", "leave.view_all", "attendance.view_all", "*"]}>
{withRouteFallback(<WecomApprovalArchivePage />)}
</RequirePermission>
}
/>
<Route
path="mobile/capture"
element={
<RequirePermission anyOf={["invoice.upload", "invoice.manage"]}>
{withRouteFallback(<MobileCapturePage />)}
</RequirePermission>
}
/>
<Route
path="mobile/reimbursements"
element={
<RequirePermission anyOf={["expense.view_own", "expense.view_all"]}>
{withRouteFallback(<MobileReimbursementsPage />)}
</RequirePermission>
}
/>
<Route
path="mobile/reimbursements/:reimbursementId"
element={
<RequirePermission anyOf={["expense.view_own", "expense.view_all"]}>
{withRouteFallback(<MobileReimbursementDetailPage />)}
</RequirePermission>
}
/>
<Route
path="mobile/leave"
element={
<RequirePermission anyOf={["leave.view_own", "leave.view_all"]}>
{withRouteFallback(<MobileLeavePage />)}
</RequirePermission>
}
/>
<Route
path="mobile/leave/:leaveRequestId"
element={
<RequirePermission anyOf={["leave.view_own", "leave.view_all"]}>
{withRouteFallback(<MobileLeavePage />)}
</RequirePermission>
}
/>
<Route
path="mobile/purchase-requests"
element={
<RequirePermission
anyOf={["procurement.create", "procurement.view_own", "procurement.view_all", "procurement.manage"]}
>
{withRouteFallback(<MobilePurchaseRequestsPage />)}
</RequirePermission>
}
/>
<Route
path="mobile/purchase-requests/:purchaseRequestId"
element={
<RequirePermission
anyOf={["procurement.create", "procurement.view_own", "procurement.view_all", "procurement.manage"]}
>
{withRouteFallback(<MobilePurchaseRequestDetailPage />)}
</RequirePermission>
}
/>
<Route
path="mobile/assets/scan"
element={
<RequirePermission
anyOf={["asset.view_own", "asset.view_all", "asset.manage", "asset.transfer", "asset.inventory"]}
>
{withRouteFallback(<MobileAssetScanPage />)}
</RequirePermission>
}
/>
<Route
path="mobile/approvals"
element={
<RequirePermission
anyOf={[
"expense.view_own",
"leave.view_own",
"procurement.view_own",
"travel.view_own",
"expense.approve",
"leave.approve",
"procurement.approve",
"travel.approve",
]}
>
{withRouteFallback(<MobileApprovalsPage />)}
</RequirePermission>
}
/>
<Route
path="mobile/approvals/:taskId"
element={
<RequirePermission
anyOf={[
"expense.view_own",
"leave.view_own",
"procurement.view_own",
"travel.view_own",
"expense.approve",
"leave.approve",
"procurement.approve",
"travel.approve",
]}
>
{withRouteFallback(<MobileWorkflowTaskRoutePage />)}
</RequirePermission>
}
/>
<Route path="notifications" element={withRouteFallback(<NotificationsPage />)} />
<Route
path="audit-logs"
element={
<RequirePermission anyOf={getManagementRoutePermissions("audit_logs")}>
{withRouteFallback(<AuditLogsPage />)}
</RequirePermission>
}
/>
<Route
path="delegations"
element={
<RequirePermission anyOf={["expense.proxy_manage"]}>
{withRouteFallback(<DelegationsPage />)}
</RequirePermission>
}
/>
<Route
path="payroll"
element={
<RequirePermission anyOf={["payroll.manage"]}>
{withRouteFallback(<PayrollPage />)}
</RequirePermission>
}
/>
<Route
path="performance"
element={
<RequirePermission anyOf={getManagementRoutePermissions("performance")}>
{withRouteFallback(<PerformancePage />)}
</RequirePermission>
}
/>
<Route
path="organization"
element={
<RequirePermission anyOf={getManagementRoutePermissions("organization")}>
{withRouteFallback(<OrganizationPage />)}
</RequirePermission>
}
/>
<Route
path="people"
element={
<RequirePermission anyOf={getManagementRoutePermissions("people")}>
{withRouteFallback(<PeoplePage />)}
</RequirePermission>
}
/>
<Route
path="people/stats"
element={
<RequirePermission anyOf={getManagementRoutePermissions("people_stats")}>
{withRouteFallback(<PeopleStatsPage />)}
</RequirePermission>
}
/>
<Route
path="people/:employeeId"
element={
<RequirePermission anyOf={getManagementRoutePermissions("employee_profile")}>
{withRouteFallback(<EmployeeProfilePage />)}
</RequirePermission>
}
/>
<Route
path="system/users"
element={
<RequirePermission anyOf={getManagementRoutePermissions("user_accounts")}>
{withRouteFallback(<UserManagementPage />)}
</RequirePermission>
}
/>
<Route
path="system/departments"
element={
<RequirePermission anyOf={getManagementRoutePermissions("departments")}>
{withRouteFallback(<DepartmentsPage />)}
</RequirePermission>
}
/>
<Route
path="system/master-data"
element={
<RequirePermission anyOf={getManagementRoutePermissions("company_information")}>
{withRouteFallback(<MasterDataPage />)}
</RequirePermission>
}
/>
<Route
path="system/payment-accounts"
element={
<RequirePermission anyOf={getManagementRoutePermissions("payment_accounts")}>
{withRouteFallback(<PaymentAccountsPage />)}
</RequirePermission>
}
/>
<Route
path="system/notification-channels"
element={
<RequirePermission anyOf={getManagementRoutePermissions("notification_channels")}>
{withRouteFallback(<NotificationChannelsPage />)}
</RequirePermission>
}
/>
<Route
path="system/workspace"
element={
<RequirePermission anyOf={getManagementRoutePermissions("workspace")}>
{withRouteFallback(<WorkspaceSetupPage />)}
</RequirePermission>
}
/>
<Route
path="system/settings"
element={
<RequirePermission anyOf={getManagementRoutePermissions("management")}>
{withRouteFallback(<SystemSettingsPage />)}
</RequirePermission>
}
/>
<Route
path="system/approval-rules"
element={
<RequirePermission anyOf={getManagementRoutePermissions("approval_rules")}>
{withRouteFallback(<ApprovalRulesPage />)}
</RequirePermission>
}
/>
<Route
path="system/expense-standards"
element={
<RequirePermission anyOf={getManagementRoutePermissions("expense_standards")}>
{withRouteFallback(<ExpenseStandardsPage />)}
</RequirePermission>
}
/>
<Route
path="system/feature-flags"
element={
<RequirePermission anyOf={getManagementRoutePermissions("feature_flags")}>
{withRouteFallback(<FeatureFlagsPage />)}
</RequirePermission>
}
/>
<Route
path="system/announcements"
element={
<RequirePermission anyOf={getManagementRoutePermissions("system_announcements")}>
{withRouteFallback(<PortalAnnouncementsPage />)}
</RequirePermission>
}
/>
<Route
path="system/workflow-definitions"
element={
<RequirePermission anyOf={getManagementRoutePermissions("workflow_definitions")}>
{withRouteFallback(<WorkflowDefinitionsPage />)}
</RequirePermission>
}
/>
<Route
path="system/cutover"
element={
<RequirePermission anyOf={getManagementRoutePermissions("cutover")}>
{withRouteFallback(<CutoverConsolePage />)}
</RequirePermission>
}
/>
<Route path="settings/profile" element={<ProfilePage />} />
</Route>
<Route path="/403" element={<ForbiddenPage />} />
<Route path="*" element={<NotFoundPage />} />
</Route>
);
export const appRouter = createBrowserRouter(createRoutesFromElements(routeElements));