/// ============================================================ /// 【课堂习题 · 数据加载】(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 questions; /// view 全量题号最大值(错题模式下供页面保留原始题号总数) final int originalMaxSort; } class ExerciseLoad { ExerciseLoad._(); /// edit 模式加载(对标 loadStartOvercome) /// /// [inputRecordId] 路由带入的 recordId;[currentIndex] 为起始大题索引, /// 该题会写入当前 startTime;[baseSubjectIdOverride] 为路由/base 传入的 /// 学科 id(对应 Vue 的 baseSubjectId ?? resRecord.baseSubjectId)。 static Future 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 = >[]; 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 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 = >[]; 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 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 _injectDebugQuestions( List> 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.from(item)); } } debugPrint('[ExerciseLoad] 已追加 ${list.length} 道测试 JSON 试题'); } } catch (e) { debugPrint('[ExerciseLoad] 测试 JSON 追加失败: $e'); } } }