// // NavigationRouter.swift // suixinkan // // Created by Codex on 2026/6/18. // import Foundation /// 应用内可导航目标,承载各 Tab 内部的路由目的地。 enum AppRoute: Hashable { case placeholder(title: String) case home(HomeRoute) case profile(ProfileRoute) case orders(OrdersRoute) /// 返回该路由通过 push 后是否隐藏底部 TabBar。 var hidesTabBarWhenPushed: Bool { true } } @MainActor /// 单个导航栈的路径容器,用于保存某个 Tab 的导航历史。 final class RouterPath { var onChange: (() -> Void)? var path: [AppRoute] = [] { didSet { onChange?() } } /// 将指定路由压入当前 Tab 的导航栈。 func navigate(to route: AppRoute) { path.append(route) } /// 从当前 Tab 的导航栈弹出最后一级路由。 func navigateBack() { guard !path.isEmpty else { return } path.removeLast() } /// 清空当前 Tab 的导航栈。 func reset() { path = [] } } @MainActor /// 主导航状态中心,管理当前 Tab 和每个 Tab 独立的导航路径。 final class AppRouter { var onChange: (() -> Void)? var selectedTab: AppTab = .home { didSet { onChange?() } } var selectedOrdersEntry: OrdersEntry = .storeOrders { didSet { onChange?() } } private(set) var pendingOrderScanCode: String? { didSet { onChange?() } } private let routers: [AppTab: RouterPath] init() { var builtRouters: [AppTab: RouterPath] = [:] for tab in AppTab.allCases { builtRouters[tab] = RouterPath() } routers = builtRouters routers.values.forEach { router in router.onChange = { [weak self] in self?.onChange?() } } } /// 获取指定 Tab 对应的路由路径容器。 func router(for tab: AppTab) -> RouterPath { guard let router = routers[tab] else { assertionFailure("Missing router path for tab: \(tab)") return RouterPath() } return router } /// 切换到指定主 Tab。 func select(_ tab: AppTab) { selectedTab = tab } /// 切换到订单 Tab,并指定订单内部子入口。 func selectOrders(entry: OrdersEntry) { selectedOrdersEntry = entry selectedTab = .orders } /// 切换到核销订单入口,并暂存全局扫码结果等待订单页消费。 func routeToOrderVerification(scannedCode: String) { pendingOrderScanCode = scannedCode selectOrders(entry: .verificationOrders) } /// 读取并清空待处理的全局扫码结果,确保同一次扫码只被处理一次。 func consumePendingOrderScanCode() -> String? { let code = pendingOrderScanCode pendingOrderScanCode = nil return code } /// 重置主 Tab 选择和所有 Tab 的导航历史。 func reset() { selectedTab = .home selectedOrdersEntry = .storeOrders pendingOrderScanCode = nil routers.values.forEach { $0.reset() } } }