// // WildPhotographerReportModels.swift // suixinkan // import CoreLocation import Foundation import MapKit import UIKit /// 举报摄影师页面状态,用于演示加载、空态和异常态。 enum WildReportPageState: String, CaseIterable, Hashable { case normal case loading case empty case error var title: String { switch self { case .normal: return "正常" case .loading: return "加载" case .empty: return "空状态" case .error: return "异常" } } } /// 举报附件类型,区分现场图片、视频和支付截图。 enum WildReportAttachmentKind: Hashable { case image case video case payment } /// 举报类型,用于提交页单选并写入 Mock 举报记录。 enum WildReportType: String, CaseIterable, Hashable { case suspectedWild = "疑似打野" case noWorkBadge = "未戴工牌" case other = "其他" var iconName: String { switch self { case .suspectedWild: return "person.crop.circle.badge.exclamationmark" case .noWorkBadge: return "lanyardcard" case .other: return "ellipsis.circle" } } } /// 举报附件实体,表示一条演示用的上传项。 struct WildReportAttachment: Hashable { let id: UUID let kind: WildReportAttachmentKind let title: String init(id: UUID = UUID(), kind: WildReportAttachmentKind, title: String) { self.id = id self.kind = kind self.title = title } } /// 举报处理状态。 enum WildReportStatus: String, Hashable, CaseIterable { case pending = "待处理" case processing = "处理中" case processed = "已处理" var displayColor: UIColor { switch self { case .pending: return AppColor.warning case .processing: return AppColor.primary case .processed: return AppColor.success } } var backgroundColor: UIColor { switch self { case .pending: return AppColor.warningBackground case .processing: return AppColor.infoBackground case .processed: return AppColor.successBackground } } } /// 我的举报列表筛选条件。 enum WildReportListFilter: String, CaseIterable, Hashable { case all = "全部" case pending = "待处理" case processing = "处理中" case processed = "已处理" } /// 补充证据记录实体。 struct WildReportSupplementaryEvidence: Hashable { let id: String let submitTime: String let text: String let imageCount: Int let videoCount: Int var summaryText: String { var parts: [String] = [] if !text.isEmpty { parts.append(text) } if imageCount > 0 { parts.append("\(imageCount)张图片") } if videoCount > 0 { parts.append("\(videoCount)段视频") } return parts.isEmpty ? "补充证据" : parts.joined(separator: " · ") } } /// 举报位置解析工具。 enum WildReportLocationParser { /// 将「景区 · 详细地址」拆成两段展示文案。 static func split(_ location: String) -> (scenic: String, detail: String) { let parts = location .components(separatedBy: " · ") .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } if parts.count >= 2 { return (parts[0], parts[1]) } return (location, location) } } /// 附近风险点实体。 struct WildReportRiskPoint: Hashable { let id: Int let title: String let location: String let distance: String let riskLevel: String let evidenceCount: Int let latestTime: String let description: String } /// 举报记录实体。 struct WildReportRecord: Hashable { let id: String let title: String let reportType: WildReportType let status: WildReportStatus let location: String let submitTime: String let description: String let wechatID: String let phoneNumber: String let imageCount: Int let videoCount: Int let handlerInfo: WildReportHandlerInfo? var contactDisplayText: String { switch (wechatID.isEmpty, phoneNumber.isEmpty) { case (true, true): return "未填写" case (false, false): return "\(wechatID) / \(phoneNumber)" case (false, true): return wechatID case (true, false): return phoneNumber } } } /// 处理完成后的媒体项。 struct WildReportCompletionMediaItem: Hashable { enum Kind: Hashable { case image case video } let id: String let kind: Kind let coverURL: String let duration: String? } /// 举报处理人信息实体。 struct WildReportHandlerInfo: Hashable { let handlerName: String let handlerPhone: String let processingAt: String let processedAt: String let processOpinion: String let completionMedia: [WildReportCompletionMediaItem] var maskedHandlerPhone: String { guard handlerPhone.count == 11, handlerPhone.allSatisfy(\.isNumber) else { return handlerPhone } return "\(handlerPhone.prefix(3))****\(handlerPhone.suffix(4))" } var completionMediaSummary: String { let imageCount = completionMedia.filter { $0.kind == .image }.count let videoCount = completionMedia.filter { $0.kind == .video }.count switch (imageCount, videoCount) { case (0, 0): return "0项" case (_, 0): return "\(imageCount)张" case (0, _): return "\(videoCount)段" default: return "\(imageCount)张 \(videoCount)段" } } } /// 举报进度节点状态。 enum WildReportProgressStepState: Hashable { case completed case current case pending } /// 举报进度时间轴节点。 struct WildReportProgressStep: Hashable { let id: String let title: String let timeText: String let state: WildReportProgressStepState } /// 举报人材料预览项。 enum WildReportReporterMediaItem: Hashable { case image(index: Int) case video(index: Int) var id: String { switch self { case .image(let index): return "reporter-image-\(index)" case .video(let index): return "reporter-video-\(index)" } } } /// 举报提交结果,用于成功页展示。 struct WildReportSubmitResult: Hashable { let record: WildReportRecord let imageCount: Int let videoCount: Int } /// 风险地图标记类型。 enum WildReportMapMarkerKind: Hashable { case selfLocation case wildPhotographer case processingClue case peerPhotographer var color: UIColor { switch self { case .selfLocation: return AppColor.primary case .wildPhotographer: return AppColor.danger case .processingClue: return AppColor.warning case .peerPhotographer: return AppColor.success } } var title: String { switch self { case .selfLocation: return "我的位置" case .wildPhotographer: return "疑似打野" case .processingClue: return "处理中线索" case .peerPhotographer: return "内部摄影师" } } } /// 雷达地图标记颜色类型。 enum WildReportRadarMarkerType: String, Hashable { case blue case green case red } /// 雷达线索举报内容条目。 struct WildReportRadarReportContent: Hashable { let time: String let content: String } /// 雷达线索处理记录。 struct WildReportRadarProcessRecord: Hashable { let handlerName: String let phone: String let maskedPhone: String let statusText: String let processResult: String let processedAt: String? let processingAt: String? } /// 雷达线索处理时间轴节点。 struct WildReportRadarProcessingTimelineItem: Hashable { let title: String let time: String let status: String } /// 雷达地图活动线索详情。 struct WildReportRadarActiveClue: Hashable { let id: String let type: WildReportRadarMarkerType let displayTitle: String let statusText: String let actionText: String let nearestAbnormalText: String? let distance: String? let direction: String? let updatedAt: String? let name: String? let storeName: String? let avatar: String? let summary: String? let detail: String? let distanceText: String? let reporterName: String? let reporterPhone: String? let maskedReporterPhone: String? let reportContents: [WildReportRadarReportContent] let images: [String] let processingStarted: Bool let completed: Bool let record: WildReportRadarProcessRecord? let processingTimeline: [WildReportRadarProcessingTimelineItem] let completionPhoto: String? } /// 风险地图标记详情。 enum WildReportMapMarkerDetail: Hashable { case activeClue(WildReportRadarActiveClue) } /// 风险地图标记实体。 struct WildReportMapMarker: Hashable { let id: String let title: String let coordinate: CLLocationCoordinate2D let kind: WildReportMapMarkerKind let detail: WildReportMapMarkerDetail static func == (lhs: WildReportMapMarker, rhs: WildReportMapMarker) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } } extension CLLocationCoordinate2D: @retroactive Equatable { public static func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool { lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude } }