fix: resolve 3 compilation errors

- NotificationListView.swift: unwrap optional String? for Text()
- LocalCache.swift: add @escaping to fetch closure parameter
- ContentView.swift: reformat ZXTabBar body for item scope fix

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
WangDL 2026-05-18 11:35:23 +08:00
parent 89d89e542c
commit be7fca5305
3 changed files with 105 additions and 23 deletions

View File

@ -21,42 +21,124 @@ struct ContentView: View {
struct ZXTabBar: View { struct ZXTabBar: View {
@Binding var active: String @Binding var active: String
private let items = [("ai","AI","brain.head.profile"),("library","知识库","books.vertical.fill"),("study","学习","bolt.fill"),("analysis","分析","chart.bar.fill"),("profile","我的","person.fill")] private let items = [
var body: some View{HStack(spacing:0){ForEach(items,id:\.0){item in let on=item.0==active;Button{active=item.0}label:{VStack(spacing:4){ZStack{if on{Circle().fill(Color.zxPurple.opacity(0.2)).frame(width:28,height:28).scaleEffect(1.4)};Image(systemName:item.2).font(.system(size:22,weight:on ? .semibold:.regular)).foregroundColor(on ? Color.zxPurple:Color.zxF03)};Text(item.1).font(.system(size:10,weight:on ? .semibold:.regular)).foregroundColor(on ? Color.zxPurple:Color.zxF03)}}.frame(maxWidth:.infinity)}.accessibilityLabel("\(item.1)标签")}.padding(.top,6).padding(.bottom,34).frame(height:83).background(.ultraThinMaterial).background(Color.zxBg0.opacity(0.95)).overlay(alignment:.top){Rectangle().fill(Color.zxBorder008).frame(height:1)}} ("ai", "AI", "brain.head.profile"),
("library", "知识库", "books.vertical.fill"),
("study", "学习", "bolt.fill"),
("analysis", "分析", "chart.bar.fill"),
("profile", "我的", "person.fill"),
]
var body: some View {
HStack(spacing: 0) {
ForEach(items, id: \.0) { item in
let on = item.0 == active
Button {
active = item.0
} label: {
VStack(spacing: 4) {
ZStack {
if on {
Circle()
.fill(Color.zxPurple.opacity(0.2))
.frame(width: 28, height: 28)
.scaleEffect(1.4)
}
Image(systemName: item.2)
.font(.system(size: 22, weight: on ? .semibold : .regular))
.foregroundColor(on ? Color.zxPurple : Color.zxF03)
}
Text(item.1)
.font(.system(size: 10, weight: on ? .semibold : .regular))
.foregroundColor(on ? Color.zxPurple : Color.zxF03)
}
.frame(maxWidth: .infinity)
}
.accessibilityLabel("\(item.1)标签")
}
}
.padding(.top, 6)
.padding(.bottom, 34)
.frame(height: 83)
.background(.ultraThinMaterial)
.background(Color.zxBg0.opacity(0.95))
.overlay(alignment: .top) {
Rectangle().fill(Color.zxBorder008).frame(height: 1)
}
}
} }
struct ZXIconBtn: View { struct ZXIconBtn: View {
let icon: String; let size: CGFloat; var branded = false; let action: () -> Void let icon: String; let size: CGFloat; var branded = false; let action: () -> Void
var body: some View {Button(action:action){Image(systemName:icon).font(.system(size:size*0.44)).frame(width:size,height:size)}.foregroundColor(branded ? .white:Color.zxF05).background(branded ? AnyView(ZXGradient.brand):AnyView(Color(hex:"#FFFFFF",opacity:0.05))).clipShape(RoundedRectangle(cornerRadius:10)).overlay{if !branded{RoundedRectangle(cornerRadius:10).stroke(Color.zxBorder008,lineWidth:1)}}} var body: some View {
Button(action: action) {
Image(systemName: icon).font(.system(size: size * 0.44)).frame(width: size, height: size)
}
.foregroundColor(branded ? .white : Color.zxF05)
.background(branded ? AnyView(ZXGradient.brand) : AnyView(Color(hex: "#FFFFFF", opacity: 0.05)))
.clipShape(RoundedRectangle(cornerRadius: 10))
.overlay {
if !branded {
RoundedRectangle(cornerRadius: 10).stroke(Color.zxBorder008, lineWidth: 1)
}
}
}
} }
struct ZXScoreBox: View {let score:Int;let bg:Color;let fg:Color struct ZXScoreBox: View {
var body: some View {Text("\(score)").font(.system(size:12,weight:.heavy)).foregroundColor(fg).frame(width:36,height:36).background(bg).clipShape(RoundedRectangle(cornerRadius:10))} let score: Int; let bg: Color; let fg: Color
var body: some View {
Text("\(score)")
.font(.system(size: 12, weight: .heavy))
.foregroundColor(fg)
.frame(width: 36, height: 36)
.background(bg)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
} }
struct ZXWeakRow: View { struct ZXWeakRow: View {
let score: Int; let topic: String; let lib: String; let priority: String let score: Int; let topic: String; let lib: String; let priority: String
var body: some View { var body: some View {
HStack(spacing: 12) { HStack(spacing: 12) {
Text("\(score)").font(.system(size:13,weight:.heavy)).foregroundColor(Color.zxYellow) Text("\(score)").font(.system(size: 13, weight: .heavy)).foregroundColor(Color.zxYellow)
.frame(width:40,height:40).background(Color.zxYellowBG(0.15)).clipShape(RoundedRectangle(cornerRadius:12)) .frame(width: 40, height: 40).background(Color.zxYellowBG(0.15)).clipShape(RoundedRectangle(cornerRadius: 12))
VStack(alignment:.leading,spacing:2){ VStack(alignment: .leading, spacing: 2) {
Text(topic).font(.system(size:13,weight:.semibold)).foregroundColor(Color.zxF0) Text(topic).font(.system(size: 13, weight: .semibold)).foregroundColor(Color.zxF0)
Text(lib).font(.system(size:11)).foregroundColor(Color.zxF04) Text(lib).font(.system(size: 11)).foregroundColor(Color.zxF04)
}.frame(maxWidth:.infinity,alignment:.leading) }.frame(maxWidth: .infinity, alignment: .leading)
Text("\(priority)优先").font(.system(size:11,weight:.bold)) Text("\(priority)优先").font(.system(size: 11, weight: .bold))
.foregroundColor(priority=="" ? Color.zxRed:Color.zxYellow) .foregroundColor(priority == "" ? Color.zxRed : Color.zxYellow)
.padding(.horizontal,8).padding(.vertical,3) .padding(.horizontal, 8).padding(.vertical, 3)
.background((priority=="" ? Color.zxRedBG(0.15):Color.zxYellowBG(0.15))).clipShape(Capsule()) .background((priority == "" ? Color.zxRedBG(0.15) : Color.zxYellowBG(0.15))).clipShape(Capsule())
} }
.padding(.horizontal,16).padding(.vertical,12) .padding(.horizontal, 16).padding(.vertical, 12)
.background(Color.zxYellowBG(0.06)) .background(Color.zxYellowBG(0.06))
.overlay(RoundedRectangle(cornerRadius:14).stroke(Color(hex:"#F59E0B",opacity:0.15),lineWidth:1)) .overlay(RoundedRectangle(cornerRadius: 14).stroke(Color(hex: "#F59E0B", opacity: 0.15), lineWidth: 1))
.clipShape(RoundedRectangle(cornerRadius:14)) .clipShape(RoundedRectangle(cornerRadius: 14))
} }
} }
struct ZXAIInputBar: View { struct ZXAIInputBar: View {
@Binding var text:String;let onSend:()->Void @Binding var text: String; let onSend: () -> Void
var body: some View {HStack(spacing:10){Image(systemName:"sparkles").font(.system(size:16)).foregroundColor(Color.zxPurple);TextField("问 AI 任何学习问题…",text:$text).font(.system(size:14)).tint(Color.zxPurple).accessibilityLabel("AI 学习问题输入框");Spacer();Image(systemName:"mic.fill").font(.system(size:18)).foregroundColor(Color.zxF03).accessibilityLabel("语音输入");Button(action:onSend){Image(systemName:"arrow.up").font(.system(size:14,weight:.bold)).foregroundColor(.white).frame(width:30,height:30).background(ZXGradient.brand).clipShape(RoundedRectangle(cornerRadius:9))}.zxPressable().disabled(text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty).accessibilityLabel("发送消息")}.padding(.horizontal,14).padding(.vertical,10).background(.ultraThinMaterial).background(Color.zxFill004).overlay(RoundedRectangle(cornerRadius:20).stroke(Color.zxBorder008,lineWidth:1)).clipShape(RoundedRectangle(cornerRadius:20)).padding(.horizontal,20).padding(.bottom,34)} var body: some View {
HStack(spacing: 10) {
Image(systemName: "sparkles").font(.system(size: 16)).foregroundColor(Color.zxPurple)
TextField("问 AI 任何学习问题…", text: $text).font(.system(size: 14)).tint(Color.zxPurple).accessibilityLabel("AI 学习问题输入框")
Spacer()
Image(systemName: "mic.fill").font(.system(size: 18)).foregroundColor(Color.zxF03).accessibilityLabel("语音输入")
Button(action: onSend) {
Image(systemName: "arrow.up").font(.system(size: 14, weight: .bold)).foregroundColor(.white)
.frame(width: 30, height: 30).background(ZXGradient.brand).clipShape(RoundedRectangle(cornerRadius: 9))
}
.zxPressable()
.disabled(text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
.accessibilityLabel("发送消息")
}
.padding(.horizontal, 14).padding(.vertical, 10)
.background(.ultraThinMaterial).background(Color.zxFill004)
.overlay(RoundedRectangle(cornerRadius: 20).stroke(Color.zxBorder008, lineWidth: 1))
.clipShape(RoundedRectangle(cornerRadius: 20))
.padding(.horizontal, 20).padding(.bottom, 34)
}
} }

View File

@ -70,7 +70,7 @@ extension LocalCache {
func cacheFirst<T: Codable>( func cacheFirst<T: Codable>(
key: String, key: String,
ttl: TimeInterval = 300, ttl: TimeInterval = 300,
fetch: @Sendable () async throws -> T fetch: @Sendable @escaping () async throws -> T
) async throws -> T { ) async throws -> T {
if let cached: T = getWithExpiry(key, ttl: ttl) { if let cached: T = getWithExpiry(key, ttl: ttl) {
Task { try? await refreshCache(key: key, ttl: ttl, fetch: fetch) } Task { try? await refreshCache(key: key, ttl: ttl, fetch: fetch) }
@ -84,7 +84,7 @@ extension LocalCache {
private func refreshCache<T: Codable>( private func refreshCache<T: Codable>(
key: String, key: String,
ttl: TimeInterval, ttl: TimeInterval,
fetch: @Sendable () async throws -> T fetch: @Sendable @escaping () async throws -> T
) async throws { ) async throws {
let fresh = try await fetch() let fresh = try await fetch()
setWithExpiry(fresh, forKey: key, ttl: ttl) setWithExpiry(fresh, forKey: key, ttl: ttl)

View File

@ -94,7 +94,7 @@ struct ZXNotificationItemRow: View {
Circle().fill(Color.zxPurple).frame(width: 6, height: 6) Circle().fill(Color.zxPurple).frame(width: 6, height: 6)
} }
} }
Text(item.content).font(.system(size: 12)).foregroundColor(Color.zxF04).lineLimit(2) Text(item.content ?? "").font(.system(size: 12)).foregroundColor(Color.zxF04).lineLimit(2)
if let createdAt = item.createdAt { if let createdAt = item.createdAt {
Text(createdAt.prefix(10).description).font(.system(size: 10)).foregroundColor(Color.zxF03) Text(createdAt.prefix(10).description).font(.system(size: 10)).foregroundColor(Color.zxF03)
} }