// // CustomMainTabBar.swift // suixinkan // // Created by Codex on 2026/6/23. // import SwiftUI /// 自定义主 TabBar,展示四个一级入口和中间扫码核销按钮。 struct CustomMainTabBar: View { @Binding var selectedTab: AppTab let orderBadgeText: String? let onScanTap: () -> Void private let items = CustomMainTabItem.defaultItems var body: some View { HStack(alignment: .center, spacing: 0) { CustomMainTabBarItem( item: items[0], selectedTab: $selectedTab, badgeText: nil ) CustomMainTabBarItem( item: items[1], selectedTab: $selectedTab, badgeText: orderBadgeText ) CustomMainTabBarCenterAction(onScanTap: onScanTap) .frame(width: 74) CustomMainTabBarItem( item: items[2], selectedTab: $selectedTab, badgeText: nil ) CustomMainTabBarItem( item: items[3], selectedTab: $selectedTab, badgeText: nil ) } .frame(maxWidth: .infinity) .frame(height: 72) .padding(.horizontal, 22) .background(.white) .overlay(alignment: .top) { Rectangle() .fill(Color.black.opacity(0.04)) .frame(height: 0.5) } } } /// 自定义 TabBar 单个入口的静态配置。 private struct CustomMainTabItem { let tab: AppTab let title: String let selectedImage: String let unselectedImage: String static let defaultItems: [CustomMainTabItem] = [ .init(tab: .home, title: "首页", selectedImage: "TabHomeSelected", unselectedImage: "TabHomeUnselected"), .init(tab: .orders, title: "订单", selectedImage: "TabOrderSelected", unselectedImage: "TabOrderUnselected"), .init(tab: .statistics, title: "数据", selectedImage: "TabDataSelected", unselectedImage: "TabDataUnselected"), .init(tab: .profile, title: "我的", selectedImage: "TabProfileSelected", unselectedImage: "TabProfileUnselected") ] } /// 自定义 TabBar 的普通入口按钮。 private struct CustomMainTabBarItem: View { let item: CustomMainTabItem @Binding var selectedTab: AppTab let badgeText: String? private var isSelected: Bool { selectedTab == item.tab } var body: some View { Button { selectedTab = item.tab } label: { VStack(spacing: 3) { ZStack(alignment: .topTrailing) { Image(isSelected ? item.selectedImage : item.unselectedImage) .renderingMode(.original) .resizable() .scaledToFit() .frame(width: 24, height: 24) if let badgeText { CustomMainTabBadge(text: badgeText) } } Text(item.title) .font(.system(size: 12, weight: isSelected ? .medium : .regular)) .foregroundStyle(isSelected ? AppDesign.primary : Color(hex: 0x7D8DA3)) .lineLimit(1) .minimumScaleFactor(0.8) } .frame(maxWidth: .infinity) .frame(height: 58) .contentShape(Rectangle()) } .buttonStyle(.plain) .accessibilityLabel(item.title) .accessibilityIdentifier("main.tab.\(item.tab.rawValue)") } } /// 自定义 TabBar 中间的扫码核销按钮。 private struct CustomMainTabBarCenterAction: View { let onScanTap: () -> Void var body: some View { Button(action: onScanTap) { ZStack { Circle() .fill(AppDesign.primary) .frame(width: 58, height: 58) Image(systemName: "qrcode.viewfinder") .font(.system(size: 30, weight: .bold)) .foregroundStyle(.white) } .frame(width: 70, height: 64) .contentShape(Circle()) } .buttonStyle(.plain) .accessibilityLabel("扫码核销") .accessibilityIdentifier("main.scan") } } /// 自定义 TabBar 的订单数量角标。 private struct CustomMainTabBadge: View { let text: String var body: some View { Text(text) .font(.system(size: 9, weight: .bold)) .foregroundStyle(.white) .frame(minWidth: 15, minHeight: 15) .padding(.horizontal, 2) .background(Color(hex: 0xEF4444), in: Capsule()) .offset(x: 9, y: -7) } } #Preview { CustomMainTabBar( selectedTab: .constant(.orders), orderBadgeText: "8", onScanTap: {} ) }