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