fix(ios): 重写 LibraryDetailPage body — 清理 ZStack/VStack 嵌套结构

- ZStack { Color; VStack { ... } } 结构正确分离
- ScrollView 内 VStack 的 if/else 分支独立清晰
- Source 列表去掉多余外层 VStack

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-05-28 20:52:05 +08:00
parent 6bb0ae6e29
commit 011dabcb43

View File

@ -149,85 +149,92 @@ struct LibraryDetailPage: View {
}
var body: some View {
ZStack { Color.zxBg0.ignoresSafeArea(); VStack(spacing: 0) {
Picker("", selection: $detailTab) {
Text("知识点").tag(0)
Text("资料来源").tag(1)
}
.pickerStyle(.segmented)
.padding(.horizontal, 20).padding(.top, 8)
ZStack {
Color.zxBg0.ignoresSafeArea()
VStack(spacing: 0) {
Picker("", selection: $detailTab) {
Text("知识点").tag(0)
Text("资料来源").tag(1)
}
.pickerStyle(.segmented)
.padding(.horizontal, 20).padding(.top, 8)
ScrollView { VStack(spacing: 12) {
if detailTab == 0 {
if viewModel.isLoading && viewModel.items.isEmpty {
VStack(spacing: 12) { ZXLoadingView(size: 36, lineWidth: 3); Text("加载中…").font(.system(size: 13)).foregroundColor(Color.zxF04) }
.frame(maxWidth: .infinity).padding(.top, 80)
}
ForEach(viewModel.items) { item in
if isSelectMode {
Button {
if selectedIds.contains(item.id) { selectedIds.remove(item.id) }
else { selectedIds.insert(item.id) }
} label: {
HStack(spacing: 10) {
Image(systemName: selectedIds.contains(item.id) ? "checkmark.circle.fill" : "circle")
.font(.system(size: 20))
.foregroundColor(selectedIds.contains(item.id) ? Color.zxPrimary : Color.zxF03)
ZXCardRow(icon: "doc.text", title: item.title, desc: item.summary ?? item.content ?? "", status: item.status ?? "active", c: Color.zxGreen)
ScrollView {
VStack(spacing: 12) {
if detailTab == 0 {
if viewModel.isLoading && viewModel.items.isEmpty {
VStack(spacing: 12) {
ZXLoadingView(size: 36, lineWidth: 3)
Text("加载中…").font(.system(size: 13)).foregroundColor(Color.zxF04)
}
.frame(maxWidth: .infinity).padding(.top, 80)
}
}
.foregroundColor(.primary)
} else {
NavigationLink(value: Route.knowledgeDetail(item: item)) {
ZXCardRow(icon: "doc.text", title: item.title, desc: item.summary ?? item.content ?? "", status: item.status ?? "active", c: Color.zxGreen)
}
}
}
if viewModel.items.isEmpty && !viewModel.isLoading {
Text("暂无知识点").font(.system(size: 13)).foregroundColor(Color.zxF03).padding(.top, 40)
}
if viewModel.hasMore {
ZXLoadMoreFooter { await viewModel.loadMore(knowledgeBaseId: knowledgeBaseId) }
}
} else {
// Tab
if isLoadingSources {
VStack(spacing: 12) { ProgressView().tint(Color.zxPurple); Text("加载中…").font(.system(size: 13)).foregroundColor(Color.zxF04) }.padding(.top, 80)
} else if sources.isEmpty {
Text("暂无资料来源").font(.system(size: 13)).foregroundColor(Color.zxF03).padding(.top, 40)
} else {
ForEach(sources) { src in
VStack(spacing: 0) {
HStack(spacing: 12) {
Image(systemName: src.type == "file" ? "doc.fill" : "link")
.font(.system(size: 18)).foregroundColor(Color.zxPurple)
.frame(width: 40, height: 40).background(Color.zxPurpleBG(0.12)).clipShape(RoundedRectangle(cornerRadius: 10))
VStack(alignment: .leading, spacing: 2) {
Text(src.title ?? src.originalFilename ?? "未命名").font(.system(size: 14, weight: .semibold)).foregroundColor(Color.zxF0).lineLimit(1)
HStack(spacing: 6) {
Text(src.parseStatus ?? "pending").font(.system(size: 10, weight: .semibold))
.foregroundColor(src.parseStatus == "completed" ? Color.zxGreen : Color.zxAmber)
.padding(.horizontal, 6).padding(.vertical, 1)
.background((src.parseStatus == "completed" ? Color.zxGreen : Color.zxAmber).opacity(0.12)).clipShape(Capsule())
if let len = src.textLength, len > 0 { Text("\(len)").font(.system(size: 10)).foregroundColor(Color.zxF04) }
ForEach(viewModel.items) { item in
if isSelectMode {
Button {
if selectedIds.contains(item.id) { selectedIds.remove(item.id) }
else { selectedIds.insert(item.id) }
} label: {
HStack(spacing: 10) {
Image(systemName: selectedIds.contains(item.id) ? "checkmark.circle.fill" : "circle")
.font(.system(size: 20))
.foregroundColor(selectedIds.contains(item.id) ? Color.zxPrimary : Color.zxF03)
ZXCardRow(icon: "doc.text", title: item.title, desc: item.summary ?? item.content ?? "", status: item.status ?? "active", c: Color.zxGreen)
}
}
Spacer()
Button {
Task { await deleteSource(src) }
} label: {
Image(systemName: "trash").font(.system(size: 14)).foregroundColor(Color.zxF03)
.foregroundColor(.primary)
} else {
NavigationLink(value: Route.knowledgeDetail(item: item)) {
ZXCardRow(icon: "doc.text", title: item.title, desc: item.summary ?? item.content ?? "", status: item.status ?? "active", c: Color.zxGreen)
}
}
.padding(12).background(Color.zxFill003).clipShape(RoundedRectangle(cornerRadius: 14))
}
if viewModel.items.isEmpty && !viewModel.isLoading {
Text("暂无知识点").font(.system(size: 13)).foregroundColor(Color.zxF03).padding(.top, 40)
}
if viewModel.hasMore {
ZXLoadMoreFooter { await viewModel.loadMore(knowledgeBaseId: knowledgeBaseId) }
}
} else {
if isLoadingSources {
VStack(spacing: 12) {
ProgressView().tint(Color.zxPurple)
Text("加载中…").font(.system(size: 13)).foregroundColor(Color.zxF04)
}.padding(.top, 80)
} else if sources.isEmpty {
Text("暂无资料来源").font(.system(size: 13)).foregroundColor(Color.zxF03).padding(.top, 40)
} else {
ForEach(sources) { src in
HStack(spacing: 12) {
Image(systemName: src.type == "file" ? "doc.fill" : "link")
.font(.system(size: 18)).foregroundColor(Color.zxPurple)
.frame(width: 40, height: 40).background(Color.zxPurpleBG(0.12)).clipShape(RoundedRectangle(cornerRadius: 10))
VStack(alignment: .leading, spacing: 2) {
Text(src.title ?? src.originalFilename ?? "未命名").font(.system(size: 14, weight: .semibold)).foregroundColor(Color.zxF0).lineLimit(1)
HStack(spacing: 6) {
Text(src.parseStatus ?? "pending").font(.system(size: 10, weight: .semibold))
.foregroundColor(src.parseStatus == "completed" ? Color.zxGreen : Color.zxAmber)
.padding(.horizontal, 6).padding(.vertical, 1)
.background((src.parseStatus == "completed" ? Color.zxGreen : Color.zxAmber).opacity(0.12)).clipShape(Capsule())
if let len = src.textLength, len > 0 { Text("\(len)").font(.system(size: 10)).foregroundColor(Color.zxF04) }
}
}
Spacer()
Button {
Task { await deleteSource(src) }
} label: {
Image(systemName: "trash").font(.system(size: 14)).foregroundColor(Color.zxF03)
}
}
.padding(12).background(Color.zxFill003).clipShape(RoundedRectangle(cornerRadius: 14))
}
}
}
}
.padding(.horizontal, 20).padding(.bottom, 80)
}
}
.padding(.horizontal, 20).padding(.bottom, 80)
.scrollIndicators(.hidden)
.zxPullToRefresh { await viewModel.refresh(knowledgeBaseId: knowledgeBaseId) }
.scrollIndicators(.hidden)
.zxPullToRefresh { await viewModel.refresh(knowledgeBaseId: knowledgeBaseId) }
}
}
.navigationBarTitleDisplayMode(.inline).toolbarBackground(.hidden, for: .navigationBar).animatedTabBarHide()