fix: M-CHAT-A5/A9/A11 修复三个暂缓 issue
- A5: MaterialReaderView 传入 knowledgeBaseId (Route 新增参数) - A9: 会话列表项显示 scope 图标 + 类型标签 - A11: 会话列表左滑归档 + 右滑置顶/取消置顶 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
491c3e7ef0
commit
0af5b6554d
@ -31,7 +31,7 @@ enum Route: Hashable {
|
||||
case quizResult(quizId: String, attemptId: String)
|
||||
|
||||
// Material Reader
|
||||
case materialReader(materialId: String, filePath: String, materialType: MaterialType)
|
||||
case materialReader(materialId: String, filePath: String, materialType: MaterialType, knowledgeBaseId: String? = nil, title: String = "")
|
||||
case materialDetail(knowledgeBaseId: String, fileName: String, fileType: MaterialType, fileSize: UInt64, filePath: String, uploadDate: String?)
|
||||
|
||||
// Profile
|
||||
@ -109,7 +109,7 @@ extension Route {
|
||||
case .quizList(let kbId): AnyView(QuizListView(knowledgeBaseId: kbId))
|
||||
case .quizTake(let id): AnyView(QuizTakerView(quizId: id))
|
||||
case .quizResult(let qid, let aid): AnyView(QuizResultView(quizId: qid, attemptId: aid))
|
||||
case .materialReader(let mid, let path, let mt): AnyView(MaterialReaderView(materialId: mid, filePath: path, materialType: mt))
|
||||
case .materialReader(let mid, let path, let mt, let kbId, let title): AnyView(MaterialReaderView(materialId: mid, filePath: path, materialType: mt, knowledgeBaseId: kbId, title: title))
|
||||
case .materialDetail(let kbId, let name, let type, let size, let path, let date):
|
||||
AnyView(MaterialDetailView(knowledgeBaseId: kbId, fileName: name, fileType: type, fileSize: size, filePath: path, uploadDate: date))
|
||||
}
|
||||
|
||||
@ -104,11 +104,22 @@ struct AIChatPage: View {
|
||||
Task { await vm.loadSession(s.id) }
|
||||
} label: {
|
||||
HStack {
|
||||
// Scope icon
|
||||
Image(systemName: scopeIcon(for: s.scopeType))
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(Color.zxF04)
|
||||
.frame(width: 20)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(s.title ?? "对话").font(.system(size: 14)).foregroundColor(Color.zxF0)
|
||||
Text(s.updatedAt?.prefix(10).description ?? "").font(.system(size: 11)).foregroundColor(Color.zxF04)
|
||||
HStack(spacing: 4) {
|
||||
Text(scopeLabel(for: s)).font(.system(size: 10)).foregroundColor(Color.zxF04)
|
||||
Text(s.updatedAt?.prefix(10).description ?? "").font(.system(size: 10)).foregroundColor(Color.zxF04)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
if s.isPinned == true {
|
||||
Image(systemName: "pin.fill").font(.system(size: 10)).foregroundColor(Color.zxF04)
|
||||
}
|
||||
}.padding(.vertical, 4)
|
||||
}.foregroundColor(.primary)
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
||||
@ -117,11 +128,25 @@ struct AIChatPage: View {
|
||||
do {
|
||||
try await RagChatService.shared.deleteSession(id: s.id)
|
||||
sessions.removeAll { $0.id == s.id }
|
||||
} catch {
|
||||
// Keep session in list if delete failed
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
} label: { Label("删除", systemImage: "trash") }
|
||||
Button {
|
||||
Task {
|
||||
try? await RagChatService.shared.updateSession(id: s.id, dto: UpdateChatSessionRequest(isArchived: true))
|
||||
sessions.removeAll { $0.id == s.id }
|
||||
}
|
||||
} label: { Label("归档", systemImage: "archivebox") }
|
||||
.tint(Color.zxF04)
|
||||
}
|
||||
.swipeActions(edge: .leading, allowsFullSwipe: true) {
|
||||
Button {
|
||||
Task {
|
||||
try? await RagChatService.shared.updateSession(id: s.id, dto: UpdateChatSessionRequest(isPinned: !(s.isPinned ?? false)))
|
||||
if let updated = try? await RagChatService.shared.listSessions() { sessions = updated }
|
||||
}
|
||||
} label: { Label(s.isPinned == true ? "取消置顶" : "置顶", systemImage: s.isPinned == true ? "pin.slash" : "pin") }
|
||||
.tint(Color.zxPurple)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,6 +174,25 @@ struct AIChatPage: View {
|
||||
proxy.scrollTo(vm.messages.last?.id, anchor: .bottom)
|
||||
}
|
||||
}
|
||||
|
||||
private func scopeIcon(for type: String?) -> String {
|
||||
switch type {
|
||||
case "knowledge_base": return "books.vertical"
|
||||
case "folder": return "folder"
|
||||
case "material": return "doc.text"
|
||||
case "knowledge_item": return "lightbulb"
|
||||
default: return "bubble.left"
|
||||
}
|
||||
}
|
||||
|
||||
private func scopeLabel(for session: ChatSession) -> String {
|
||||
guard let t = session.scopeType, t != "global" else { return "" }
|
||||
let map: [String: String] = [
|
||||
"knowledge_base": "知识库", "folder": "分类",
|
||||
"material": "资料", "knowledge_item": "知识点"
|
||||
]
|
||||
return map[t] ?? t
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Message Card
|
||||
|
||||
@ -102,13 +102,15 @@ struct MaterialReaderView: View {
|
||||
@State private var restoreBlockId: String?
|
||||
|
||||
private let title: String
|
||||
private let knowledgeBaseId: String?
|
||||
|
||||
// Event collector — records reading events during the session
|
||||
private let collector = ReadingEventCollector.shared
|
||||
private let positionStore = ReadingPositionStore.shared
|
||||
|
||||
init(materialId: String, filePath: String, materialType: MaterialType, title: String = "", showQuickLook: Binding<Bool> = .constant(false), showNoteSheet: Binding<Bool> = .constant(false)) {
|
||||
init(materialId: String, filePath: String, materialType: MaterialType, knowledgeBaseId: String? = nil, title: String = "", showQuickLook: Binding<Bool> = .constant(false), showNoteSheet: Binding<Bool> = .constant(false)) {
|
||||
self.title = title
|
||||
self.knowledgeBaseId = knowledgeBaseId
|
||||
self._showQuickLook = showQuickLook
|
||||
self._showNoteSheet = showNoteSheet
|
||||
_vm = StateObject(wrappedValue: MaterialReaderViewModel(
|
||||
@ -157,7 +159,7 @@ struct MaterialReaderView: View {
|
||||
scopeType: .material,
|
||||
scopeId: vm.materialId,
|
||||
scopeName: title,
|
||||
parentKnowledgeBaseId: nil,
|
||||
parentKnowledgeBaseId: knowledgeBaseId,
|
||||
createdFrom: "material_reader"
|
||||
))) {
|
||||
Image(systemName: "bubble.left.and.bubble.right")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user