feat: 上传进度
This commit is contained in:
parent
d0f0b09f8a
commit
a63d363001
@ -4,6 +4,7 @@ import PhotosUI
|
|||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
|
<<<<<<< HEAD
|
||||||
// MARK: - Photo Picker
|
// MARK: - Photo Picker
|
||||||
=======
|
=======
|
||||||
/// 上传管理器,处理图片上传
|
/// 上传管理器,处理图片上传
|
||||||
@ -174,22 +175,54 @@ struct UploadResults {
|
|||||||
}
|
}
|
||||||
|
|
||||||
>>>>>>> 5611df8 (feat: 素材上传成)
|
>>>>>>> 5611df8 (feat: 素材上传成)
|
||||||
|
=======
|
||||||
|
// MARK: - Data Models
|
||||||
|
|
||||||
|
/// 上传进度信息
|
||||||
|
public struct UploadProgress {
|
||||||
|
public let current: Int
|
||||||
|
public let total: Int
|
||||||
|
public let progress: Double
|
||||||
|
public let isOriginal: Bool
|
||||||
|
|
||||||
|
public init(current: Int, total: Int, progress: Double, isOriginal: Bool) {
|
||||||
|
self.current = current
|
||||||
|
self.total = total
|
||||||
|
self.progress = progress
|
||||||
|
self.isOriginal = isOriginal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 上传结果,包含原图和压缩图的上传信息
|
||||||
|
public struct UploadResults {
|
||||||
|
public let original: ImageUploaderGetID.UploadResult
|
||||||
|
public let compressed: ImageUploaderGetID.UploadResult
|
||||||
|
|
||||||
|
public init(original: ImageUploaderGetID.UploadResult,
|
||||||
|
compressed: ImageUploaderGetID.UploadResult) {
|
||||||
|
self.original = original
|
||||||
|
self.compressed = compressed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Photo Picker
|
||||||
|
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
/// 照片选择器,封装了系统相册选择功能
|
/// 照片选择器,封装了系统相册选择功能
|
||||||
/// 使用UIViewControllerRepresentable包装PHPickerViewController,提供SwiftUI兼容的图片选择界面
|
|
||||||
struct PhotoPicker: UIViewControllerRepresentable {
|
struct PhotoPicker: UIViewControllerRepresentable {
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
=======
|
=======
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
/// 绑定的已选图片数组,用于存储用户选择的图片
|
/// 绑定的已选图片数组,用于存储用户选择的图片
|
||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
@Binding var selectedImages: [UIImage]
|
@Binding var selectedImages: [UIImage]
|
||||||
|
|
||||||
/// 最多可选图片数量,默认为1
|
|
||||||
let selectionLimit: Int
|
let selectionLimit: Int
|
||||||
|
|
||||||
/// 图片过滤器,默认为图片类型,可过滤特定类型的媒体
|
|
||||||
let filter: PHPickerFilter
|
let filter: PHPickerFilter
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
@ -224,15 +257,30 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||||||
onImageUploaded: ((Result<UploadResults, Error>) -> Void)? = nil
|
onImageUploaded: ((Result<UploadResults, Error>) -> Void)? = nil
|
||||||
) {
|
) {
|
||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
=======
|
||||||
|
var onImageUploaded: ((Result<UploadResults, Error>) -> Void)?
|
||||||
|
var onUploadProgress: ((UploadProgress) -> Void)?
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
|
init(selectedImages: Binding<[UIImage]>,
|
||||||
|
selectionLimit: Int = 1,
|
||||||
|
filter: PHPickerFilter = .images,
|
||||||
|
onImageUploaded: ((Result<UploadResults, Error>) -> Void)? = nil,
|
||||||
|
onUploadProgress: ((UploadProgress) -> Void)? = nil) {
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
self._selectedImages = selectedImages
|
self._selectedImages = selectedImages
|
||||||
self.selectionLimit = selectionLimit
|
self.selectionLimit = selectionLimit
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
self.onImageUploaded = onImageUploaded
|
self.onImageUploaded = onImageUploaded
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
self.onUploadProgress = onUploadProgress
|
self.onUploadProgress = onUploadProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UIViewControllerRepresentable
|
// MARK: - UIViewControllerRepresentable
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
// MARK: - UIViewControllerRepresentable 协议方法
|
// MARK: - UIViewControllerRepresentable 协议方法
|
||||||
=======
|
=======
|
||||||
@ -242,6 +290,8 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
|
||||||
/// 创建并返回配置好的PHPickerViewController
|
/// 创建并返回配置好的PHPickerViewController
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
func makeUIViewController(context: Context) -> PHPickerViewController {
|
func makeUIViewController(context: Context) -> PHPickerViewController {
|
||||||
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
|
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
|
||||||
configuration.filter = filter
|
configuration.filter = filter
|
||||||
@ -253,15 +303,14 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||||||
return picker
|
return picker
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 更新视图控制器(空实现,因为不需要更新)
|
|
||||||
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {}
|
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {}
|
||||||
|
|
||||||
/// 创建协调器,用于处理PHPickerViewController的代理方法
|
|
||||||
func makeCoordinator() -> Coordinator {
|
func makeCoordinator() -> Coordinator {
|
||||||
Coordinator(self)
|
Coordinator(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Coordinator
|
// MARK: - Coordinator
|
||||||
|
<<<<<<< HEAD
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
|
|
||||||
// MARK: - 协调器类
|
// MARK: - 协调器类
|
||||||
@ -269,25 +318,26 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
|
||||||
/// 协调器类,处理PHPickerViewController的代理方法
|
/// 协调器类,处理PHPickerViewController的代理方法
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
class Coordinator: NSObject, PHPickerViewControllerDelegate {
|
class Coordinator: NSObject, PHPickerViewControllerDelegate {
|
||||||
/// 对父视图的弱引用
|
|
||||||
let parent: PhotoPicker
|
let parent: PhotoPicker
|
||||||
|
<<<<<<< HEAD
|
||||||
private let uploadService = ImageUploadService.shared
|
private let uploadService = ImageUploadService.shared
|
||||||
|
|
||||||
/// 图片上传器实例
|
/// 图片上传器实例
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
private let uploader = ImageUploaderGetID()
|
private let uploader = ImageUploaderGetID()
|
||||||
|
|
||||||
init(_ parent: PhotoPicker) {
|
init(_ parent: PhotoPicker) {
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 当用户完成图片选择时调用
|
|
||||||
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
||||||
// 清空已选图片
|
|
||||||
parent.selectedImages.removeAll()
|
parent.selectedImages.removeAll()
|
||||||
|
|
||||||
// 使用DispatchGroup管理多个异步任务
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
<<<<<<< HEAD
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
var loadedImages: [Int: UIImage] = [:]
|
var loadedImages: [Int: UIImage] = [:]
|
||||||
var uploadResults: [Int: ImageUploadService.UploadResults] = [:]
|
var uploadResults: [Int: ImageUploadService.UploadResults] = [:]
|
||||||
@ -300,10 +350,14 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||||||
var uploadResults: [Int: (original: ImageUploaderGetID.UploadResult?,
|
var uploadResults: [Int: (original: ImageUploaderGetID.UploadResult?,
|
||||||
compressed: ImageUploaderGetID.UploadResult?)] = [:]
|
compressed: ImageUploaderGetID.UploadResult?)] = [:]
|
||||||
>>>>>>> 5611df8 (feat: 素材上传成)
|
>>>>>>> 5611df8 (feat: 素材上传成)
|
||||||
|
=======
|
||||||
|
var loadedImages: [Int: UIImage] = [:]
|
||||||
|
var uploadResults: [Int: (original: ImageUploaderGetID.UploadResult?,
|
||||||
|
compressed: ImageUploaderGetID.UploadResult?)] = [:]
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
|
|
||||||
// 遍历所有选中的图片
|
|
||||||
for (index, result) in results.enumerated() {
|
for (index, result) in results.enumerated() {
|
||||||
group.enter() // 进入组
|
group.enter()
|
||||||
|
|
||||||
if result.itemProvider.canLoadObject(ofClass: UIImage.self) {
|
if result.itemProvider.canLoadObject(ofClass: UIImage.self) {
|
||||||
result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] (image, error) in
|
result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] (image, error) in
|
||||||
@ -320,15 +374,14 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 保存原始图片
|
|
||||||
loadedImages[index] = image
|
loadedImages[index] = image
|
||||||
|
|
||||||
// 2. 压缩图片(质量压缩到50%)
|
|
||||||
guard let compressedImage = image.jpegData(compressionQuality: 0.5).flatMap(UIImage.init(data:)) else {
|
guard let compressedImage = image.jpegData(compressionQuality: 0.5).flatMap(UIImage.init(data:)) else {
|
||||||
group.leave()
|
group.leave()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
// 3. 上传原图
|
// 3. 上传原图
|
||||||
self.uploader.uploadImage(image) { [weak self] originalResult in
|
self.uploader.uploadImage(image) { [weak self] originalResult in
|
||||||
guard let self = self else {
|
guard let self = self else {
|
||||||
@ -371,19 +424,89 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||||||
print("❌ 压缩图上传失败: \(error.localizedDescription)")
|
print("❌ 压缩图上传失败: \(error.localizedDescription)")
|
||||||
uploadResults[index] = (originalUploadResult, nil)
|
uploadResults[index] = (originalUploadResult, nil)
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
self.uploader.uploadImage(
|
||||||
|
image,
|
||||||
|
progress: { [weak self] progress in
|
||||||
|
let progressInfo = UploadProgress(
|
||||||
|
current: Int(progress * 100),
|
||||||
|
total: 100,
|
||||||
|
progress: progress,
|
||||||
|
isOriginal: true
|
||||||
|
)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self?.parent.onUploadProgress?(progressInfo)
|
||||||
|
}
|
||||||
|
print("📤 原图上传进度: \(Int(progress * 100))%")
|
||||||
|
},
|
||||||
|
completion: { [weak self] originalResult in
|
||||||
|
guard let self = self else {
|
||||||
|
group.leave()
|
||||||
|
return
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
}
|
}
|
||||||
|
|
||||||
case .failure(let error):
|
switch originalResult {
|
||||||
print("❌ 原图上传失败: \(error.localizedDescription)")
|
case .success(let originalUploadResult):
|
||||||
group.leave()
|
self.uploader.uploadImage(
|
||||||
|
compressedImage,
|
||||||
|
progress: { [weak self] progress in
|
||||||
|
let progressInfo = UploadProgress(
|
||||||
|
current: Int(progress * 100),
|
||||||
|
total: 100,
|
||||||
|
progress: progress,
|
||||||
|
isOriginal: false
|
||||||
|
)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self?.parent.onUploadProgress?(progressInfo)
|
||||||
|
}
|
||||||
|
print("📊 压缩图上传进度: \(Int(progress * 100))%")
|
||||||
|
},
|
||||||
|
completion: { compressedResult in
|
||||||
|
defer { group.leave() }
|
||||||
|
|
||||||
|
switch compressedResult {
|
||||||
|
case .success(let compressedUploadResult):
|
||||||
|
uploadResults[index] = (originalUploadResult, compressedUploadResult)
|
||||||
|
print("✅ 原图和压缩图上传成功!")
|
||||||
|
print("📂 原图信息:")
|
||||||
|
print(" - 文件ID: \(originalUploadResult.fileId)")
|
||||||
|
print(" - 文件大小: \(originalUploadResult.fileSize) 字节")
|
||||||
|
print("📦 压缩图信息:")
|
||||||
|
print(" - 文件ID: \(compressedUploadResult.fileId)")
|
||||||
|
print(" - 文件大小: \(compressedUploadResult.fileSize) 字节")
|
||||||
|
|
||||||
|
MaterialService.shared.uploadMaterialInfo(
|
||||||
|
fileId: originalUploadResult.fileId,
|
||||||
|
previewFileId: compressedUploadResult.fileId
|
||||||
|
) { success, errorMessage in
|
||||||
|
if success {
|
||||||
|
print("✅ 文件信息上传成功 素材上传成功!!!!!")
|
||||||
|
} else if let errorMessage = errorMessage {
|
||||||
|
print("❌ 文件信息上传失败: \(errorMessage)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
print("❌ 压缩图上传失败: \(error.localizedDescription)")
|
||||||
|
uploadResults[index] = (originalUploadResult, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
print("❌ 原图上传失败: \(error.localizedDescription)")
|
||||||
|
group.leave()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
// 所有图片加载完成后的处理
|
// 所有图片加载完成后的处理
|
||||||
group.notify(queue: .main) {
|
group.notify(queue: .main) {
|
||||||
@ -407,35 +530,39 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
=======
|
=======
|
||||||
// 所有上传任务完成后的处理
|
// 所有上传任务完成后的处理
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
group.notify(queue: .main) { [weak self] in
|
group.notify(queue: .main) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
// 1. 更新选中的图片(只显示原图)
|
|
||||||
let sortedImages = loadedImages.sorted { $0.key < $1.key }.map { $0.value }
|
let sortedImages = loadedImages.sorted { $0.key < $1.key }.map { $0.value }
|
||||||
self.parent.selectedImages.append(contentsOf: sortedImages)
|
self.parent.selectedImages.append(contentsOf: sortedImages)
|
||||||
|
|
||||||
// 2. 检查是否所有上传都成功
|
|
||||||
if let firstResult = uploadResults.first?.value,
|
if let firstResult = uploadResults.first?.value,
|
||||||
let original = firstResult.original,
|
let original = firstResult.original,
|
||||||
let compressed = firstResult.compressed {
|
let compressed = firstResult.compressed {
|
||||||
// 3. 如果成功,返回上传结果
|
|
||||||
let results = UploadResults(original: original, compressed: compressed)
|
let results = UploadResults(original: original, compressed: compressed)
|
||||||
self.parent.onImageUploaded?(.success(results))
|
self.parent.onImageUploaded?(.success(results))
|
||||||
} else {
|
} else {
|
||||||
// 4. 如果失败,返回错误
|
self.parent.onImageUploaded?(.failure(NSError(
|
||||||
self.parent.onImageUploaded?(.failure(NSError(domain: "com.wake.upload",
|
domain: "com.wake.upload",
|
||||||
code: -1,
|
code: -1,
|
||||||
userInfo: [NSLocalizedDescriptionKey: "上传过程中出现错误"])))
|
userInfo: [NSLocalizedDescriptionKey: "上传过程中出现错误"]
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
// 5. 关闭图片选择器
|
// 5. 关闭图片选择器
|
||||||
>>>>>>> 5611df8 (feat: 素材上传成)
|
>>>>>>> 5611df8 (feat: 素材上传成)
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
picker.dismiss(animated: true)
|
picker.dismiss(animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
// MARK: - Avatar Uploader Component
|
// MARK: - Avatar Uploader Component
|
||||||
=======
|
=======
|
||||||
@ -444,28 +571,25 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||||||
/// 头像上传视图,提供头像选择功能
|
/// 头像上传视图,提供头像选择功能
|
||||||
/// 封装了头像显示和选择逻辑,支持点击选择新头像
|
/// 封装了头像显示和选择逻辑,支持点击选择新头像
|
||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
=======
|
||||||
|
// MARK: - Avatar Uploader
|
||||||
|
|
||||||
|
/// 头像上传视图,提供头像选择功能
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
struct AvatarUploader: View {
|
struct AvatarUploader: View {
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
/// 绑定的当前选中头像图片
|
|
||||||
@Binding var selectedImage: UIImage?
|
@Binding var selectedImage: UIImage?
|
||||||
|
|
||||||
/// 头像显示尺寸
|
|
||||||
let size: CGFloat
|
let size: CGFloat
|
||||||
|
<<<<<<< HEAD
|
||||||
var onUploadComplete: ((Result<ImageUploadService.UploadResults, Error>) -> Void)?
|
var onUploadComplete: ((Result<ImageUploadService.UploadResults, Error>) -> Void)?
|
||||||
|
|
||||||
/// 上传完成回调,返回上传结果或错误
|
/// 上传完成回调,返回上传结果或错误
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
var onUploadComplete: ((Result<UploadResults, Error>) -> Void)?
|
var onUploadComplete: ((Result<UploadResults, Error>) -> Void)?
|
||||||
|
|
||||||
// MARK: - State
|
|
||||||
|
|
||||||
/// 控制图片选择器的显示状态
|
|
||||||
@State private var isImagePickerPresented = false
|
@State private var isImagePickerPresented = false
|
||||||
|
|
||||||
// MARK: - Body
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
// 头像按钮,点击后显示图片选择器
|
|
||||||
Button(action: { isImagePickerPresented = true }) {
|
Button(action: { isImagePickerPresented = true }) {
|
||||||
ZStack {
|
ZStack {
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
@ -473,7 +597,6 @@ struct AvatarUploader: View {
|
|||||||
=======
|
=======
|
||||||
>>>>>>> a4890a4 (feat: 图片上传互获取url)
|
>>>>>>> a4890a4 (feat: 图片上传互获取url)
|
||||||
if let selectedImage = selectedImage {
|
if let selectedImage = selectedImage {
|
||||||
// 显示已选中的头像
|
|
||||||
Image(uiImage: selectedImage)
|
Image(uiImage: selectedImage)
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFill()
|
.scaledToFill()
|
||||||
@ -481,6 +604,7 @@ struct AvatarUploader: View {
|
|||||||
.clipShape(RoundedRectangle(cornerRadius: size * 0.1))
|
.clipShape(RoundedRectangle(cornerRadius: size * 0.1))
|
||||||
} else {
|
} else {
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
|
<<<<<<< HEAD
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
// Default avatar container
|
// Default avatar container
|
||||||
=======
|
=======
|
||||||
@ -488,6 +612,8 @@ struct AvatarUploader: View {
|
|||||||
=======
|
=======
|
||||||
// 默认头像占位视图
|
// 默认头像占位视图
|
||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
Color.gray.opacity(0.1)
|
Color.gray.opacity(0.1)
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
.overlay(
|
.overlay(
|
||||||
@ -503,6 +629,7 @@ struct AvatarUploader: View {
|
|||||||
}
|
}
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
|
<<<<<<< HEAD
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
.contentShape(Rectangle()) // Make the entire area tappable
|
.contentShape(Rectangle()) // Make the entire area tappable
|
||||||
}
|
}
|
||||||
@ -517,8 +644,12 @@ struct AvatarUploader: View {
|
|||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle()) // 使用无样式按钮
|
.buttonStyle(PlainButtonStyle()) // 使用无样式按钮
|
||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
=======
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
}
|
||||||
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
.sheet(isPresented: $isImagePickerPresented) {
|
.sheet(isPresented: $isImagePickerPresented) {
|
||||||
// 显示图片选择器
|
|
||||||
PhotoPicker(
|
PhotoPicker(
|
||||||
selectedImages: Binding(
|
selectedImages: Binding(
|
||||||
get: { [selectedImage].compactMap { $0 } },
|
get: { [selectedImage].compactMap { $0 } },
|
||||||
@ -526,10 +657,12 @@ struct AvatarUploader: View {
|
|||||||
selectedImage = images.first
|
selectedImage = images.first
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
selectionLimit: 1, // 限制只能选择一张图片
|
selectionLimit: 1,
|
||||||
onImageUploaded: { result in
|
onImageUploaded: { result in
|
||||||
// 图片上传完成后的处理
|
|
||||||
onUploadComplete?(result)
|
onUploadComplete?(result)
|
||||||
|
},
|
||||||
|
onUploadProgress: { progress in
|
||||||
|
print("上传进度:\(progress.current)/\(progress.total),进度:\(progress.progress * 100)%")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,6 +83,9 @@ public class ImageUploaderGetID: ObservableObject {
|
|||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - image: 要上传的图片
|
/// - image: 要上传的图片
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
/// - progress: 上传进度回调 (0.0 到 1.0)
|
/// - progress: 上传进度回调 (0.0 到 1.0)
|
||||||
/// - completion: 完成回调
|
/// - completion: 完成回调
|
||||||
public func uploadImage(
|
public func uploadImage(
|
||||||
@ -90,10 +93,13 @@ public class ImageUploaderGetID: ObservableObject {
|
|||||||
progress: @escaping (Double) -> Void,
|
progress: @escaping (Double) -> Void,
|
||||||
completion: @escaping (Result<UploadResult, Error>) -> Void
|
completion: @escaping (Result<UploadResult, Error>) -> Void
|
||||||
) {
|
) {
|
||||||
|
<<<<<<< HEAD
|
||||||
=======
|
=======
|
||||||
/// - completion: 完成回调,返回Result类型的结果
|
/// - completion: 完成回调,返回Result类型的结果
|
||||||
public func uploadImage(_ image: UIImage, completion: @escaping (Result<UploadResult, Error>) -> Void) {
|
public func uploadImage(_ image: UIImage, completion: @escaping (Result<UploadResult, Error>) -> Void) {
|
||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
print("🔄 开始准备上传图片...")
|
print("🔄 开始准备上传图片...")
|
||||||
|
|
||||||
// 1. 转换图片为Data
|
// 1. 转换图片为Data
|
||||||
@ -109,6 +115,9 @@ public class ImageUploaderGetID: ObservableObject {
|
|||||||
switch result {
|
switch result {
|
||||||
case .success((let fileId, let uploadURL)):
|
case .success((let fileId, let uploadURL)):
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
print("📤 获取到上传URL,开始上传文件...")
|
print("📤 获取到上传URL,开始上传文件...")
|
||||||
|
|
||||||
// 3. 上传文件
|
// 3. 上传文件
|
||||||
@ -138,6 +147,7 @@ public class ImageUploaderGetID: ObservableObject {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
print("❌ 获取上传URL失败: \(error.localizedDescription)")
|
print("❌ 获取上传URL失败: \(error.localizedDescription)")
|
||||||
=======
|
=======
|
||||||
@ -147,6 +157,10 @@ public class ImageUploaderGetID: ObservableObject {
|
|||||||
}
|
}
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
=======
|
||||||
|
case .failure(let error):
|
||||||
|
print("❌ 获取上传URL失败: \(error.localizedDescription)")
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
completion(.failure(error))
|
completion(.failure(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,6 +320,9 @@ public class ImageUploaderGetID: ObservableObject {
|
|||||||
task.resume()
|
task.resume()
|
||||||
}
|
}
|
||||||
<<<<<<< HEAD
|
<<<<<<< HEAD
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
|
|
||||||
/// 上传文件到指定URL
|
/// 上传文件到指定URL
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
@ -334,6 +351,7 @@ public class ImageUploaderGetID: ObservableObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
completion(.failure(UploadError.invalidResponse))
|
completion(.failure(UploadError.invalidResponse))
|
||||||
return
|
return
|
||||||
@ -341,6 +359,11 @@ public class ImageUploaderGetID: ObservableObject {
|
|||||||
|
|
||||||
guard httpResponse.statusCode >= 200 && httpResponse.statusCode < 300 else {
|
guard httpResponse.statusCode >= 200 && httpResponse.statusCode < 300 else {
|
||||||
let statusCode = httpResponse.statusCode
|
let statusCode = httpResponse.statusCode
|
||||||
|
=======
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse,
|
||||||
|
(200...299).contains(httpResponse.statusCode) else {
|
||||||
|
let statusCode = (response as? HTTPURLResponse)?.statusCode ?? -1
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
completion(.failure(UploadError.serverError("上传失败,状态码: \(statusCode)")))
|
completion(.failure(UploadError.serverError("上传失败,状态码: \(statusCode)")))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -361,7 +384,11 @@ public class ImageUploaderGetID: ObservableObject {
|
|||||||
task?.progress.cancel()
|
task?.progress.cancel()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
<<<<<<< HEAD
|
||||||
// iOS 11 以下版本使用通知
|
// iOS 11 以下版本使用通知
|
||||||
|
=======
|
||||||
|
// Fallback for earlier iOS versions
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
var lastProgress: Double = 0
|
var lastProgress: Double = 0
|
||||||
let timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
|
let timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
|
||||||
let bytesSent = task.countOfBytesSent
|
let bytesSent = task.countOfBytesSent
|
||||||
@ -486,8 +513,11 @@ private extension URLSessionTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
=======
|
=======
|
||||||
>>>>>>> a207b78 (feat: 确认上传)
|
>>>>>>> a207b78 (feat: 确认上传)
|
||||||
|
=======
|
||||||
|
>>>>>>> 8e641fd (feat: 上传进度)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - 响应模型
|
// MARK: - 响应模型
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user