fix: 优化相册素材状态与删除保护

This commit is contained in:
2026-08-17 16:04:03 +08:00
parent b127495e52
commit c34c4e1923
6 changed files with 254 additions and 81 deletions
@@ -48,6 +48,7 @@ final class TravelAlbumDetailViewModel {
private(set) var isRefreshing = false
private(set) var isSelectionMode = false
private(set) var selectedMaterialIds: Set<Int> = []
private var selectedMaterialPurchaseStates: [Int: Bool] = [:]
var onStateChange: (() -> Void)?
var onShowMessage: ((String) -> Void)?
@@ -142,12 +143,29 @@ final class TravelAlbumDetailViewModel {
selectedTab == .all ? allPhotoCount : purchasedPhotoCount
}
/// 当前选中素材中的已购买数量。
var selectedPurchasedMaterialCount: Int {
selectedMaterialIds.count - selectedDeletableMaterialIds.count
}
/// 删除确认弹窗文案;选中已购素材时明确说明保护规则与实际删除数量。
var deleteSelectedConfirmationMessage: String {
let selectedCount = selectedMaterialIds.count
let purchasedCount = selectedPurchasedMaterialCount
guard purchasedCount > 0 else {
return "确定删除选中的 \(selectedCount) 张素材吗?"
}
let deletableCount = selectedCount - purchasedCount
return "已选择 \(selectedCount) 张素材,其中 \(purchasedCount) 张已购买。"
+ "不会删除已购买的项目,只会删除 \(deletableCount) 张未购买的项目。"
}
/// 切换素材 tab。
func selectTab(_ tab: Tab, api: any TravelAlbumServing) async {
guard selectedTab != tab else { return }
selectedTab = tab
isSelectionMode = false
selectedMaterialIds = []
clearMaterialSelection()
notifyStateChange()
await loadMaterials(reset: true, api: api)
}
@@ -223,10 +241,9 @@ final class TravelAlbumDetailViewModel {
/// 切换选择模式。
func toggleSelectionMode() {
guard selectedTab == .all else { return }
isSelectionMode.toggle()
if !isSelectionMode {
selectedMaterialIds = []
clearMaterialSelection()
}
notifyStateChange()
}
@@ -234,14 +251,12 @@ final class TravelAlbumDetailViewModel {
/// 切换素材选中状态。
func toggleMaterialSelection(_ material: TravelAlbumMaterial) {
guard isSelectionMode else { return }
guard material.status == 1 else {
onShowMessage?("仅未购买素材可删除")
return
}
if selectedMaterialIds.contains(material.id) {
selectedMaterialIds.remove(material.id)
selectedMaterialPurchaseStates.removeValue(forKey: material.id)
} else {
selectedMaterialIds.insert(material.id)
selectedMaterialPurchaseStates[material.id] = material.isPurchased
}
notifyStateChange()
}
@@ -249,7 +264,7 @@ final class TravelAlbumDetailViewModel {
/// AI 修图任务提交成功后退出选择模式并清空当前选择。
func completeAIRetouchSubmission() {
isSelectionMode = false
selectedMaterialIds = []
clearMaterialSelection()
notifyStateChange()
}
@@ -264,6 +279,7 @@ final class TravelAlbumDetailViewModel {
guard let index = materials.firstIndex(where: { $0.id == id }) else { return }
let material = materials.remove(at: index)
selectedMaterialIds.remove(id)
selectedMaterialPurchaseStates.removeValue(forKey: id)
allPhotoCount = max(0, allPhotoCount - 1)
if material.isPurchased {
purchasedPhotoCount = max(0, purchasedPhotoCount - 1)
@@ -273,16 +289,20 @@ final class TravelAlbumDetailViewModel {
/// 删除已选素材。
func deleteSelectedMaterials(api: any TravelAlbumServing) async {
let ids = selectedMaterialIds.sorted()
guard !ids.isEmpty else {
guard !selectedMaterialIds.isEmpty else {
onShowMessage?("请选择要删除的素材")
return
}
let ids = selectedDeletableMaterialIds
guard !ids.isEmpty else {
onShowMessage?("选中的素材均已购买,未删除任何项目")
return
}
do {
try await api.batchDeleteMaterials(ids: ids)
onShowMessage?("删除成功")
isSelectionMode = false
selectedMaterialIds = []
clearMaterialSelection()
await refreshAll(api: api)
} catch is CancellationError {
return
@@ -307,4 +327,18 @@ final class TravelAlbumDetailViewModel {
private func notifyStateChange() {
onStateChange?()
}
private func clearMaterialSelection() {
selectedMaterialIds = []
selectedMaterialPurchaseStates = [:]
}
private var selectedDeletableMaterialIds: [Int] {
selectedMaterialIds
.filter { id in
guard selectedMaterialPurchaseStates[id] == false else { return false }
return materials.first(where: { $0.id == id })?.isPurchased != true
}
.sorted()
}
}