This commit is contained in:
chenshuainan
2026-09-28 20:12:34 +08:00
parent 847986f861
commit 5f53bd8f94
60 changed files with 14624 additions and 398 deletions
@@ -0,0 +1,172 @@
/// ============================================================
/// 【课堂习题 · 数据加载】(1:1 迁移 Vue exerciseLoad.js)
///
/// - loadStartOvercome:edit 模式,GetRecordData + GetListByRecordQuestion,
/// 解析题目、追加 sort、补 hasOptionChildrenQuestion、挂学科 id;
/// - loadSubmitReport:view 模式,GetSubmitReport,错题模式过滤重排。
///
/// 调试:loadStartOvercome 在接口返回后会(仅 kDebugMode)追加
/// assets/mock/class_exercises_edit.json(取自 Vue exerciseLoad.js)。
/// ============================================================
library;
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:new_learn/core/api/conquerPackage/classExercises/api.dart';
import 'package:new_learn/core/utils/question/exam_answer_card.dart';
import 'package:new_learn/core/utils/question/question_constant.dart';
import 'package:new_learn/core/utils/question/question_parser.dart';
import 'package:new_learn/features/conquerPackage/classExercises/controllers/exercise_session.dart';
import 'package:new_learn/features/conquerPackage/classExercises/controllers/exercise_sort.dart';
import 'package:new_learn/features/conquerPackage/classExercises/models/question_model.dart';
class ExerciseLoadResult {
const ExerciseLoadResult({
required this.recordId,
required this.navBarTitle,
required this.baseSubjectId,
required this.questions,
required this.originalMaxSort,
});
final String recordId;
final String navBarTitle;
final int? baseSubjectId;
final List<QuestionModel> questions;
/// view 全量题号最大值(错题模式下供页面保留原始题号总数)
final int originalMaxSort;
}
class ExerciseLoad {
ExerciseLoad._();
/// edit 模式加载(对标 loadStartOvercome)
///
/// [inputRecordId] 路由带入的 recordId;[currentIndex] 为起始大题索引,
/// 该题会写入当前 startTime;[baseSubjectIdOverride] 为路由/base 传入的
/// 学科 id(对应 Vue 的 baseSubjectId ?? resRecord.baseSubjectId)。
static Future<ExerciseLoadResult> loadStartOvercome({
required String inputRecordId,
int currentIndex = 0,
int? baseSubjectIdOverride,
}) async {
final api = ClassExercisesApi.instance;
final resRecord = await api.getRecordData(inputRecordId);
final resList = await api.getListByRecordQuestion(inputRecordId);
// 接口照常请求;debug 测试时再把 exerciseLoad.js 里的 JSON 试题追加进去
await _injectDebugQuestions(resList.items);
final navBarTitle = '${resRecord.name}专项智能攻克';
final subjectId = baseSubjectIdOverride ?? resRecord.baseSubjectId;
final subjectType = getSubjectTypeById(subjectId) ?? 0;
final parsed = <Map<String, dynamic>>[];
for (var i = 0; i < resList.items.length; i++) {
final item = resList.items[i];
if (i == currentIndex) {
item['startTime'] = getCurrentISO();
}
// 1=图片作答 / 手写;0=输入框
item['iSFilllankItemUpload'] = subjectType;
parsed.add(parseQuestionData(item, mergeOriginalChilds: true));
}
var list = appendQuestionSort(parsed);
list = appendExamQuestionChildOptionStatus(list, subjectId);
final bsid = subjectId;
for (final q in list) {
q['baseSubjectId'] = bsid;
}
return ExerciseLoadResult(
recordId: resRecord.recordId,
navBarTitle: navBarTitle,
baseSubjectId: bsid,
questions: list.map(QuestionModel.new).toList(),
originalMaxSort: 0,
);
}
/// view 模式加载(对标 loadSubmitReport)
///
/// [isWrong] 为 true 时只保留答错题并重排 wrongSort,同时把起始索引置 0
/// (调用方不要再用旧 currentIndex)。
static Future<ExerciseLoadResult> loadSubmitReport({
required String packId,
bool isWrong = false,
int currentIndex = 0,
}) async {
final api = ClassExercisesApi.instance;
final res = await api.getSubmitReport(packId);
final navBarTitle = '${res.name}专项智能攻克';
final subjectType = getSubjectTypeById(res.baseSubjectId) ?? 0;
final parsed = <Map<String, dynamic>>[];
for (var i = 0; i < res.questions.length; i++) {
final item = res.questions[i];
if (i == currentIndex) {
item['startTime'] = getCurrentISO();
}
item['iSFilllankItemUpload'] = subjectType;
parsed.add(parseQuestionData(item, mergeOriginalChilds: true));
}
var fullList = appendQuestionSort(parsed);
fullList = appendExamQuestionChildOptionStatus(fullList, res.baseSubjectId);
var maxSort = 0;
for (final item in fullList.map(QuestionModel.new).toList()) {
final end = getQuestionSortRange(item).end;
if (end > maxSort) maxSort = end;
}
List<QuestionModel> questions;
if (isWrong) {
questions = filterWrongQuestions(fullList.map(QuestionModel.new).toList());
} else {
questions = fullList.map(QuestionModel.new).toList();
}
for (final q in questions) {
q.baseSubjectId = res.baseSubjectId;
}
return ExerciseLoadResult(
recordId: res.id,
navBarTitle: navBarTitle,
baseSubjectId: res.baseSubjectId,
questions: questions,
originalMaxSort: maxSort,
);
}
/// 【仅 debug 测试】真实接口请求完成后,把 Vue exerciseLoad.js 里的
/// JSON 试题追加到列表尾部(对标 JS 里 `res.push(...listX)`)。
/// release 包直接 no-op;测完删掉 [loadStartOvercome] 里的调用即可。
static Future<void> _injectDebugQuestions(
List<Map<String, dynamic>> items,
) async {
if (!kDebugMode) return;
try {
final raw = await rootBundle
.loadString('assets/mock/class_exercises_edit.json');
final list = jsonDecode(raw);
if (list is List) {
for (final item in list) {
if (item is Map) {
items.add(Map<String, dynamic>.from(item));
}
}
debugPrint('[ExerciseLoad] 已追加 ${list.length} 道测试 JSON 试题');
}
} catch (e) {
debugPrint('[ExerciseLoad] 测试 JSON 追加失败: $e');
}
}
}