350 lines
9.3 KiB
Swift
350 lines
9.3 KiB
Swift
//
|
||
// 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
|
||
}
|
||
}
|