// // TaskCloudFileSelectionViewModel.swift // suixinkan // // Created by Codex on 2026/6/23. // import Foundation import Combine /// 云盘文件类型筛选实体,表示发布任务选择附件时的过滤方式。 enum TaskCloudFileFilter: Int, CaseIterable, Identifiable { case all = 0 case video = 1 case image = 2 var id: Int { rawValue } /// 返回筛选项展示文案。 var title: String { switch self { case .all: "全部" case .video: "视频" case .image: "图片" } } } /// 云盘文件选择 ViewModel,负责目录浏览、筛选、分页和附件选择。 @MainActor final class TaskCloudFileSelectionViewModel: ObservableObject { @Published var path: [CloudDriveFile] = [CloudDriveFile(id: 0, name: "云盘")] @Published var files: [CloudDriveFile] = [] @Published var total = 0 @Published var page = 1 @Published var searchText = "" @Published var selectedFilter: TaskCloudFileFilter = .all @Published var selectedFiles: [CloudDriveFile] = [] @Published var isLoading = false @Published var isLoadingMore = false @Published var errorMessage: String? private let pageSize = 20 /// 当前目录 ID。 var currentFolderId: Int { path.last?.id ?? 0 } /// 判断当前目录是否还有下一页。 var hasMore: Bool { files.count < total } /// 重新加载当前目录第一页。 func reload(api: any TaskServing) async throws { isLoading = true errorMessage = nil defer { isLoading = false } do { let payload = try await requestFiles(api: api, page: 1) page = 1 files = payload.list total = payload.total } catch { files = [] total = 0 page = 1 errorMessage = error.localizedDescription throw error } } /// 加载当前目录下一页。 func loadMore(api: any TaskServing) async throws { guard hasMore, !isLoadingMore else { return } isLoadingMore = true defer { isLoadingMore = false } let nextPage = page + 1 let payload = try await requestFiles(api: api, page: nextPage) page = nextPage total = payload.total files.append(contentsOf: payload.list) } /// 进入指定文件夹并刷新列表。 func enterFolder(_ folder: CloudDriveFile, api: any TaskServing) async throws { guard folder.isFolder else { return } path.append(folder) try await reload(api: api) } /// 回到指定面包屑目录并刷新列表。 func popToFolder(at index: Int, api: any TaskServing) async throws { guard path.indices.contains(index) else { return } path = Array(path.prefix(index + 1)) try await reload(api: api) } /// 切换文件选中状态,文件夹不会被选为附件。 func toggleSelection(_ file: CloudDriveFile) { guard !file.isFolder else { return } if let index = selectedFiles.firstIndex(where: { $0.id == file.id }) { selectedFiles.remove(at: index) } else { selectedFiles.append(file) } } /// 判断指定云盘文件是否已选中。 func isSelected(_ file: CloudDriveFile) -> Bool { selectedFiles.contains { $0.id == file.id } } /// 将当前选中的云盘文件转换为发布任务表单附件。 func makeSelectionItems() -> [TaskCloudSelectionItem] { selectedFiles.map { TaskCloudSelectionItem(id: $0.id, fileName: $0.name, fileType: $0.type, remark: "") } } /// 组装并发送云盘文件列表请求。 private func requestFiles(api: any TaskServing, page: Int) async throws -> ListPayload { try await api.cloudFileList( parentFolderId: currentFolderId, name: searchText.trimmingCharacters(in: .whitespacesAndNewlines), type: selectedFilter.rawValue, orderBy: 2, page: page, pageSize: pageSize ) } }