完善景区排队设置页与全局按钮配置迁移。
重构排队设置与变更日志交互,补充 Overlay 与资源,并将多页面主操作按钮统一到 UIButton Configuration,同步更新相关单元测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -28,6 +28,20 @@ struct ScenicQueueHomeData: Decodable, Equatable {
|
||||
/// 排队 home 接口 stats 节点。
|
||||
struct ScenicQueueHomeStats: Decodable, Equatable {
|
||||
let type: Int
|
||||
|
||||
/// 创建 stats 节点;Android DTO 在服务端未返回 type 时默认使用 0。
|
||||
init(type: Int = 0) {
|
||||
self.type = type
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
type = try container.decodeIfPresent(Int.self, forKey: .type) ?? 0
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
}
|
||||
}
|
||||
|
||||
/// 排队 home 接口分页列表节点。
|
||||
@ -426,6 +440,43 @@ struct ScenicQueueSettingChangeLogItem: Decodable, Equatable, Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
extension ScenicQueueSettingChangeLogItem {
|
||||
/// Android 日志卡片的单行元信息:时间、操作人和手机号尾号。
|
||||
var metadataLine: String {
|
||||
let time: String
|
||||
if let createdAt, !createdAt.isEmpty {
|
||||
let input = DateFormatter()
|
||||
input.locale = Locale(identifier: "en_US_POSIX")
|
||||
input.timeZone = TimeZone(identifier: "Asia/Shanghai")
|
||||
let formats = ["yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd'T'HH:mm:ssXXXXX", "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"]
|
||||
let parsedDate = formats.lazy.compactMap { format -> Date? in
|
||||
input.dateFormat = format
|
||||
return input.date(from: createdAt)
|
||||
}.first
|
||||
if let date = parsedDate {
|
||||
let output = DateFormatter()
|
||||
output.locale = Locale(identifier: "en_US_POSIX")
|
||||
output.timeZone = TimeZone(identifier: "Asia/Shanghai")
|
||||
output.dateFormat = "yyyy-MM-dd HH:mm"
|
||||
time = output.string(from: date)
|
||||
} else {
|
||||
time = String(createdAt.replacingOccurrences(of: "T", with: " ").prefix(16))
|
||||
}
|
||||
} else {
|
||||
time = "—"
|
||||
}
|
||||
let name = operatorName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? "未知" : operatorName.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let tail = operatorPhoneTail.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return tail.isEmpty ? "\(time) \(name)" : "\(time) \(name) 尾号\(tail)"
|
||||
}
|
||||
|
||||
/// 日志卡片主摘要,优先使用 summary,空时回退 displayText。
|
||||
var cardSummary: String {
|
||||
let value = summary.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return value.isEmpty ? displayText : value
|
||||
}
|
||||
}
|
||||
|
||||
/// 打卡点列表响应。
|
||||
struct ScenicSpotListResponse: Decodable, Equatable {
|
||||
let list: [ScenicSpotItem]
|
||||
|
||||
@ -6,11 +6,56 @@
|
||||
import Foundation
|
||||
|
||||
/// 当前排队列表中的叫号状态。
|
||||
enum ScenicQueueCallStatus: Equatable {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user