import Foundation @Observable final class BrowseViewModel { var items: [ContentItem] = [] var category: ContentCategory = .movie var filter = FilterState() var currentPage = 1 var totalPages = 1 var isLoading = false var isLoadingMore = false var error: String? private var cacheKey: String { "\(category.rawValue)_\(filter.sort)_\(filter.genre)_\(filter.region)_\(filter.year)" } var hasData: Bool { !items.isEmpty } @MainActor func loadContentIfNeeded() async { if hasData && !ContentCache.shared.isExpired(key: cacheKey) { return } await loadContent() } @MainActor func loadContent() async { isLoading = true error = nil currentPage = 1 totalPages = 1 items = [] do { let html = try await APIClient.shared.fetchCategoryPage( category: category, page: 1, filter: filter ) let newItems = try HTMLParser.parseContentList(html: html, defaultCategory: category) let pagination = try HTMLParser.parsePagination(html: html) self.items = newItems self.currentPage = pagination.current self.totalPages = pagination.total ContentCache.shared.markFresh(key: cacheKey) } catch is CancellationError { } catch let error as URLError where error.code == .cancelled { } catch { self.error = error.localizedDescription } isLoading = false } @MainActor func loadMore() async { guard !isLoadingMore, !isLoading, currentPage < totalPages else { return } isLoadingMore = true let nextPage = currentPage + 1 do { let html = try await APIClient.shared.fetchCategoryPage( category: category, page: nextPage, filter: filter ) let newItems = try HTMLParser.parseContentList(html: html, defaultCategory: category) let pagination = try HTMLParser.parsePagination(html: html) self.items.append(contentsOf: newItems) self.currentPage = pagination.current self.totalPages = pagination.total } catch is CancellationError { } catch let error as URLError where error.code == .cancelled { } catch { // 加载更多失败不显示全局错误,允许重试 } isLoadingMore = false } func changeCategory(_ newCategory: ContentCategory) async { category = newCategory filter = FilterState() await loadContentIfNeeded() } func applyFilter(_ newFilter: FilterState) async { filter = newFilter await loadContent() } }