新增举报摄影师模块并接入首页路由,桌面应用名改为随心瞰商家版。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -0,0 +1,349 @@
|
||||
//
|
||||
// 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user