ios-projects/AIStudyApp/AIStudyApp/Features/AI/DailyThinkingPage.swift

197 lines
14 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// DailyThinkingPage.swift - Page 14: Daily Thinking
//
import SwiftUI
struct DailyThinkingPage: View {
@State private var answer = ""
@State private var submitted = false
var body: some View {
ZStack { Color.zxBg0.ignoresSafeArea()
VStack(spacing: 0) {
ZXBackHeader(title: "今日思考", subtitle: "过拟合", onBack: nil, trailing: {
ZXIconBtn(icon: "bookmark", size: 36) {}
})
ScrollView { VStack(spacing: 16) {
VStack(alignment: .leading, spacing: 12) {
HStack {
Image(systemName: "sparkles").foregroundColor(Color.zxAccent)
Text("解释\"注意力机制\"在 Transformer 中的作用").font(.system(size: 15, weight: .bold)).foregroundColor(Color.zxF0)
}
Text("AI会从三个方面评估你的回答核心概念理解 · 理论深度 · 实际应用能力").font(.system(size: 12)).foregroundColor(Color.zxF04)
}
.padding(16).background(ZXGradient.thinkingCard).clipShape(RoundedRectangle(cornerRadius: 16))
VStack(alignment: .leading, spacing: 8) {
Text("你的回答").font(.system(size: 13, weight: .semibold)).foregroundColor(Color.zxF04)
TextEditor(text: $answer)
.font(.system(size: 13)).foregroundColor(Color.zxF0).tint(Color.zxPurple)
.frame(minHeight: 160).scrollContentBackground(.hidden)
.padding(12).background(Color.zxFill004).clipShape(RoundedRectangle(cornerRadius: 14)).overlay(RoundedRectangle(cornerRadius: 14).stroke(Color.zxBorder008, lineWidth: 1))
}
if !submitted {
NavigationLink(destination: AIFeedbackPageView()) {
Text("提交回答,获取 AI 反馈").font(.system(size: 14, weight: .bold)).foregroundColor(.white)
.frame(maxWidth: .infinity).frame(height: 52)
.background(ZXGradient.ctaPurple).clipShape(RoundedRectangle(cornerRadius: 16))
.shadow(color: Color(hex: "#7C6EFA", opacity: 0.3), radius: 24)
}
}
}.padding(.horizontal, 20).padding(.bottom, 120) }
.scrollIndicators(.hidden)
}
}
.navigationBarHidden(true)
}
}
// Back Header
struct ZXBackHeader<T: View>: View {
let title: String; let subtitle: String?; var onBack: (() -> Void)?
@ViewBuilder var trailing: () -> T
@Environment(\.dismiss) private var dismiss
var body: some View {
HStack {
Button { (onBack ?? { dismiss() })() } label: {
Image(systemName: "chevron.left").font(.system(size: 18)).foregroundColor(Color.zxF007)
.frame(width: 36, height: 36).background(Color.zxFill005).clipShape(RoundedRectangle(cornerRadius: 10))
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.zxBorder008, lineWidth: 1))
}
VStack(spacing: 1) {
Text(title).font(.system(size: 15, weight: .bold)).foregroundColor(Color.zxF0)
if let s = subtitle { Text(s).font(.system(size: 11)).foregroundColor(Color.zxF03) }
}.frame(maxWidth: .infinity)
trailing()
}
.padding(.horizontal, 16).padding(.top, ZXSpacing.statusBarH + 8).padding(.bottom, 12)
.background(Color.zxBg0)
}
}
// RecallTest
struct RecallTestPage: View {
@State private var input = ""
var body: some View {
ZStack { Color.zxBg0.ignoresSafeArea()
VStack(spacing: 0) {
ZXBackHeader(title: "回忆测试", subtitle: "机器学习 · 偏差-方差权衡") {}
ScrollView { VStack(spacing: 16) {
Text("请回忆并写下你对「偏差-方差权衡」的理解").font(.system(size: 14)).foregroundColor(Color.zxF04)
TextEditor(text: $input).frame(minHeight: 200).scrollContentBackground(.hidden).padding(12).background(Color.zxFill004).clipShape(RoundedRectangle(cornerRadius: 14)).overlay(RoundedRectangle(cornerRadius: 14).stroke(Color.zxBorder008, lineWidth: 1))
Button {} label: { Text("提交").font(.system(size: 14, weight: .bold)).foregroundColor(.white).frame(maxWidth: .infinity).frame(height: 52).background(ZXGradient.ctaPurple).clipShape(RoundedRectangle(cornerRadius: 16)) }
}.padding(.horizontal, 20).padding(.bottom, 80) }.scrollIndicators(.hidden)
}
}.navigationBarHidden(true)
}
}
// WeakPoints
struct WeakPointsPage: View {
var body: some View {
ZStack { Color.zxBg0.ignoresSafeArea()
VStack(spacing: 0) {
ZXBackHeader(title: "薄弱知识点", subtitle: "共 23 个待巩固") {}
ScrollView { VStack(spacing: 12) {
ZXWeakRow(score: 32, topic: "贝叶斯定理应用", lib: "机器学习", priority: "")
ZXWeakRow(score: 41, topic: "正态分布性质", lib: "高等数学", priority: "")
ZXWeakRow(score: 55, topic: "词根 spect- 相关词汇", lib: "英语词汇", priority: "")
ZXWeakRow(score: 48, topic: "协方差与相关系数", lib: "机器学习", priority: "")
ZXWeakRow(score: 36, topic: "梯度下降优化", lib: "机器学习", priority: "")
}.padding(.horizontal, 20).padding(.bottom, 80) }.scrollIndicators(.hidden)
}
}.navigationBarHidden(true)
}
}
// AIFeedback - Page 15
struct AIFeedbackPageView: View {
var body: some View {
ZStack { Color.zxBg0.ignoresSafeArea()
VStack(spacing: 0) {
ZXBackHeader(title: "AI 反馈", subtitle: "今日思考 · 过拟合", trailing: {
ZXIconBtn(icon: "bookmark", size: 36) {}
})
ScrollView { VStack(spacing: 16) {
// Score
HStack(spacing: 20) {
ZStack {
Circle().trim(from: 0, to: 0.78).stroke(ZXGradient.brand, style: StrokeStyle(lineWidth: 10, lineCap: .round)).rotationEffect(.degrees(-90)).frame(width: 80, height: 80)
VStack(spacing: 0) { Text("78").font(.system(size: 22, weight: .heavy)).foregroundColor(Color.zxPurple); Text("/ 100").font(.system(size: 9)).foregroundColor(Color.zxF04) }
}
VStack(alignment: .leading, spacing: 2) {
Text("良好掌握").font(.system(size: 18, weight: .heavy)).foregroundColor(Color.zxF0)
Text("理解核心概念,但缺少理论深度和解决方案").font(.system(size: 12)).foregroundColor(Color.zxF0045).lineSpacing(4)
}
Spacer()
}
.padding(20).background(ZXGradient.feedbackScore).clipShape(RoundedRectangle(cornerRadius: 20)).overlay(RoundedRectangle(cornerRadius: 20).stroke(Color(hex: "#7C6EFA", opacity: 0.2), lineWidth: 1))
//
VStack(alignment: .leading, spacing: 8) { Text("你的回答").font(.system(size: 13, weight: .semibold)).foregroundColor(Color.zxF04); Text("过拟合就像一个学生只会「死记硬背」考题,而不是真正理解知识…").font(.system(size: 13)).foregroundColor(Color.zxF007).lineSpacing(6).padding(14).background(Color.zxFill004).clipShape(RoundedRectangle(cornerRadius: 14)).overlay(RoundedRectangle(cornerRadius: 14).stroke(Color.zxBorder006, lineWidth: 1)) }
//
VStack(alignment: .leading, spacing: 8) {
HStack(spacing: 8) { Image(systemName: "checkmark.circle.fill").foregroundColor(Color.zxGreen); Text("答对的部分").font(.system(size: 14, weight: .bold)).foregroundColor(Color.zxF0) }
ForEach([("正确识别出过拟合是\"记住训练数据\"而非\"学习规律\""),("使用了死记硬背类比,方向正确且贴切")], id: \.self) { s in
HStack(alignment: .top, spacing: 12) { Circle().fill(Color.zxGreen).frame(width: 6, height: 6).padding(.top, 6); Text(s).font(.system(size: 13)).foregroundColor(Color(hex: "#F0F0FF", opacity: 0.75)).lineSpacing(4) }
.padding(12).background(Color(hex: "#34D399", opacity: 0.07)).clipShape(RoundedRectangle(cornerRadius: 12)).overlay(RoundedRectangle(cornerRadius: 12).stroke(Color(hex: "#34D399", opacity: 0.18), lineWidth: 1))
}
}
//
VStack(alignment: .leading, spacing: 10) {
HStack(spacing: 8) { Image(systemName: "exclamationmark.triangle.fill").foregroundColor(Color.zxYellow); Text("需要完善").font(.system(size: 14, weight: .bold)).foregroundColor(Color.zxF0) }
ForEach([("缺少对「方差」和「偏差」权衡的说明", "过拟合本质是高方差问题,可以提到偏差-方差权衡"),("未提及正则化、Dropout 等解决方案", "完整答案通常要说明\"如何解决\"")], id: \.0) { p, d in
VStack(alignment: .leading, spacing: 4) { Text(p).font(.system(size: 13, weight: .bold)).foregroundColor(Color.zxF0); Text(d).font(.system(size: 12)).foregroundColor(Color.zxF05).lineSpacing(4) }
.padding(14).frame(maxWidth: .infinity, alignment: .leading).background(Color(hex: "#F59E0B", opacity: 0.07)).clipShape(RoundedRectangle(cornerRadius: 14)).overlay(RoundedRectangle(cornerRadius: 14).stroke(Color(hex: "#F59E0B", opacity: 0.18), lineWidth: 1))
}
}
//
VStack(alignment: .leading, spacing: 8) {
Text("✨ 参考答案要点").font(.system(size: 13, weight: .bold)).foregroundColor(Color.zxAccent)
Text("过拟合是模型复杂度过高导致的高方差问题。偏差-方差权衡是核心概念。解决方法包括正则化、Dropout、数据增强、早停等。").font(.system(size: 13)).foregroundColor(Color.zxF007).lineSpacing(6)
}.padding(16).background(Color(hex: "#7C6EFA", opacity: 0.07)).clipShape(RoundedRectangle(cornerRadius: 16)).overlay(RoundedRectangle(cornerRadius: 16).stroke(Color(hex: "#7C6EFA", opacity: 0.2), lineWidth: 1))
//
Button {} label: { Label("加入待巩固,安排间隔复习", systemImage: "bolt.fill").font(.system(size: 14, weight: .bold)).foregroundColor(.white).frame(maxWidth: .infinity).frame(height: 52).background(ZXGradient.ctaPurple).clipShape(RoundedRectangle(cornerRadius: 16)).shadow(color: Color(hex: "#7C6EFA", opacity: 0.3), radius: 24) }
HStack(spacing: 12) { ZXOutlineBtn(text: "深入提问"); ZXOutlineBtn(text: "再来一题") }
}.padding(.horizontal, 20).padding(.bottom, 80) }.scrollIndicators(.hidden)
}
}.navigationBarHidden(true)
}
}
struct ZXOutlineBtn: View { let text: String
var body: some View { Button {} label: { HStack(spacing: 4) { Text(text).font(.system(size: 13)); Image(systemName: "chevron.right").font(.system(size: 14)) }.foregroundColor(Color.zxF05).frame(maxWidth: .infinity).frame(height: 44).background(Color.zxFill005).clipShape(RoundedRectangle(cornerRadius: 14)).overlay(RoundedRectangle(cornerRadius: 14).stroke(Color.zxBorder008, lineWidth: 1)) } }
}
// AIChat - Page 11
struct AIChatPage: View {
@State private var msg = ""
@State private var msgs: [(role: String, content: String)] = [("ai", "你好!我是你的 AI 学习助手。我可以帮你解答学习问题、分析薄弱点、制定复习计划。请告诉我你想学习什么?")]
var body: some View {
ZStack { Color.zxBg0.ignoresSafeArea()
VStack(spacing: 0) {
ZXBackHeader(title: "AI 对话", subtitle: "学习助手") {}
ScrollViewReader { proxy in ScrollView { VStack(spacing: 16) {
ForEach(Array(msgs.enumerated()), id: \.offset) { i, m in
HStack(alignment: .top, spacing: 8) {
if m.role == "ai" {
Image(systemName: "brain.head.profile").foregroundColor(Color.zxPurple).frame(width: 28, height: 28).background(Color(hex: "#7C6EFA", opacity: 0.15)).clipShape(Circle())
}
Text(m.content).font(.system(size: 14)).foregroundColor(m.role == "user" ? .white : Color.zxF007).padding(12).background(m.role == "user" ? AnyView(ZXGradient.brandPurple) : AnyView(Color.zxFill004)).clipShape(RoundedRectangle(cornerRadius: 16))
if m.role == "user" { Circle().frame(width: 28, height: 28).foregroundColor(Color.zxPurpleBG(0.2)).overlay(Text("").font(.system(size: 10, weight: .bold)).foregroundColor(Color.zxPurple)) }
}
.frame(maxWidth: .infinity, alignment: m.role == "user" ? .trailing : .leading)
}
}.padding(.horizontal, 20).padding(.bottom, 100).id("bottom") }.scrollIndicators(.hidden)
.onChange(of: msgs.count) { withAnimation { proxy.scrollTo("bottom") } } }
ZXAIInputBar(text: $msg, onSend: { guard !msg.isEmpty else { return }; msgs.append(("user", msg)); msg = ""; DispatchQueue.main.asyncAfter(deadline: .now() + 1) { msgs.append(("ai", "好的,我理解你的问题。建议你从基础概念开始,逐步深入理解。需要我帮你制定具体的学习计划吗?")) } })
}
}.navigationBarHidden(true)
}
}