Add scenic selection and cooperation order flows aligned with Android.

Upgrade scenic picker with search, location sorting, permission apply/status, and blocking home dialog; add cooperation order module and order source picker improvements with unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 10:48:43 +08:00
parent 3c4ca7eefb
commit 2bea05b1d9
50 changed files with 5603 additions and 191 deletions

View File

@ -20,9 +20,59 @@ struct OrderSourcePerson: Equatable {
struct OrderSourceLead: Equatable {
let key: String
let displayText: String
let userMobile: String
let createdAt: String
let userPhone: String
let remark: String
let images: [String]
}
enum OrderSourceMapping {
/// API Android `toOrderSourceLead()`
static func lead(from entity: ReferralOrderEntity) -> OrderSourceLead {
let orderNo = entity.displayReferralOrderNo
let remark: String
if !entity.remark.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
remark = entity.remark
} else if !entity.statusLabel.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
remark = entity.statusLabel
} else {
remark = "带客单 \(orderNo)"
}
return OrderSourceLead(
key: orderNo,
userPhone: entity.userMobile,
remark: remark,
images: entity.images
)
}
/// Android `toOrderSourceSelection()`
static func selection(from referral: BoundReferralOrderEntity) -> OrderSourceSelection? {
let referralNo = referral.orderNo.trimmingCharacters(in: .whitespacesAndNewlines)
guard !referralNo.isEmpty else { return nil }
let personName = referral.displaySalerName.isEmpty ? "" : referral.displaySalerName
let person = OrderSourcePerson(
key: referral.saleUserId > 0 ? String(referral.saleUserId) : "",
name: personName,
phone: referral.salerPhone
)
let remark: String
if !referral.remark.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
remark = referral.remark
} else if !referral.displayText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
remark = referral.displayText
} else {
remark = "带客单号 \(referralNo)"
}
let lead = OrderSourceLead(
key: referralNo,
userPhone: referral.userMobile,
remark: remark,
images: []
)
return OrderSourceSelection(person: person, lead: lead)
}
}
struct OrderSourceSelection: Equatable {
@ -54,13 +104,14 @@ final class OrderListViewModel {
private(set) var orderSourceLeads: [String: [OrderSourceLead]] = [:]
private(set) var orderSourceSelections: [String: OrderSourceSelection] = [:]
private(set) var loadingOrderSourceLeadsFor: String?
private(set) var isLoadingOrderSourcePeople = false
var onStateChange: (() -> Void)?
var onShowMessage: ((String) -> Void)?
var onNavigateBindAcquirer: ((Int) -> Void)?
private var currentPage = 1
private var totalCount = 0
private var loadingOrderSourcePeople = false
private let apiDateFormatter: DateFormatter = {
let formatter = DateFormatter()
@ -188,7 +239,7 @@ final class OrderListViewModel {
return
case SaleUserQrParser.methodSaleUserBind:
if let saleUserId = SaleUserQrParser.parseSaleUserId(scanResult), saleUserId > 0 {
onShowMessage?("获客员绑定功能待接入")
onNavigateBindAcquirer?(saleUserId)
} else {
onShowMessage?("请扫描正确的获客员二维码")
}
@ -287,27 +338,27 @@ final class OrderListViewModel {
}
func orderSourceSelection(for order: OrderEntity) -> OrderSourceSelection? {
if let selection = orderSourceSelections[order.orderNumber] { return selection }
guard let referral = order.referralOrder else { return nil }
let person = OrderSourcePerson(
key: String(referral.saleUserId),
name: referral.displaySalerName,
phone: referral.salerPhone
)
let lead = OrderSourceLead(
key: referral.orderNo,
displayText: referral.displayText.isEmpty ? referral.orderNo : referral.displayText,
userMobile: referral.userMobile,
createdAt: referral.createdAt
)
return OrderSourceSelection(person: person, lead: lead)
orderSourceSelection(orderNumber: order.orderNumber, referralOrder: order.referralOrder)
}
/// Sheet 使
func orderSourceSelection(orderNumber: String, referralOrder: BoundReferralOrderEntity? = nil) -> OrderSourceSelection? {
if let selection = orderSourceSelections[orderNumber] { return selection }
if let referral = referralOrder ?? orderList.first(where: { $0.orderNumber == orderNumber })?.referralOrder {
return OrderSourceMapping.selection(from: referral)
}
return nil
}
func onOrderSourceSheetOpen(api: OrderAPI) async {
orderSourceLeads = [:]
guard !loadingOrderSourcePeople else { return }
loadingOrderSourcePeople = true
defer { loadingOrderSourcePeople = false }
guard !isLoadingOrderSourcePeople else { return }
isLoadingOrderSourcePeople = true
notifyStateChange()
defer {
isLoadingOrderSourcePeople = false
notifyStateChange()
}
let storeId = AppStore.shared.currentStoreId
let storeIdParam = storeId > 0 ? String(storeId) : nil
do {
@ -331,15 +382,7 @@ final class OrderListViewModel {
}
do {
let response = try await api.referralOrders(saleUserId: person.key)
let leads = response.list.map {
OrderSourceLead(
key: $0.displayReferralOrderNo,
displayText: $0.displayReferralOrderNo,
userMobile: $0.userMobile,
createdAt: $0.createdAt
)
}
orderSourceLeads[person.key] = leads
orderSourceLeads[person.key] = response.list.map(OrderSourceMapping.lead(from:))
notifyStateChange()
} catch {
onShowMessage?(error.localizedDescription)