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:
parent
89d89e542c
commit
be7fca5305
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user