从 iOS 17 Observation 迁移至 iOS 16 兼容的 Combine 架构
将最低部署版本降至 iOS 16,以 ObservableObject 替换 @Observable,新增导航与 UI 兼容层,并补充登录冒烟 UI 测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -6,44 +6,43 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
import Combine
|
||||
import Photos
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 排队管理列表 ViewModel,负责打卡点门禁、列表分页、动作和实时事件。
|
||||
final class QueueManagementViewModel {
|
||||
var loading = false
|
||||
var loadingMore = false
|
||||
var items: [QueueItem] = []
|
||||
var scenicSpots: [ScenicSpotItem] = []
|
||||
var selectedSpotId: Int?
|
||||
var selectedListType: QueueListType = .queueing
|
||||
var queueCount = 0
|
||||
var avgWaitMin = 0.0
|
||||
var totalCount = 0
|
||||
var hasMore = false
|
||||
var page = 1
|
||||
var lastSyncTimeText = "--"
|
||||
var queueGatePassed = false
|
||||
var selectedSpotName = "--"
|
||||
var showStartShootingButton = true
|
||||
var autoCallAheadCount = 0
|
||||
var quickCallButtonEnabled = false
|
||||
var prepareCallButtonEnabled = false
|
||||
var remoteCallAnnouncement: ScenicQueueRemoteCallAnnouncement?
|
||||
var errorMessage: String?
|
||||
final class QueueManagementViewModel: ObservableObject {
|
||||
@Published var loading = false
|
||||
@Published var loadingMore = false
|
||||
@Published var items: [QueueItem] = []
|
||||
@Published var scenicSpots: [ScenicSpotItem] = []
|
||||
@Published var selectedSpotId: Int?
|
||||
@Published var selectedListType: QueueListType = .queueing
|
||||
@Published var queueCount = 0
|
||||
@Published var avgWaitMin = 0.0
|
||||
@Published var totalCount = 0
|
||||
@Published var hasMore = false
|
||||
@Published var page = 1
|
||||
@Published var lastSyncTimeText = "--"
|
||||
@Published var queueGatePassed = false
|
||||
@Published var selectedSpotName = "--"
|
||||
@Published var showStartShootingButton = true
|
||||
@Published var autoCallAheadCount = 0
|
||||
@Published var quickCallButtonEnabled = false
|
||||
@Published var prepareCallButtonEnabled = false
|
||||
@Published var remoteCallAnnouncement: ScenicQueueRemoteCallAnnouncement?
|
||||
@Published var errorMessage: String?
|
||||
|
||||
@ObservationIgnored private let pageSize = 10
|
||||
@ObservationIgnored private let socketClient = ScenicQueueSocketClient()
|
||||
@ObservationIgnored private var realtimeSpotId: Int?
|
||||
@ObservationIgnored private var realtimeScenicId: Int?
|
||||
@ObservationIgnored private var currentUserId: String?
|
||||
@ObservationIgnored private var currentScenicId: Int?
|
||||
@ObservationIgnored private var pendingRemoteCalledRecordIds = Set<Int64>()
|
||||
@ObservationIgnored private var handledRemoteCallEventIds = Set<String>()
|
||||
private let pageSize = 10
|
||||
private let socketClient = ScenicQueueSocketClient()
|
||||
@Published private var realtimeSpotId: Int?
|
||||
@Published private var realtimeScenicId: Int?
|
||||
@Published private var currentUserId: String?
|
||||
@Published private var currentScenicId: Int?
|
||||
@Published private var pendingRemoteCalledRecordIds = Set<Int64>()
|
||||
@Published private var handledRemoteCallEventIds = Set<String>()
|
||||
|
||||
/// 待处理排队数量。
|
||||
var pendingCount: Int {
|
||||
@ -417,41 +416,40 @@ final class QueueManagementViewModel {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 排队设置 ViewModel,负责设置读取、校验、保存、二维码和日志。
|
||||
final class ScenicQueueSettingsViewModel {
|
||||
var loading = false
|
||||
var message: String?
|
||||
var scenicSpots: [ScenicSpotItem] = []
|
||||
var selectedSpotId: Int?
|
||||
var photoEstimateMin = "0"
|
||||
var photoEstimateSec = "30"
|
||||
var firstAhead = "0"
|
||||
var firstSms = false
|
||||
var firstPhone = false
|
||||
var secondAhead = "0"
|
||||
var secondSms = false
|
||||
var secondPhone = false
|
||||
var broadcastIntervalSec = "50"
|
||||
var countdownThresholdSec = "15"
|
||||
var maxQueueRangeKm = "0.00"
|
||||
var localQueueLimit = "0"
|
||||
var localPassDelay = "1"
|
||||
var showStartShootingButton = true
|
||||
var autoCallAheadCount = "0"
|
||||
var queueOpen = true
|
||||
var businessStartTime = ScenicQueueSettingsViewModel.businessTime(hour: 10, minute: 0)
|
||||
var businessEndTime = ScenicQueueSettingsViewModel.businessTime(hour: 20, minute: 0)
|
||||
var customTtsText = ""
|
||||
var presetVoices: [String] = []
|
||||
var quickCallButtonEnabled = false
|
||||
var prepareCallButtonEnabled = false
|
||||
var logs: [ScenicQueueSettingChangeLogItem] = []
|
||||
var qrcodeURL = ""
|
||||
final class ScenicQueueSettingsViewModel: ObservableObject {
|
||||
@Published var loading = false
|
||||
@Published var message: String?
|
||||
@Published var scenicSpots: [ScenicSpotItem] = []
|
||||
@Published var selectedSpotId: Int?
|
||||
@Published var photoEstimateMin = "0"
|
||||
@Published var photoEstimateSec = "30"
|
||||
@Published var firstAhead = "0"
|
||||
@Published var firstSms = false
|
||||
@Published var firstPhone = false
|
||||
@Published var secondAhead = "0"
|
||||
@Published var secondSms = false
|
||||
@Published var secondPhone = false
|
||||
@Published var broadcastIntervalSec = "50"
|
||||
@Published var countdownThresholdSec = "15"
|
||||
@Published var maxQueueRangeKm = "0.00"
|
||||
@Published var localQueueLimit = "0"
|
||||
@Published var localPassDelay = "1"
|
||||
@Published var showStartShootingButton = true
|
||||
@Published var autoCallAheadCount = "0"
|
||||
@Published var queueOpen = true
|
||||
@Published var businessStartTime = ScenicQueueSettingsViewModel.businessTime(hour: 10, minute: 0)
|
||||
@Published var businessEndTime = ScenicQueueSettingsViewModel.businessTime(hour: 20, minute: 0)
|
||||
@Published var customTtsText = ""
|
||||
@Published var presetVoices: [String] = []
|
||||
@Published var quickCallButtonEnabled = false
|
||||
@Published var prepareCallButtonEnabled = false
|
||||
@Published var logs: [ScenicQueueSettingChangeLogItem] = []
|
||||
@Published var qrcodeURL = ""
|
||||
|
||||
@ObservationIgnored private let speaker = ScenicQueueSpeechService()
|
||||
@ObservationIgnored private var currentUserId: String?
|
||||
@ObservationIgnored private var currentScenicId: Int?
|
||||
private let speaker = ScenicQueueSpeechService()
|
||||
@Published private var currentUserId: String?
|
||||
@Published private var currentScenicId: Int?
|
||||
|
||||
/// 加载设置。设置页允许自动选中第一个打卡点。
|
||||
func load(api: any ScenicQueueServing, scenicId: Int?, userId: String?, spots: [ScenicSpotItem]) async {
|
||||
|
||||
Reference in New Issue
Block a user