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

@ -31,6 +31,7 @@ struct OperatingAreaItem: Decodable, Equatable, Identifiable {
let typeText: String
let auditStatusText: String
///
enum CodingKeys: String, CodingKey {
case id
case name
@ -40,6 +41,7 @@ struct OperatingAreaItem: Decodable, Equatable, Identifiable {
case auditStatusText = "audit_status_text"
}
///
init(
id: Int = 0,
name: String = "",
@ -56,6 +58,7 @@ struct OperatingAreaItem: Decodable, Equatable, Identifiable {
self.auditStatusText = auditStatusText
}
///
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.operatingDecodeLossyInt(forKey: .id) ?? 0
@ -76,6 +79,7 @@ enum OperatingMapArea: Decodable, Equatable {
case array([OperatingMapArea])
case object([String: OperatingMapArea])
///
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if container.decodeNil() {
@ -106,6 +110,7 @@ enum OperatingMapArea: Decodable, Equatable {
return fromJSONObject(raw)
}
/// fromJSONObject
private static func fromJSONObject(_ value: Any) -> OperatingMapArea {
switch value {
case is NSNull:
@ -140,6 +145,7 @@ struct OperatingFenceRing: Identifiable, Equatable {
let points: [OperatingGeoPoint]
let isCurrentStore: Bool
///
init(itemId: Int, regionName: String, points: [OperatingGeoPoint], isCurrentStore: Bool, ringIndex: Int) {
self.id = "\(itemId)-\(ringIndex)"
self.itemId = itemId
@ -184,6 +190,7 @@ enum OperatingAreaParser {
return parseElement(area)
}
/// Element
private static func parseElement(_ value: OperatingMapArea) -> [[OperatingGeoPoint]] {
switch value {
case .null, .bool, .number:
@ -199,6 +206,7 @@ enum OperatingAreaParser {
}
}
/// Object
private static func parseObject(_ object: [String: OperatingMapArea]) -> [[OperatingGeoPoint]] {
let type: String?
if case let .string(rawType)? = object["type"] {
@ -231,6 +239,7 @@ enum OperatingAreaParser {
}
}
/// ArrayRoot
private static func parseArrayRoot(_ array: [OperatingMapArea]) -> [[OperatingGeoPoint]] {
guard let first = array.first else { return [] }
switch first {
@ -257,6 +266,7 @@ enum OperatingAreaParser {
}
}
/// ObjectRing
private static func parseObjectRing(_ array: [OperatingMapArea]) -> [OperatingGeoPoint]? {
let points = array.compactMap { element -> OperatingGeoPoint? in
guard case let .object(object) = element else { return nil }
@ -265,6 +275,7 @@ enum OperatingAreaParser {
return points.count >= 3 ? points : nil
}
/// Ring
private static func parseRing(_ array: [OperatingMapArea]) -> [OperatingGeoPoint]? {
let points = array.compactMap { element -> OperatingGeoPoint? in
switch element {
@ -283,6 +294,7 @@ enum OperatingAreaParser {
return points.count >= 3 ? points : nil
}
/// point
private static func pointFromObject(_ object: [String: OperatingMapArea]) -> OperatingGeoPoint? {
let lat = object["lat"]?.numberValue ?? object["latitude"]?.numberValue
let lng = object["lng"]?.numberValue ?? object["lon"]?.numberValue ?? object["longitude"]?.numberValue
@ -290,6 +302,7 @@ enum OperatingAreaParser {
return OperatingGeoPoint(latitude: lat, longitude: lng)
}
/// Pair
private static func normalizePair(_ a: Double, _ b: Double) -> OperatingGeoPoint {
if looksLikeLngLatPair(lng: a, lat: b) {
return OperatingGeoPoint(latitude: b, longitude: a)
@ -303,24 +316,29 @@ enum OperatingAreaParser {
return OperatingGeoPoint(latitude: a, longitude: b)
}
/// looksLikeLngLat
private static func looksLikeLngLatPair(lng: Double, lat: Double) -> Bool {
(-90.0...90.0).contains(lat) && abs(lng) <= 180.0 && abs(lng) > abs(lat)
}
/// looksLikeLatLng
private static func looksLikeLatLngPair(lat: Double, lng: Double) -> Bool {
(-90.0...90.0).contains(lat) && abs(lng) <= 180.0 && abs(lat) <= abs(lng)
}
}
///
private struct OperatingDynamicCodingKey: CodingKey {
let stringValue: String
let intValue: Int?
///
init?(stringValue: String) {
self.stringValue = stringValue
intValue = nil
}
///
init?(intValue: Int) {
stringValue = String(intValue)
self.intValue = intValue
@ -341,6 +359,7 @@ private extension OperatingMapArea {
}
private extension KeyedDecodingContainer {
/// operating
func operatingDecodeLossyString(forKey key: Key) throws -> String {
if let value = try? decodeIfPresent(String.self, forKey: key) {
return value
@ -357,6 +376,7 @@ private extension KeyedDecodingContainer {
return ""
}
/// operating
func operatingDecodeLossyInt(forKey key: Key) throws -> Int? {
if let value = try? decodeIfPresent(Int.self, forKey: key) {
return value