import SwiftUI import os.log /// 媒体上传状态 public enum MediaUploadStatus: Equatable { case pending case uploading(progress: Double) case completed(fileId: String) case failed(Error) public static func == (lhs: MediaUploadStatus, rhs: MediaUploadStatus) -> Bool { switch (lhs, rhs) { case (.pending, .pending): return true case (.uploading(let lhsProgress), .uploading(let rhsProgress)): return lhsProgress == rhsProgress case (.completed(let lhsId), .completed(let rhsId)): return lhsId == rhsId case (.failed, .failed): return false // Errors don't need to be equatable default: return false } } public var description: String { switch self { case .pending: return "等待上传" case .uploading(let progress): return "上传中 \(Int(progress * 100))%" case .completed(let fileId): return "上传完成 (ID: \(fileId.prefix(8))...)" case .failed(let error): return "上传失败: \(error.localizedDescription)" } } } /// 媒体上传管理器 public class MediaUploadManager: ObservableObject { /// 已选媒体文件 @Published public var selectedMedia: [MediaType] = [] /// 上传状态 @Published public var uploadStatus: [String: MediaUploadStatus] = [:] private let uploader = ImageUploadService() public init() {} /// 添加上传媒体 public func addMedia(_ media: [MediaType]) { selectedMedia.append(contentsOf: media) } /// 移除指定索引的媒体 public func removeMedia(at index: Int) { guard index < selectedMedia.count else { return } selectedMedia.remove(at: index) // 更新状态字典 var newStatus: [String: MediaUploadStatus] = [:] uploadStatus.forEach { key, value in if let keyInt = Int(key), keyInt < index { newStatus[key] = value } else if let keyInt = Int(key), keyInt > index { newStatus["\(keyInt - 1)"] = value } } uploadStatus = newStatus } /// 清空所有媒体 public func clearAllMedia() { selectedMedia.removeAll() uploadStatus.removeAll() } /// 开始上传所有选中的媒体 public func startUpload() { print("🔄 开始批量上传 \(selectedMedia.count) 个文件") // 重置上传状态 uploadStatus.removeAll() for (index, media) in selectedMedia.enumerated() { let id = "\(index)" uploadStatus[id] = .pending // Convert MediaType to ImageUploadService.MediaType let uploadMediaType: ImageUploadService.MediaType switch media { case .image(let image): uploadMediaType = .image(image) case .video(let url, let thumbnail): uploadMediaType = .video(url, thumbnail) } uploadMedia(uploadMediaType, id: id) } } /// 获取上传结果 public func getUploadResults() -> [String: String] { var results: [String: String] = [:] for (id, status) in uploadStatus { if case .completed(let fileId) = status { results[id] = fileId } } return results } /// 检查是否所有上传都已完成 public var isAllUploaded: Bool { guard !selectedMedia.isEmpty else { return false } return uploadStatus.allSatisfy { _, status in if case .completed = status { return true } return false } } // MARK: - Private Methods private func uploadMedia(_ media: ImageUploadService.MediaType, id: String) { print("🔄 开始处理媒体: \(id)") uploadStatus[id] = .uploading(progress: 0) uploader.uploadMedia( media, progress: { progress in print("📊 上传进度 (\(id)): \(progress.current)%") DispatchQueue.main.async { self.uploadStatus[id] = .uploading(progress: progress.progress) } }, completion: { [weak self] result in guard let self = self else { return } DispatchQueue.main.async { switch result { case .success(let uploadResult): print("✅ 上传成功 (\(id)): \(uploadResult.fileId)") self.uploadStatus[id] = .completed(fileId: uploadResult.fileId) case .failure(let error): print("❌ 上传失败 (\(id)): \(error.localizedDescription)") self.uploadStatus[id] = .failed(error) } } } ) } } // MARK: - Preview Helper /// 示例视图,展示如何使用 MediaUploadManager struct MediaUploadExample: View { @StateObject private var uploadManager = MediaUploadManager() @State private var showMediaPicker = false // 添加图片和视频选择限制参数 let imageSelectionLimit: Int let videoSelectionLimit: Int init(imageSelectionLimit: Int = 10, videoSelectionLimit: Int = 10) { self.imageSelectionLimit = imageSelectionLimit self.videoSelectionLimit = videoSelectionLimit } var body: some View { NavigationView { VStack(spacing: 20) { // 选择媒体按钮 Button(action: { showMediaPicker = true }) { Label("选择媒体", systemImage: "photo.on.rectangle") .font(.headline) .frame(maxWidth: .infinity) .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(10) } .padding(.horizontal) // 显示选择限制信息 VStack(alignment: .leading, spacing: 4) { Text("选择限制:") .font(.subheadline) .foregroundColor(.secondary) Text("• 最多选择 \(imageSelectionLimit) 张图片") .font(.caption) .foregroundColor(.secondary) Text("• 最多选择 \(videoSelectionLimit) 个视频") .font(.caption) .foregroundColor(.secondary) } .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal) // 显示已选媒体 MediaSelectionView(uploadManager: uploadManager) // 上传按钮 Button(action: { uploadManager.startUpload() }) { Text("开始上传") .font(.headline) .frame(maxWidth: .infinity) .padding() .background(uploadManager.selectedMedia.isEmpty ? Color.gray : Color.green) .foregroundColor(.white) .cornerRadius(10) } .padding(.horizontal) .disabled(uploadManager.selectedMedia.isEmpty) Spacer() } .navigationTitle("媒体上传") .sheet(isPresented: $showMediaPicker) { MediaPicker( selectedMedia: $uploadManager.selectedMedia, imageSelectionLimit: imageSelectionLimit, videoSelectionLimit: videoSelectionLimit, onDismiss: { showMediaPicker = false } ) } } } } /// 媒体选择视图组件 struct MediaSelectionView: View { @ObservedObject var uploadManager: MediaUploadManager var body: some View { if !uploadManager.selectedMedia.isEmpty { VStack(spacing: 10) { Text("已选择 \(uploadManager.selectedMedia.count) 个媒体文件") .font(.headline) // 显示媒体缩略图和上传状态 List { ForEach(0.. Color { switch status { case .pending: return .secondary case .uploading: return .blue case .completed: return .green case .failed: return .red } } } /// 媒体缩略图视图 private struct MediaThumbnailView: View { let media: MediaType let onDelete: (() -> Void)? var body: some View { ZStack(alignment: .topTrailing) { if let thumbnail = media.thumbnail { Image(uiImage: thumbnail) .resizable() .scaledToFill() .frame(width: 80, height: 80) .cornerRadius(8) .clipped() if media.isVideo { Image(systemName: "video.fill") .foregroundColor(.white) .padding(4) .background(Color.black.opacity(0.6)) .clipShape(Circle()) .padding(4) } } } } } #Preview { // 在预览中显示自定义限制 MediaUploadExample( imageSelectionLimit: 5, videoSelectionLimit: 2 ) .environmentObject(AuthState.shared) }