增强有线传图手动上传并发能力,并稳定列表行布局。

指定/勾选/批量上传支持恢复仅本地持久化的待传照片,手动上传与 pipeline 均按 3 路并发;pipeline 任务签名去重避免状态回退,照片行固定高度并预留进度条占位。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-30 15:10:47 +08:00
parent 5ebcec1cfc
commit 660852ffad
7 changed files with 283 additions and 72 deletions

View File

@ -25,11 +25,12 @@ enum WiredTransferDesign {
static let filterChipHeight: CGFloat = 34
static let filterChipCornerRadius: CGFloat = 8
static let tabRowHeight: CGFloat = 68
static let thumbnailSize: CGFloat = 48
static let photoRowHeight: CGFloat = 74
static let thumbnailSize: CGFloat = 56
static let thumbnailCornerRadius: CGFloat = 6
static let bottomButtonHeight: CGFloat = 44
static let bottomButtonCornerRadius: CGFloat = 10
static let progressBarHeight: CGFloat = 2
static let progressBarHeight: CGFloat = 3
static let cardHorizontalPadding: CGFloat = 16
static let cardOverlap: CGFloat = 10
}

View File

@ -20,71 +20,63 @@ struct WiredTransferPhotoRow: View {
private var canSelect: Bool { photo.canSelectForSpecifyUpload }
private var canRetry: Bool { photo.status == .failed || photo.status == .pending }
private var rowOpacity: Double { selectUploadMode && !canSelect ? 0.45 : 1 }
private var showsProgress: Bool { photo.status == .transferring || photo.status == .uploading }
var body: some View {
VStack(spacing: 0) {
HStack(alignment: .center, spacing: 8) {
HStack(alignment: .center, spacing: 8) {
HStack(alignment: .center, spacing: 8) {
if selectUploadMode {
WiredTransferSelectIndicator(selected: selected, enabled: canSelect)
}
if selectUploadMode {
WiredTransferSelectIndicator(selected: selected, enabled: canSelect)
}
thumbnailView
.frame(width: WiredTransferDesign.thumbnailSize, height: WiredTransferDesign.thumbnailSize)
.clipShape(RoundedRectangle(cornerRadius: WiredTransferDesign.thumbnailCornerRadius))
.opacity(rowOpacity)
thumbnailView
.frame(width: WiredTransferDesign.thumbnailSize, height: WiredTransferDesign.thumbnailSize)
.clipShape(RoundedRectangle(cornerRadius: WiredTransferDesign.thumbnailCornerRadius))
.opacity(rowOpacity)
VStack(alignment: .leading, spacing: 3) {
HStack(spacing: 4) {
Text(photo.fileName)
.font(.system(size: 12, weight: .medium))
.foregroundStyle(WiredTransferDesign.text333.opacity(rowOpacity))
.lineLimit(1)
Spacer(minLength: 0)
WiredTransferStatusBadge(status: photo.status)
.opacity(rowOpacity)
}
Text(metaText)
.font(.system(size: 10))
.foregroundStyle(WiredTransferDesign.text999.opacity(rowOpacity))
VStack(alignment: .leading, spacing: 5) {
HStack(spacing: 4) {
Text(photo.fileName)
.font(.system(size: 12, weight: .medium))
.foregroundStyle(WiredTransferDesign.text333.opacity(rowOpacity))
.lineLimit(1)
Spacer(minLength: 0)
WiredTransferStatusBadge(status: photo.status)
.opacity(rowOpacity)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
.onTapGesture {
if selectUploadMode {
if canSelect { onToggleSelection() }
} else {
onPhotoClick()
}
}
if !selectUploadMode {
Menu {
Button("重传") { onRetry() }
.disabled(!canRetry)
Button("删除", role: .destructive) { onDelete() }
} label: {
Image(systemName: "ellipsis")
.font(.system(size: 16))
.foregroundStyle(WiredTransferDesign.text666)
.frame(width: 32, height: 32)
.rotationEffect(.degrees(90))
}
Text(metaText)
.font(.system(size: 10))
.foregroundStyle(WiredTransferDesign.text999.opacity(rowOpacity))
.lineLimit(1)
progressSlot
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
.onTapGesture {
if selectUploadMode {
if canSelect { onToggleSelection() }
} else {
onPhotoClick()
}
}
.padding(.horizontal, 10)
.padding(.vertical, 8)
if photo.status == .transferring || photo.status == .uploading {
ProgressView(value: Double(photo.progress), total: 100)
.tint(AppDesign.primary)
.frame(height: WiredTransferDesign.progressBarHeight)
.padding(.horizontal, 10)
.padding(.bottom, 8)
if !selectUploadMode {
Menu {
Button("重传") { onRetry() }
.disabled(!canRetry)
Button("删除", role: .destructive) { onDelete() }
} label: {
Image(systemName: "ellipsis")
.font(.system(size: 16))
.foregroundStyle(WiredTransferDesign.text666)
.frame(width: 32, height: 32)
.rotationEffect(.degrees(90))
}
}
}
.frame(height: WiredTransferDesign.photoRowHeight)
.padding(.horizontal, 10)
}
private var metaText: String {
@ -92,6 +84,13 @@ struct WiredTransferPhotoRow: View {
return "\(photo.fileSizeText) \(photo.resolutionText)"
}
private var progressSlot: some View {
ProgressView(value: showsProgress ? Double(photo.progress) : 0, total: 100)
.tint(AppDesign.primary)
.opacity(showsProgress ? 1 : 0)
.frame(height: WiredTransferDesign.progressBarHeight)
}
@ViewBuilder
private var thumbnailView: some View {
if let url = URL(string: photo.thumbnailURL), !photo.thumbnailURL.isEmpty {