Advance UIKit rewrite with AMap integration and core UI modules.

Integrate高德 SDK with simulator-safe build flags, add map views for operating area and punch points, refactor main tabs and key feature screens to UIKit with Diffable lists, and document Swift concurrency defaults in AGENTS.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 15:16:12 +08:00
parent 9edf993432
commit d99a5b1bf8
124 changed files with 5195 additions and 1536 deletions

View File

@ -13,6 +13,7 @@ struct ScenicQueueStatsData: Decodable, Equatable {
let avgWaitMin: Double
let time: String
///
enum CodingKeys: String, CodingKey {
case queueCount = "queue_count"
case avgWaitMin = "avg_wait_min"
@ -41,6 +42,7 @@ struct ScenicQueueHomeData: Decodable, Equatable {
let list: ScenicQueueHomeListBlock?
let time: String?
///
enum CodingKeys: String, CodingKey {
case stats
case list
@ -73,6 +75,7 @@ struct ScenicQueueHomeData: Decodable, Equatable {
struct ScenicQueueHomeStats: Decodable, Equatable {
let type: Int
///
enum CodingKeys: String, CodingKey {
case type
}
@ -96,6 +99,7 @@ struct ScenicQueueHomeListBlock: Decodable, Equatable {
let page: Int
let pageSize: Int
///
enum CodingKeys: String, CodingKey {
case list
case total
@ -174,6 +178,7 @@ struct ScenicQueueTicket: Decodable, Equatable, Identifiable {
isMissRequeue == 1
}
///
enum CodingKeys: String, CodingKey {
case id
case queueCode = "queue_code"
@ -198,6 +203,7 @@ struct ScenicQueueTicket: Decodable, Equatable, Identifiable {
case missRequeueText = "is_miss_requeue_text"
}
///
enum AlternateCodingKeys: String, CodingKey {
case queueNo = "queue_no"
case queueNumber = "queue_number"
@ -289,6 +295,7 @@ struct ScenicQueueTicket: Decodable, Equatable, Identifiable {
missRequeueText = try container.decodeLossyString(forKey: .missRequeueText).trimmingCharacters(in: .whitespacesAndNewlines)
}
///
private static func formatQueueTime(_ raw: String) -> String {
let text = raw.trimmingCharacters(in: .whitespacesAndNewlines)
guard !text.isEmpty else { return "--" }
@ -317,6 +324,7 @@ struct ScenicQueueActionRequest: Encodable {
struct SocketTokenResponse: Decodable, Equatable {
let socketToken: String
///
enum CodingKeys: String, CodingKey {
case socketToken = "socket_token"
}
@ -340,6 +348,7 @@ struct ScenicQueueActionData: Decodable, Equatable {
let statusText: String
let calledAt: String?
///
enum CodingKeys: String, CodingKey {
case id
case status
@ -375,6 +384,7 @@ struct ScenicQueueRequeueInsertBeforeRequest: Encodable {
let recordId: Int64
let operatorId: Int
///
enum CodingKeys: String, CodingKey {
case recordId = "record_id"
case operatorId = "operator_id"
@ -389,6 +399,7 @@ struct ScenicQueueUserMarkRequest: Encodable, Equatable {
let queueBanDays: Int?
let operatorId: Int?
///
enum CodingKeys: String, CodingKey {
case uid
case scenicId = "scenic_id"
@ -433,6 +444,7 @@ struct ScenicQueueRemoteCallAnnouncement: Identifiable, Equatable {
}
private extension KeyedDecodingContainer {
///
func decodeLossyString(forKey key: Key) throws -> String {
if let value = try? decodeIfPresent(String.self, forKey: key) {
return value
@ -449,6 +461,7 @@ private extension KeyedDecodingContainer {
return ""
}
///
func decodeLossyInt(forKey key: Key) throws -> Int? {
if let value = try? decodeIfPresent(Int.self, forKey: key) {
return value
@ -467,6 +480,7 @@ private extension KeyedDecodingContainer {
return nil
}
/// Double
func decodeLossyDouble(forKey key: Key) throws -> Double? {
if let value = try? decodeIfPresent(Double.self, forKey: key) {
return value
@ -484,6 +498,7 @@ private extension KeyedDecodingContainer {
}
private extension String {
/// non
func nonEmptyOrDefault(_ fallback: String) -> String {
let text = trimmingCharacters(in: .whitespacesAndNewlines)
return text.isEmpty ? fallback : text

View File

@ -13,6 +13,7 @@ struct ScenicQueueSettingData: Decodable, Equatable {
let setting: ScenicQueueSettingItem?
let time: String?
///
enum CodingKeys: String, CodingKey {
case exists
case setting
@ -64,6 +65,7 @@ struct ScenicQueueSettingItem: Decodable, Equatable {
let createdAt: String
let updatedAt: String
///
enum CodingKeys: String, CodingKey {
case id
case scenicId = "scenic_id"
@ -131,6 +133,7 @@ struct ScenicQueueVoiceBroadcastItem: Codable, Identifiable, Equatable {
let content: String
let sortOrder: Int
///
enum CodingKeys: String, CodingKey {
case content
case sortOrder = "sort_order"
@ -175,6 +178,7 @@ struct ScenicQueueSaveSettingRequest: Encodable, Equatable {
let status: Int
let remark: String?
///
enum CodingKeys: String, CodingKey {
case scenicId = "scenic_id"
case scenicSpotId = "scenic_spot_id"
@ -208,6 +212,7 @@ struct ScenicQueueSettingChangeLogData: Decodable, Equatable {
let pageSize: Int
let list: [ScenicQueueSettingChangeLogItem]
///
enum CodingKeys: String, CodingKey {
case total
case page
@ -243,6 +248,7 @@ struct ScenicQueueSettingChangeLogItem: Decodable, Equatable, Identifiable {
let displayText: String
let createdAt: String
///
enum CodingKeys: String, CodingKey {
case id
case scenicSpotName = "scenic_spot_name"
@ -270,6 +276,7 @@ struct ScenicQueueSettingChangeLogItem: Decodable, Equatable, Identifiable {
struct ScenicQueueShootQueueQRCodeData: Decodable, Equatable {
let qrcodeUrl: String
///
enum CodingKeys: String, CodingKey {
case qrcodeUrl = "qrcode_url"
}
@ -309,6 +316,7 @@ struct ScenicQueueSettingsSnapshot: Codable, Equatable {
var businessStartTime: String = "10:00"
var businessEndTime: String = "20:00"
///
enum CodingKeys: String, CodingKey {
case shootMinute = "shoot_minute"
case shootSecond = "shoot_second"
@ -334,6 +342,7 @@ struct ScenicQueueSettingsSnapshot: Codable, Equatable {
}
private extension KeyedDecodingContainer {
///
func decodeLossyString(forKey key: Key) throws -> String {
if let value = try? decodeIfPresent(String.self, forKey: key) { return value }
if let value = try? decodeIfPresent(Int.self, forKey: key) { return String(value) }
@ -342,6 +351,7 @@ private extension KeyedDecodingContainer {
return ""
}
///
func decodeLossyInt(forKey key: Key) throws -> Int? {
if let value = try? decodeIfPresent(Int.self, forKey: key) { return value }
if let value = try? decodeIfPresent(Double.self, forKey: key) { return Int(value) }
@ -354,6 +364,7 @@ private extension KeyedDecodingContainer {
return nil
}
///
func decodeLossyBool(forKey key: Key) throws -> Bool? {
if let value = try? decodeIfPresent(Bool.self, forKey: key) { return value }
if let value = try? decodeIfPresent(Int.self, forKey: key) { return value != 0 }

View File

@ -155,17 +155,20 @@ enum ScenicQueueSettingsStore {
UserDefaults.standard.set(data, forKey: "scenic_\(scenicId)_spot_\(spotId)_\(ScenicQueueLocalSettings.presetVoicesKey)")
}
/// dUserId
private static func normalizedUserId(_ userId: String?) -> String {
let text = userId?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return text.isEmpty ? "anonymous" : text
}
/// positive
private static func positiveInt(forKey key: String) -> Int? {
guard UserDefaults.standard.object(forKey: key) != nil else { return nil }
let value = UserDefaults.standard.integer(forKey: key)
return value > 0 ? value : nil
}
/// legacy
private static func legacyPositiveInt(forKey key: String) -> Int? {
positiveInt(forKey: key)
}