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 (
)
}