fix: make source items tappable in 资料来源 tab

Wraps each KnowledgeSource row in NavigationLink to MaterialDetailView.
Previously sources were display-only with no tap action.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-18 21:27:10 +08:00
parent 1c0a625192
commit bb9c9afb4d

View File

@ -260,38 +260,47 @@ struct LibraryDetailPage: View {
Text("暂无资料来源").font(.system(size: 14)).foregroundColor(Color.zxF03).padding(.top, 40)
} else {
ForEach(sources) { src in
HStack(spacing: 12) {
Image(systemName: src.type == "file" ? "doc" : "link")
.font(.system(size: 18)).foregroundColor(Color.zxPurple)
.frame(width: 36, height: 36)
VStack(alignment: .leading, spacing: 2) {
Text(src.title ?? src.originalFilename ?? "未命名")
.font(.system(size: 15, weight: .medium)).foregroundColor(Color.zxF0).lineLimit(1)
if let len = src.textLength, len > 0 {
Text("\(len)")
.font(.system(size: 13)).foregroundColor(Color.zxF04)
NavigationLink(value: Route.materialDetail(
knowledgeBaseId: knowledgeBaseId,
fileName: src.originalFilename ?? src.title ?? "未命名",
fileType: materialTypeFromMime(src.mimeType),
fileSize: 0,
filePath: "",
uploadDate: src.createdAt
)) {
HStack(spacing: 12) {
Image(systemName: src.type == "file" ? "doc" : "link")
.font(.system(size: 18)).foregroundColor(Color.zxPurple)
.frame(width: 36, height: 36)
VStack(alignment: .leading, spacing: 2) {
Text(src.originalFilename ?? src.title ?? "未命名")
.font(.system(size: 15, weight: .medium)).foregroundColor(Color.zxF0).lineLimit(1)
if let len = src.textLength, len > 0 {
Text("\(len)")
.font(.system(size: 13)).foregroundColor(Color.zxF04)
}
// Reading status badge
if let status = sourceReadingStatus[src.id], status != "not_started" {
Text(status == "read" ? "已读" : "阅读中")
.font(.system(size: 10, weight: .medium))
.foregroundColor(status == "read" ? Color.green : Color.zxPurple)
.padding(.horizontal, 6).padding(.vertical, 2)
.background((status == "read" ? Color.green : Color.zxPurple).opacity(0.1))
.clipShape(Capsule())
}
}
// Reading status badge
if let status = sourceReadingStatus[src.id], status != "not_started" {
Text(status == "read" ? "已读" : "阅读中")
.font(.system(size: 10, weight: .medium))
.foregroundColor(status == "read" ? Color.green : Color.zxPurple)
.padding(.horizontal, 6).padding(.vertical, 2)
.background((status == "read" ? Color.green : Color.zxPurple).opacity(0.1))
.clipShape(Capsule())
Spacer()
Button {
Task { await deleteSource(src) }
} label: {
Image("icon-trash").resizable().scaledToFit().frame(width: 16, height: 16).foregroundColor(Color.zxF03)
}
}
Spacer()
Button {
Task { await deleteSource(src) }
} label: {
Image("icon-trash").resizable().scaledToFit().frame(width: 16, height: 16).foregroundColor(Color.zxF03)
.padding(.vertical, 14)
.overlay(alignment: .bottom) {
Color.zxHairline.frame(height: 0.5)
}
}
.padding(.vertical, 14)
.overlay(alignment: .bottom) {
Color.zxHairline.frame(height: 0.5)
}
}
}
}
@ -1065,6 +1074,10 @@ struct KnowledgeDetailPage: View {
_vm = StateObject(wrappedValue: KnowledgeDetailViewModel(item: item))
}
@State private var navigateToReader = false
@State private var readerPath: String = ""
@State private var readerType: MaterialType = .text
var body: some View {
Group {
switch vm.loadState {
@ -1477,6 +1490,18 @@ struct EditKnowledgePage: View {
// MARK: - Helpers
private func materialTypeFromMime(_ mime: String?) -> MaterialType {
guard let m = mime?.lowercased() else { return .markdown }
if m.contains("pdf") { return .pdf }
if m.contains("epub") { return .epub }
if m.contains("word") || m.contains("docx") || m.contains("doc") { return .word }
if m.contains("excel") || m.contains("xlsx") { return .excel }
if m.contains("powerpoint") || m.contains("pptx") { return .powerPoint }
if m.contains("image") { return .image }
if m.contains("text/plain") { return .text }
return .markdown
}
private func writeTempFile(content: String, itemId: String) throws -> String {
let dir = FileManager.default.temporaryDirectory
let url = dir.appendingPathComponent("kd_text_\(itemId).md")