Files
suixinkan_uikit/suixinkan/Features/ScenicQueue/Models/ScenicQueueModels.swift
汉秋 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
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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: "语音播报"
}
}
}