Add custom main tab bar with global scan-to-verify flow.
Introduce configurable custom TabBar with order badge, central scanner routing to verification orders, and update the migration checklist for recently completed modules. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
155
suixinkan/Features/Main/Views/CustomMainTabBar.swift
Normal file
155
suixinkan/Features/Main/Views/CustomMainTabBar.swift
Normal file
@ -0,0 +1,155 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 自定义 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("扫码核销")
|
||||
}
|
||||
}
|
||||
|
||||
/// 自定义 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: {}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user