Add submit-task flow aligned with Android and extract profile edit page.
Implement task creation with cloud/local media, order linking, and OSS upload; wire home entry and move profile nickname/avatar editing to ProfileEditViewController. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
260
suixinkan/Features/Task/Models/TaskModels.swift
Normal file
260
suixinkan/Features/Task/Models/TaskModels.swift
Normal file
@ -0,0 +1,260 @@
|
||||
//
|
||||
// TaskModels.swift
|
||||
// suixinkan
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// 创建任务请求体,对齐 Android `AddTaskRequest`。
|
||||
struct AddTaskRequest: Encodable, Sendable, Equatable {
|
||||
let scenicId: Int
|
||||
let name: String
|
||||
let orderNumber: String?
|
||||
let remark: String
|
||||
let urgentHour: Int
|
||||
let cloudFile: [TaskCloudFileItem]
|
||||
let uploadFile: [TaskUploadFileItem]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case scenicId = "scenic_id"
|
||||
case name
|
||||
case orderNumber = "order_number"
|
||||
case remark = "photog_remark"
|
||||
case urgentHour = "urgent_hour"
|
||||
case cloudFile = "cloud_file"
|
||||
case uploadFile = "upload_file"
|
||||
}
|
||||
}
|
||||
|
||||
/// 云盘文件引用,对齐 Android `CloudFileItem`。
|
||||
struct TaskCloudFileItem: Encodable, Sendable, Equatable {
|
||||
let fileId: Int
|
||||
let remark: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case fileId = "file_id"
|
||||
case remark
|
||||
}
|
||||
}
|
||||
|
||||
/// 本地上传文件引用,对齐 Android `UploadFileItem`。
|
||||
struct TaskUploadFileItem: Encodable, Sendable, Equatable {
|
||||
let fileUrl: String
|
||||
let fileName: String
|
||||
let remark: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case fileUrl = "file_url"
|
||||
case fileName = "file_name"
|
||||
case remark
|
||||
}
|
||||
}
|
||||
|
||||
/// 可选关联订单,对齐 Android `AvailableOrderResponse`。
|
||||
struct AvailableOrder: Decodable, Sendable, Equatable, Hashable {
|
||||
let projectName: String
|
||||
let orderNumber: String
|
||||
let orderStatus: Int
|
||||
let orderStatusLabel: String
|
||||
let payTime: String
|
||||
let userId: Int
|
||||
let userPhone: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case projectName = "project_name"
|
||||
case orderNumber = "order_number"
|
||||
case orderStatus = "order_status"
|
||||
case orderStatusLabel = "order_status_label"
|
||||
case payTime = "pay_time"
|
||||
case userId = "user_id"
|
||||
case userPhone = "user_phone"
|
||||
}
|
||||
|
||||
init(
|
||||
projectName: String = "",
|
||||
orderNumber: String = "",
|
||||
orderStatus: Int = 0,
|
||||
orderStatusLabel: String = "",
|
||||
payTime: String = "",
|
||||
userId: Int = 0,
|
||||
userPhone: String = ""
|
||||
) {
|
||||
self.projectName = projectName
|
||||
self.orderNumber = orderNumber
|
||||
self.orderStatus = orderStatus
|
||||
self.orderStatusLabel = orderStatusLabel
|
||||
self.payTime = payTime
|
||||
self.userId = userId
|
||||
self.userPhone = userPhone
|
||||
}
|
||||
}
|
||||
|
||||
/// 云盘文件/文件夹实体,对齐 Android `CloudFileEntity`。
|
||||
struct CloudFile: Decodable, Sendable, Equatable, Hashable {
|
||||
let id: Int
|
||||
let parentFolderId: Int
|
||||
let fileUrl: String
|
||||
let coverUrl: String
|
||||
let updatedAt: String
|
||||
let name: String
|
||||
let createdAt: String
|
||||
let childNum: Int
|
||||
let type: Int
|
||||
let fileSize: Int64
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case parentFolderId = "parent_folder_id"
|
||||
case fileUrl = "file_url"
|
||||
case coverUrl = "cover_url"
|
||||
case updatedAt = "updated_at"
|
||||
case name
|
||||
case createdAt = "created_at"
|
||||
case childNum = "child_num"
|
||||
case type
|
||||
case fileSize = "file_size"
|
||||
}
|
||||
|
||||
init(
|
||||
id: Int = 0,
|
||||
parentFolderId: Int = 0,
|
||||
fileUrl: String = "",
|
||||
coverUrl: String = "",
|
||||
updatedAt: String = "",
|
||||
name: String = "",
|
||||
createdAt: String = "",
|
||||
childNum: Int = 0,
|
||||
type: Int = 0,
|
||||
fileSize: Int64 = 0
|
||||
) {
|
||||
self.id = id
|
||||
self.parentFolderId = parentFolderId
|
||||
self.fileUrl = fileUrl
|
||||
self.coverUrl = coverUrl
|
||||
self.updatedAt = updatedAt
|
||||
self.name = name
|
||||
self.createdAt = createdAt
|
||||
self.childNum = childNum
|
||||
self.type = type
|
||||
self.fileSize = fileSize
|
||||
}
|
||||
|
||||
/// 是否为文件夹。
|
||||
var isFolder: Bool { type == 99 }
|
||||
|
||||
/// 是否为图片。
|
||||
var isImage: Bool { type == 2 }
|
||||
|
||||
/// 是否为视频。
|
||||
var isVideo: Bool { type == 1 }
|
||||
|
||||
/// 是否为可导入的媒体文件。
|
||||
var isSelectableMedia: Bool { isImage || isVideo }
|
||||
}
|
||||
|
||||
/// 云盘列表响应。
|
||||
struct CloudFileListResponse: Decodable, Sendable, Equatable {
|
||||
let total: Int
|
||||
let list: [CloudFile]
|
||||
|
||||
init(total: Int = 0, list: [CloudFile] = []) {
|
||||
self.total = total
|
||||
self.list = list
|
||||
}
|
||||
}
|
||||
|
||||
/// 云盘路径节点,用于面包屑导航。
|
||||
struct CloudPathItem: Sendable, Equatable, Hashable {
|
||||
let id: Int
|
||||
let name: String
|
||||
}
|
||||
|
||||
/// 任务素材来源。
|
||||
enum TaskFileSource: String, Sendable, Equatable {
|
||||
case cloud
|
||||
case upload
|
||||
}
|
||||
|
||||
/// 任务素材项,对齐 Android `TaskFileItem`。
|
||||
struct TaskFileItem: Sendable, Equatable, Hashable, Identifiable {
|
||||
let id: String
|
||||
let source: TaskFileSource
|
||||
let cloudFile: CloudFile?
|
||||
let uploadFileName: String?
|
||||
let uploadFileType: Int
|
||||
let uploadTaskId: String?
|
||||
var uploadFileUrl: String?
|
||||
var uploadCoverUrl: String?
|
||||
var remark: String
|
||||
var isUploading: Bool
|
||||
var uploadProgress: Int
|
||||
|
||||
init(
|
||||
id: String? = nil,
|
||||
source: TaskFileSource,
|
||||
cloudFile: CloudFile? = nil,
|
||||
uploadFileUrl: String? = nil,
|
||||
uploadFileName: String? = nil,
|
||||
uploadFileType: Int = 0,
|
||||
uploadCoverUrl: String? = nil,
|
||||
remark: String = "",
|
||||
uploadTaskId: String? = nil,
|
||||
isUploading: Bool = false,
|
||||
uploadProgress: Int = 0
|
||||
) {
|
||||
if let id {
|
||||
self.id = id
|
||||
} else if source == .cloud, let cloudFile {
|
||||
self.id = "cloud_\(cloudFile.id)"
|
||||
} else if let uploadTaskId {
|
||||
self.id = "upload_\(uploadTaskId)"
|
||||
} else {
|
||||
self.id = UUID().uuidString
|
||||
}
|
||||
self.source = source
|
||||
self.cloudFile = cloudFile
|
||||
self.uploadFileUrl = uploadFileUrl
|
||||
self.uploadFileName = uploadFileName
|
||||
self.uploadFileType = uploadFileType
|
||||
self.uploadCoverUrl = uploadCoverUrl
|
||||
self.remark = remark
|
||||
self.uploadTaskId = uploadTaskId
|
||||
self.isUploading = isUploading
|
||||
self.uploadProgress = uploadProgress
|
||||
}
|
||||
|
||||
/// 展示用缩略图 URL。
|
||||
var previewURLString: String {
|
||||
switch source {
|
||||
case .cloud:
|
||||
let cover = cloudFile?.coverUrl ?? ""
|
||||
if !cover.isEmpty { return cover }
|
||||
return cloudFile?.fileUrl ?? ""
|
||||
case .upload:
|
||||
if uploadFileType == 1 {
|
||||
return uploadCoverUrl ?? uploadFileUrl ?? ""
|
||||
}
|
||||
return uploadFileUrl ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
/// 文件类型:1=视频,2=图片。
|
||||
var fileType: Int {
|
||||
switch source {
|
||||
case .cloud:
|
||||
return cloudFile?.type ?? 0
|
||||
case .upload:
|
||||
return uploadFileType
|
||||
}
|
||||
}
|
||||
|
||||
/// 展示文件名。
|
||||
var displayName: String {
|
||||
switch source {
|
||||
case .cloud:
|
||||
return cloudFile?.name ?? ""
|
||||
case .upload:
|
||||
return uploadFileName ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user