wangdl 4ebb70c036 feat: 图标线型化 + 首页重设计 + 知识库卡片优化 + 知识点列表重构
- 所有 SF Symbol .fill 图标替换为线性版本
- 自定义加载动画全部替换为原生 ProgressView/refreshable
- StudyHomeView 重设计:优先级驱动主行动卡片
- ZLibraryCard 重新设计:封面图自适应、信息布局优化
- LibraryDetailPage:顶部KB信息区、···菜单、排序、长按操作
- 知识点列表:文件类型图标、学习时长、分割线样式
- 弥散渐变顶部背景
- 新增 icon-folder、icon-xmark SVG

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 20:07:15 +08:00

185 lines
6.4 KiB
Swift

import Combine
import Foundation
@MainActor
class LibraryViewModel: ObservableObject {
@Published var knowledgeBases: [KnowledgeBase] = []
@Published var isLoading = false
@Published var isRefreshing = false
@Published var isLoadingMore = false
@Published var errorMessage: String?
@Published var hasMore = true
private var currentPage = 1
private let pageSize = 20
var currentFilter: LibraryFilter = .all
enum LibraryFilter: String, CaseIterable { case all = "全部", mine = "我的", subscribed = "已订阅", official = "官方" }
func loadKnowledgeBases() async {
isLoading = true; errorMessage = nil; currentPage = 1
do {
knowledgeBases = try await fetchKBs(page: 1)
hasMore = knowledgeBases.count >= pageSize
} catch { if knowledgeBases.isEmpty { errorMessage = "加载知识库失败" } }
isLoading = false
}
func refresh() async {
isRefreshing = true; currentPage = 1
do { knowledgeBases = try await fetchKBs(page: 1); hasMore = knowledgeBases.count >= pageSize } catch {}
isRefreshing = false
}
func loadMore() async {
guard !isLoadingMore, hasMore else { return }
isLoadingMore = true; currentPage += 1
do {
let more = try await fetchKBs(page: currentPage)
knowledgeBases.append(contentsOf: more); hasMore = more.count >= pageSize
} catch { currentPage -= 1 }
isLoadingMore = false
}
func createKnowledgeBase(title: String, description: String?) async -> KnowledgeBase? {
do {
let kb = try await KnowledgeBaseService.shared.create(title: title, description: description)
knowledgeBases.insert(kb, at: 0)
ZXToastManager.shared.success("知识库已创建")
return kb
} catch {
ZXToastManager.shared.error("创建失败")
return nil
}
}
func deleteKnowledgeBase(id: String) async {
do {
_ = try await KnowledgeBaseService.shared.delete(id: id)
knowledgeBases.removeAll { $0.id == id }
ZXToastManager.shared.success("已删除")
} catch { ZXToastManager.shared.error("删除失败") }
}
func togglePin(id: String) async {
do {
let _: GenericSuccessResponse? = try? await APIClient.shared.request("/knowledge-bases/\(id)/pin", method: "POST")
await loadKnowledgeBases()
} catch {}
}
private func fetchKBs(page: Int) async throws -> [KnowledgeBase] {
switch currentFilter {
case .all: return try await KnowledgeBaseService.shared.list(page: page, limit: pageSize)
case .mine: return try await KnowledgeBaseService.shared.list(page: page, limit: pageSize) // default is user's own
case .subscribed: return try await KnowledgeBaseService.shared.listSubscribed(page: page, limit: pageSize)
case .official: return [] // TODO: when backend supports ownerType=official
}
}
}
@MainActor
class LibraryDetailViewModel: ObservableObject {
@Published var items: [KnowledgeItem] = []
@Published var knowledgeBase: KnowledgeBase?
@Published var isLoading = false
@Published var isRefreshing = false
@Published var isLoadingMore = false
@Published var errorMessage: String?
@Published var hasMore = true
private var currentPage = 1
private let pageSize = 20
func loadItems(knowledgeBaseId: String) async {
isLoading = true
errorMessage = nil
currentPage = 1
do {
async let kb = try? KnowledgeBaseService.shared.detail(id: knowledgeBaseId)
async let list = try? KnowledgeItemService.shared.list(knowledgeBaseId: knowledgeBaseId)
let (kbResult, listResult) = await (kb, list)
knowledgeBase = kbResult
items = listResult ?? []
hasMore = items.count >= pageSize
} catch {
if items.isEmpty { errorMessage = "加载知识点失败" }
}
isLoading = false
}
func refresh(knowledgeBaseId: String) async {
isRefreshing = true
currentPage = 1
do {
items = try await KnowledgeItemService.shared.list(knowledgeBaseId: knowledgeBaseId)
hasMore = items.count >= pageSize
} catch {}
isRefreshing = false
}
func loadMore(knowledgeBaseId: String) async {
guard !isLoadingMore, hasMore else { return }
isLoadingMore = true
currentPage += 1
do {
let more = try await KnowledgeItemService.shared.list(knowledgeBaseId: knowledgeBaseId)
items.append(contentsOf: more)
hasMore = more.count >= pageSize
} catch {
currentPage -= 1
}
isLoadingMore = false
}
func loadKnowledgeBase(id: String) async {
do {
knowledgeBase = try await KnowledgeBaseService.shared.detail(id: id)
} catch {}
}
func deleteKnowledgeBase(id: String) async {
do {
_ = try await KnowledgeBaseService.shared.delete(id: id)
ZXToastManager.shared.success("知识库已删除")
} catch {
ZXToastManager.shared.error("删除失败")
}
}
func addItem(knowledgeBaseId: String, title: String, content: String?) async -> KnowledgeItem? {
do {
let item = try await KnowledgeItemService.shared.create(
knowledgeBaseId: knowledgeBaseId, title: title, content: content
)
items.append(item)
ZXToastManager.shared.success("知识点已添加")
return item
} catch {
ZXToastManager.shared.error("添加失败")
return nil
}
}
func deleteItem(id: String) async {
do {
try await KnowledgeItemService.shared.delete(id: id)
items.removeAll { $0.id == id }
ZXToastManager.shared.success("知识点已删除")
} catch {
ZXToastManager.shared.error("删除失败")
}
}
func batchDeleteItems(ids: [String]) async {
do {
let resp = try await KnowledgeItemService.shared.batchDelete(ids: ids)
items.removeAll { ids.contains($0.id) }
ZXToastManager.shared.success(resp.message ?? "已删除")
} catch {
ZXToastManager.shared.error("批量删除失败")
}
}
}