// // MainTabsView.swift // suixinkan // // Created by Codex on 2026/6/18. // import SwiftUI /// 主 TabBar 样式,决定登录后首页使用系统 TabView 还是自定义底部栏。 enum MainTabBarStyle { case system case custom } /// 主 TabBar 配置,后续需要切回系统 TabView 时只修改 activeStyle。 enum MainTabBarConfiguration { static let activeStyle: MainTabBarStyle = .custom } /// 主 Tab 容器视图,根据配置选择系统 TabView 或自定义 TabBar 外壳。 struct MainTabsView: View { var body: some View { switch MainTabBarConfiguration.activeStyle { case .system: SystemMainTabsView() case .custom: CustomMainTabsView() } } } /// 系统 TabView 外壳,保留原生 TabBar 行为以便后续切换回系统实现。 private struct SystemMainTabsView: View { @Environment(AppRouter.self) private var appRouter var body: some View { @Bindable var appRouter = appRouter TabView(selection: $appRouter.selectedTab) { ForEach(AppTab.allCases) { tab in TabNavigationStackHost(tab: tab) .tabItem { tab.label } .tag(tab) } } } } /// 自定义 TabBar 外壳,复用每个 Tab 独立 NavigationStack 并保留已访问页面状态。 private struct CustomMainTabsView: View { @Environment(AccountContext.self) private var accountContext @Environment(AppRouter.self) private var appRouter @Environment(OrdersAPI.self) private var ordersAPI @Environment(ToastCenter.self) private var toastCenter @State private var badgeViewModel = MainTabBadgeViewModel() @State private var showScanner = false @State private var loadedTabs: Set = [.home] private var orderBadgeText: String? { guard let count = badgeViewModel.pendingWriteOffCount, count > 0 else { return nil } return "\(count)" } var body: some View { @Bindable var appRouter = appRouter ZStack { ForEach(AppTab.allCases) { tab in PreservedTabPage( tab: tab, selectedTab: appRouter.selectedTab, isLoaded: loadedTabs.contains(tab) ) { CustomTabNavigationStackHost( tab: tab, selectedTab: $appRouter.selectedTab, orderBadgeText: orderBadgeText, onScanTap: { showScanner = true } ) } } } .ignoresSafeArea(.keyboard, edges: .bottom) .fullScreenCover(isPresented: $showScanner) { OrderScannerPage( onClose: { showScanner = false }, onSuccess: { rawCode in showScanner = false appRouter.routeToOrderVerification(scannedCode: rawCode) }, onFailure: { error in showScanner = false toastCenter.show(error.localizedDescription) } ) } .task { loadedTabs.insert(appRouter.selectedTab) await refreshOrderBadge() } .onChange(of: appRouter.selectedTab) { _, selectedTab in loadedTabs.insert(selectedTab) guard selectedTab == .orders else { return } Task { await refreshOrderBadge() } } .onChange(of: accountContext.currentScenic?.id) { _, _ in Task { await refreshOrderBadge() } } .onChange(of: accountContext.currentStore?.id) { _, _ in Task { await refreshOrderBadge() } } } /// 刷新订单 Tab 待核销角标数量。 private func refreshOrderBadge() async { await badgeViewModel.refreshPendingWriteOffCount( api: ordersAPI, scenicId: accountContext.currentScenic?.id, storeId: accountContext.currentStore?.id ) } } /// 自定义 TabBar 模式下的单个 Tab 导航栈,TabBar 只属于根页面内容。 private struct CustomTabNavigationStackHost: View { @Environment(AppRouter.self) private var appRouter let tab: AppTab @Binding var selectedTab: AppTab let orderBadgeText: String? let onScanTap: () -> Void var body: some View { NavigationStack(path: appRouter.binding(for: tab)) { VStack(spacing: 0) { tab.rootView .frame(maxWidth: .infinity, maxHeight: .infinity) CustomMainTabBar( selectedTab: $selectedTab, orderBadgeText: orderBadgeText, onScanTap: onScanTap ) } .ignoresSafeArea(.keyboard, edges: .bottom) .navigationTitle(tab.title) .navigationDestination(for: AppRoute.self) { route in route.destinationView .toolbar(route.hidesTabBarWhenPushed ? .hidden : .visible, for: .tabBar) } } .environment(appRouter.router(for: tab)) } } /// 单个 Tab 对应的导航栈,供系统和自定义 TabBar 外壳共同复用。 private struct TabNavigationStackHost: View { @Environment(AppRouter.self) private var appRouter let tab: AppTab var body: some View { NavigationStack(path: appRouter.binding(for: tab)) { tab.rootView .navigationTitle(tab.title) .navigationDestination(for: AppRoute.self) { route in route.destinationView .toolbar(route.hidesTabBarWhenPushed ? .hidden : .visible, for: .tabBar) } } .environment(appRouter.router(for: tab)) } } /// 自定义 TabBar 中保持已加载 Tab 页面存活的容器。 private struct PreservedTabPage: View { let tab: AppTab let selectedTab: AppTab let isLoaded: Bool @ViewBuilder let content: () -> Content var body: some View { if isLoaded { content() .frame(maxWidth: .infinity, maxHeight: .infinity) .opacity(selectedTab == tab ? 1 : 0) .zIndex(selectedTab == tab ? 1 : 0) .allowsHitTesting(selectedTab == tab) .accessibilityHidden(selectedTab != tab) } } } #Preview { MainTabsView() .environment(AppRouter()) .environment(AccountContext()) .environment(OrdersAPI(client: APIClient())) .environment(ToastCenter()) }