diff --git a/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportDesign.swift b/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportDesign.swift new file mode 100644 index 0000000..bf219f9 --- /dev/null +++ b/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportDesign.swift @@ -0,0 +1,11 @@ +import SwiftUI + +/// 相机历史导入页布局常量。 +enum CameraHistoryImportDesign { + static let gridColumns = 3 + static let gridHorizontalSpacing: CGFloat = 2 + static let gridVerticalSpacing: CGFloat = 2 + static let contentHorizontalPadding: CGFloat = 12 + static let sectionSpacing: CGFloat = 12 + static let thumbnailCornerRadius: CGFloat = 4 +} diff --git a/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportPhotoCell.swift b/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportPhotoCell.swift index 9879e75..555d54d 100644 --- a/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportPhotoCell.swift +++ b/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportPhotoCell.swift @@ -11,51 +11,61 @@ struct CameraHistoryImportPhotoCell: View { @State private var thumbnail: UIImage? var body: some View { - ZStack(alignment: .topTrailing) { - Group { - if let thumbnail { - Image(uiImage: thumbnail) - .resizable() - .scaledToFill() - } else { - Color.gray.opacity(0.15) - .overlay { - ProgressView() - .scaleEffect(0.7) - } + Color.clear + .aspectRatio(1, contentMode: .fit) + .overlay { + thumbnailContent + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + .clipShape(RoundedRectangle(cornerRadius: CameraHistoryImportDesign.thumbnailCornerRadius)) + .overlay(alignment: .topTrailing) { + statusBadge + } + .contentShape(Rectangle()) + .onTapGesture { + guard !isImported else { return } + onToggle() + } + .task(id: photo.id) { + guard thumbnail == nil else { return } + if let data = await thumbnailProvider(), let image = UIImage(data: data) { + thumbnail = image } } - .frame(maxWidth: .infinity) - .aspectRatio(1, contentMode: .fit) - .clipped() + } - if isImported { - Text("已导入") - .font(.system(size: 10, weight: .medium)) - .foregroundStyle(.white) - .padding(.horizontal, 6) - .padding(.vertical, 3) - .background(Color.black.opacity(0.55)) - .clipShape(RoundedRectangle(cornerRadius: 4)) - .padding(6) - } else { - Image(systemName: isSelected ? "checkmark.circle.fill" : "circle") - .font(.system(size: 22)) - .foregroundStyle(isSelected ? AppDesign.primary : .white) - .shadow(radius: 2) - .padding(6) - } + @ViewBuilder + private var thumbnailContent: some View { + if let thumbnail { + Image(uiImage: thumbnail) + .resizable() + .scaledToFill() + } else { + Color.gray.opacity(0.15) + .overlay { + ProgressView() + .scaleEffect(0.7) + } } - .contentShape(Rectangle()) - .onTapGesture { - guard !isImported else { return } - onToggle() - } - .task(id: photo.id) { - guard thumbnail == nil else { return } - if let data = await thumbnailProvider(), let image = UIImage(data: data) { - thumbnail = image - } + } + + @ViewBuilder + private var statusBadge: some View { + if isImported { + Text("已导入") + .font(.system(size: 10, weight: .medium)) + .foregroundStyle(.white) + .padding(.horizontal, 6) + .padding(.vertical, 3) + .background(Color.black.opacity(0.55)) + .clipShape(RoundedRectangle(cornerRadius: 4)) + .padding(6) + } else { + Image(systemName: isSelected ? "checkmark.circle.fill" : "circle") + .font(.system(size: 22)) + .foregroundStyle(isSelected ? AppDesign.primary : .white) + .shadow(color: .black.opacity(0.35), radius: 2, x: 0, y: 1) + .padding(6) } } } diff --git a/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportSectionHeader.swift b/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportSectionHeader.swift index b8a3979..54cb8d1 100644 --- a/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportSectionHeader.swift +++ b/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportSectionHeader.swift @@ -21,8 +21,9 @@ struct CameraHistoryImportSectionHeader: View { .foregroundStyle(AppDesign.primary) } } - .padding(.horizontal, 12) + .padding(.horizontal, CameraHistoryImportDesign.contentHorizontalPadding) .padding(.vertical, 8) + .frame(maxWidth: .infinity, alignment: .leading) .background(WiredTransferDesign.pageBackground) } } diff --git a/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportView.swift b/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportView.swift index ace26e4..481bd8a 100644 --- a/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportView.swift +++ b/suixinkan/Features/TravelAlbum/Views/CameraHistoryImport/CameraHistoryImportView.swift @@ -70,10 +70,10 @@ struct CameraHistoryImportView: View { private func photoList(sections: [CameraPhotoSection]) -> some View { ScrollView { - LazyVStack(spacing: 12, pinnedViews: [.sectionHeaders]) { + LazyVStack(spacing: CameraHistoryImportDesign.sectionSpacing, pinnedViews: [.sectionHeaders]) { ForEach(sections) { section in Section { - LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())], spacing: 2) { + LazyVGrid(columns: gridColumns, spacing: CameraHistoryImportDesign.gridVerticalSpacing) { ForEach(section.photos, id: \.id) { photo in CameraHistoryImportPhotoCell( photo: photo, @@ -94,10 +94,18 @@ struct CameraHistoryImportView: View { } } } - .padding(.horizontal, 2) + .padding(.horizontal, CameraHistoryImportDesign.contentHorizontalPadding) + .padding(.bottom, 16) } } + private var gridColumns: [GridItem] { + Array( + repeating: GridItem(.flexible(), spacing: CameraHistoryImportDesign.gridHorizontalSpacing), + count: CameraHistoryImportDesign.gridColumns + ) + } + private func emptyView(message: String) -> some View { VStack(spacing: 16) { Text(message)