迁移合作订单完整能力,并统一 AppRoleCode 角色权限与首页菜单展示。
新增 CooperationOrder 模块(双 Tab 列表、获客员绑定、主 Tab 扫码)、订单来源/带客单绑定及配套 API 测试;同步引入 roleCode 权限匹配与相关单元测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -14,9 +14,11 @@ struct OrdersView: View {
|
||||
@EnvironmentObject private var appRouter: AppRouter
|
||||
@EnvironmentObject private var router: RouterPath
|
||||
@Environment(\.ordersAPI) private var ordersAPI
|
||||
@Environment(\.cooperationOrderAPI) private var cooperationOrderAPI
|
||||
@EnvironmentObject private var toastCenter: ToastCenter
|
||||
@Environment(\.globalLoading) private var globalLoading
|
||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||
@Environment(\.openURL) private var openURL
|
||||
|
||||
@StateObject private var viewModel = OrdersViewModel()
|
||||
@State private var manualOrderNumber = ""
|
||||
@ -27,6 +29,8 @@ struct OrdersView: View {
|
||||
@State private var pendingVerifyOrderNumber: String?
|
||||
@State private var matchedScannedOrderNumber: String?
|
||||
@State private var scanHintMessage: String?
|
||||
@State private var bindAcquirerSaleUserId: Int?
|
||||
@State private var referralBindOrderNumber: String?
|
||||
|
||||
private var currentScenicId: Int? {
|
||||
accountContext.currentScenic?.id
|
||||
@ -36,8 +40,8 @@ struct OrdersView: View {
|
||||
accountContext.currentStore?.id
|
||||
}
|
||||
|
||||
private var currentRoleId: Int? {
|
||||
permissionContext.currentRole?.id
|
||||
private var currentAppRole: AppRoleCode? {
|
||||
permissionContext.currentAppRole
|
||||
}
|
||||
|
||||
private var contentMaxWidth: CGFloat {
|
||||
@ -139,6 +143,32 @@ struct OrdersView: View {
|
||||
onApply: { Task { await reload() } }
|
||||
)
|
||||
}
|
||||
.fullScreenCover(item: bindAcquirerBinding) { item in
|
||||
NavigationStack {
|
||||
BindAcquirerView(saleUserId: item.saleUserId, onSuccess: {
|
||||
bindAcquirerSaleUserId = nil
|
||||
})
|
||||
}
|
||||
}
|
||||
.appNavigationDestination(item: referralBindBinding) { item in
|
||||
ReferralOrderSelectView(orderNumber: item.orderNumber) {
|
||||
Task { await reload(showLoading: false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var bindAcquirerBinding: Binding<BindAcquirerSheetItem?> {
|
||||
Binding(
|
||||
get: { bindAcquirerSaleUserId.map(BindAcquirerSheetItem.init(saleUserId:)) },
|
||||
set: { bindAcquirerSaleUserId = $0?.saleUserId }
|
||||
)
|
||||
}
|
||||
|
||||
private var referralBindBinding: Binding<OrderNumberSheetItem?> {
|
||||
Binding(
|
||||
get: { referralBindOrderNumber.map(OrderNumberSheetItem.init(orderNumber:)) },
|
||||
set: { referralBindOrderNumber = $0?.orderNumber }
|
||||
)
|
||||
}
|
||||
|
||||
private var entrySection: some View {
|
||||
@ -413,6 +443,48 @@ struct OrdersView: View {
|
||||
orderInfoRow("付款方式", item.payTypeName.isEmpty ? "--" : item.payTypeName)
|
||||
orderInfoRow("手机号", maskPhoneNumber(item.phone))
|
||||
orderInfoRow("关联项目", item.projectName.isEmpty ? "--" : item.projectName)
|
||||
|
||||
if viewModel.shouldShowOrderSource(order: item, appRole: currentAppRole) {
|
||||
OrderSourcePicker(
|
||||
selection: viewModel.getOrderSourceSelection(for: item),
|
||||
people: viewModel.orderSourcePeople,
|
||||
leadsForPerson: { person in
|
||||
viewModel.orderSourceLeads[person.key] ?? []
|
||||
},
|
||||
leadsLoading: viewModel.loadingOrderSourceLeadsFor != nil,
|
||||
onSheetOpen: {
|
||||
Task {
|
||||
await viewModel.prepareOrderSourceSheet(
|
||||
api: cooperationOrderAPI,
|
||||
storeId: currentStoreId
|
||||
)
|
||||
}
|
||||
},
|
||||
onPersonClick: { person in
|
||||
Task {
|
||||
await viewModel.loadOrderSourceLeads(api: cooperationOrderAPI, person: person)
|
||||
}
|
||||
},
|
||||
onSelectionChange: { selection in
|
||||
Task {
|
||||
do {
|
||||
try await viewModel.bindOrderSource(
|
||||
api: cooperationOrderAPI,
|
||||
orderNumber: item.orderNumber,
|
||||
selection: selection
|
||||
)
|
||||
toastCenter.show("绑定成功")
|
||||
await reload(showLoading: false)
|
||||
} catch {
|
||||
toastCenter.show(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
},
|
||||
callPhone: { phone in
|
||||
dialPhone(phone)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(AppMetrics.Spacing.medium)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
@ -522,7 +594,7 @@ struct OrdersView: View {
|
||||
api: ordersAPI,
|
||||
scenicId: currentScenicId,
|
||||
storeId: currentStoreId,
|
||||
roleId: currentRoleId,
|
||||
appRole: currentAppRole,
|
||||
showLoading: showLoading
|
||||
)
|
||||
}
|
||||
@ -544,7 +616,7 @@ struct OrdersView: View {
|
||||
|
||||
private func loadMoreStoreOrders() async {
|
||||
do {
|
||||
try await viewModel.loadMoreStoreOrders(api: ordersAPI, scenicId: currentScenicId, roleId: currentRoleId)
|
||||
try await viewModel.loadMoreStoreOrders(api: ordersAPI, scenicId: currentScenicId, appRole: currentAppRole)
|
||||
} catch {
|
||||
toastCenter.show(error.localizedDescription)
|
||||
}
|
||||
@ -595,15 +667,30 @@ struct OrdersView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// 消费主 TabBar 全局扫码结果,并复用订单页内部扫码核销处理。
|
||||
/// 消费主 TabBar 全局扫码结果,优先识别获客员绑定二维码。
|
||||
private func consumePendingScanCodeIfNeeded() async {
|
||||
guard let rawCode = appRouter.consumePendingOrderScanCode() else { return }
|
||||
if SaleUserQrParser.isSaleUserBindQR(rawCode) {
|
||||
guard let saleUserId = SaleUserQrParser.parseSaleUserId(rawCode), saleUserId > 0 else {
|
||||
toastCenter.show("请扫描正确的获客员二维码")
|
||||
return
|
||||
}
|
||||
bindAcquirerSaleUserId = saleUserId
|
||||
return
|
||||
}
|
||||
viewModel.selectedEntry = .verificationOrders
|
||||
appRouter.selectOrders(entry: .verificationOrders)
|
||||
await reload(showLoading: false)
|
||||
handleScanResult(rawCode)
|
||||
}
|
||||
|
||||
/// 拨打客户或获客员手机号。
|
||||
private func dialPhone(_ phone: String) {
|
||||
let trimmed = phone.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty, let url = URL(string: "tel:\(trimmed)") else { return }
|
||||
openURL(url)
|
||||
}
|
||||
|
||||
/// 处理扫码返回的原始内容,并进入二次确认核销流程。
|
||||
private func handleScanResult(_ rawCode: String) {
|
||||
guard let result = viewModel.matchedWriteOffOrder(for: rawCode) else {
|
||||
|
||||
Reference in New Issue
Block a user