完善样片管理列表、详情与上传页 UI 交互。

对齐 Android 样片模块布局与加载状态,补充共享 UI 组件与图片资源,并增强列表解析容错与单元测试覆盖。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-13 15:00:18 +08:00
parent cbf2db649e
commit afcd412f11
16 changed files with 862 additions and 134 deletions

View File

@ -26,6 +26,7 @@ final class SampleListViewModel {
private(set) var canLoadMore = false
private(set) var filterStatus: SampleFilterStatus = .all
private(set) var searchText = ""
private(set) var operatingSampleIDs: Set<Int> = []
var onStateChange: (() -> Void)?
var onShowMessage: ((String) -> Void)?
@ -33,6 +34,11 @@ final class SampleListViewModel {
private var currentPage = 1
private var totalCount = 0
private let pageSize = 10
private var isRequestingList = false
private var isLoadingMore = false
///
var isOperating: Bool { !operatingSampleIDs.isEmpty }
///
func loadInitial(api: any SampleManagementServing) async {
@ -67,15 +73,26 @@ final class SampleListViewModel {
}
///
func toggleListing(sampleId: Int, checked: Bool, api: any SampleManagementServing) async {
guard let currentItem = items.first(where: { $0.id == sampleId }) else { return }
@discardableResult
func toggleListing(sampleId: Int, checked: Bool, api: any SampleManagementServing) async -> Bool {
guard let currentItem = items.first(where: { $0.id == sampleId }) else { return !checked }
let previousState = currentItem.isListed
guard currentItem.status == 1 else {
onShowMessage?("审核通过才可以操作上下架")
return
notifyStateChange()
return previousState
}
let targetStatus = checked ? 1 : 0
guard currentItem.listingStatus != targetStatus else { return }
guard currentItem.listingStatus != targetStatus else { return previousState }
guard !operatingSampleIDs.contains(sampleId) else { return previousState }
operatingSampleIDs.insert(sampleId)
notifyStateChange()
defer {
operatingSampleIDs.remove(sampleId)
notifyStateChange()
}
do {
try await api.setListingStatus(id: sampleId, listingStatus: targetStatus)
@ -97,25 +114,38 @@ final class SampleListViewModel {
)
}
notifyStateChange()
return checked
} catch is CancellationError {
return
return previousState
} catch {
onShowMessage?(error.localizedDescription.isEmpty ? "操作失败" : error.localizedDescription)
return previousState
}
}
private func load(reset: Bool, showLoading: Bool, api: any SampleManagementServing) async {
guard !isRequestingList else {
if isRefreshing {
isRefreshing = false
notifyStateChange()
}
return
}
if reset {
currentPage = 1
canLoadMore = false
} else {
guard canLoadMore, !isLoading else { return }
guard canLoadMore, !isLoading, !isLoadingMore else { return }
isLoadingMore = true
currentPage += 1
}
isRequestingList = true
isLoading = showLoading
notifyStateChange()
defer {
isRequestingList = false
if !reset { isLoadingMore = false }
isLoading = false
isRefreshing = false
notifyStateChange()
@ -276,7 +306,7 @@ final class UploadSampleViewModel {
onShowMessage?("请先选择景区")
return
}
if projectIsLoading && !refresh { return }
guard !projectIsLoading else { return }
if refresh {
projectCurrentPage = 1
projectList = []