// // WildPhotographerReportModels.swift // suixinkan // // Created by Codex on 2026/7/1. // import CoreLocation import MapKit import SwiftUI /// 举报摄影师页面状态,用于演示加载、空态和异常态。 enum WildReportPageState: String, CaseIterable, Identifiable { case normal case loading case empty case error var id: String { rawValue } var title: String { switch self { case .normal: "正常" case .loading: "加载" case .empty: "空状态" case .error: "异常" } } } /// 举报附件类型,区分现场图片、视频和支付截图。 enum WildReportAttachmentKind { case image case video case payment } /// 举报类型,用于提交页单选并写入 Mock 举报记录。 enum WildReportType: String, CaseIterable, Identifiable, Hashable { case suspectedWild = "疑似打野" case noWorkBadge = "未戴工牌" case other = "其他" var id: String { rawValue } var iconName: String { switch self { case .suspectedWild: "person.crop.circle.badge.exclamationmark" case .noWorkBadge: "lanyardcard" case .other: "ellipsis.circle" } } } /// 举报附件实体,表示一条演示用的上传项。 struct WildReportAttachment: Identifiable { let id = UUID() let kind: WildReportAttachmentKind let title: String } /// 举报处理状态。 enum WildReportStatus: String, Hashable, CaseIterable { case pending = "待处理" case processing = "处理中" case processed = "已处理" var displayColor: Color { switch self { case .pending: AppDesign.warning case .processing: AppDesign.primary case .processed: AppDesign.success } } } /// 我的举报列表筛选条件。 enum WildReportListFilter: String, CaseIterable, Identifiable { case all = "全部" case pending = "待处理" case processing = "处理中" case processed = "已处理" var id: String { rawValue } } extension WildReportStatus { var listDisplayText: String { rawValue } var listDisplayColor: Color { displayColor } } /// 补充证据记录实体。 struct WildReportSupplementaryEvidence: Identifiable, 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: Identifiable { 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: Identifiable, 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): "未填写" case (false, false): "\(wechatID) / \(phoneNumber)" case (false, true): wechatID case (true, false): phoneNumber } } } /// 处理完成后的媒体项。 struct WildReportCompletionMediaItem: Identifiable, 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 { case completed case current case pending } /// 举报进度时间轴节点。 struct WildReportProgressStep: Identifiable { let id: String let title: String let timeText: String let state: WildReportProgressStepState } /// 举报人材料预览项。 enum WildReportReporterMediaItem: Identifiable { case image(index: Int) case video(index: Int) var id: String { switch self { case .image(let index): "reporter-image-\(index)" case .video(let index): "reporter-video-\(index)" } } } /// 举报提交结果,用于成功页展示。 struct WildReportSubmitResult: Identifiable, Hashable { let record: WildReportRecord let imageCount: Int let videoCount: Int var id: String { record.id } } /// 风险地图标记类型。 enum WildReportMapMarkerKind { case selfLocation case wildPhotographer case processingClue case peerPhotographer } /// 风险地图媒体预览项。 struct WildReportMapMediaItem: Identifiable, Hashable { let id: String let title: String let systemImage: String let tint: Color let isVideo: Bool } /// 风险地图网络图片预览包装。 struct WildReportPreviewImageURL: Identifiable, Hashable { let url: String var id: String { url } } /// 雷达地图标记颜色类型。 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: Identifiable, 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 } }