// // CloudStoragePickForTaskViewModel.swift // suixinkan // import Foundation /// 任务云盘多选 ViewModel,对齐 Android 任务模式云盘浏览与多选逻辑。 final class CloudStoragePickForTaskViewModel { private(set) var pathStack: [CloudPathItem] = [CloudPathItem(id: 0, name: "全部文件")] private(set) var files: [CloudFile] = [] private(set) var selectedFiles: [Int: CloudFile] = [:] private(set) var searchText = "" private(set) var filterType = 0 private(set) var sortType = 2 private(set) var isLoading = false private(set) var isRefreshing = false private(set) var canLoadMore = false var onStateChange: (() -> Void)? var onShowMessage: ((String) -> Void)? private var currentPage = 1 private var totalCount = 0 private let pageSize = 20 private let importedFileIDs: Set init(importedFileIDs: Set = []) { self.importedFileIDs = importedFileIDs } /// 当前文件夹 ID。 var currentFolderID: Int { pathStack.last?.id ?? 0 } /// 已选文件列表。 var selectedFileList: [CloudFile] { Array(selectedFiles.values) } /// 文件是否已在任务页导入。 func isImported(_ fileID: Int) -> Bool { importedFileIDs.contains(fileID) } /// 文件是否已选中。 func isSelected(_ fileID: Int) -> Bool { selectedFiles[fileID] != nil } /// 更新搜索词。 func updateSearchText(_ text: String) { searchText = text notifyStateChange() } /// 更新类型筛选。 func updateFilterType(_ type: Int) { filterType = type notifyStateChange() } /// 更新排序方式。 func updateSortType(_ type: Int) { sortType = type notifyStateChange() } /// 首次或刷新加载。 func refresh(api: TaskAPI) async { currentPage = 1 isRefreshing = true notifyStateChange() await loadPage(api: api, reset: true) isRefreshing = false notifyStateChange() } /// 加载更多。 func loadMore(api: TaskAPI) async { guard canLoadMore, !isLoading else { return } currentPage += 1 await loadPage(api: api, reset: false) } /// 点击文件或文件夹。 func handleItemTap(_ file: CloudFile, api: TaskAPI) async { if file.isFolder { pathStack.append(CloudPathItem(id: file.id, name: file.name)) searchText = "" await refresh(api: api) return } guard file.isSelectableMedia else { return } if isImported(file.id) { onShowMessage?("已选择") return } if selectedFiles[file.id] != nil { selectedFiles.removeValue(forKey: file.id) } else { selectedFiles[file.id] = file } notifyStateChange() } /// 跳转面包屑路径。 func navigateToPathIndex(_ index: Int, api: TaskAPI) async { guard index >= 0, index < pathStack.count else { return } pathStack = Array(pathStack.prefix(index + 1)) searchText = "" await refresh(api: api) } /// 返回上级目录。 func navigateBack(api: TaskAPI) async -> Bool { guard pathStack.count > 1 else { return false } pathStack.removeLast() searchText = "" await refresh(api: api) return true } private func loadPage(api: TaskAPI, reset: Bool) async { isLoading = true notifyStateChange() defer { isLoading = false notifyStateChange() } do { let response = try await api.cloudFileList( parentFolderId: currentFolderID, name: searchText, type: filterType, orderBy: sortType, page: currentPage, pageSize: pageSize ) totalCount = response.total if reset { files = response.list } else { files.append(contentsOf: response.list) } canLoadMore = files.count < totalCount } catch is CancellationError { return } catch { if reset { files = [] } canLoadMore = false onShowMessage?(error.localizedDescription) } } private func notifyStateChange() { onStateChange?() } }