// // CooperationAcquirerView.swift // suixinkan // // Created by Codex on 2026/6/29. // import SwiftUI /// 合作获客员列表页,支持查看已绑定获客员与扫码绑定。 struct CooperationAcquirerView: View { @EnvironmentObject private var accountContext: AccountContext @EnvironmentObject private var toastCenter: ToastCenter @Environment(\.cooperationOrderAPI) private var cooperationOrderAPI @Environment(\.globalLoading) private var globalLoading @Environment(\.openURL) private var openURL @StateObject private var viewModel = CooperationAcquirerViewModel() @State private var showScanner = false @State private var showRemarkAlert = false @State private var remarkEditingAcquirer: CooperativeSaler? @State private var remarkDraft = "" let onBind: (Int) -> Void var body: some View { ScrollView { LazyVStack(spacing: 12) { if viewModel.hasLoadedOnce && viewModel.acquirers.isEmpty { Text("暂无合作获客员") .font(.system(size: 14)) .foregroundStyle(CooperationOrderColors.text999) .frame(maxWidth: .infinity) .padding(.vertical, 48) } else { ForEach(viewModel.acquirers, id: \.displayId) { acquirer in CooperativeSalerCard(acquirer: acquirer) { phone in dialPhone(phone) } onEditRemark: { remarkEditingAcquirer = acquirer remarkDraft = acquirer.photogRemarkName showRemarkAlert = true } } } } .padding(.horizontal, 14) .padding(.vertical, 12) } .navigationTitle("合作获客员") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("扫码绑定") { showScanner = true } .font(.system(size: 14)) .foregroundStyle(CooperationOrderColors.primary) } } .background(CooperationOrderColors.pageBg.ignoresSafeArea()) .refreshable { await reload(showLoading: false) } .fullScreenCover(isPresented: $showScanner) { OrderScannerPage( onClose: { showScanner = false }, onSuccess: { raw in showScanner = false if let saleUserId = SaleUserQrParser.parseSaleUserId(raw) { onBind(saleUserId) } else { toastCenter.show("请扫描正确的获客员二维码") } }, onFailure: { error in showScanner = false toastCenter.show(error.localizedDescription) } ) } .task { await reload(showLoading: true) } .alert("修改备注", isPresented: $showRemarkAlert) { TextField("备注名", text: $remarkDraft) Button("取消", role: .cancel) { remarkEditingAcquirer = nil } Button("保存") { // 必须在弹窗关闭前同步捕获,避免 alert dismiss 清空 state 导致接口未调用。 guard let acquirer = remarkEditingAcquirer else { return } if let message = CooperationOrderRemarkName.validationMessage(for: remarkDraft) { toastCenter.show(message) return } let saleUserId = acquirer.saleUserId let draft = remarkDraft showRemarkAlert = false remarkEditingAcquirer = nil Task { await saveRemark(saleUserId: saleUserId, draft: draft) } } } message: { if let acquirer = remarkEditingAcquirer { Text("为 \(acquirer.salerRealName.cooperationNonEmptyOrDefault("—")) 设置备注名") } } } /// 保存当前编辑中的获客员备注名。 private func saveRemark(saleUserId: Int, draft: String) async { guard saleUserId > 0 else { return } await globalLoading.withOptionalLoading(true) { do { try await viewModel.updateRemark( saleUserId: saleUserId, remark: draft, api: cooperationOrderAPI ) toastCenter.show("备注已保存") } catch { toastCenter.show(error.localizedDescription) } } } private func reload(showLoading: Bool) async { await globalLoading.withOptionalLoading(showLoading) { await viewModel.reload(api: cooperationOrderAPI, storeId: accountContext.currentStore?.id) } } private func dialPhone(_ phone: String) { let trimmed = phone.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty, let url = URL(string: "tel:\(trimmed)") else { return } openURL(url) } }