Files
lujiuyinandCursor 6336feff27 完善景区排队设置页与全局按钮配置迁移。
重构排队设置与变更日志交互,补充 Overlay 与资源,并将多页面主操作按钮统一到 UIButton Configuration,同步更新相关单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-14 16:21:53 +08:00

223 lines
6.4 KiB
Swift

//
// ScenicQueueModels.swift
// suixinkan
//
import Foundation
/// 当前排队列表中的叫号状态。
enum ScenicQueueCallStatus: Hashable {
case waiting
case called
}
/// 当前排队卡片在指定拍摄会话与设置下的操作展示状态。
struct ScenicQueueTicketActionState: Equatable {
let skipEnabled: Bool
let primaryVisible: Bool
let primaryEnabled: Bool
let primaryTitle: String
let recallVisible: Bool
let completeVisible: Bool
/// 按 Android 排队卡片规则生成操作状态。
static func make(
ticket: ScenicQueueTicket,
shootingQueueNo: String?,
remainSeconds: Int,
showStartShootingButton: Bool
) -> ScenicQueueTicketActionState {
let isShooting = shootingQueueNo == ticket.queueNo
if isShooting {
return ScenicQueueTicketActionState(
skipEnabled: false,
primaryVisible: true,
primaryEnabled: false,
primaryTitle: "剩余 \(max(0, remainSeconds)) 秒",
recallVisible: false,
completeVisible: ticket.showCompleteAction
)
}
let primaryVisible = ticket.status == .waiting || showStartShootingButton
return ScenicQueueTicketActionState(
skipEnabled: true,
primaryVisible: primaryVisible,
primaryEnabled: true,
primaryTitle: ticket.status == .waiting ? "叫号" : "开始拍摄",
recallVisible: ticket.status == .called,
completeVisible: ticket.showCompleteAction
)
}
}
/// 排队首页 Diffable Data Source 的稳定列表项。
enum ScenicQueueListItem: Hashable {
case current(ScenicQueueTicket)
case skipped(ScenicQueueSkippedTicket)
}
/// 当前排队列表展示实体。
struct ScenicQueueTicket: Hashable {
let recordId: Int64
let queueNo: String
let phoneMasked: String
let dialPhoneDigits: String
let status: ScenicQueueCallStatus
let statusText: String
let waitMinutes: Int
let peopleAhead: Int
let showCompleteAction: Bool
let queueBanLabel: String
let identityTag: String
let queueTimeDisplay: String
let queueCountToday: Int
let uid: Int64
let markAsPhotog: Int
let markAsFreelancePhotog: Int
let isMissRequeue: Bool
let missRequeueText: String
/// 是否展示身份角标,普通用户不展示。
var shouldShowIdentityTag: Bool {
let text = identityTag.trimmingCharacters(in: .whitespacesAndNewlines)
return !text.isEmpty && text != "普通用户"
}
/// 返回叫号后本地更新后的副本。
func markedCalled(statusText newStatusText: String) -> ScenicQueueTicket {
ScenicQueueTicket(
recordId: recordId,
queueNo: queueNo,
phoneMasked: phoneMasked,
dialPhoneDigits: dialPhoneDigits,
status: .called,
statusText: newStatusText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? statusText : newStatusText,
waitMinutes: waitMinutes,
peopleAhead: peopleAhead,
showCompleteAction: true,
queueBanLabel: queueBanLabel,
identityTag: identityTag,
queueTimeDisplay: queueTimeDisplay,
queueCountToday: queueCountToday,
uid: uid,
markAsPhotog: markAsPhotog,
markAsFreelancePhotog: markAsFreelancePhotog,
isMissRequeue: isMissRequeue,
missRequeueText: missRequeueText
)
}
}
/// 过号列表展示实体。
struct ScenicQueueSkippedTicket: Hashable {
let recordId: Int64
let queueNo: String
let phoneMasked: String
let dialPhoneDigits: String
let statusText: String
let skippedTimeDisplay: String
let queueBanLabel: String
let isMissRequeue: Bool
let missRequeueText: String
}
/// 排队页顶部统计。
struct ScenicQueueSummary: Equatable {
let peopleInQueue: Int
let avgWaitMinutes: Int
static let empty = ScenicQueueSummary(peopleInQueue: 0, avgWaitMinutes: 0)
}
/// 打卡点下拉选项。
struct ScenicQueuePunchSpotOption: Hashable {
let id: String
let label: String
}
/// 排队设置本地快照,用于快速恢复 UI 与叫号配置。
struct ScenicQueueSettingsSnapshot: Codable, Equatable {
var shootMinute: Int = 0
var shootSecond: Int = 0
var firstAheadCount: Int = 0
var firstSms: Bool = false
var firstPhone: Bool = false
var secondAheadCount: Int = 0
var secondSms: Bool = false
var secondPhone: Bool = false
var broadcastIntervalSec: Int = 50
var countdownThresholdSec: Int = 15
var queueDistanceMeter: Int = 0
var queueTakeLimit: Int = 0
var missCallRequeueOffset: Int = 1
var showStartShootingButton: Bool = true
var autoCallAheadCount: Int = 0
var quickCallButtonEnabled: Bool = false
var prepareCallButtonEnabled: Bool = false
var businessOpen: Bool = true
var businessStartTime: String = "10:00"
var businessEndTime: String = "20:00"
}
/// 待确认的过号操作。
struct ScenicQueuePendingSkip: Equatable {
let recordId: Int64
let queueNo: String
}
/// 待确认的重新排队操作。
struct ScenicQueuePendingRequeue: Equatable {
let recordId: Int64
let queueNo: String
}
/// 待确认的完成操作。
struct ScenicQueuePendingFinish: Equatable {
let recordId: Int64
let queueNo: String
}
/// 待确认的叫号操作。
struct ScenicQueuePendingCall: Equatable {
let recordId: Int64
let queueNo: String
let isRecall: Bool
}
/// 待确认的用户标记操作。
struct ScenicQueuePendingMark: Equatable {
let uid: Int64
let queueNo: String
let phoneMasked: String
let markAsFreelancePhotog: Int
}
/// 排队模块数字字段键,用于校验定位。
enum ScenicQueueNumericFieldKey: String {
case queueDistanceMeter
case queueTakeLimit
case missCallRequeueOffset
case autoCallAheadCount
case shootMinute
case shootSecond
case firstNoticeAhead
case secondNoticeAhead
case broadcastIntervalSec
case countdownReadableThresholdSec
}
/// 排队设置页 Tab。
enum ScenicQueueSettingsTab: Int, CaseIterable {
case basic
case notify
case voice
var title: String {
switch self {
case .basic: "基础配置"
case .notify: "通知配置"
case .voice: "语音播报"
}
}
}