Files
suixinkan_ios_new/suixinkan/Features/WildPhotographerReport/Models/WildPhotographerReportModels.swift

350 lines
9.0 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.

//
// 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
}
}