Files
suixinkan_ios_new/suixinkan/Features/TravelAlbum/Views/TravelAlbumDetailView.swift
汉秋 e91a46c315 修复有线传图新拍照片不显示,并优化旅拍相册列表交互。
未绑定相册的新拍照片被错误过滤导致列表始终为空;同时调整入口卡片点击区域与详情素材网格布局。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 10:20:26 +08:00

162 lines
6.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// TravelAlbumDetailView.swift
// suixinkan
//
// Created by Codex on 2026/6/29.
//
import SwiftUI
///
struct TravelAlbumDetailView: View {
let albumID: Int
@EnvironmentObject private var accountContext: AccountContext
@Environment(\.travelAlbumAPI) private var travelAlbumAPI
@EnvironmentObject private var toastCenter: ToastCenter
@Environment(\.globalLoading) private var globalLoading
@Environment(\.dismiss) private var dismiss
@StateObject private var viewModel = TravelAlbumDetailViewModel()
@State private var showDeleteConfirm = false
private var photoStore: WiredTransferPhotoStore {
WiredTransferPhotoStore(
accountPrefixProvider: { accountContext.accountCachePrefix ?? "guest_" },
userIDProvider: { accountContext.profile?.userId ?? "" }
)
}
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: AppMetrics.Spacing.medium) {
if let album = viewModel.album {
albumHeader(album)
}
if viewModel.materials.isEmpty, !viewModel.isLoading {
AppContentUnavailableView("暂无素材", systemImage: "photo.on.rectangle")
.frame(maxWidth: .infinity, minHeight: 200)
} else {
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())], spacing: 8) {
ForEach(viewModel.materials) { material in
materialCell(material)
}
}
}
}
.padding(.horizontal, AppMetrics.Spacing.pageHorizontal)
.padding(.vertical, AppMetrics.Spacing.medium)
}
.background(Color(hex: 0xF5F7FA))
.navigationTitle(viewModel.album?.name ?? "相册详情")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
if let album = viewModel.album {
NavigationLink {
WiredCameraTransferView(context: WiredTransferContext(
albumId: album.id,
albumName: album.name,
phone: album.displayPhone,
orderNumber: album.orderNumber
))
} label: {
Image(systemName: "cable.connector")
}
.accessibilityLabel("有线传图")
}
Button(role: .destructive) {
showDeleteConfirm = true
} label: {
Image(systemName: "trash")
}
.accessibilityLabel("删除相册")
}
}
.confirmationDialog("确认删除该相册?", isPresented: $showDeleteConfirm, titleVisibility: .visible) {
Button("删除", role: .destructive) {
Task { await deleteAlbum() }
}
Button("取消", role: .cancel) {}
}
.task(id: albumID) {
globalLoading.show()
defer { globalLoading.hide() }
await viewModel.load(api: travelAlbumAPI, albumID: albumID)
}
.refreshable {
await viewModel.load(api: travelAlbumAPI, albumID: albumID)
}
.onChange(of: viewModel.errorMessage) { message in
guard let message, !message.isEmpty else { return }
toastCenter.show(message)
}
}
private func albumHeader(_ album: TravelAlbumItem) -> some View {
VStack(alignment: .leading, spacing: 8) {
Text(album.type == 1 ? "先拍再买" : "买了再拍")
.font(.system(size: AppMetrics.FontSize.caption))
.foregroundStyle(AppDesign.primary)
if !album.displayPhone.isEmpty {
Label(album.displayPhone, systemImage: "phone.fill")
.font(.system(size: AppMetrics.FontSize.subheadline))
.foregroundStyle(AppDesign.textSecondary)
}
if !album.orderNumber.isEmpty {
Text("订单号:\(album.orderNumber)")
.font(.system(size: AppMetrics.FontSize.caption))
.foregroundStyle(AppDesign.textSecondary)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(AppMetrics.Spacing.large)
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))
}
private func materialCell(_ material: TravelAlbumMaterial) -> some View {
ZStack(alignment: .topTrailing) {
RemoteImage(
urlString: material.coverURL.isEmpty ? material.fileURL : material.coverURL,
contentMode: .fill
) {
Color(hex: 0xE5E7EB)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
Menu {
Button(role: .destructive) {
Task {
await viewModel.deleteMaterial(api: travelAlbumAPI, materialID: material.id, albumID: albumID)
}
} label: {
Label("删除", systemImage: "trash")
}
} label: {
Image(systemName: "ellipsis")
.padding(6)
.background(Color.black.opacity(0.35))
.foregroundStyle(.white)
.clipShape(Circle())
.padding(4)
}
}
.aspectRatio(1, contentMode: .fit)
.frame(maxWidth: .infinity)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
private func deleteAlbum() async {
globalLoading.show()
defer { globalLoading.hide() }
let success = await viewModel.deleteAlbum(api: travelAlbumAPI, albumID: albumID, photoStore: photoStore)
if success {
toastCenter.show("相册已删除")
dismiss()
}
}
}