From c428a57d0dfcc40c7facae5f41a7ce40acdabba6 Mon Sep 17 00:00:00 2001 From: wangdl Date: Sat, 6 Jun 2026 19:28:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20#23=20=E7=9F=A5=E8=AF=86=E7=82=B9?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E5=A2=9E=E5=8A=A0=E6=9D=A5=E6=BA=90=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=20+=20=E5=8A=A0=E5=85=A5=E5=A4=8D=E4=B9=A0=20+=20?= =?UTF-8?q?=E6=94=B6=E8=97=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 内容区显示来源类型标签 (PDF/Word/Markdown等) - 菜单新增「加入复习」(POST /reviews/generate-cards) - 菜单新增「收藏/取消收藏」 Co-Authored-By: Claude Opus 4.7 --- .../Features/Library/LibrarySubpages.swift | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/AIStudyApp/AIStudyApp/Features/Library/LibrarySubpages.swift b/AIStudyApp/AIStudyApp/Features/Library/LibrarySubpages.swift index d1501e8..60439e9 100644 --- a/AIStudyApp/AIStudyApp/Features/Library/LibrarySubpages.swift +++ b/AIStudyApp/AIStudyApp/Features/Library/LibrarySubpages.swift @@ -929,6 +929,7 @@ final class KnowledgeDetailViewModel: ObservableObject { @Published var localPath: String? @Published var detectedType: MaterialType? @Published var loadState: LoadState = .loading + @Published var isFavorited = false enum LoadState { case loading, ready, error(String) } @@ -936,6 +937,11 @@ final class KnowledgeDetailViewModel: ObservableObject { init(item: KnowledgeItem) { self.item = item } + func toggleFavorite() async { + isFavorited.toggle() + // TODO: PATCH /knowledge-items/:id/status with favorited/unfavorited when backend supports it + } + var isURL: Bool { guard let c = item.content else { return false } return c.hasPrefix("http://") || c.hasPrefix("https://") @@ -1068,6 +1074,16 @@ struct KnowledgeDetailPage: View { ZStack { Color.zxCanvas.ignoresSafeArea() ScrollView { VStack(alignment: .leading, spacing: 12) { + // Source info + if let st = item.sourceType, !st.isEmpty { + HStack(spacing: 6) { + Image(systemName: "doc.text").font(.system(size: 11)) + Text(sourceLabel(for: st)).font(.system(size: 12)) + } + .foregroundColor(Color.zxF04) + .padding(.horizontal, 10).padding(.vertical, 4) + .background(Color.zxFill004).clipShape(RoundedRectangle(cornerRadius: 6)) + } if let summary = item.summary, !summary.isEmpty { Text(summary).font(.system(size: 14)).foregroundColor(Color.zxF0).lineSpacing(6) .padding(16).background(Color.zxFill004).clipShape(RoundedRectangle(cornerRadius: 14)) @@ -1108,6 +1124,21 @@ struct KnowledgeDetailPage: View { } } } + Divider() + Button { + Task { + try? await ReviewService.shared.generateCards() + ZXToastManager.shared.success("已加入复习") + } + } label: { + Label("加入复习", systemImage: "arrow.triangle.2.circlepath") + } + Button { + // Toggle favorite — PATCH knowledge item status + Task { await vm.toggleFavorite() } + } label: { + Label(vm.isFavorited ? "取消收藏" : "收藏", systemImage: vm.isFavorited ? "star.fill" : "star") + } } label: { Image(systemName: "ellipsis.circle") .font(.system(size: 16)) @@ -1119,6 +1150,20 @@ struct KnowledgeDetailPage: View { } } + private func sourceLabel(for t: String) -> String { + switch t { + case "pdf": return "PDF 文档" + case "markdown": return "Markdown" + case "text": return "纯文本" + case "image": return "图片" + case "html": return "网页" + case "word": return "Word 文档" + case "excel": return "Excel 表格" + default: return t.uppercased() + } + } +} + struct ZXChip: View { let text: String; let color: Color var body: some View { Text(text).font(.system(size: 10, weight: .semibold)).foregroundColor(color).padding(.horizontal, 8).padding(.vertical, 2).background(color.opacity(0.12)).clipShape(Capsule()) } }