import { Suspense, lazy, useEffect } from 'react' import { Spin } from 'antd' import { BrowserRouter, HashRouter, Navigate, Outlet, Route, Routes, useLocation, } from 'react-router' import { getAdminRole, hasAdminSession } from '@/utils/admin-auth' import { isClaimPath, isH5ClaimPath } from '@/utils/claim-path' import { isCollectMaterialLinkPath } from '@/utils/collect-link' import { isRedeemPath } from '@/utils/redeem' import { isWorkerSite, WORKER_SITE_ORIGIN } from '@/utils/site' import { hasWorkerSession } from '@/utils/worker-auth' import { resolveLegacyWorkerPath } from '@/utils/worker-routes' import AdminLayout from '@/layouts/AdminLayout' import WorkerLayout from '@/layouts/WorkerLayout' const AdminDashboardPage = lazy(() => import('@/pages/admin/AdminDashboardPage')) const AdminFinancePage = lazy(() => import('@/pages/admin/AdminFinancePage')) const AdminFinanceLedgerPage = lazy(() => import('@/pages/admin/AdminFinanceLedgerPage')) const AdminSalesProfitPage = lazy(() => import('@/pages/admin/AdminSalesProfitPage')) const AdminCostCenterPage = lazy(() => import('@/pages/admin/AdminCostCenterPage')) const AdminFulfillmentChannelPage = lazy(() => import('@/pages/admin/AdminFulfillmentChannelPage')) const AdminFundPage = lazy(() => import('@/pages/admin/AdminFundPage')) const AdminAuditLogsPage = lazy(() => import('@/pages/admin/AdminAuditLogsPage')) const AdminKuaishouIndustryPage = lazy(() => import('@/pages/admin/AdminKuaishouIndustryPage')) const AdminDouyinMashidaCertificatePage = lazy( () => import('@/pages/admin/AdminDouyinMashidaCertificatePage'), ) const AdminLoginPage = lazy(() => import('@/pages/admin/AdminLoginPage')) const AdminOrderDetailPage = lazy(() => import('@/pages/admin/AdminOrderDetailPage')) const AdminOrdersPage = lazy(() => import('@/pages/admin/AdminOrdersPage')) const AdminPlatformFulfillmentPage = lazy( () => import('@/pages/admin/platform/AdminPlatformFulfillmentPage'), ) const AdminPlatformShopsPage = lazy(() => import('@/pages/admin/platform/AdminPlatformShopsPage')) const AdminTaskDetailPage = lazy(() => import('@/pages/admin/AdminTaskDetailPage')) const AdminTasksPage = lazy(() => import('@/pages/admin/AdminTasksPage')) const AdminUsersPage = lazy(() => import('@/pages/admin/AdminUsersPage')) const AdminDevMockPage = lazy(() => import('@/pages/admin/AdminDevMockPage')) const AdminWorkerPlatformPage = lazy(() => import('@/pages/admin/AdminWorkerPlatformPage')) const ClaimPage = lazy(() => import('@/pages/claim/ClaimPage')) const H5ClaimEntryPage = lazy(() => import('@/pages/claim/H5ClaimEntryPage')) const CollectPage = lazy(() => import('@/pages/collect/CollectPage')) const CollectMaterialLinkEntryPage = lazy( () => import('@/pages/collect/CollectMaterialLinkEntryPage'), ) const CollectMaterialLinkPage = lazy(() => import('@/pages/collect/CollectMaterialLinkPage')) const NotFoundPage = lazy(() => import('@/pages/NotFoundPage')) const RedeemPage = lazy(() => import('@/pages/redeem/RedeemPage')) const WorkerHallPage = lazy(() => import('@/pages/worker/WorkerHallPage')) const WorkerLoginPage = lazy(() => import('@/pages/worker/WorkerLoginPage')) const WorkerOrdersPage = lazy(() => import('@/pages/worker/WorkerOrdersPage')) const WorkerAfterSalesPage = lazy(() => import('@/pages/worker/WorkerAfterSalesPage')) const WorkerConsignmentPage = lazy(() => import('@/pages/worker/WorkerConsignmentPage')) const WorkerProfilePage = lazy(() => import('@/pages/worker/WorkerProfilePage')) function RequireAdmin() { const location = useLocation() if (!hasAdminSession()) { return } return } function RequireRole({ roles }: { roles: Array<'admin' | 'operator' | 'support' | 'finance'> }) { if (!roles.includes(getAdminRole())) { return ( ) } return } function LoginRoute() { if (hasAdminSession()) { return } return } function RequireWorker() { const location = useLocation() if (!hasWorkerSession()) { return } return } export default function App() { const legacyWorkerPath = resolveLegacyWorkerPath(window.location.hash) if (legacyWorkerPath) { return } // 路径式领取链接(`https://<域名>/claim/`,不带 `#`)复用主站现成的领取页: // 买家输 UID → 出二维码 → 扫码绑定 → 比对 UID → 发货完成。 // 必须早于站点判断,用独立的 BrowserRouter 分支承载,主站 HashRouter 不受影响。 // 注意:主站打手领取页是 HashRouter 里的 `#/claim/:token`,它的 pathname 是 `/`, // 不会命中这里(判定见 utils/claim-path.isClaimPath),两个入口互不影响。 if (isClaimPath(window.location.pathname)) { return } // H5 领取入口(`https://<域名>/h5` 及其子路径,不带 `#`)。 // 判定是**命名空间级**:`/h5` 本身(只有前缀、没有订单号)也算命中。 // 否则 `https://<域名>/h5` 会落回主站 HashRouter,被后台路由接管成 // `https://<域名>/h5/#/admin/dashboard` 后台登录页(买家点领取链接看到后台登录,历史缺陷)。 if (isH5ClaimPath(window.location.pathname)) { return } // 固定兑换链接是路径式(`https://<域名>/redeem`,不带 `#`),必须早于站点判断, // 用独立的 BrowserRouter 分支承载,主站 HashRouter 不受影响。 if (isRedeemPath(window.location.pathname)) { return } // 补资料页(`/supplement`):路径式公开页,先于站点判断, // 保证在后台域名和打手端域名下都能打开。带订单号直接进补资料页,不带订单号进输入页。 if (isCollectMaterialLinkPath(window.location.pathname)) { return } if (isWorkerSite()) { return } return } function AdminApplication() { return ( }> } /> } /> }> }> } /> }> } /> } /> } /> }> } /> } /> }> } /> } /> } /> } /> } /> }> } /> } /> {/* 旧「配置 → 对接客服」地址已并入接单平台 tab,书签继续可用。 */} } /> }> } /> } /> } /> }> } /> } /> } /> } /> } /> } /> } /> ) } function RedeemApplication() { return ( }> } /> } /> ) } /** * 路径式领取页(`/claim/`)的独立路由树。 * * 复用主站 HashRouter 里的**同一个** `ClaimPage`:它只依赖 `useParams` 取 token, * 内部不 navigate、也不读 hash,所以换成 BrowserRouter 承载不需要复制页面。 * * `isClaimPath` 是命名空间判定(`/claim` 及子路径都命中),所以必须保留兜底路由: * 只有前缀(`/claim`)或多段路径(`/claim/a/b`)时 `ClaimPage` 取不到 token, * 由页面自己展示「链接无效」,既不会路由空屏,也不会漏回后台路由。 */ function ClaimApplication() { useClearStrayHash() return ( }> } /> } /> ) } /** * 补资料页(`/supplement`)的独立路由树。 * * - `/supplement`:输入平台订单号; * - `/supplement/<订单号>`:直接打开对应订单的补资料页; * - 其余子路径:交给补资料页展示「订单号无效」。 * * 路径式公开页,不带 hash;不依赖后台会话。 */ function CollectMaterialLinkApplication() { useClearStrayHash() return ( }> } /> } /> } /> ) } /** * H5 领取入口(`/h5` 命名空间)的独立路由树。 * * - `/h5`:只有前缀、没有订单号 → 入口页(买家补订单号 / 粘贴完整链接); * - `/h5/<订单号>`:与分享链接完全相同的领取流程; * - 其余子路径(如 `/h5/a/b`):没有订单号可领,回入口页重新输入, * 避免"前缀下面任何一条路径"漏回后台路由。 * * 全程路径式(不带 `#`):后台 HashRouter 永远拿不到 `/h5` 这几个路径。 */ function H5ClaimApplication() { useClearStrayHash() return ( }> } /> } /> } /> ) } /** * 公开领取页是**路径式**的、从不使用 hash 路由,但历史链接可能带着后台残留的 hash * (例如 `https://<域名>/h5#/admin/dashboard`)。 * 挂载时把 hash 抹掉,避免买家在地址栏看到、并转发一条"看着像后台地址"的领取链接。 */ function useClearStrayHash() { useEffect(() => { if (!window.location.hash) return const { pathname, search } = window.location window.history.replaceState(window.history.state, '', `${pathname}${search}`) }, []) } function WorkerApplication() { return ( }> } /> }> }> } /> } /> } /> } /> } /> } /> ) } function WorkerSiteRedirect({ path }: { path: string }) { useEffect(() => { window.location.replace(`${WORKER_SITE_ORIGIN}${path}`) }, [path]) return } function RouteLoading() { return (
) }