Migrate from iOS 17 Observation to iOS 16-compatible Combine architecture.

Lower deployment target to iOS 16, replace @Observable with ObservableObject, add navigation and UI compatibility shims, and include login smoke UI tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 10:16:35 +08:00
parent c32a610ee0
commit 26f4d0e671
127 changed files with 2320 additions and 1465 deletions

View File

@ -9,16 +9,16 @@ import SwiftUI
/// Tab
struct OrdersView: View {
@Environment(AccountContext.self) private var accountContext
@Environment(PermissionContext.self) private var permissionContext
@Environment(AppRouter.self) private var appRouter
@Environment(RouterPath.self) private var router
@Environment(OrdersAPI.self) private var ordersAPI
@Environment(ToastCenter.self) private var toastCenter
@EnvironmentObject private var accountContext: AccountContext
@EnvironmentObject private var permissionContext: PermissionContext
@EnvironmentObject private var appRouter: AppRouter
@EnvironmentObject private var router: RouterPath
@Environment(\.ordersAPI) private var ordersAPI
@EnvironmentObject private var toastCenter: ToastCenter
@Environment(\.globalLoading) private var globalLoading
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
@State private var viewModel = OrdersViewModel()
@StateObject private var viewModel = OrdersViewModel()
@State private var manualOrderNumber = ""
@State private var showDateFilterSheet = false
@State private var showScanner = false
@ -62,7 +62,7 @@ struct OrdersView: View {
entrySection
if currentScenicId == nil {
ContentUnavailableView(
AppContentUnavailableView(
"缺少经营上下文",
systemImage: "mountain.2",
description: Text("请先在首页选择景区后查看订单。")
@ -80,7 +80,7 @@ struct OrdersView: View {
.padding(.bottom, AppMetrics.Spacing.pageVertical)
.frame(maxWidth: .infinity)
}
.onChange(of: matchedScannedOrderNumber) { _, orderNumber in
.onChange(of: matchedScannedOrderNumber) { orderNumber in
guard let orderNumber else { return }
withAnimation(.easeInOut(duration: 0.25)) {
scrollProxy.scrollTo(orderNumber, anchor: .center)
@ -96,15 +96,15 @@ struct OrdersView: View {
await reload()
await consumePendingScanCodeIfNeeded()
}
.onChange(of: appRouter.selectedOrdersEntry) { _, entry in
.onChange(of: appRouter.selectedOrdersEntry) { entry in
guard viewModel.selectedEntry != entry else { return }
viewModel.selectedEntry = entry
Task { await reload() }
}
.onChange(of: appRouter.pendingOrderScanCode) { _, _ in
.onChange(of: appRouter.pendingOrderScanCode) { _ in
Task { await consumePendingScanCodeIfNeeded() }
}
.onChange(of: accountContext.currentScenic?.id) { _, _ in
.onChange(of: accountContext.currentScenic?.id) { _ in
Task { await reload() }
}
.confirmationDialog("确认核销该订单?", isPresented: $showVerifyConfirmation, titleVisibility: .visible) {
@ -233,7 +233,7 @@ struct OrdersView: View {
.frame(maxWidth: .infinity)
.frame(minHeight: 260)
} else if viewModel.storeOrders.isEmpty {
ContentUnavailableView("暂无订单", systemImage: "tray", description: Text("可切换筛选条件或下拉刷新。"))
AppContentUnavailableView("暂无订单", systemImage: "tray", description: Text("可切换筛选条件或下拉刷新。"))
.frame(minHeight: 260)
} else {
ForEach(viewModel.storeOrders) { item in
@ -306,7 +306,7 @@ struct OrdersView: View {
.frame(maxWidth: .infinity)
.frame(minHeight: 260)
} else if viewModel.writeOffOrders.isEmpty {
ContentUnavailableView("暂无核销订单", systemImage: "tray", description: Text("可下拉刷新或切换景区查看。"))
AppContentUnavailableView("暂无核销订单", systemImage: "tray", description: Text("可下拉刷新或切换景区查看。"))
.frame(minHeight: 260)
} else {
ForEach(viewModel.writeOffOrders) { item in
@ -735,11 +735,11 @@ private struct OrdersDateFilterSheet: View {
#Preview {
NavigationStack {
OrdersView()
.environment(AccountContext())
.environment(PermissionContext())
.environment(AppRouter())
.environment(RouterPath())
.environment(OrdersAPI(client: APIClient()))
.environment(ToastCenter())
.environmentObject(AccountContext())
.environmentObject(PermissionContext())
.environmentObject(AppRouter())
.environmentObject(RouterPath())
.environment(\.ordersAPI, OrdersAPI(client: APIClient()))
.environmentObject(ToastCenter())
}
}