整个课程和数据UI重新设计版本

This commit is contained in:
shenlei
2026-09-18 17:55:44 +09:00
parent febfd30f49
commit 0a4f97d105
154 changed files with 12366 additions and 4784 deletions
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import '../../core/app_state.dart';
import '../../core/app_theme.dart';
import '../../core/assessment_bank.dart';
import '../../core/courses/courses.dart';
import '../../core/models.dart';
import '../../core/voice_service.dart';
import '../../widgets/app_widgets.dart';
@@ -221,7 +221,7 @@ class _AssessmentPageState extends State<AssessmentPage>
results[task.id] = correct;
widget.state.recordAssessmentAttempt(
taskId: task.id,
skill: _skillLabel(task.skill),
skill: task.skill.label,
correct: correct,
rawAnswer: answer == null ? controller.text.trim() : task.choices[answer],
spoken: task.skill == AssessmentSkill.speaking,
@@ -254,7 +254,7 @@ class _AssessmentPageState extends State<AssessmentPage>
results[speaking.id] = false;
widget.state.recordAssessmentPending(
taskId: speaking.id,
skill: _skillLabel(AssessmentSkill.speaking),
skill: AssessmentSkill.speaking.label,
reason: '设备麦克风或语音识别不可用,等待补测。',
);
}
@@ -327,13 +327,20 @@ class _AssessmentPageState extends State<AssessmentPage>
SectionCard(
tint: AppColors.warm,
child: Text(
'待评估:${record.pendingSkills.map(_skillLabel).join('、')}。技术问题不会计为语言错误。',
'待评估:${record.pendingSkills.map((skill) => skill.label).join('、')}。技术问题不会计为语言错误。',
),
),
if (record.failedSkills.isNotEmpty) const Text('建议补练:'),
for (final skill in record.failedSkills)
SectionCard(child: Text(_remediation(skill))),
],
if (_wrongTasks.isNotEmpty) ...[
const Text(
'错题回顾',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
for (final wrong in _wrongTasks) _WrongAnswerCard(wrong: wrong),
],
if (!record.passed && replacementFor(widget.pack.id) != null)
SecondaryButton(
label: '开始替换题补测',
@@ -359,7 +366,7 @@ class _AssessmentPageState extends State<AssessmentPage>
child: SpacedColumn(
children: [
Text(
_skillLabel(task.skill),
task.skill.label,
style: Theme.of(context).textTheme.headlineMedium,
),
const Text('评估中不提供翻译、句框或答案。需要帮助请退出后先做补练。'),
@@ -435,16 +442,50 @@ class _AssessmentPageState extends State<AssessmentPage>
);
}
/// Tasks answered wrong in this attempt, with the learner's latest answer.
List<({AssessmentTask task, String answer})> get _wrongTasks => [
for (final task in widget.pack.tasks)
if (results[task.id] == false)
if (widget.state.attemptEvidence.reversed
.where((item) => item.taskId == task.id)
.firstOrNull
case final evidence? when evidence.outcome != EvidenceKind.pending)
(task: task, answer: evidence.rawAnswer ?? ''),
];
String _remediation(AssessmentSkill skill) => switch (skill) {
AssessmentSkill.listening => '听力:回到数字、星期/时间和场景听辨复习;下一次会使用不同音频。',
AssessmentSkill.speaking => '口语:练姓名、号码、物品、家人、星期、整点和请求重复;确认未编辑转写后再补测。',
AssessmentSkill.reading => '阅读:复习人物、地点、数字和时间信息定位,再阅读不同短对话。',
AssessmentSkill.writing => '写作:分别练完整的姓名、地点、喜好、物品、星期/时间句,不使用句框。',
};
String _skillLabel(AssessmentSkill skill) => switch (skill) {
AssessmentSkill.listening => '听力',
AssessmentSkill.speaking => '口语',
AssessmentSkill.reading => '阅读',
AssessmentSkill.writing => '写作',
};
}
class _WrongAnswerCard extends StatelessWidget {
const _WrongAnswerCard({required this.wrong});
final ({AssessmentTask task, String answer}) wrong;
@override
Widget build(BuildContext context) {
final task = wrong.task;
return SectionCard(
child: SpacedColumn(
spacing: 6,
children: [
Text(
'${task.skill.label} · ${task.audio ?? task.prompt}',
style: const TextStyle(fontWeight: FontWeight.w600),
),
Text(
'你的答案:${wrong.answer.isEmpty ? '(未作答)' : wrong.answer}',
style: TextStyle(color: AppColors.warmInk),
),
Text(
assessmentAnswerGuide(task, wrong.answer),
style: TextStyle(color: AppColors.green),
),
],
),
);
}
}