140 lines
4.2 KiB
Swift
140 lines
4.2 KiB
Swift
//
|
||
// NavigationRouter.swift
|
||
// suixinkan
|
||
//
|
||
// Created by Codex on 2026/6/18.
|
||
//
|
||
|
||
import Combine
|
||
import SwiftUI
|
||
|
||
/// 应用内可导航目标,承载各 Tab 内部的路由目的地。
|
||
enum AppRoute: Hashable {
|
||
case placeholder(title: String)
|
||
case home(HomeRoute)
|
||
case profile(ProfileRoute)
|
||
case orders(OrdersRoute)
|
||
|
||
/// 返回该路由通过 NavigationStack push 后是否隐藏底部 TabBar。
|
||
var hidesTabBarWhenPushed: Bool {
|
||
true
|
||
}
|
||
|
||
/// 构建当前路由对应的目标页面。
|
||
@ViewBuilder
|
||
var destinationView: some View {
|
||
switch self {
|
||
case .placeholder(let title):
|
||
PlaceholderDetailView(title: title)
|
||
case .home(let route):
|
||
route.destinationView
|
||
case .profile(let route):
|
||
route.destinationView
|
||
case .orders(let route):
|
||
route.destinationView
|
||
}
|
||
}
|
||
}
|
||
|
||
extension OrdersRoute {
|
||
/// 构建订单模块二级路由对应的目标页面。
|
||
@ViewBuilder
|
||
var destinationView: some View {
|
||
switch self {
|
||
case .depositDetail(let orderNumber):
|
||
DepositOrderDetailView(orderNumber: orderNumber)
|
||
case .depositShootingInfo(let orderNumber, let scenicSpotId, let photogUid):
|
||
DepositOrderShootingInfoView(orderNumber: orderNumber, scenicSpotId: scenicSpotId, photogUid: photogUid)
|
||
case .historicalShooting(let orderNumber):
|
||
HistoricalShootingInfoView(orderNumber: orderNumber)
|
||
case .multiTravelTaskUpload(let orderNumber):
|
||
MultiTravelTaskUploadView(initialOrderNumber: orderNumber)
|
||
case .orderTrailer(let orderNumber, let title):
|
||
OrderTrailerView(orderNumber: orderNumber, title: title)
|
||
case .writeOffList:
|
||
WriteOffListView()
|
||
}
|
||
}
|
||
}
|
||
|
||
@MainActor
|
||
/// 单个 NavigationStack 的路径容器,用于保存某个 Tab 的导航历史。
|
||
final class RouterPath: ObservableObject {
|
||
@Published var path: [AppRoute] = []
|
||
|
||
/// 将指定路由压入当前 Tab 的导航栈。
|
||
func navigate(to route: AppRoute) {
|
||
path.append(route)
|
||
}
|
||
|
||
/// 清空当前 Tab 的导航栈。
|
||
func reset() {
|
||
path = []
|
||
}
|
||
}
|
||
|
||
@MainActor
|
||
/// 主导航状态中心,管理当前 Tab 和每个 Tab 独立的 NavigationStack 路径。
|
||
final class AppRouter: ObservableObject {
|
||
@Published var selectedTab: AppTab = .home
|
||
@Published var selectedOrdersEntry: OrdersEntry = .storeOrders
|
||
@Published private(set) var pendingOrderScanCode: String?
|
||
private let routers: [AppTab: RouterPath]
|
||
|
||
init() {
|
||
routers = Dictionary(
|
||
uniqueKeysWithValues: AppTab.allCases.map { tab in
|
||
(tab, RouterPath())
|
||
}
|
||
)
|
||
}
|
||
|
||
/// 获取指定 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
|
||
selectedTab = .orders
|
||
router(for: .orders).navigate(to: .orders(.writeOffList))
|
||
}
|
||
|
||
/// 打开核销管理页。
|
||
func openWriteOffList() {
|
||
selectedTab = .orders
|
||
router(for: .orders).navigate(to: .orders(.writeOffList))
|
||
}
|
||
|
||
/// 读取并清空待处理的全局扫码结果,确保同一次扫码只被处理一次。
|
||
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() }
|
||
}
|
||
}
|