将扫码页提升到列表层弹出并补齐 AppDelegate.window,避免扫码时 UIKit 取主窗口崩溃;Toast 改用独立 UIWindow 显示在 sheet 之上;合作获客员支持短信验证修改分成比例,同时优化冷启动根视图创建时机并补充相关测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
144 lines
5.6 KiB
Swift
144 lines
5.6 KiB
Swift
//
|
||
// CooperationOrderListView.swift
|
||
// suixinkan
|
||
//
|
||
// Created by Codex on 2026/6/29.
|
||
//
|
||
|
||
import SwiftUI
|
||
|
||
/// 合作订单主页,展示获客线索与获客订单双 Tab 列表。
|
||
struct CooperationOrderListView: View {
|
||
@EnvironmentObject private var toastCenter: ToastCenter
|
||
@Environment(\.cooperationOrderAPI) private var cooperationOrderAPI
|
||
@Environment(\.globalLoading) private var globalLoading
|
||
@StateObject private var viewModel = CooperationOrderListViewModel()
|
||
@State private var route: CooperationOrderRoute?
|
||
/// 扫码绑定页在列表层弹出,避免在 navigationDestination 子页内嵌 fullScreenCover 触发 SwiftUI 栈溢出。
|
||
@State private var showBindScanner = false
|
||
|
||
var body: some View {
|
||
VStack(spacing: 0) {
|
||
CooperationOrderTabs(
|
||
selectedTab: viewModel.selectedTab,
|
||
onTabSelected: { viewModel.onTabSelected($0) }
|
||
)
|
||
|
||
if viewModel.selectedTab == 1 {
|
||
CooperationOrderSearchBar(searchText: $viewModel.searchText) {
|
||
Task { await searchOrders(showLoading: true) }
|
||
}
|
||
}
|
||
|
||
ScrollView {
|
||
LazyVStack(spacing: 12) {
|
||
if viewModel.selectedTab == 0 {
|
||
if viewModel.hasLoadedLeadsOnce && viewModel.leads.isEmpty {
|
||
emptyState("暂无获客员线索")
|
||
} else {
|
||
ForEach(Array(viewModel.leads.enumerated()), id: \.element.id) { index, lead in
|
||
ReferralLeadCard(lead: lead)
|
||
.onAppear {
|
||
guard index >= viewModel.leads.count - 3 else { return }
|
||
Task { await viewModel.loadMore(api: cooperationOrderAPI) }
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if viewModel.hasLoadedOrdersOnce && viewModel.orders.isEmpty {
|
||
emptyState("暂无相关合作订单")
|
||
} else {
|
||
ForEach(Array(viewModel.orders.enumerated()), id: \.element.id) { index, order in
|
||
AcquisitionOrderCard(order: order)
|
||
.onAppear {
|
||
guard index >= viewModel.orders.count - 3 else { return }
|
||
Task { await viewModel.loadMore(api: cooperationOrderAPI) }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if viewModel.isLoadingMore {
|
||
ProgressView()
|
||
.tint(CooperationOrderColors.primary)
|
||
.frame(width: 24, height: 24)
|
||
.padding(16)
|
||
}
|
||
}
|
||
.padding(.horizontal, 14)
|
||
.padding(.vertical, 12)
|
||
}
|
||
.refreshable {
|
||
await reloadCurrentTab(showLoading: false)
|
||
}
|
||
}
|
||
.navigationTitle("合作订单")
|
||
.navigationBarTitleDisplayMode(.inline)
|
||
.toolbar {
|
||
ToolbarItem(placement: .topBarTrailing) {
|
||
Button("合作获客员") {
|
||
route = .acquirer
|
||
}
|
||
.font(.system(size: 14))
|
||
.foregroundStyle(CooperationOrderColors.primary)
|
||
}
|
||
}
|
||
.background(CooperationOrderColors.pageBg.ignoresSafeArea())
|
||
.fullScreenCover(isPresented: $showBindScanner) {
|
||
OrderScannerPage(
|
||
onClose: { showBindScanner = false },
|
||
onSuccess: { raw in
|
||
showBindScanner = false
|
||
guard let saleUserId = SaleUserQrParser.parseSaleUserId(raw), saleUserId > 0 else {
|
||
toastCenter.show("请扫描正确的获客员二维码")
|
||
return
|
||
}
|
||
route = .bind(saleUserId: saleUserId)
|
||
},
|
||
onFailure: { error in
|
||
showBindScanner = false
|
||
toastCenter.show(error.localizedDescription)
|
||
}
|
||
)
|
||
}
|
||
.appNavigationDestination(item: $route) { destination in
|
||
switch destination {
|
||
case .acquirer:
|
||
CooperationAcquirerView(onScanBind: {
|
||
showBindScanner = true
|
||
})
|
||
case let .bind(saleUserId):
|
||
BindAcquirerView(saleUserId: saleUserId, onSuccess: {
|
||
route = .acquirer
|
||
})
|
||
}
|
||
}
|
||
.task {
|
||
await reloadCurrentTab(showLoading: true)
|
||
}
|
||
.onChange(of: viewModel.selectedTab) { _ in
|
||
Task { await reloadCurrentTab(showLoading: true) }
|
||
}
|
||
}
|
||
|
||
private func reloadCurrentTab(showLoading: Bool) async {
|
||
await globalLoading.withOptionalLoading(showLoading) {
|
||
await viewModel.refreshCurrentTab(api: cooperationOrderAPI)
|
||
}
|
||
}
|
||
|
||
private func searchOrders(showLoading: Bool) async {
|
||
await globalLoading.withOptionalLoading(showLoading) {
|
||
await viewModel.searchOrders(api: cooperationOrderAPI)
|
||
}
|
||
}
|
||
|
||
private func emptyState(_ text: String) -> some View {
|
||
Text(text)
|
||
.font(.system(size: 14))
|
||
.foregroundStyle(CooperationOrderColors.text999)
|
||
.frame(maxWidth: .infinity)
|
||
.padding(.vertical, 48)
|
||
}
|
||
}
|