// // ReferralOrderSelectView.swift // suixinkan // // Created by Codex on 2026/6/29. // import SwiftUI import Combine /// 带客单全页选择 ViewModel,分步选择获客员与带客单并确认绑定。 @MainActor final class ReferralOrderSelectViewModel: ObservableObject { enum Step { case selectAcquirer case selectReferralOrder } @Published var step: Step = .selectAcquirer @Published var acquirers: [CooperativeSaler] = [] @Published var selectedAcquirer: CooperativeSaler? @Published var referralOrders: [ReferralOrder] = [] @Published var selectedReferralOrder: ReferralOrder? @Published var isLoadingMore = false @Published var canLoadMore = false @Published private(set) var hasLoadedAcquirersOnce = false @Published private(set) var hasLoadedReferralOrdersOnce = false private var orderNumber = "" private var currentPage = 1 private var totalCount = 0 private var isReloadingReferralOrders = false private let pageSize = 10 /// 初始化订单号并加载获客员列表。 func initOrder(orderNumber: String, api: CooperationOrderServing, storeId: Int?) async { if self.orderNumber == orderNumber && !acquirers.isEmpty { return } self.orderNumber = orderNumber await loadAcquirers(api: api, storeId: storeId) } private func loadAcquirers(api: CooperationOrderServing, storeId: Int?) async { do { let storeParam = storeId.flatMap { $0 > 0 ? $0 : nil } let data = try await api.cooperativeSalers(storeId: storeParam, page: 1, pageSize: 50) acquirers = data.list if data.list.count == 1 { await selectAcquirer(data.list[0], api: api) } else { step = .selectAcquirer } } catch { if !hasLoadedAcquirersOnce { acquirers = [] } } hasLoadedAcquirersOnce = true } /// 选择获客员并加载其带客单列表。 func selectAcquirer(_ acquirer: CooperativeSaler, api: CooperationOrderServing) async { selectedAcquirer = acquirer selectedReferralOrder = nil hasLoadedReferralOrdersOnce = false step = .selectReferralOrder await refreshReferralOrders(api: api) } /// 返回获客员选择步骤。 func backToAcquirerStep() { if acquirers.count <= 1 { return } step = .selectAcquirer selectedReferralOrder = nil referralOrders = [] } /// 选中带客单。 func selectReferralOrder(_ order: ReferralOrder) { selectedReferralOrder = order } /// 刷新带客单列表。 func refreshReferralOrders(api: CooperationOrderServing) async { guard let acquirer = selectedAcquirer else { return } guard !isReloadingReferralOrders else { return } isReloadingReferralOrders = true defer { isReloadingReferralOrders = false } currentPage = 1 isLoadingMore = false await loadReferralOrders(api: api, saleUserId: acquirer.displayId) } /// 加载更多带客单。 func loadMoreReferralOrders(api: CooperationOrderServing) async { guard !isReloadingReferralOrders, !isLoadingMore, canLoadMore, let acquirer = selectedAcquirer else { return } isLoadingMore = true currentPage += 1 await loadReferralOrders(api: api, saleUserId: acquirer.displayId) } private func loadReferralOrders(api: CooperationOrderServing, saleUserId: Int) async { do { let data = try await api.saleUserReferralOrders( saleUserId: saleUserId, page: currentPage, pageSize: pageSize ) totalCount = data.total if currentPage == 1 { referralOrders = data.list } else { referralOrders.append(contentsOf: data.list) } canLoadMore = referralOrders.count < totalCount } catch { if currentPage == 1, !hasLoadedReferralOrdersOnce { referralOrders = [] } if currentPage > 1 { currentPage = max(currentPage - 1, 1) } } if currentPage == 1 { hasLoadedReferralOrdersOnce = true } isLoadingMore = false } /// 确认绑定选中的带客单。 func confirmBind(api: CooperationOrderServing) async throws { guard let referralOrder = selectedReferralOrder else { throw NSError(domain: "ReferralOrder", code: 1, userInfo: [NSLocalizedDescriptionKey: "请选择带客单"]) } guard !orderNumber.isEmpty else { throw NSError(domain: "ReferralOrder", code: 2, userInfo: [NSLocalizedDescriptionKey: "订单号无效"]) } try await api.saleUserBindReferralOrder( orderNumber: orderNumber, referralOrderNo: referralOrder.displayReferralOrderNo ) } } /// 带客单全页选择视图,用于订单管理绑定订单来源。 struct ReferralOrderSelectView: View { @EnvironmentObject private var accountContext: AccountContext @EnvironmentObject private var toastCenter: ToastCenter @Environment(\.cooperationOrderAPI) private var cooperationOrderAPI @Environment(\.globalLoading) private var globalLoading @Environment(\.dismiss) private var dismiss @StateObject private var viewModel = ReferralOrderSelectViewModel() let orderNumber: String let onBound: () -> Void @State private var binding = false var body: some View { Group { if viewModel.step == .selectAcquirer { acquirerList } else { referralOrderList } } .navigationTitle(viewModel.step == .selectAcquirer ? "选择获客员" : "选择带客单") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button { if viewModel.step == .selectReferralOrder && viewModel.acquirers.count > 1 { viewModel.backToAcquirerStep() } else { dismiss() } } label: { Image(systemName: "chevron.backward") } } } .safeAreaInset(edge: .bottom) { if viewModel.step == .selectReferralOrder { Button { Task { await confirmBind() } } label: { Text(binding ? "提交中..." : "确认绑定") .font(.system(size: 16)) .foregroundStyle(.white) .frame(maxWidth: .infinity) .frame(height: 48) .background( viewModel.selectedReferralOrder != nil ? OrderSourceColors.primary : OrderSourceColors.primary.opacity(0.4), in: RoundedRectangle(cornerRadius: 8) ) } .buttonStyle(.plain) .disabled(viewModel.selectedReferralOrder == nil || binding) .padding(.horizontal, 16) .padding(.vertical, 10) .background(.white) } } .background(OrderSourceColors.pageBg.ignoresSafeArea()) .task { await globalLoading.withLoading { await viewModel.initOrder( orderNumber: orderNumber, api: cooperationOrderAPI, storeId: accountContext.currentStore?.id ) } if viewModel.acquirers.isEmpty { toastCenter.show("暂无合作获客员,请先绑定获客员") dismiss() } } } private func selectAcquirer(_ acquirer: CooperativeSaler) async { await globalLoading.withLoading { await viewModel.selectAcquirer(acquirer, api: cooperationOrderAPI) } } private var acquirerList: some View { ScrollView { LazyVStack(spacing: 12) { ForEach(viewModel.acquirers, id: \.displayId) { acquirer in AcquirerSelectCard( name: acquirer.displayName, phone: acquirer.displayPhone ) { Task { await selectAcquirer(acquirer) } } } } .padding(.horizontal, 14) .padding(.vertical, 12) } } private var referralOrderList: some View { ScrollView { LazyVStack(spacing: 12) { if let acquirer = viewModel.selectedAcquirer, !acquirer.displayName.isEmpty { Text("获客员:\(acquirer.displayName)") .font(.system(size: 13)) .foregroundStyle(OrderSourceColors.text666) .frame(maxWidth: .infinity, alignment: .leading) .padding(.bottom, 4) } if viewModel.hasLoadedReferralOrdersOnce && viewModel.referralOrders.isEmpty { Text("暂无可绑定带客单") .font(.system(size: 14)) .foregroundStyle(OrderSourceColors.text999) .frame(maxWidth: .infinity) .padding(.vertical, 32) } else { ForEach(Array(viewModel.referralOrders.enumerated()), id: \.element.id) { index, order in let selected = viewModel.selectedReferralOrder?.id == order.id ReferralOrderSelectCard( order: order, selected: selected ) { viewModel.selectReferralOrder(order) } .onAppear { guard index >= viewModel.referralOrders.count - 3 else { return } guard viewModel.canLoadMore, !viewModel.isLoadingMore else { return } Task { await viewModel.loadMoreReferralOrders(api: cooperationOrderAPI) } } } if viewModel.isLoadingMore { ProgressView() .tint(OrderSourceColors.primary) .frame(maxWidth: .infinity) .padding(.vertical, 16) } } } .padding(.horizontal, 14) .padding(.vertical, 12) } .refreshable { await globalLoading.withOptionalLoading(false) { await viewModel.refreshReferralOrders(api: cooperationOrderAPI) } } } private func confirmBind() async { binding = true defer { binding = false } do { try await viewModel.confirmBind(api: cooperationOrderAPI) onBound() dismiss() } catch { toastCenter.show(error.localizedDescription) } } } /// 订单号 Sheet 标识,用于导航到带客单选择页。 struct OrderNumberSheetItem: Identifiable { let orderNumber: String var id: String { orderNumber } }