从 iOS 17 Observation 迁移至 iOS 16 兼容的 Combine 架构
将最低部署版本降至 iOS 16,以 ObservableObject 替换 @Observable,新增导航与 UI 兼容层,并补充登录冒烟 UI 测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -9,12 +9,12 @@ import SwiftUI
|
||||
|
||||
/// 排队管理首页,展示当前打卡点队列和操作入口。
|
||||
struct QueueManagementView: View {
|
||||
@Environment(AccountContext.self) private var accountContext
|
||||
@Environment(ScenicSpotContext.self) private var scenicSpotContext
|
||||
@Environment(ScenicQueueAPI.self) private var queueAPI
|
||||
@Environment(ScenicQueueRuntime.self) private var queueRuntime
|
||||
@Environment(ToastCenter.self) private var toastCenter
|
||||
@State private var viewModel = QueueManagementViewModel()
|
||||
@EnvironmentObject private var accountContext: AccountContext
|
||||
@EnvironmentObject private var scenicSpotContext: ScenicSpotContext
|
||||
@Environment(\.scenicQueueAPI) private var queueAPI
|
||||
@EnvironmentObject private var queueRuntime: ScenicQueueRuntime
|
||||
@EnvironmentObject private var toastCenter: ToastCenter
|
||||
@StateObject private var viewModel = QueueManagementViewModel()
|
||||
@State private var pendingAction: QueueActionConfirmation?
|
||||
@State private var markTarget: QueueItem?
|
||||
@State private var processingId: Int64?
|
||||
@ -27,7 +27,7 @@ struct QueueManagementView: View {
|
||||
VStack(spacing: AppMetrics.Spacing.medium) {
|
||||
header
|
||||
if currentScenicId == nil {
|
||||
ContentUnavailableView("请先选择景区", systemImage: "map", description: Text("排队管理需要当前景区上下文"))
|
||||
AppContentUnavailableView("请先选择景区", systemImage: "map", description: Text("排队管理需要当前景区上下文"))
|
||||
.frame(minHeight: 260)
|
||||
} else if !viewModel.queueGatePassed {
|
||||
gateCard
|
||||
@ -79,12 +79,12 @@ struct QueueManagementView: View {
|
||||
.refreshable {
|
||||
await reload()
|
||||
}
|
||||
.onChange(of: viewModel.remoteCallAnnouncement) { _, announcement in
|
||||
.onChange(of: viewModel.remoteCallAnnouncement) { announcement in
|
||||
if let announcement {
|
||||
toastCenter.show("远端已叫号 \(announcement.queueCode.isEmpty ? "当前游客" : announcement.queueCode)")
|
||||
}
|
||||
}
|
||||
.onChange(of: viewModel.errorMessage) { _, message in
|
||||
.onChange(of: viewModel.errorMessage) { message in
|
||||
if let message { toastCenter.show(message) }
|
||||
}
|
||||
}
|
||||
@ -163,7 +163,7 @@ struct QueueManagementView: View {
|
||||
ProgressView("加载中...")
|
||||
.frame(maxWidth: .infinity, minHeight: 240)
|
||||
} else if viewModel.items.isEmpty {
|
||||
ContentUnavailableView(viewModel.selectedListType.emptyText, systemImage: "person.crop.circle.badge.questionmark")
|
||||
AppContentUnavailableView(viewModel.selectedListType.emptyText, systemImage: "person.crop.circle.badge.questionmark")
|
||||
.frame(maxWidth: .infinity, minHeight: 240)
|
||||
.background(.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))
|
||||
} else {
|
||||
|
||||
@ -9,12 +9,12 @@ import SwiftUI
|
||||
|
||||
/// 排队设置页面。
|
||||
struct ScenicQueueSettingsView: View {
|
||||
@Environment(AccountContext.self) private var accountContext
|
||||
@Environment(ScenicSpotContext.self) private var scenicSpotContext
|
||||
@Environment(ScenicQueueAPI.self) private var queueAPI
|
||||
@Environment(ScenicQueueRuntime.self) private var queueRuntime
|
||||
@Environment(ToastCenter.self) private var toastCenter
|
||||
@State private var viewModel = ScenicQueueSettingsViewModel()
|
||||
@EnvironmentObject private var accountContext: AccountContext
|
||||
@EnvironmentObject private var scenicSpotContext: ScenicSpotContext
|
||||
@Environment(\.scenicQueueAPI) private var queueAPI
|
||||
@EnvironmentObject private var queueRuntime: ScenicQueueRuntime
|
||||
@EnvironmentObject private var toastCenter: ToastCenter
|
||||
@StateObject private var viewModel = ScenicQueueSettingsViewModel()
|
||||
@State private var selectedTab: ScenicQueueSettingsTab = .basic
|
||||
@State private var qrcodeURL: String?
|
||||
@State private var saving = false
|
||||
@ -75,7 +75,7 @@ struct ScenicQueueSettingsView: View {
|
||||
await viewModel.load(api: queueAPI, scenicId: currentScenicId, userId: currentUserId, spots: scenicSpotContext.spots)
|
||||
await viewModel.loadSettingChangeLogs(api: queueAPI, scenicId: currentScenicId)
|
||||
}
|
||||
.onChange(of: viewModel.message) { _, message in
|
||||
.onChange(of: viewModel.message) { message in
|
||||
if let message { toastCenter.show(message) }
|
||||
}
|
||||
}
|
||||
@ -106,7 +106,7 @@ struct ScenicQueueSettingsView: View {
|
||||
DatePicker("营业开始", selection: $viewModel.businessStartTime, displayedComponents: .hourAndMinute)
|
||||
DatePicker("营业结束", selection: $viewModel.businessEndTime, displayedComponents: .hourAndMinute)
|
||||
field("最大排队范围", text: $viewModel.maxQueueRangeKm, keyboard: .decimalPad, unit: "公里")
|
||||
.onChange(of: viewModel.maxQueueRangeKm) { _, value in viewModel.updateMaxQueueRange(value) }
|
||||
.onChange(of: viewModel.maxQueueRangeKm) { value in viewModel.updateMaxQueueRange(value) }
|
||||
field("排队次数限制", text: $viewModel.localQueueLimit, keyboard: .numberPad, unit: "次")
|
||||
field("过号顺延", text: $viewModel.localPassDelay, keyboard: .numberPad, unit: "位")
|
||||
field("拍摄分钟", text: $viewModel.photoEstimateMin, keyboard: .numberPad, unit: "分")
|
||||
@ -192,7 +192,7 @@ struct ScenicQueueSettingsView: View {
|
||||
}
|
||||
}
|
||||
if viewModel.logs.isEmpty {
|
||||
ContentUnavailableView("暂无配置日志", systemImage: "clock.arrow.circlepath")
|
||||
AppContentUnavailableView("暂无配置日志", systemImage: "clock.arrow.circlepath")
|
||||
.frame(maxWidth: .infinity, minHeight: 180)
|
||||
} else {
|
||||
ForEach(viewModel.logs) { log in
|
||||
|
||||
Reference in New Issue
Block a user