232 lines
6.9 KiB
Swift
232 lines
6.9 KiB
Swift
//
|
||
// TravelAlbumDetailViewModel.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import Foundation
|
||
|
||
/// 旅拍相册详情 ViewModel,负责相册信息、素材分页筛选和删除状态。
|
||
final class TravelAlbumDetailViewModel {
|
||
enum Tab: Int, Sendable {
|
||
case all = 0
|
||
case purchased = 1
|
||
}
|
||
|
||
/// 排序方式。
|
||
enum SortOption: Int, CaseIterable, Sendable {
|
||
case createdAsc = 1
|
||
case createdDesc = 2
|
||
case fileNameAsc = 3
|
||
case fileNameDesc = 4
|
||
|
||
var title: String {
|
||
switch self {
|
||
case .createdAsc: "创建时间正序"
|
||
case .createdDesc: "创建时间倒序"
|
||
case .fileNameAsc: "文件名正序"
|
||
case .fileNameDesc: "文件名倒序"
|
||
}
|
||
}
|
||
}
|
||
|
||
private(set) var album: TravelAlbum?
|
||
private(set) var materials: [TravelAlbumMaterial] = []
|
||
private(set) var allPhotoCount = 0
|
||
private(set) var selectedTab: Tab = .all
|
||
private(set) var sortOption: SortOption = .createdDesc
|
||
private(set) var isLoading = true
|
||
private(set) var isRefreshing = false
|
||
private(set) var isSelectionMode = false
|
||
private(set) var selectedMaterialIds: Set<Int> = []
|
||
|
||
var onStateChange: (() -> Void)?
|
||
var onShowMessage: ((String) -> Void)?
|
||
var onDeletedAlbum: ((Int) -> Void)?
|
||
|
||
let albumId: Int
|
||
private var currentPage = 1
|
||
private var canLoadMore = false
|
||
private var isLoadingMore = false
|
||
private let pageSize = 30
|
||
|
||
init(albumId: Int) {
|
||
self.albumId = albumId
|
||
}
|
||
|
||
/// 重新加载详情与素材。
|
||
func refreshAll(api: any TravelAlbumServing) async {
|
||
guard albumId > 0 else {
|
||
isLoading = false
|
||
onShowMessage?("相册不存在")
|
||
notifyStateChange()
|
||
return
|
||
}
|
||
isRefreshing = true
|
||
notifyStateChange()
|
||
await loadAlbumInfo(api: api)
|
||
await loadAllPhotoCount(api: api)
|
||
await loadMaterials(reset: true, api: api)
|
||
isRefreshing = false
|
||
isLoading = false
|
||
notifyStateChange()
|
||
}
|
||
|
||
/// 拉取相册详情。
|
||
func loadAlbumInfo(api: any TravelAlbumServing) async {
|
||
do {
|
||
album = try await api.info(id: albumId)
|
||
notifyStateChange()
|
||
} catch is CancellationError {
|
||
return
|
||
} catch {
|
||
onShowMessage?(error.localizedDescription)
|
||
}
|
||
}
|
||
|
||
/// 拉取全部照片数量。
|
||
func loadAllPhotoCount(api: any TravelAlbumServing) async {
|
||
do {
|
||
let response = try await api.materialList(
|
||
userEquityTravelId: albumId,
|
||
page: 1,
|
||
pageSize: 1,
|
||
orderBy: sortOption.rawValue,
|
||
isPurchased: nil
|
||
)
|
||
allPhotoCount = response.total
|
||
notifyStateChange()
|
||
} catch is CancellationError {
|
||
return
|
||
} catch {
|
||
return
|
||
}
|
||
}
|
||
|
||
/// 切换素材 tab。
|
||
func selectTab(_ tab: Tab, api: any TravelAlbumServing) async {
|
||
guard selectedTab != tab else { return }
|
||
selectedTab = tab
|
||
isSelectionMode = false
|
||
selectedMaterialIds = []
|
||
notifyStateChange()
|
||
await loadMaterials(reset: true, api: api)
|
||
}
|
||
|
||
/// 切换排序。
|
||
func setSortOption(_ option: SortOption, api: any TravelAlbumServing) async {
|
||
guard sortOption != option else { return }
|
||
sortOption = option
|
||
notifyStateChange()
|
||
await loadMaterials(reset: true, api: api)
|
||
}
|
||
|
||
/// 拉取素材列表。
|
||
func loadMaterials(reset: Bool, api: any TravelAlbumServing) async {
|
||
guard albumId > 0 else { return }
|
||
if reset {
|
||
currentPage = 1
|
||
canLoadMore = false
|
||
} else {
|
||
guard canLoadMore, !isLoadingMore else { return }
|
||
isLoadingMore = true
|
||
currentPage += 1
|
||
}
|
||
|
||
do {
|
||
let response = try await api.materialList(
|
||
userEquityTravelId: albumId,
|
||
page: currentPage,
|
||
pageSize: pageSize,
|
||
orderBy: sortOption.rawValue,
|
||
isPurchased: selectedTab == .purchased ? 1 : nil
|
||
)
|
||
materials = reset ? response.list : materials + response.list
|
||
canLoadMore = materials.count < response.total
|
||
if selectedTab == .all {
|
||
allPhotoCount = response.total
|
||
}
|
||
isLoadingMore = false
|
||
notifyStateChange()
|
||
} catch is CancellationError {
|
||
return
|
||
} catch {
|
||
if !reset && currentPage > 1 {
|
||
currentPage -= 1
|
||
}
|
||
isLoadingMore = false
|
||
onShowMessage?(error.localizedDescription)
|
||
}
|
||
}
|
||
|
||
/// 根据滚动位置触发加载更多。
|
||
func loadMoreIfNeeded(lastVisibleIndex: Int, api: any TravelAlbumServing) async {
|
||
if lastVisibleIndex >= materials.count - 6 {
|
||
await loadMaterials(reset: false, api: api)
|
||
}
|
||
}
|
||
|
||
/// 切换选择模式。
|
||
func toggleSelectionMode() {
|
||
guard selectedTab == .all else { return }
|
||
isSelectionMode.toggle()
|
||
if !isSelectionMode {
|
||
selectedMaterialIds = []
|
||
}
|
||
notifyStateChange()
|
||
}
|
||
|
||
/// 切换素材选中状态。
|
||
func toggleMaterialSelection(_ material: TravelAlbumMaterial) {
|
||
guard isSelectionMode else { return }
|
||
guard material.status == 1 else {
|
||
onShowMessage?("仅未购买素材可删除")
|
||
return
|
||
}
|
||
if selectedMaterialIds.contains(material.id) {
|
||
selectedMaterialIds.remove(material.id)
|
||
} else {
|
||
selectedMaterialIds.insert(material.id)
|
||
}
|
||
notifyStateChange()
|
||
}
|
||
|
||
/// 删除已选素材。
|
||
func deleteSelectedMaterials(api: any TravelAlbumServing) async {
|
||
let ids = Array(selectedMaterialIds)
|
||
guard !ids.isEmpty else {
|
||
onShowMessage?("请选择要删除的素材")
|
||
return
|
||
}
|
||
do {
|
||
for id in ids {
|
||
try await api.deleteMaterial(id: id)
|
||
}
|
||
onShowMessage?("删除成功")
|
||
isSelectionMode = false
|
||
selectedMaterialIds = []
|
||
await refreshAll(api: api)
|
||
} catch is CancellationError {
|
||
return
|
||
} catch {
|
||
onShowMessage?(error.localizedDescription.isEmpty ? "删除失败" : error.localizedDescription)
|
||
}
|
||
}
|
||
|
||
/// 删除当前相册。
|
||
func deleteAlbum(api: any TravelAlbumServing) async {
|
||
do {
|
||
try await api.deleteAlbum(id: albumId)
|
||
onShowMessage?("相册已删除")
|
||
onDeletedAlbum?(albumId)
|
||
} catch is CancellationError {
|
||
return
|
||
} catch {
|
||
onShowMessage?(error.localizedDescription.isEmpty ? "删除失败" : error.localizedDescription)
|
||
}
|
||
}
|
||
|
||
private func notifyStateChange() {
|
||
onStateChange?()
|
||
}
|
||
}
|