wangdl 70b9ee1250 fix(ios): 加号/垃圾桶图标尺寸放大 + 加号颜色统一
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 13:32:10 +08:00

139 lines
8.6 KiB
Swift

//
// LibraryHomeView.swift - Page 7
//
import SwiftUI
struct LibraryHomeView: View {
@StateObject private var viewModel = LibraryViewModel()
@State private var s = ""
var body: some View {
ZStack { ZXGradient.page.ignoresSafeArea()
VStack(spacing: 0) {
HStack { Text("知识库").font(.system(size: 22, weight: .heavy)).foregroundColor(Color.zxF0).tracking(-0.5); Spacer()
NavigationLink(value: Route.librarySearch) {
Image("icon-search").resizable().scaledToFit().frame(width: 18, height: 18).foregroundColor(Color.zxF05)
.frame(width: 36, height: 36).background(Color(hex:"#FFFFFF",opacity:0.05))
.clipShape(RoundedRectangle(cornerRadius: 10))
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.zxBorder008, lineWidth: 1))
}.accessibilityLabel("搜索知识库")
NavigationLink(value: Route.libraryCreate) {
Image("icon-plus").resizable().scaledToFit().frame(width: 18, height: 18).foregroundColor(.white)
.frame(width: 36, height: 36).background(ZXGradient.brand).clipShape(RoundedRectangle(cornerRadius: 10))
}.accessibilityLabel("创建新知识库")
}.padding(.horizontal, 20).padding(.top, 8).padding(.bottom, 12)
//
HStack(spacing: 8) { Image(systemName: "magnifyingglass").font(.system(size: 16)).foregroundColor(Color.zxF03); TextField("搜索知识库或知识点…", text: $s).font(.system(size: 14)).tint(Color.zxPurple) }
.padding(.horizontal, 14).frame(height: 44).background(Color.zxFill004).overlay(RoundedRectangle(cornerRadius: 14).stroke(Color.zxBorder008, lineWidth: 1)).clipShape(RoundedRectangle(cornerRadius: 14)).padding(.horizontal, 20).padding(.bottom, 12)
// chips
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
ForEach(LibraryViewModel.LibraryFilter.allCases, id: \.rawValue) { f in
Button {
viewModel.currentFilter = f
Task { await viewModel.loadKnowledgeBases() }
} label: {
Text(f.rawValue).font(.system(size: 13, weight: .medium))
.foregroundColor(viewModel.currentFilter == f ? Color.zxOnPrimary : Color.zxF05)
.padding(.horizontal, 14).padding(.vertical, 7)
.background(viewModel.currentFilter == f ? AnyView(ZXGradient.brand) : AnyView(Color.zxFill004))
.clipShape(Capsule())
.overlay(viewModel.currentFilter == f ? nil : Capsule().stroke(Color.zxBorder008, lineWidth: 1))
}
}
}.padding(.horizontal, 20)
}.padding(.bottom, 12)
ScrollView { VStack(spacing: 12) {
if viewModel.isLoading && viewModel.knowledgeBases.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.knowledgeBases) { kb in
NavigationLink(value: Route.libraryDetail(knowledgeBaseId: kb.id)) {
ZLibraryCard(coverUrl: kb.coverUrl, name: kb.title, desc: kb.description ?? "", items: kb.itemCount ?? 0, last: lastStudiedText(kb.lastStudiedAt), isPinned: kb.isPinned ?? false, visibility: kb.visibility ?? "private", ownerType: kb.ownerType ?? "user")
}
}
if viewModel.knowledgeBases.isEmpty && !viewModel.isLoading {
Text(emptyText).font(.system(size: 14)).foregroundColor(Color.zxF04).padding(.top, 60)
}
if viewModel.hasMore {
ZXLoadMoreFooter { await viewModel.loadMore() }
}
}.padding(.horizontal, 20).padding(.bottom, 120) }
.scrollIndicators(.hidden)
.zxPullToRefresh { await viewModel.refresh() }
}
}
.task { await viewModel.loadKnowledgeBases() }
.navigationDestination(for: Route.self) { $0.destination }
}
private var emptyText: String {
switch viewModel.currentFilter {
case .subscribed: return "还没有订阅任何知识库"
case .official: return "暂无官方知识库"
default: return "还没有知识库,点击右上角 + 创建"
}
}
private func lastStudiedText(_ iso: String?) -> String {
guard let iso else { return "未学习" }
return iso.prefix(10).description
}
}
struct ZLibraryCard: View { let coverUrl: String?; let name: String; let desc: String; let items: Int; let last: String; let isPinned: Bool; let visibility: String; let ownerType: String
var body: some View { VStack(spacing: 0) {
HStack(spacing: 12) {
ZStack {
RoundedRectangle(cornerRadius: 13).fill(Color.zxPurpleBG(0.12)).frame(width: 56, height: 56)
if let url = coverUrl, let imageUrl = URL(string: url) {
AsyncImage(url: imageUrl) { phase in
switch phase {
case .success(let img): img.resizable().scaledToFill().frame(width: 56, height: 56).clipShape(RoundedRectangle(cornerRadius: 13))
default: Image(systemName: "books.vertical.fill").font(.system(size: 22)).foregroundColor(Color.zxPurple.opacity(0.5))
}
}
} else {
Image(systemName: "books.vertical.fill").font(.system(size: 22)).foregroundColor(Color.zxPurple.opacity(0.5))
}
}
VStack(alignment: .leading, spacing: 4) {
HStack(spacing: 6) {
Text(name).font(.system(size: 16, weight: .bold)).foregroundColor(Color.zxF0)
if isPinned { Image(systemName: "pin.fill").font(.system(size: 10)).foregroundColor(Color.zxOrange) }
if visibility == "public" { Text("公开").font(.system(size: 9, weight: .semibold)).foregroundColor(Color.zxGreen).padding(.horizontal, 5).padding(.vertical, 1).background(Color.zxGreen.opacity(0.12)).clipShape(Capsule()) }
}
if !desc.isEmpty { Text(desc).font(.system(size: 12)).foregroundColor(Color.zxF04).lineLimit(1) }
}
Spacer()
}.padding(16)
HStack {
HStack(spacing: 4) { Image(systemName: "clock").font(.system(size: 10)); Text("\(items) 项 · \(last)").font(.system(size: 11)) }.foregroundColor(Color.zxF03)
Spacer()
}.padding(.horizontal, 16).padding(.bottom, 12) }
.background(Color.zxFill003).clipShape(RoundedRectangle(cornerRadius: 20)).overlay(RoundedRectangle(cornerRadius: 20).stroke(Color.zxBorder006, lineWidth: 1)) }
}
struct LibrarySearchView: View {
@State private var query = ""
var body: some View {
ZStack { Color.zxBg0.ignoresSafeArea()
VStack(spacing: 0) {
HStack(spacing: 8) { Image(systemName: "magnifyingglass").font(.system(size: 16)).foregroundColor(Color.zxF03); TextField("搜索知识库或知识点…", text: $query).font(.system(size: 14)).tint(Color.zxPurple) }
.padding(.horizontal, 14).frame(height: 44).background(Color.zxFill004).overlay(RoundedRectangle(cornerRadius: 14).stroke(Color.zxBorder008, lineWidth: 1)).clipShape(RoundedRectangle(cornerRadius: 14))
.padding(.horizontal, 20).padding(.top, 8).padding(.bottom, 16)
ScrollView { VStack(spacing: 12) {
if query.isEmpty {
VStack(spacing: 12) {
Image(systemName: "magnifyingglass").font(.system(size: 36)).foregroundColor(Color.zxF03)
Text("搜索知识点、知识库或标签").font(.system(size: 13)).foregroundColor(Color.zxF03)
}.padding(.top, 80)
}
}.padding(.horizontal, 20) }.scrollIndicators(.hidden)
}
}.navigationBarTitleDisplayMode(.inline).hideTabBarWithAnimation().toolbarBackground(.hidden, for: .navigationBar)
}
}