WangDL 7066200b7b feat: MVVM 架构、全套 UI 页面、浅深色主题、本地持久化、等待名单、AI 动效
- 架构层:ViewModel/ObservableObject、Service/Repository、网络层 APIClient/APIEndpoint/APIError
- 设计系统:Color(light:dark:) 自适应 28 色 Token、ColorSchemeManager 深浅色切换
- 全页面:AI 对话/反馈/回忆/薄弱点、知识库 CRUD、学习工作台、复习计划、学习分析、个人中心/设置
- 登录与引导:Sign in with Apple、AppSession 状态管理、引导流程、演示模式
- 本地持久化:FileCache + PersistenceController(学习任务/复习任务/学习记录)
- 本地化:zh-Hans Localizable.strings ~120 条、ZXStrings 程序化引用、LanguageManager
- 组件库:ZXTabBar/ZXBackHeader/ZXSTaskRow/ZXChartView/ZXTypingIndicator 等 22 个共享组件
- 等待名单:WaitlistView 邮箱收集表单
- 动效:ZXTypingIndicator AI 打字动画、ZXShimmerModifier 骨架屏
- 测试:StudyHomeViewModel/AIChatViewModel/ReviewPlanViewModel/FileCache 共 28 条
- Dynamic Type 支持 + 范围限制

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:22:50 +08:00

48 lines
2.3 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.

import SwiftUI
struct ZXSTaskRow: View {
let task: StudyTaskEntity; var action: () -> Void
var body: some View {
Button(action: { withAnimation(.spring(response: 0.35, dampingFraction: 0.7)) { action() } }) {
HStack(spacing: 12) {
Image(systemName: task.isDone ? "checkmark.circle.fill" : "circle")
.font(.system(size: 20))
.foregroundColor(task.isDone ? Color.zxGreen : Color.zxF02)
VStack(alignment: .leading, spacing: 4) {
Text(task.title)
.font(.system(size: 13, weight: .semibold))
.foregroundColor(task.isDone ? Color.zxF04 : Color.zxF0)
.strikethrough(task.isDone)
HStack(spacing: 8) {
Text(task.taskType)
.font(.system(size: 10, weight: .semibold))
.foregroundColor(task.color)
.padding(.horizontal, 6).padding(.vertical, 1)
.background(task.color.opacity(0.12)).clipShape(Capsule())
Text("\(task.minutes) 分钟")
.font(.system(size: 10))
.foregroundColor(Color.zxF035)
}
}
Spacer()
if !task.isDone {
Image(systemName: "play.fill")
.font(.system(size: 14)).foregroundColor(.white)
.frame(width: 32, height: 32)
.background(ZXGradient.brand).clipShape(RoundedRectangle(cornerRadius: 10))
}
}
.padding(.horizontal, 16).padding(.vertical, 12)
.background(task.isDone ? Color.zxFill003 : Color.zxFill005)
.overlay(RoundedRectangle(cornerRadius: 14).stroke(
task.isDone ? Color.zxFill005 : Color.zxBorder008, lineWidth: 1
))
.clipShape(RoundedRectangle(cornerRadius: 14))
.opacity(task.isDone ? 0.6 : 1)
}
.foregroundColor(.primary)
.accessibilityLabel("\(task.title)\(task.isDone ? "已完成" : "未完成")")
.accessibilityHint(task.isDone ? "双击取消完成" : "双击标记完成")
}
}