Files
suixinkan_uikit/suixinkan/Features/PunchPoint/Models/PunchPointModels.swift

442 lines
15 KiB
Swift
Raw 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.

//
// PunchPointModels.swift
// suixinkan
//
import CoreLocation
import Foundation
/// Android `PunchPointFilterType`
enum PunchPointFilterType: Int, CaseIterable, Sendable, Equatable, Hashable {
case all = 0
case inOperation = 1
case paused = 2
case pendingReview = 3
case rejected = 4
///
var title: String {
switch self {
case .all: "全部"
case .inOperation: "运营中"
case .paused: "已暂停"
case .pendingReview: "未审核"
case .rejected: "审核未通过"
}
}
/// `status`
var apiStatus: Int { rawValue }
}
///
struct PunchPointListResponse: Decodable, Sendable, Equatable {
let total: Int
let list: [PunchPointItem]
init(total: Int = 0, list: [PunchPointItem] = []) {
self.total = total
self.list = list
}
}
///
struct PunchPointRegion: Codable, Sendable, Equatable, Hashable {
let lat: Double
let lot: Double
let address: String
let scenicSpotString: String?
enum CodingKeys: String, CodingKey {
case lat
case lot
case address
case scenicSpotString = "scenic_spot_str"
}
init(lat: Double = 0, lot: Double = 0, address: String = "", scenicSpotString: String? = nil) {
self.lat = lat
self.lot = lot
self.address = address
self.scenicSpotString = scenicSpotString
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
lat = try container.decodeIfPresent(Double.self, forKey: .lat) ?? 0
lot = try container.decodeIfPresent(Double.self, forKey: .lot) ?? 0
address = try container.decodeIfPresent(String.self, forKey: .address) ?? ""
scenicSpotString = try container.decodeIfPresent(String.self, forKey: .scenicSpotString)
}
}
/// Android `ScenicSpotItem`
struct PunchPointItem: Decodable, Sendable, Equatable, Hashable, Identifiable {
let id: Int64
let name: String
let status: Int
let statusLabel: String
let region: PunchPointRegion?
let scenicSpotString: String
let guideImages: [String]
let mpQrcode: String?
let createdAt: String
let creatorId: Int64?
let creator: String?
let creatorPhone: String?
let auditor: String?
let auditTime: String?
let auditRemark: String?
enum CodingKeys: String, CodingKey {
case id
case name
case status
case statusLabel = "status_label"
case region
case scenicSpotString = "scenic_spot_str"
case guideImages = "guide_imgs"
case mpQrcode = "mp_qrcode"
case createdAt = "created_at"
case creatorId = "creator_id"
case creator
case creatorPhone = "creator_phone"
case auditor
case auditTime = "audit_time"
case auditRemark = "audit_remark"
}
init(
id: Int64 = 0,
name: String = "",
status: Int = 0,
statusLabel: String = "",
region: PunchPointRegion? = nil,
scenicSpotString: String = "",
guideImages: [String] = [],
mpQrcode: String? = nil,
createdAt: String = "",
creatorId: Int64? = nil,
creator: String? = nil,
creatorPhone: String? = nil,
auditor: String? = nil,
auditTime: String? = nil,
auditRemark: String? = nil
) {
self.id = id
self.name = name
self.status = status
self.statusLabel = statusLabel
self.region = region
self.scenicSpotString = scenicSpotString
self.guideImages = guideImages
self.mpQrcode = mpQrcode
self.createdAt = createdAt
self.creatorId = creatorId
self.creator = creator
self.creatorPhone = creatorPhone
self.auditor = auditor
self.auditTime = auditTime
self.auditRemark = auditRemark
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeFlexibleInt64(forKey: .id) ?? 0
name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0
statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? ""
region = try container.decodeIfPresent(PunchPointRegion.self, forKey: .region)
scenicSpotString = try container.decodeIfPresent(String.self, forKey: .scenicSpotString) ?? ""
guideImages = try container.decodeIfPresent([String].self, forKey: .guideImages) ?? []
mpQrcode = try container.decodeIfPresent(String.self, forKey: .mpQrcode)
createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
creatorId = try container.decodeFlexibleInt64(forKey: .creatorId)
creator = try container.decodeIfPresent(String.self, forKey: .creator)
creatorPhone = try container.decodeIfPresent(String.self, forKey: .creatorPhone)
auditor = try container.decodeIfPresent(String.self, forKey: .auditor)
auditTime = try container.decodeIfPresent(String.self, forKey: .auditTime)
auditRemark = try container.decodeIfPresent(String.self, forKey: .auditRemark)
}
///
var displayAddress: String {
let address = region?.address.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if !address.isEmpty { return address }
return scenicSpotString.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
/// Android `ScenicSpotDetailResponse`
struct PunchPointDetail: Decodable, Sendable, Equatable {
let id: Int64
let name: String
let status: Int
let statusLabel: String
let region: PunchPointRegion?
let scenicSpotString: String
let description: String?
let guideImages: [String]
let mpQrcode: String?
let createdAt: String
let creatorId: Int64?
let creator: String?
let creatorPhone: String?
let auditor: String?
let auditTime: String?
let auditRemark: String?
enum CodingKeys: String, CodingKey {
case id
case name
case status
case statusLabel = "status_label"
case region
case scenicSpotString = "scenic_spot_str"
case description
case guideImages = "guide_imgs"
case mpQrcode = "mp_qrcode"
case createdAt = "created_at"
case creatorId = "creator_id"
case creator
case creatorPhone = "creator_phone"
case auditor
case auditTime = "audit_time"
case auditRemark = "audit_remark"
}
init(
id: Int64 = 0,
name: String = "",
status: Int = 0,
statusLabel: String = "",
region: PunchPointRegion? = nil,
scenicSpotString: String = "",
description: String? = nil,
guideImages: [String] = [],
mpQrcode: String? = nil,
createdAt: String = "",
creatorId: Int64? = nil,
creator: String? = nil,
creatorPhone: String? = nil,
auditor: String? = nil,
auditTime: String? = nil,
auditRemark: String? = nil
) {
self.id = id
self.name = name
self.status = status
self.statusLabel = statusLabel
self.region = region
self.scenicSpotString = scenicSpotString
self.description = description
self.guideImages = guideImages
self.mpQrcode = mpQrcode
self.createdAt = createdAt
self.creatorId = creatorId
self.creator = creator
self.creatorPhone = creatorPhone
self.auditor = auditor
self.auditTime = auditTime
self.auditRemark = auditRemark
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeFlexibleInt64(forKey: .id) ?? 0
name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0
statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? ""
region = try container.decodeIfPresent(PunchPointRegion.self, forKey: .region)
scenicSpotString = try container.decodeIfPresent(String.self, forKey: .scenicSpotString) ?? ""
description = try container.decodeIfPresent(String.self, forKey: .description)
guideImages = try container.decodeIfPresent([String].self, forKey: .guideImages) ?? []
mpQrcode = try container.decodeIfPresent(String.self, forKey: .mpQrcode)
createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
creatorId = try container.decodeFlexibleInt64(forKey: .creatorId)
creator = try container.decodeIfPresent(String.self, forKey: .creator)
creatorPhone = try container.decodeIfPresent(String.self, forKey: .creatorPhone)
auditor = try container.decodeIfPresent(String.self, forKey: .auditor)
auditTime = try container.decodeIfPresent(String.self, forKey: .auditTime)
auditRemark = try container.decodeIfPresent(String.self, forKey: .auditRemark)
}
///
var displayAddress: String {
let address = region?.address.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if !address.isEmpty { return address }
return scenicSpotString.trimmingCharacters(in: .whitespacesAndNewlines)
}
///
var coordinate: CLLocationCoordinate2D? {
guard let region else { return nil }
return CLLocationCoordinate2D(latitude: region.lat, longitude: region.lot)
}
}
///
struct PunchPointSaveRequest: Encodable, Sendable, Equatable {
let id: Int64?
let scenicAreaId: String
let name: String
let description: String?
let region: PunchPointSaveRegion
let scenicSpotString: String
let guideImages: [String]
enum CodingKeys: String, CodingKey {
case id
case scenicAreaId = "scenic_area_id"
case name
case description
case region
case scenicSpotString = "scenic_spot_str"
case guideImages = "guide_imgs"
}
init(
id: Int64? = nil,
scenicAreaId: String,
name: String,
description: String?,
region: PunchPointSaveRegion,
scenicSpotString: String,
guideImages: [String]
) {
self.id = id
self.scenicAreaId = scenicAreaId
self.name = name
self.description = description
self.region = region
self.scenicSpotString = scenicSpotString
self.guideImages = guideImages
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(id, forKey: .id)
try container.encode(scenicAreaId, forKey: .scenicAreaId)
try container.encode(name, forKey: .name)
try container.encodeIfPresent(description, forKey: .description)
try container.encode(region, forKey: .region)
try container.encode(scenicSpotString, forKey: .scenicSpotString)
try container.encode(guideImages, forKey: .guideImages)
}
}
///
struct PunchPointSaveRegion: Encodable, Sendable, Equatable {
let lot: Double
let lat: Double
let address: String
}
///
struct PunchPointIDRequest: Encodable, Sendable, Equatable {
let id: Int64
}
///
struct PunchPointImageState: Sendable, Equatable, Hashable, Identifiable {
let id: UUID
var data: Data
var previewURL: String?
var fileName: String
var uploadedURL: String
var isUploading: Bool
var uploadProgress: Int
var errorMessage: String?
init(
id: UUID = UUID(),
data: Data,
previewURL: String? = nil,
fileName: String,
uploadedURL: String = "",
isUploading: Bool = false,
uploadProgress: Int = 0,
errorMessage: String? = nil
) {
self.id = id
self.data = data
self.previewURL = previewURL
self.fileName = fileName
self.uploadedURL = uploadedURL
self.isUploading = isUploading
self.uploadProgress = uploadProgress
self.errorMessage = errorMessage
}
///
static func remote(url: String) -> PunchPointImageState {
PunchPointImageState(
data: Data(),
previewURL: url,
fileName: URL(string: url)?.lastPathComponent.nonEmptyTrimmed ?? "punch_point.jpg",
uploadedURL: url,
isUploading: false,
uploadProgress: 100
)
}
///
var isUploaded: Bool { !uploadedURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
}
///
struct PunchPointUploadDialogState: Sendable, Equatable {
let title: String
let progress: Int
}
/// UI
enum PunchPointDisplayFormatter {
///
static func maskedPhone(_ phone: String?) -> String {
guard let phone = phone?.trimmingCharacters(in: .whitespacesAndNewlines), !phone.isEmpty else { return "--" }
guard phone.count >= 11 else { return phone }
let prefix = phone.prefix(3)
let suffix = phone.suffix(phone.count - 7)
return "\(prefix)****\(suffix)"
}
/// /
static func statusColors(_ status: Int) -> (background: UInt, text: UInt) {
switch status {
case 1:
return (0xF0FDF4, 0x09BE4F)
case 2:
return (0xFFF0E2, 0xFF7B00)
case 3:
return (0xEFF6FF, 0x0073FF)
case 4:
return (0xFFE7E7, 0xEF4444)
default:
return (0xF4F4F4, 0x666666)
}
}
}
private extension KeyedDecodingContainer {
func decodeFlexibleInt64(forKey key: Key) throws -> Int64? {
if let value = try decodeIfPresent(Int64.self, forKey: key) {
return value
}
if let value = try decodeIfPresent(Int.self, forKey: key) {
return Int64(value)
}
if let value = try decodeIfPresent(String.self, forKey: key) {
return Int64(value)
}
return nil
}
}
private extension String {
var nonEmptyTrimmed: String? {
let text = trimmingCharacters(in: .whitespacesAndNewlines)
return text.isEmpty ? nil : text
}
}