diff --git a/.gitignore b/.gitignore index 999669b..166ba42 100644 --- a/.gitignore +++ b/.gitignore @@ -214,4 +214,16 @@ marking_app/lib/pages/homework_correction/job_personal_detail.g.dart marking_app/lib/common/model/event_bus/jobs/job_do_papers_submit_check_switch_bus.g.dart marking_app/lib/common/model/event_bus/jobs/job_do_synchro_tab.g.dart marking_app/lib/pages/homework_correction/widget/top_count.g.dart +marking_app/lib/common/model/event_bus/job_home_refresh_bus.g.dart +marking_app/lib/common/model/job/job_knowledge_points_detail.g.dart +marking_app/lib/common/model/job/job_knowledge_points.g.dart marking_app/lib/pages/homework_correction/job_knowledge_points.g.dart +marking_app/lib/pages/homework_correction/job_knowledge_points_detail.g.dart +marking_app/lib/common/model/job/job_knowledge_detail_student.g.dart +marking_app/lib/common/model/job/job_knowledge_detail_student.g.dart +marking_app/lib/pages/homework_correction/job_home.g.dart +marking_app/lib/common/model/marking/keyboard_assist_event.g.dart +marking_app/lib/common/model/marking/marking_history_zoom_info.g.dart +marking_app/lib/common/model/job/job_handwriting.g.dart +marking_app/lib/utils/my_time_util.g.dart +marking_app/lib/pages/homework_correction/widget/answer_handwriting.g.dart diff --git a/marking_app/assets/images/2.0x/job_home_answer_record.png b/marking_app/assets/images/2.0x/job_home_answer_record.png new file mode 100644 index 0000000..06b07c3 Binary files /dev/null and b/marking_app/assets/images/2.0x/job_home_answer_record.png differ diff --git a/marking_app/assets/images/2.0x/job_home_history.png b/marking_app/assets/images/2.0x/job_home_history.png new file mode 100644 index 0000000..63f66d3 Binary files /dev/null and b/marking_app/assets/images/2.0x/job_home_history.png differ diff --git a/marking_app/assets/images/2.0x/job_home_knowledge.png b/marking_app/assets/images/2.0x/job_home_knowledge.png new file mode 100644 index 0000000..8b6fda0 Binary files /dev/null and b/marking_app/assets/images/2.0x/job_home_knowledge.png differ diff --git a/marking_app/assets/images/2.0x/job_home_marking.png b/marking_app/assets/images/2.0x/job_home_marking.png new file mode 100644 index 0000000..4c51ed0 Binary files /dev/null and b/marking_app/assets/images/2.0x/job_home_marking.png differ diff --git a/marking_app/assets/images/2.0x/job_home_marking_set.png b/marking_app/assets/images/2.0x/job_home_marking_set.png new file mode 100644 index 0000000..bcdc6d3 Binary files /dev/null and b/marking_app/assets/images/2.0x/job_home_marking_set.png differ diff --git a/marking_app/assets/images/2.0x/job_home_top_bgm.png b/marking_app/assets/images/2.0x/job_home_top_bgm.png new file mode 100644 index 0000000..6983041 Binary files /dev/null and b/marking_app/assets/images/2.0x/job_home_top_bgm.png differ diff --git a/marking_app/assets/images/2.0x/job_home_youxian.png b/marking_app/assets/images/2.0x/job_home_youxian.png new file mode 100644 index 0000000..69ffdeb Binary files /dev/null and b/marking_app/assets/images/2.0x/job_home_youxian.png differ diff --git a/marking_app/assets/images/3.0x/job_home_answer_record.png b/marking_app/assets/images/3.0x/job_home_answer_record.png new file mode 100644 index 0000000..b6c192a Binary files /dev/null and b/marking_app/assets/images/3.0x/job_home_answer_record.png differ diff --git a/marking_app/assets/images/3.0x/job_home_history.png b/marking_app/assets/images/3.0x/job_home_history.png new file mode 100644 index 0000000..4df1334 Binary files /dev/null and b/marking_app/assets/images/3.0x/job_home_history.png differ diff --git a/marking_app/assets/images/3.0x/job_home_knowledge.png b/marking_app/assets/images/3.0x/job_home_knowledge.png new file mode 100644 index 0000000..b6aebd4 Binary files /dev/null and b/marking_app/assets/images/3.0x/job_home_knowledge.png differ diff --git a/marking_app/assets/images/3.0x/job_home_marking.png b/marking_app/assets/images/3.0x/job_home_marking.png new file mode 100644 index 0000000..15baf5f Binary files /dev/null and b/marking_app/assets/images/3.0x/job_home_marking.png differ diff --git a/marking_app/assets/images/3.0x/job_home_marking_set.png b/marking_app/assets/images/3.0x/job_home_marking_set.png new file mode 100644 index 0000000..8eea382 Binary files /dev/null and b/marking_app/assets/images/3.0x/job_home_marking_set.png differ diff --git a/marking_app/assets/images/3.0x/job_home_top_bgm.png b/marking_app/assets/images/3.0x/job_home_top_bgm.png new file mode 100644 index 0000000..7a62344 Binary files /dev/null and b/marking_app/assets/images/3.0x/job_home_top_bgm.png differ diff --git a/marking_app/assets/images/3.0x/job_home_youxian.png b/marking_app/assets/images/3.0x/job_home_youxian.png new file mode 100644 index 0000000..956d7ef Binary files /dev/null and b/marking_app/assets/images/3.0x/job_home_youxian.png differ diff --git a/marking_app/assets/images/4.0x/job_home_answer_record.png b/marking_app/assets/images/4.0x/job_home_answer_record.png new file mode 100644 index 0000000..25d26b9 Binary files /dev/null and b/marking_app/assets/images/4.0x/job_home_answer_record.png differ diff --git a/marking_app/assets/images/4.0x/job_home_history.png b/marking_app/assets/images/4.0x/job_home_history.png new file mode 100644 index 0000000..d03f369 Binary files /dev/null and b/marking_app/assets/images/4.0x/job_home_history.png differ diff --git a/marking_app/assets/images/4.0x/job_home_knowledge.png b/marking_app/assets/images/4.0x/job_home_knowledge.png new file mode 100644 index 0000000..28ead35 Binary files /dev/null and b/marking_app/assets/images/4.0x/job_home_knowledge.png differ diff --git a/marking_app/assets/images/4.0x/job_home_marking.png b/marking_app/assets/images/4.0x/job_home_marking.png new file mode 100644 index 0000000..1fa6449 Binary files /dev/null and b/marking_app/assets/images/4.0x/job_home_marking.png differ diff --git a/marking_app/assets/images/4.0x/job_home_marking_set.png b/marking_app/assets/images/4.0x/job_home_marking_set.png new file mode 100644 index 0000000..2b509cd Binary files /dev/null and b/marking_app/assets/images/4.0x/job_home_marking_set.png differ diff --git a/marking_app/assets/images/4.0x/job_home_top_bgm.png b/marking_app/assets/images/4.0x/job_home_top_bgm.png new file mode 100644 index 0000000..11513dc Binary files /dev/null and b/marking_app/assets/images/4.0x/job_home_top_bgm.png differ diff --git a/marking_app/assets/images/4.0x/job_home_youxian.png b/marking_app/assets/images/4.0x/job_home_youxian.png new file mode 100644 index 0000000..945a8cc Binary files /dev/null and b/marking_app/assets/images/4.0x/job_home_youxian.png differ diff --git a/marking_app/assets/images/icon_back_green.png b/marking_app/assets/images/icon_back_green.png new file mode 100644 index 0000000..3df3757 Binary files /dev/null and b/marking_app/assets/images/icon_back_green.png differ diff --git a/marking_app/assets/images/icon_back_orange.png b/marking_app/assets/images/icon_back_orange.png new file mode 100644 index 0000000..ea7f871 Binary files /dev/null and b/marking_app/assets/images/icon_back_orange.png differ diff --git a/marking_app/assets/images/job_home_answer_record.png b/marking_app/assets/images/job_home_answer_record.png new file mode 100644 index 0000000..21fd223 Binary files /dev/null and b/marking_app/assets/images/job_home_answer_record.png differ diff --git a/marking_app/assets/images/job_home_history.png b/marking_app/assets/images/job_home_history.png new file mode 100644 index 0000000..3e00774 Binary files /dev/null and b/marking_app/assets/images/job_home_history.png differ diff --git a/marking_app/assets/images/job_home_knowledge.png b/marking_app/assets/images/job_home_knowledge.png new file mode 100644 index 0000000..866ad4c Binary files /dev/null and b/marking_app/assets/images/job_home_knowledge.png differ diff --git a/marking_app/assets/images/job_home_marking.png b/marking_app/assets/images/job_home_marking.png new file mode 100644 index 0000000..81e0e6e Binary files /dev/null and b/marking_app/assets/images/job_home_marking.png differ diff --git a/marking_app/assets/images/job_home_marking_set.png b/marking_app/assets/images/job_home_marking_set.png new file mode 100644 index 0000000..3fd1ca0 Binary files /dev/null and b/marking_app/assets/images/job_home_marking_set.png differ diff --git a/marking_app/assets/images/job_home_top_bgm.png b/marking_app/assets/images/job_home_top_bgm.png new file mode 100644 index 0000000..2515c1e Binary files /dev/null and b/marking_app/assets/images/job_home_top_bgm.png differ diff --git a/marking_app/assets/images/job_home_youxian.png b/marking_app/assets/images/job_home_youxian.png new file mode 100644 index 0000000..4e37a46 Binary files /dev/null and b/marking_app/assets/images/job_home_youxian.png differ diff --git a/marking_app/assets/images/right_icon_blue.png b/marking_app/assets/images/right_icon_blue.png new file mode 100644 index 0000000..87bb071 Binary files /dev/null and b/marking_app/assets/images/right_icon_blue.png differ diff --git a/marking_app/lib/common/model/event_bus/job_home_refresh_bus.dart b/marking_app/lib/common/model/event_bus/job_home_refresh_bus.dart new file mode 100644 index 0000000..32f2485 --- /dev/null +++ b/marking_app/lib/common/model/event_bus/job_home_refresh_bus.dart @@ -0,0 +1,12 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'job_home_refresh_bus.g.dart'; + +@JsonSerializable() +class JobHomeRefreshBus extends Object { + JobHomeRefreshBus(); + + factory JobHomeRefreshBus.fromJson(Map srcJson) => _$JobHomeRefreshBusFromJson(srcJson); + + Map toJson() => _$JobHomeRefreshBusToJson(this); +} diff --git a/marking_app/lib/common/model/job/gesture_recording.dart b/marking_app/lib/common/model/job/gesture_recording.dart index a2ea382..5e36be8 100644 --- a/marking_app/lib/common/model/job/gesture_recording.dart +++ b/marking_app/lib/common/model/job/gesture_recording.dart @@ -14,11 +14,30 @@ class GestureRecording { bool scopeBox; + int intervalTime; // 间隔时间 + GestureRecording({ required this.eraser, required this.annotationSwitch, this.data, this.usageTime, this.scopeBox = false, + this.intervalTime = 0, + }); +} + +/** + * 手势记录(原稿笔记还原) + */ +class GestureHandwritingRecording { + int stroke; + int usageTime; // 用时 + Offset data; + int intervalTime; // 间隔时间 + GestureHandwritingRecording({ + required this.stroke, + required this.data, + required this.usageTime, + required this.intervalTime, }); } diff --git a/marking_app/lib/common/model/job/job_handwriting.dart b/marking_app/lib/common/model/job/job_handwriting.dart new file mode 100644 index 0000000..72931c9 --- /dev/null +++ b/marking_app/lib/common/model/job/job_handwriting.dart @@ -0,0 +1,48 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'job_handwriting.g.dart'; + +@JsonSerializable() +class JobHandwriting extends Object { + @JsonKey(name: 'lattices') + List lattices; + + @JsonKey(name: 'paperPicture') + String paperPicture; + + @JsonKey(name: 'pageNum') + int pageNum; + + @JsonKey(name: 'pageCount') + int pageCount; + + JobHandwriting(this.lattices, this.paperPicture, this.pageNum, this.pageCount); + + factory JobHandwriting.fromJson(Map srcJson) => _$JobHandwritingFromJson(srcJson); + + Map toJson() => _$JobHandwritingToJson(this); +} + +@JsonSerializable() +class Lattices extends Object { + @JsonKey(name: 'stroke') + int stroke; + + @JsonKey(name: 'x') + double x; + + @JsonKey(name: 'y') + double y; + + @JsonKey(name: 'time') + int time; + + @JsonKey(name: 'intervalTime') + int intervalTime; + + Lattices(this.stroke, this.x, this.y, this.time, [this.intervalTime = 0]); + + factory Lattices.fromJson(Map srcJson) => _$LatticesFromJson(srcJson); + + Map toJson() => _$LatticesToJson(this); +} diff --git a/marking_app/lib/common/model/job/job_knowledge_detail_student.dart b/marking_app/lib/common/model/job/job_knowledge_detail_student.dart new file mode 100644 index 0000000..43f34b2 --- /dev/null +++ b/marking_app/lib/common/model/job/job_knowledge_detail_student.dart @@ -0,0 +1,29 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'job_knowledge_detail_student.g.dart'; + + +@JsonSerializable() +class JobKnowledgeDetailStudent extends Object { + + @JsonKey(name: 'studentId') + int studentId; + + @JsonKey(name: 'studentName') + String studentName; + + @JsonKey(name: 'isAnswer') + bool isAnswer; + + @JsonKey(name: 'isCorrect') + bool isCorrect; + + JobKnowledgeDetailStudent(this.studentId,this.studentName,this.isAnswer,this.isCorrect,); + + factory JobKnowledgeDetailStudent.fromJson(Map srcJson) => _$JobKnowledgeDetailStudentFromJson(srcJson); + + Map toJson() => _$JobKnowledgeDetailStudentToJson(this); + +} + + diff --git a/marking_app/lib/common/model/job/job_knowledge_points.dart b/marking_app/lib/common/model/job/job_knowledge_points.dart new file mode 100644 index 0000000..8e23efb --- /dev/null +++ b/marking_app/lib/common/model/job/job_knowledge_points.dart @@ -0,0 +1,29 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'job_knowledge_points.g.dart'; + + +@JsonSerializable() +class KnowledgePoints extends Object { + + @JsonKey(name: 'knowledgeId') + int knowledgeId; + + @JsonKey(name: 'knowledgeName') + String knowledgeName; + + @JsonKey(name: 'correctRate') + int correctRate; + + @JsonKey(name: 'count') + int count; + + KnowledgePoints(this.knowledgeId,this.knowledgeName,this.correctRate,this.count,); + + factory KnowledgePoints.fromJson(Map srcJson) => _$KnowledgePointsFromJson(srcJson); + + Map toJson() => _$KnowledgePointsToJson(this); + +} + + diff --git a/marking_app/lib/common/model/job/job_knowledge_points_detail.dart b/marking_app/lib/common/model/job/job_knowledge_points_detail.dart new file mode 100644 index 0000000..4009d97 --- /dev/null +++ b/marking_app/lib/common/model/job/job_knowledge_points_detail.dart @@ -0,0 +1,38 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'job_knowledge_points_detail.g.dart'; + + +@JsonSerializable() +class KnowledgePointsDetail extends Object { + + @JsonKey(name: 'jobId') + int jobId; + + @JsonKey(name: 'jobName') + String jobName; + + @JsonKey(name: 'publishTime') + String publishTime; + + @JsonKey(name: 'questionId') + int questionId; + + @JsonKey(name: 'questionNo') + String questionNo; + + @JsonKey(name: 'sectionId') + int sectionId; + + @JsonKey(name: 'correctRate') + int correctRate; + + KnowledgePointsDetail(this.jobId,this.jobName,this.publishTime,this.questionId,this.questionNo,this.sectionId,this.correctRate,); + + factory KnowledgePointsDetail.fromJson(Map srcJson) => _$KnowledgePointsDetailFromJson(srcJson); + + Map toJson() => _$KnowledgePointsDetailToJson(this); + +} + + diff --git a/marking_app/lib/common/model/job/test_questions_image_info.dart b/marking_app/lib/common/model/job/test_questions_image_info.dart index fed08ac..e9a1e7c 100644 --- a/marking_app/lib/common/model/job/test_questions_image_info.dart +++ b/marking_app/lib/common/model/job/test_questions_image_info.dart @@ -36,12 +36,7 @@ class TestQuestionsImageInfo extends Object { double? imageHeightOffsetend; TestQuestionsImageInfo( - {required this.width, - required this.height, - required this.url, - required this.boxWidth, - required this.boxHeight, - this.pixelRatio = 1}) { + {required this.width, required this.height, required this.url, required this.boxWidth, required this.boxHeight, this.pixelRatio = 1}) { // print('图片宽度:$width'); // print('图片高度:$height'); @@ -60,6 +55,14 @@ class TestQuestionsImageInfo extends Object { } } + // 重新计算 + void calculateStartAndEndHeight([double otherHeight = 0]) { + if (scaleHeight != null) { + imageHeightOffsetStart = (boxHeight - (scaleHeight! + otherHeight)) / 2; + imageHeightOffsetend = imageHeightOffsetStart! + scaleHeight!; + } + } + factory TestQuestionsImageInfo.fromJson(Map srcJson) => _$TestQuestionsImageInfoFromJson(srcJson); Map toJson() => _$TestQuestionsImageInfoToJson(this); diff --git a/marking_app/lib/common/model/marking/keyboard_assist_event.g.dart b/marking_app/lib/common/model/marking/keyboard_assist_event.g.dart deleted file mode 100644 index 13cca58..0000000 --- a/marking_app/lib/common/model/marking/keyboard_assist_event.g.dart +++ /dev/null @@ -1,18 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'keyboard_assist_event.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -KeyboardAssistEvent _$KeyboardAssistEventFromJson(Map json) => - KeyboardAssistEvent( - openAuxiliary: json['openAuxiliary'] as bool? ?? false, - ); - -Map _$KeyboardAssistEventToJson( - KeyboardAssistEvent instance) => - { - 'openAuxiliary': instance.openAuxiliary, - }; diff --git a/marking_app/lib/common/model/marking/marking_history_zoom_info.g.dart b/marking_app/lib/common/model/marking/marking_history_zoom_info.g.dart deleted file mode 100644 index 07a8b6a..0000000 --- a/marking_app/lib/common/model/marking/marking_history_zoom_info.g.dart +++ /dev/null @@ -1,27 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'marking_history_zoom_info.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -MarkingHistoryZoomInfo _$MarkingHistoryZoomInfoFromJson( - Map json) => - MarkingHistoryZoomInfo( - markingUserId: json['markingUserId'] as int, - questionNum: json['questionNum'] as String, - scale: (json['scale'] as num).toDouble(), - positionY: (json['positionY'] as num).toDouble(), - positionX: (json['positionX'] as num).toDouble(), - ); - -Map _$MarkingHistoryZoomInfoToJson( - MarkingHistoryZoomInfo instance) => - { - 'markingUserId': instance.markingUserId, - 'questionNum': instance.questionNum, - 'scale': instance.scale, - 'positionY': instance.positionY, - 'positionX': instance.positionX, - }; diff --git a/marking_app/lib/common/model/marking/marking_list_params.dart b/marking_app/lib/common/model/marking/marking_list_params.dart index 2906d6e..bfb8e0d 100644 --- a/marking_app/lib/common/model/marking/marking_list_params.dart +++ b/marking_app/lib/common/model/marking/marking_list_params.dart @@ -15,7 +15,7 @@ part 'marking_list_params.g.dart'; @JsonSerializable() class MarkingListParams extends BasePage { @JsonKey(name: 'isFinish') - bool isFinish; + bool? isFinish; // 阅卷类型 @JsonKey(name: 'PageType') @@ -27,7 +27,7 @@ class MarkingListParams extends BasePage { int? markingType; // 1 作业 2考试 MarkingListParams({ - required this.isFinish, + this.isFinish, required this.pageType, required page, required limit, diff --git a/marking_app/lib/main.dart b/marking_app/lib/main.dart index 96d52e9..4ac72f8 100644 --- a/marking_app/lib/main.dart +++ b/marking_app/lib/main.dart @@ -24,8 +24,7 @@ void main() { WidgetsFlutterBinding.ensureInitialized(); RouterManager.initRouter(); - SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, - overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]); // 屏幕刘海 + SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]); // 屏幕刘海 SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); // 屏幕强制竖屏 // OrientationHelper.setEnabledSystemUIOverlays(SystemUiOverlay.values); @@ -81,7 +80,7 @@ class _MyAppState extends State { splitScreenMode: true, builder: (context1, child) { return MaterialApp( - title: '远轩阅卷系统', + title: '学而有道阅卷', navigatorKey: TheGlobal.navigatorKey, debugShowCheckedModeBanner: false, // locale: const Locale('zh', 'CN'), // 中文简体 , diff --git a/marking_app/lib/pages/homework_correction/answer_trajectory.dart b/marking_app/lib/pages/homework_correction/answer_trajectory.dart new file mode 100644 index 0000000..492dc3d --- /dev/null +++ b/marking_app/lib/pages/homework_correction/answer_trajectory.dart @@ -0,0 +1,249 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_easyrefresh/easy_refresh.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:marking_app/common/config/request_config.dart'; +import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/common/base_page_data.dart'; +import 'package:marking_app/common/model/common/base_structure_result.dart'; +import 'package:marking_app/common/model/job/job_student_goups.dart'; +import 'package:marking_app/common/model/job/job_task_item.dart'; +import 'package:marking_app/common/model/marking/marking_list_params.dart'; +import 'package:marking_app/components/ReturnToHomepage.dart'; +import 'package:marking_app/pages/homework_correction/widget/answer_trajectory_job.dart'; +import 'package:marking_app/pages/homework_correction/widget/student_group_list.dart'; +import 'package:marking_app/routes/RouterManager.dart'; +import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; +import 'package:marking_app/utils/fast_data.dart'; +import 'package:marking_app/utils/index.dart'; +import 'package:marking_app/utils/my_text.dart'; +import 'package:marking_app/utils/request/rest_client.dart'; + +class AnswerTrajectory extends StatefulWidget { + const AnswerTrajectory({Key? key}) : super(key: key); + + @override + State createState() => _AnswerTrajectoryState(); +} + +class _AnswerTrajectoryState extends State + with CommonMixin, SingleTickerProviderStateMixin { + late final EasyRefreshController refreshController; + late final EasyRefreshController refreshController2; + late String loginName; + List studentGroups = []; + List jobList = []; + late TabController tabController; + int tabIndex = 0; + int page = 1; + int pageSize = 10; + int total = 0; + + @override + void initState() { + super.initState(); + refreshController = EasyRefreshController(); + refreshController2 = EasyRefreshController(); + tabController = + TabController(initialIndex: tabIndex, length: 2, vsync: this); + FastData fastData = FastData.getInstance(); + fastData.getUser().then((value) { + if (value == null || value == '') return; + Map userInfo = json.decode(value); + setState(() { + loginName = userInfo['loginName']; + }); + getStudentGroups(); + getWorkList(); + }); + } + + void getStudentGroups() async { + RestClient _client = await getClient(); + BaseStructureResult> res = + await _client.getJobLevelStudentGroups(loginName); + setState(() { + if (res.code == 200) { + studentGroups = res.data!; + } else { + studentGroups = []; + } + }); + refreshController.finishRefresh(); + EasyLoading.dismiss(); + } + + void goNextPage(id, title) { + RouterManager.router.navigateTo(context, + '${RouterManager.jobPriorityReviewSetPath}?&groupId=$id&title=${Uri.encodeComponent(title)}&page=answerTrajectory', + transition: getTransition()); + } + + void getWorkList() async { + final MarkingListParams params = MarkingListParams( + page: page, + limit: pageSize, + pageType: 0, + markingType: 1, + ); + print('params=${params.limit}&page=${params.page}'); + RestClient client = await getClient(); + BaseStructureResult> res = + await client.getJobsByPage(params); + List arr = []; + if (res.success) { + if (page == 1) { + arr = res.data!.items; + } else { + arr = [...jobList, ...res.data!.items]; + } + total = res.data!.total; + } else { + jobList = []; + } + jobList = arr; + setState(() {}); + refreshController2.finishRefresh(); + EasyLoading.dismiss(); + } + + @override + void dispose() { + super.dispose(); + refreshController.dispose(); + refreshController2.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color.fromRGBO(245, 245, 245, 1), + appBar: AppBar( + backgroundColor: Colors.white, + title: Text( + '答题轨迹', + style: TextStyle(fontSize: 14.sp, color: Color(0xFF333333)), + ), + centerTitle: true, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.black), + onPressed: () => Navigator.of(context).pop(), + ), + actions: [ + ReturnToHomepage(), + ], + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 10.r, + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 14.r), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(width: 1.r, color: Color(0xFFCCCCCC)))), + child: TabBar( + onTap: (int val) { + print(val); + setState(() { + tabIndex = val; + }); + /*EasyLoading.show(status: 'loading...'); + if(val == 0){ + getStudentGroups(); + }*/ + }, + tabs: [ + SizedBox( + width: (MediaQuery.of(context).size.width - 28.r) / 2, + child: Tab( + text: '按学生', + ), + ), + SizedBox( + width: (MediaQuery.of(context).size.width - 28.r) / 2, + child: Tab( + text: '按作业', + ), + ) + ], + controller: tabController, + unselectedLabelStyle: + TextStyle(fontSize: 14.sp, color: Color(0xFF666666)), + labelStyle: TextStyle( + fontSize: 14.sp, + color: Color(0xFF6888FD), + ), + isScrollable: true, + labelColor: Color(0xFF6888FD), + unselectedLabelColor: Color(0xFF666666), + indicatorSize: TabBarIndicatorSize.label, + labelPadding: const EdgeInsets.all(0), + ), + ), + Expanded( + child: Padding( + padding: + EdgeInsets.only(top: 15.r, left: 14.r, right: 14.r), + child: + tabIndex == 0 + ? + EasyRefresh( + firstRefresh: false, + taskIndependence: true, + controller: refreshController, + header: MaterialHeader(), + footer: TaurusFooter(), + onRefresh: () async { + getStudentGroups(); + }, + child: StudentGroupList(studentGroups, goNextPage, + rightBtn: Container( + margin: EdgeInsets.only(left: 5.r), + height: 20.r, + width: 55.r, + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(20.r)), + border: Border.all( + width: 1.r, color: Color(0xFFFF9800)), + ), + child: Center( + child: Text( + '详情', + style: TextStyle( + fontSize: 10.sp, color: Color(0xFFFF9800)), + ), + ), + )), + ):EasyRefresh( + firstRefresh: false, + taskIndependence: true, + controller: refreshController2, + header: MaterialHeader(), + footer: TaurusFooter(), + onRefresh: () async { + page = 1; + setState(() {}); + getWorkList(); + }, + onLoad: () async { + if (jobList.length < total) { + EasyLoading.show(status: 'loading...'); + page = page + 1; + getWorkList(); + } + }, + child:AnswerTrajectoryJob(jobList)), + ), + ) + ], + ), + ); + } +} diff --git a/marking_app/lib/pages/homework_correction/answer_trajectory_job_detail.dart b/marking_app/lib/pages/homework_correction/answer_trajectory_job_detail.dart new file mode 100644 index 0000000..9d47897 --- /dev/null +++ b/marking_app/lib/pages/homework_correction/answer_trajectory_job_detail.dart @@ -0,0 +1,291 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_easyrefresh/easy_refresh.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/common/base_structure_result.dart'; +import 'package:marking_app/common/model/job/job_concerned_with_student.dart'; +import 'package:marking_app/common/model/job/job_concerned_with_student_params.dart'; +import 'package:marking_app/common/model/job/job_task_item.dart'; +import 'package:marking_app/components/ReturnToHomepage.dart'; +import 'package:marking_app/routes/RouterManager.dart'; +import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; +import 'package:marking_app/utils/index.dart'; +import 'package:marking_app/utils/request/rest_client.dart'; + +class AnswerTrajectoryJobDetail extends StatefulWidget { + final int jobId; + final String jobName; + final String genderName; + + const AnswerTrajectoryJobDetail( + {Key? key, + required this.jobId, + required this.jobName, + required this.genderName}) + : super(key: key); + + @override + State createState() => + _AnswerTrajectoryJobDetailState(); +} + +class _AnswerTrajectoryJobDetailState extends State + with CommonMixin { + List markList = []; + late MarkingTasks currentClass; + List students = []; + late final EasyRefreshController refreshController; + + @override + void initState() { + super.initState(); + refreshController = EasyRefreshController(); + EasyLoading.show(status: 'loading...'); + getData(); + } + + void getData() async { + RestClient _client = await getClient(); + BaseStructureResult> res = + await _client.getJobListParticipateInClass(widget.jobId); + if (res.success) { + setState(() { + markList = res.data!; + if (markList.length > 0) { + currentClass = markList[0]; + getStudentList(); + } + }); + } + } + + void getStudentList() async { + RestClient _client = await getClient(); + BaseStructureResult> res = + await _client.getJobWithStudents(JobConcernedWithStudentParams([currentClass.id])); + if (res.success) { + setState(() { + students = res.data!; + }); + } + refreshController.finishRefresh(); + EasyLoading.dismiss(); + } + + @override + void dispose() { + super.dispose(); + refreshController.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFFF5F5F5), + appBar: AppBar( + centerTitle: true, + backgroundColor: Colors.white, + title: Text( + widget.jobName, + style: TextStyle(fontSize: 14.sp, color: Color(0xFF333333)), + overflow: TextOverflow.ellipsis, + ), + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.black), + onPressed: () => Navigator.of(context).pop(), + ), + actions: [ + ReturnToHomepage(), + ], + ), + body: Column( + children: [ + SizedBox( + height: 10.r, + ), + if (markList.length > 0) + Padding( + padding: EdgeInsets.symmetric(horizontal: 14.r), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: List.generate(markList.length, (index) { + MarkingTasks item = markList[index]; + return InkWell( + onTap: (){ + if(currentClass.id != item.id){ + EasyLoading.show(status: 'loading...'); + setState(() { + currentClass = item; + }); + getStudentList(); + } + + }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: 5.r, horizontal: 10.r), + margin: EdgeInsets.only( + right: index < markList.length ? 8.r : 0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4.r), + ), + child: Center( + child: Text( + '${widget.genderName}${item.className}', + style: TextStyle( + fontSize: 10.sp, + color: currentClass.id == item.id + ? Color(0xFF6888FD) + : Color(0xFF686868)), + ), + ), + ), + ); + })), + ), + ], + ), + ), + SizedBox( + height: 10.r, + ), + Container( + height: 1.r, + color: Color(0xFFCCCCCC), + ), + students.length>0? Expanded( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 14.r, horizontal: 14.r), + child: EasyRefresh( + firstRefresh: true, + taskIndependence: true, + controller: refreshController, + header: MaterialHeader(), + footer: TaurusFooter(), + onRefresh: () async { + getStudentList(); + }, + child: students.length > 0 + ? isPad() + ? GridView( + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 10.r, + crossAxisSpacing: 10.r, + childAspectRatio: 556 / 112, + ), + children: List.generate(students.length, (index) { + var item = students[index]; + return InkWell( + onTap: (){ + RouterManager.router.navigateTo( + context, + RouterManager.quickCheckPersonalPath + + '?jobId=${widget.jobId}&studentId=${item.studentId}', + transition: getTransition(), + ); + // RouterManager.router.navigateTo(context, + // '${RouterManager.jobPersonalDetailPath}?studentId=${item.studentId}&studentName=${Uri.encodeComponent(item.studentName)}'); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10.r), + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(10.r)), + color: Colors.white, + ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + item.studentName, + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF6888FD)), + )), + + Container( + height: 20.r, + width: 70.r, + decoration: BoxDecoration( + border: Border.all(width: 1.r,color: Color(0xFFFFA41E)), + borderRadius: BorderRadius.all(Radius.circular(20.r)), + + ), + child: Center(child: Text('详情',style: TextStyle(fontSize: 10.r,color: Color(0xFFFFA41E))), + )), + ], + ), + ), + ); + }), + ) + : ListView.builder( + itemBuilder: (context, index) { + var item = students[index]; + return InkWell( + onTap: (){ + RouterManager.router.navigateTo( + context, + RouterManager.quickCheckPersonalPath + + '?jobId=${widget.jobId}&studentId=${item.studentId}', + transition: getTransition(), + ); + // RouterManager.router.navigateTo(context, + // '${RouterManager.jobPersonalDetailPath}?studentId=${item.studentId}&studentName=${Uri.encodeComponent(item.studentName)}'); + }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: 20.r, horizontal: 15.r), + margin: EdgeInsets.only(bottom: 15.r), + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(10.r)), + color: Colors.white, + ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + item.studentName, + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF6888FD)), + )), + Container( + height: 24.r, + width: 72.r, + decoration: BoxDecoration( + border: Border.all(width: 1.r,color: Color(0xFFFFA41E)), + borderRadius: BorderRadius.all(Radius.circular(20.r)), + + ), + child: Center(child: Text('详情',style: TextStyle(fontSize: 10.r,color: Color(0xFFFFA41E))), + )), + ], + ), + ), + ); + }, + itemCount: students.length, + ) + : MyEmptyWidget(), + ), + ), + ):MyEmptyWidget(), + ], + ), + ); + } +} diff --git a/marking_app/lib/pages/homework_correction/components/imgDialog.dart b/marking_app/lib/pages/homework_correction/components/imgDialog.dart new file mode 100644 index 0000000..50d9c09 --- /dev/null +++ b/marking_app/lib/pages/homework_correction/components/imgDialog.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class ImageDialog{ + static void showImgDialog(BuildContext context,String imgUrl) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + // insetPadding: EdgeInsets.symmetric(vertical: 10.r,horizontal: 45.r), + backgroundColor: Colors.transparent, + contentPadding: EdgeInsets.all(0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(15.r))), + content: Container( + width: MediaQuery.of(context).size.width - 48.r, + // height: MediaQuery.of(context).size.height * 0.4, + color: Colors.white, + // child: PhotoView(imageProvider: NetworkImage(imgUrl),backgroundDecoration: BoxDecoration(color: Colors.transparent),)), + child: Image.network(imgUrl)), + ); + }, + ); + } +} \ No newline at end of file diff --git a/marking_app/lib/pages/homework_correction/components/trajectoryView.dart b/marking_app/lib/pages/homework_correction/components/trajectoryView.dart index 534d798..c940af3 100644 --- a/marking_app/lib/pages/homework_correction/components/trajectoryView.dart +++ b/marking_app/lib/pages/homework_correction/components/trajectoryView.dart @@ -21,12 +21,7 @@ class TrajectoryView extends StatefulHookConsumerWidget { final double boxHeight; final String questionNumber; final JobNoteTakingTrajectory trajectory; - const TrajectoryView( - {required this.trajectory, - required this.questionNumber, - required this.boxHeight, - required this.boxWidth, - super.key}); + const TrajectoryView({required this.trajectory, required this.questionNumber, required this.boxHeight, required this.boxWidth, super.key}); @override TrajectoryViewState createState() => TrajectoryViewState(); @@ -152,15 +147,11 @@ class TrajectoryViewState extends ConsumerState { /// 试题范围框左上角 trajectorys - ..add(GestureRecording( - data: Offset(2.w, trajectory.pictureQuestionTop), scopeBox: true, annotationSwitch: false, eraser: false)) + ..add(GestureRecording(data: Offset(2.w, trajectory.pictureQuestionTop), scopeBox: true, annotationSwitch: false, eraser: false)) /// 右上角 ..add(GestureRecording( - data: Offset(imagInfoModel!.scaleWidth! - 4.w, trajectory.pictureQuestionTop), - scopeBox: true, - annotationSwitch: false, - eraser: false)) + data: Offset(imagInfoModel!.scaleWidth! - 4.w, trajectory.pictureQuestionTop), scopeBox: true, annotationSwitch: false, eraser: false)) /// 左下角 ..add(GestureRecording( @@ -171,8 +162,7 @@ class TrajectoryViewState extends ConsumerState { /// 右下角 ..add(GestureRecording( - data: Offset( - imagInfoModel!.scaleWidth! - 4.w, trajectory.pictureQuestionTop + trajectory.pictureQuestionHeight), + data: Offset(imagInfoModel!.scaleWidth! - 4.w, trajectory.pictureQuestionTop + trajectory.pictureQuestionHeight), scopeBox: true, annotationSwitch: false, eraser: false)); @@ -181,14 +171,26 @@ class TrajectoryViewState extends ConsumerState { ref.read(jobDrawingTrajectoryProvider.notifier).setVal(trajectorys); }); if (lattices.isNotEmpty) { - Map> map = new Map.fromIterable(lattices, - key: (key) => key.stroke, - value: (value) { - return lattices.where((item) => item.stroke == value.stroke).toList() - ..sort((a, b) => a.time.compareTo(b.time)); - }); + // Map> map = new Map.fromIterable(lattices, + // key: (key) => key.stroke, + // value: (value) { + // return lattices.where((item) => item.stroke == value.stroke).toList() + // ..sort((a, b) => a.time.compareTo(b.time)); + // }); + + var map = Map>(); + for (var i = 0; i < lattices.length; i++) { + Lattices item = lattices[i]; + if (!map.containsKey(item.stroke)) map[item.stroke] = []; + map[item.stroke]!.add(item); // 添加笔画数据 + } List keys = map.keys.toList(); + for (var i = 0; i < keys.length; i++) { + int theKey = keys[i]; + map[theKey]!.sort((a, b) => a.time.compareTo(b.time)); + } + for (var i = 0; i < keys.length; i++) { int theKey = keys[i]; int? nextKey = i + 1 < keys.length ? keys[i + 1] : null; @@ -199,8 +201,7 @@ class TrajectoryViewState extends ConsumerState { double theX = e.x * imagInfoModel!.scale!; double theY = e.y * imagInfoModel!.scale! + spacingHeight; - return GestureRecording( - eraser: false, data: Offset(theX, theY), usageTime: e.time.toInt(), annotationSwitch: false); + return GestureRecording(eraser: false, data: Offset(theX, theY), usageTime: e.time.toInt(), annotationSwitch: false); }).toList() ..add(GestureRecording(eraser: false, annotationSwitch: false)); // 原始轨迹展示 @@ -230,8 +231,7 @@ class TrajectoryViewState extends ConsumerState { if (duration2 == null && nextStrokesData != null) { // 此时最后一个笔画完成了 停止时间是在下一个笔画开始时间之差 Lattices minLattices = nextStrokesData.reduce((e1, e2) => e1.time < e2.time ? e1 : e2); - differenceInMilliseconds = - (Duration(milliseconds: minLattices.time.toInt()) - duration1).inMilliseconds; + differenceInMilliseconds = (Duration(milliseconds: minLattices.time.toInt()) - duration1).inMilliseconds; await Future.delayed(Duration(milliseconds: differenceInMilliseconds)); // 一个笔画完成进行下一个笔画的等待时间 } }; diff --git a/marking_app/lib/pages/homework_correction/do_papers_job.dart b/marking_app/lib/pages/homework_correction/do_papers_job.dart index 5710bcf..5291ed9 100644 --- a/marking_app/lib/pages/homework_correction/do_papers_job.dart +++ b/marking_app/lib/pages/homework_correction/do_papers_job.dart @@ -290,7 +290,11 @@ Widget $dropdownBoxSwitchStudentsOrTypeView(BuildContext context, {required Func var _currentTab = _useSwitchStudentAndType.currentTab.value; var _pageIndex = _currentTab?.pageIndex; if (_currentTab == null || _pageIndex == null) return; - _useSwitchStudentAndType.refreshQuestionTypeData(context, taskId: taskId, exitCallback: exitCallback, getNewData: false).then((value) { + + var theLastQuestion = _currentTab.finishCount + 1 == _currentTab.total; // 当前提交的题是最后一题 + _useSwitchStudentAndType + .refreshQuestionTypeData(context, taskId: taskId, exitCallback: exitCallback, getNewData: theLastQuestion) + .then((value) { var params = MarkingTextQuestionJobTabParamsBus(taskId, _pageIndex); if (_currentTab.finishCount < _currentTab.total) { if (_currentTab.finishCount + 1 == _currentTab.total) { @@ -366,37 +370,55 @@ Widget $dropdownBoxSwitchStudentsOrTypeView(BuildContext context, {required Func Expanded(flex: 1, child: SizedBox()), Expanded( flex: 4, - child: Container( - padding: EdgeInsets.only(left: 10.w), - decoration: BoxDecoration( - color: Color.fromRGBO(244, 244, 244, 1), - borderRadius: BorderRadius.circular(4.r), - ), - child: DropdownButton( - onTap: () { - // 打开的时候请求刷新学生 - _useSwitchStudentAndType.getDataForStudents(taskId: taskId); - }, - padding: EdgeInsets.only(right: 4.w), - icon: Icon(Icons.keyboard_arrow_down_rounded), - value: _useSwitchStudentAndType.currentStudent.value?.studentId, - underline: Container(), - isExpanded: true, - items: _useSwitchStudentAndType.studentData.value.map((e) { - return DropdownMenuItem( - child: quickText(e.studentName, color: Color.fromRGBO(79, 79, 79, 1), size: 14.sp), - value: e.studentId, - ); - }).toList(), - hint: Text('请选择学生'), // 锚点的显示文本 - onChanged: (value) { - JobConcernedWithStudent? currentStudent = _useSwitchStudentAndType.currentStudent.value; - if (currentStudent?.studentId == value) return; - _useSwitchStudentAndType.studentBusInfo.value = null; // 置空BUS通知记录 - _useSwitchStudentAndType.currentStudent.value = - _useSwitchStudentAndType.studentData.value.firstWhereOrNull((element) => element.studentId == value); - }, - ), + child: Stack( + children: [ + Container( + padding: EdgeInsets.only(left: 10.w), + decoration: BoxDecoration( + color: Color.fromRGBO(244, 244, 244, 1), + borderRadius: BorderRadius.circular(4.r), + ), + child: DropdownButton( + onTap: () { + // 打开的时候请求刷新学生 + _useSwitchStudentAndType.getDataForStudents(taskId: taskId); + }, + padding: EdgeInsets.only(right: 4.w), + icon: Icon(Icons.keyboard_arrow_down_rounded), + value: _useSwitchStudentAndType.currentStudent.value?.studentId, + underline: Container(), + isExpanded: true, + items: _useSwitchStudentAndType.studentData.value.map((e) { + return DropdownMenuItem( + child: quickText(e.studentName, color: Color.fromRGBO(79, 79, 79, 1), size: 14.sp), + value: e.studentId, + ); + }).toList(), + hint: Text('请选择学生'), // 锚点的显示文本 + onChanged: (value) { + JobConcernedWithStudent? currentStudent = _useSwitchStudentAndType.currentStudent.value; + if (currentStudent?.studentId == value) return; + _useSwitchStudentAndType.studentBusInfo.value = null; // 置空BUS通知记录 + _useSwitchStudentAndType.currentStudent.value = + _useSwitchStudentAndType.studentData.value.firstWhereOrNull((element) => element.studentId == value); + }, + ), + ), + Positioned( + left: 2.w, + child: Stack( + alignment: const FractionalOffset(0.52, 0.24), + children: [ + Icon( + const IconData(0xe63d, fontFamily: "AlibabaIcon"), + size: 12.sp, + color: _useSwitchStudentAndType.isFirst.value ? Color.fromRGBO(76, 199, 147, 1) : Color.fromRGBO(164, 164, 164, 1), + ), + quickText('优先', size: 4.sp, color: Colors.white), + ], + ), + ), + ], ), ), Expanded(flex: 1, child: SizedBox()), @@ -458,17 +480,6 @@ Widget $dropdownBoxSwitchStudentsOrTypeView(BuildContext context, {required Func ), ), ), - Stack( - alignment: const FractionalOffset(0.52, 0.24), - children: [ - Icon( - const IconData(0xe63d, fontFamily: "AlibabaIcon"), - size: 12.sp, - color: _useSwitchStudentAndType.isFirst.value ? Color.fromRGBO(76, 199, 147, 1) : Color.fromRGBO(164, 164, 164, 1), - ), - quickText('优先', size: 4.sp, color: Colors.white), - ], - ), ], ), ), @@ -852,16 +863,15 @@ Widget $examPaperAndScoringKeyboardView( ], ), ), - if (question.accuracy > 0) - Padding( - padding: EdgeInsets.only(bottom: 1.5.h), - child: quickText( - '正确率:${getDoubleRemoveZero(question.accuracy, question.accuracy.toString())}%', - size: 8.sp, - color: Colors.white, - align: TextAlign.end, - ), - ) + Padding( + padding: EdgeInsets.only(bottom: 1.5.h), + child: quickText( + '正确率:${getDoubleRemoveZero(question.accuracy, question.accuracy.toString())}%', + size: 8.sp, + color: Colors.white, + align: TextAlign.end, + ), + ) ], ), ), diff --git a/marking_app/lib/pages/homework_correction/hooks/do_papers_job/use_switch_student_and_type.dart b/marking_app/lib/pages/homework_correction/hooks/do_papers_job/use_switch_student_and_type.dart index 392b01f..c3ee127 100644 --- a/marking_app/lib/pages/homework_correction/hooks/do_papers_job/use_switch_student_and_type.dart +++ b/marking_app/lib/pages/homework_correction/hooks/do_papers_job/use_switch_student_and_type.dart @@ -140,7 +140,7 @@ class UseSwitchStudentAndType with CommonMixin, EventBusMixin { return tabJob; } } - ToastUtils.showSuccess('最后一题提交成功'); + // ToastUtils.showSuccess('最后一题提交成功'); } return tabJob; } diff --git a/marking_app/lib/pages/homework_correction/index.dart b/marking_app/lib/pages/homework_correction/index.dart index bbf528d..31788e1 100644 --- a/marking_app/lib/pages/homework_correction/index.dart +++ b/marking_app/lib/pages/homework_correction/index.dart @@ -7,6 +7,8 @@ * @Description: 阅卷主页 */ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -16,7 +18,9 @@ import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:marking_app/common/config/request_config.dart'; +import 'package:marking_app/common/model/event_bus/job_home_refresh_bus.dart'; import 'package:marking_app/common/model/job/job_task_item.dart'; +import 'package:marking_app/pages/common/event_bus_mixin.dart'; import 'package:marking_app/pages/homework_correction/components/new_version_of_homework/homework_tasks_view_item.dart'; import 'package:marking_app/provider/review_provider.dart'; import 'package:marking_app/routes/RouterManager.dart'; @@ -40,14 +44,7 @@ class HomeworkCorrection extends StatefulHookConsumerWidget { } class _HomeworkCorrectionState extends ConsumerState - with - CommonMixin, - TickerProviderStateMixin, - RefreshDataHandle, - AutomaticKeepAliveClientMixin { - @override - bool get wantKeepAlive => true; - + with CommonMixin, EventBusMixin, TickerProviderStateMixin, RefreshDataHandle { /* Tab控制器 */ late TabController _tabController; late TabController _tabController2; @@ -127,6 +124,7 @@ class _HomeworkCorrectionState extends ConsumerState _tabController.dispose(); _refreshController1.dispose(); _refreshController2.dispose(); + eventCancel(); super.dispose(); } @@ -152,8 +150,6 @@ class _HomeworkCorrectionState extends ConsumerState @override Widget build(BuildContext context) { - super.build(context); //调用super.build(返回值始终返回null,应将其忽略) - return AnnotatedRegion( value: const SystemUiOverlayStyle( systemNavigationBarColor: Color(0xFF000000), @@ -176,7 +172,19 @@ class _HomeworkCorrectionState extends ConsumerState child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded(flex: 1, child: SizedBox()), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.only(left: 10.w), + child: InkWell( + onTap: () => easyThrottle('BACK_JOB_HOME', () => Navigator.of(context).pop()), + child: Icon( + Icons.arrow_back_ios_sharp, + size: 16.sp, + ), + ), + )), Expanded( flex: 4, child: Container( @@ -226,9 +234,7 @@ class _HomeworkCorrectionState extends ConsumerState child: quickText( '待批阅', size: 14.sp, - color: _tabIndex == 0 - ? Theme.of(context).primaryColor - : const Color.fromRGBO(80, 94, 110, 1), + color: _tabIndex == 0 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), fontWeight: _tabIndex == 0 ? FontWeight.bold : null, ), ), @@ -246,9 +252,7 @@ class _HomeworkCorrectionState extends ConsumerState child: quickText( '已批阅', size: 14.sp, - color: _tabIndex == 1 - ? Theme.of(context).primaryColor - : const Color.fromRGBO(80, 94, 110, 1), + color: _tabIndex == 1 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), fontWeight: _tabIndex == 1 ? FontWeight.bold : null, ), ), @@ -257,32 +261,10 @@ class _HomeworkCorrectionState extends ConsumerState ), ), ), - Expanded( - flex: 1, - child: InkWell( - onTap: () { - RouterManager.router - .navigateTo(context, RouterManager.jobStudentGroupPath, transition: getTransition()); - }, - child: Icon(IconData(0xe63e, fontFamily: "AlibabaIcon"), - color: Color.fromRGBO(44, 48, 63, 1), size: 24.sp), - ), - ), + Expanded(flex: 1, child: SizedBox()), ], ), ), - - /* Row( - children: [ - InkWell( - onTap: (){ - - }, - child: Text('历史作业'), - ), - ], - ),*/ - if (_tabIndex == 1) $CompletedJobConditionFilter( controller: _tabController2, @@ -317,6 +299,9 @@ class _HomeworkCorrectionState extends ConsumerState data: markingDatas1, onLoad: onMyLoad, onRefresh: onMyRefresh, + eventFire: () { + eventFire(model: JobHomeRefreshBus()); + }, ), $EasyRefresh( controller: _refreshController2, @@ -325,6 +310,9 @@ class _HomeworkCorrectionState extends ConsumerState data: markingDatas2, onLoad: onMyLoad, onRefresh: onMyRefresh, + eventFire: () { + eventFire(model: JobHomeRefreshBus()); + }, ), ], ), @@ -346,6 +334,7 @@ Widget $easyRefresh({ required EasyRefreshController controller, required Future Function(EasyRefreshController controller, MarkingListParams params, int tab) onRefresh, required Future Function(EasyRefreshController controller, MarkingListParams params, int tab) onLoad, + required Function eventFire, required MarkingListParams params, required List data, required int tab, @@ -378,7 +367,10 @@ Widget $easyRefresh({ return HomeworkTasksViewItem( completed: completed, jobTaskItem: data[index], - call: () => controller.callRefresh(), + call: () { + controller.callRefresh(); + eventFire(); + }, ); }, itemCount: data.length, @@ -426,9 +418,7 @@ Widget $reviewedItem(BuildContext context, {required JobTaskItem jobTaskItem}) { alignment: Alignment.center, padding: EdgeInsets.only(left: 2.w), decoration: BoxDecoration( - color: jobTaskItem.markingTypeEnum.name == '作业' - ? const Color.fromRGBO(104, 136, 253, 1) - : const Color.fromRGBO(255, 175, 56, 1), + color: jobTaskItem.markingTypeEnum.name == '作业' ? const Color.fromRGBO(104, 136, 253, 1) : const Color.fromRGBO(255, 175, 56, 1), borderRadius: BorderRadius.only( topLeft: Radius.circular(14.r), topRight: Radius.circular(3.r), @@ -489,8 +479,7 @@ Widget $reviewedItem(BuildContext context, {required JobTaskItem jobTaskItem}) { onTap: () => easyThrottle('go_to_homework_report', () { RouterManager.router.navigateTo( context, - RouterManager.jobReportPagePath + - '?title=${Uri.encodeComponent(jobTaskItem.title)}&id=${jobTaskItem.id}', + RouterManager.jobReportPagePath + '?title=${Uri.encodeComponent(jobTaskItem.title)}&id=${jobTaskItem.id}', transition: getTransition(), ); }), @@ -533,8 +522,7 @@ Widget $theTabBar({required TabController controller, ValueChanged? onTap, if (customTime.endDate != null) { // print(customTime.startDate!.year == customTime.endDate!.year); if (!isPad() && customTime.startDate!.year == customTime.endDate!.year) { - customTimeStr = - customTime.startDate.toString().substring(5, 10) + '~${customTime.endDate.toString().substring(5, 10)}'; + customTimeStr = customTime.startDate.toString().substring(5, 10) + '~${customTime.endDate.toString().substring(5, 10)}'; } else { customTimeStr += '~${customTime.endDate?.toString().substring(0, 10)}'; } diff --git a/marking_app/lib/pages/homework_correction/job_home.dart b/marking_app/lib/pages/homework_correction/job_home.dart new file mode 100644 index 0000000..9e067a7 --- /dev/null +++ b/marking_app/lib/pages/homework_correction/job_home.dart @@ -0,0 +1,265 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:functional_widget_annotation/functional_widget_annotation.dart'; +import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/event_bus/job_home_refresh_bus.dart'; +import 'package:marking_app/common/model/marking/marking_list_params.dart'; +import 'package:marking_app/pages/common/event_bus_mixin.dart'; +import 'package:marking_app/routes/RouterManager.dart'; +import 'package:marking_app/utils/index.dart'; +import 'package:marking_app/utils/my_text.dart'; + +import 'package:badges/badges.dart' as badges; + +import '../../utils/my_future_builder.dart'; + +part 'job_home.g.dart'; + +class JobHome extends StatefulWidget { + const JobHome({super.key}); + + @override + State createState() => _JobHomeState(); +} + +class _JobHomeState extends State with CommonMixin, EventBusMixin, AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; + + @override + void initState() { + getData(); + eventOn(callback: (JobHomeRefreshBus item) => getData()); + super.initState(); + } + + @override + void dispose() { + eventCancel(); + super.dispose(); + } + + Future getData() async { + try { + var _client = await getClient(); + var _result = await _client.getJobsByPage(MarkingListParams( + isFinish: false, + page: 1, + limit: 1, + pageType: 0, + )); + var data = _result.data?.total ?? 0; + eventFire(model: QuantityToBeReviewedData(data)); + return data; + } catch (e) { + return 0; + } + } + + @override + Widget build(BuildContext context) { + super.build(context); + var spaceWidth = SizedBox(height: ScreenUtil().screenWidth / 19); + return AnnotatedRegion( + value: const SystemUiOverlayStyle( + systemNavigationBarColor: Color(0xFF000000), + systemNavigationBarDividerColor: null, + statusBarColor: Colors.white, + systemNavigationBarIconBrightness: Brightness.light, + statusBarIconBrightness: Brightness.dark, + statusBarBrightness: Brightness.light, + ), + child: RefreshIndicator( + onRefresh: () async => eventFire(model: JobHomeRefreshBus()), + child: ListView( + children: [ + Container( + constraints: BoxConstraints( + minHeight: 200.h, + maxWidth: double.infinity, + ), + // decoration: BoxDecoration( + // image: DecorationImage( + // image: AssetImage('assets/images/job_home_top_bgm.png'), + // fit: BoxFit.fitWidth, // 完全填充 + // ), + // ), + child: Image.asset('assets/images/job_home_top_bgm.png', fit: BoxFit.fitWidth), + ), + SizedBox(height: 30.h), + SlidingData([ + EntranceModel(title: '作业批阅', image: 'assets/images/job_home_marking.png', navigationUrl: RouterManager.jobMainListPagePath), + EntranceModel( + title: '学生历史作业', + image: 'assets/images/job_home_history.png', + navigationUrl: '${RouterManager.jobStudentGroupPath}?page=history', + ), + EntranceModel(title: '知识点点掌握', image: 'assets/images/job_home_knowledge.png', navigationUrl: RouterManager.jobKnowledgePointsPath) + ]), + spaceWidth, + $TermRow([ + EntranceModel(title: '答题轨迹', image: 'assets/images/job_home_answer_record.png', navigationUrl: RouterManager.answerTrajectoryPath), + EntranceModel( + title: '优先批阅设定', + image: 'assets/images/job_home_youxian.png', + navigationUrl: '${RouterManager.jobStudentGroupPath}?page=set', + ) + ], 0), + // spaceWidth, + // $TermRow([EntranceModel(title: '批阅设置', image: 'assets/images/job_home_marking_set.png', navigationUrl: '')], 0), + ], + ), + ), + ); + } +} + +class EntranceModel extends Object { + String title; + String image; + String navigationUrl; + EntranceModel({required this.title, required this.image, required this.navigationUrl}); +} + +class QuantityToBeReviewedData extends Object { + int num; + QuantityToBeReviewedData(this.num); +} + +@swidget +Widget $termRow(BuildContext context, List items, int data) { + var leng = items.length; + Widget childWidget; + switch (leng) { + case 1: + childWidget = Row(children: [Expanded(child: $TermItem(items[0], data))]); + break; + case 2: + childWidget = Row(children: [ + Expanded(flex: 9, child: $TermItem(items[0], data)), + Expanded(flex: 1, child: SizedBox()), + Expanded(flex: 9, child: $TermItem(items[1], data)), + ]); + break; + case 3: + double _theHeight = ScreenUtil().screenWidth / 19 + 54.h * 2; + childWidget = Row( + children: [ + Expanded(child: $TermItem(items[0], data, theHeight: _theHeight)), + SizedBox(width: ScreenUtil().screenWidth / 19), + Expanded( + child: SizedBox( + height: _theHeight, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + $TermItem(items[1], data), + $TermItem(items[2], data), + ], + ), + ), + ), + ], + ); + break; + default: + childWidget = Container(); + } + + return Container(padding: EdgeInsets.symmetric(horizontal: 14.w), child: childWidget); +} + +@swidget +Widget $termItem(BuildContext context, EntranceModel e, int data, {double? theHeight}) { + bool isJob = e.title == '作业批阅'; + + return Material( + color: Colors.white, + elevation: 3.r, + shadowColor: const Color.fromRGBO(231, 231, 231, 1), + borderRadius: BorderRadius.all(Radius.circular(8.r)), + child: InkWell( + onTap: () => easyThrottle('GO_TO_JOB_HOME_NAVIGATION', () { + RouterManager.router.navigateTo(context, e.navigationUrl, transition: getTransition()); + }), + + // splashColor: splashColor, + borderRadius: BorderRadius.all(Radius.circular(8.r)), + child: badges.Badge( + showBadge: isJob && data > 0, + ignorePointer: false, + badgeContent: quickText(data, color: Colors.white, size: 10.sp), + badgeAnimation: badges.BadgeAnimation.rotation( + animationDuration: Duration(seconds: 1), + colorChangeAnimationDuration: Duration(seconds: 1), + loopAnimation: false, + curve: Curves.fastOutSlowIn, + colorChangeAnimationCurve: Curves.easeInCubic, + ), + badgeStyle: badges.BadgeStyle( + badgeColor: Color.fromRGBO(255, 105, 105, 1), + shape: badges.BadgeShape.square, + borderRadius: BorderRadius.only(topLeft: Radius.circular(10.r), topRight: Radius.circular(8.5.r), bottomRight: Radius.circular(8.5.r)), + // borderSide: BorderSide(color: Colors.white, width: 2), + elevation: 1, + padding: EdgeInsets.symmetric(horizontal: isPad() ? 11.w : 16.w, vertical: 2.h), + ), + position: badges.BadgePosition.topEnd(top: 10.r, end: 10.r), + child: Container( + height: theHeight, + padding: EdgeInsets.symmetric(vertical: 12.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.r)), + // boxShadow: [ + // BoxShadow( + // color: const Color.fromRGBO(231, 231, 231, 1), + // offset: Offset(4.w, 6.h), //阴影y轴偏移量 + // blurRadius: 8, //阴影模糊程度 + // spreadRadius: 0.2, //阴影扩散程度 + // ) + // ], + // border: Border.all(width: 0.5.w, color: Color.fromARGB(255, 219, 226, 250)), + ), + alignment: Alignment.center, + child: isJob + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover), + SizedBox(height: 6.r), + quickText(e.title, size: 12.sp, color: Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500), + ], + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover), + SizedBox(width: 6.r), + quickText(e.title, size: 12.sp, color: Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500), + ], + ), + ), + )), + ); +} + +class SlidingData extends HookWidget with EventBusMixin { + final List items; + SlidingData(this.items); + + @override + Widget build(BuildContext context) { + var dataNumber = useState(null); + + useEffect(() { + eventOn(callback: (QuantityToBeReviewedData data) => (dataNumber.value = data)); + return () { + eventCancel(); + }; + }, []); + + return $TermRow(items, dataNumber.value?.num ?? 0); + } +} diff --git a/marking_app/lib/pages/homework_correction/job_knowledge_points.dart b/marking_app/lib/pages/homework_correction/job_knowledge_points.dart new file mode 100644 index 0000000..3c6e1b9 --- /dev/null +++ b/marking_app/lib/pages/homework_correction/job_knowledge_points.dart @@ -0,0 +1,486 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_easyrefresh/easy_refresh.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:functional_widget_annotation/functional_widget_annotation.dart'; +import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/common/base_structure_result.dart'; +import 'package:marking_app/common/model/job/job_knowledge_points.dart'; +import 'package:marking_app/common/model/job/job_student_history.dart'; +import 'package:marking_app/components/ReturnToHomepage.dart'; +import 'package:marking_app/pages/homework_correction/widget/personal_detail_topbar.dart'; +import 'package:marking_app/routes/RouterManager.dart'; +import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; +import 'package:marking_app/utils/index.dart'; +import 'package:marking_app/utils/my_text.dart'; +import 'package:marking_app/utils/request/rest_client.dart'; +import 'package:percent_indicator/percent_indicator.dart'; +import 'package:syncfusion_flutter_datepicker/datepicker.dart'; + +part 'job_knowledge_points.g.dart'; + +class JobKnowledgePoints extends StatefulWidget { + + const JobKnowledgePoints({Key? key,}) : super(key: key); + + @override + _JobKnowledgePointsState createState() => _JobKnowledgePointsState(); +} + +class _JobKnowledgePointsState extends State with CommonMixin, TickerProviderStateMixin { + @override + int page = 1; + int pageSize = 10; + int totalPages = 0; + late TabController tabController; + String startDataTime = CommonUtils.getWeekStartDate().toString().substring(0, 10); + String endDataTime = CommonUtils.getWeekEndDate().toString().substring(0, 10); + String customTimeStr = '自定义'; + List dataList = []; + late final EasyRefreshController refreshController; + + //文本输入框控制器 + late final TextEditingController textController; + int studentId = 0; + + @override + void initState() { + super.initState(); + textController = TextEditingController(); + + refreshController = EasyRefreshController(); + tabController = TabController(length: 3, vsync: this); + EasyLoading.show(status: 'loading...'); + getList(); + } + + @override + void dispose() { + super.dispose(); + tabController.dispose(); + textController.dispose(); + } + + void getList() async { +/* print('startDataTime=$startDataTime'); + print('endDataTime=$endDataTime');*/ + RestClient _client = await getClient(); + BaseStructureResult> res = + await _client.getKnowledgeReport(startDataTime,endDataTime,textController.text); + if (res.success) { + setState(() { + if (page == 1) { + dataList = res.data!; + } else { + dataList = [...dataList, ...res.data!]; + } + + // totalPages = res.data!.pagedList.totalPages; + }); + } + + EasyLoading.dismiss(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color.fromRGBO(245, 245, 245, 1), + appBar: AppBar( + backgroundColor: Colors.white, + title: Text('知识点掌握', style: TextStyle(fontSize: 14.sp, color: Color(0xFF333333))), + centerTitle: true, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.black), + onPressed: () => Navigator.of(context).pop(), + ), + actions: [ + ReturnToHomepage(), + ], + elevation: 0, + ), + body: Column( + children: [ + Container( + margin: EdgeInsets.all(15.r), + height: 30.r, + child: Row( + children: [ + Expanded( + child: Container( + padding: EdgeInsets.only(left: 10.r,right: 10.r), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6.r), + border: Border.all(width: 1.r,color: Color(0xFFDDDDDD)), + color: Colors.white, + ), + child: TextField( + controller: textController, + textInputAction: TextInputAction.next, + style: TextStyle( + color: const Color.fromRGBO(80, 87, 103, 1), + fontSize: 10.sp, + ), + decoration: InputDecoration( + hintText: "请输入知识点名称", + hintStyle: TextStyle(fontSize: 10.sp, color: const Color.fromRGBO(153, 153, 153, 1)), + labelStyle: TextStyle(fontSize: 10.sp, color: const Color.fromRGBO(148, 163, 182, 1)), + border: InputBorder.none, + ), + ), + ), + ), + SizedBox(width: 10.r,), + InkWell( + onTap: (){ + page = 1; + setState(() {}); + getList(); + }, + child: Container( + width: 50.r, + height: 30.r, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.r), + color: Color(0xFF6888FD), + ), + child:Center( + child: Text('查询',style: TextStyle(fontSize: 12.sp,color: Colors.white),), + ), + ), + ) + ], + ), + ), + + + jobConditionFilter(context, + controller: tabController, + customTimeStr: customTimeStr, + customTime: tabController.index != 3 || + ((endDataTime == null || endDataTime == '') && (startDataTime == null || startDataTime == '')) + ? null + : PickerDateRange( + startDataTime == null || startDataTime == '' ? null : DateTime.parse(startDataTime!), + endDataTime == null || endDataTime == '' ? null : DateTime.parse(endDataTime!), + ), onTimeFilter: (String? startTime, String? endTime) { + EasyLoading.show(status: 'loading...'); + if (startTime == null && endTime == null) { + if (tabController.index == 3) { + tabController.animateTo(0); + } + startDataTime = ''; + endDataTime = ''; + customTimeStr = '自定义'; + } else { + startDataTime = startTime != null ? startTime : ''; + endDataTime = endTime != null ? endTime : ''; + } + page = 1; + setState(() {}); + getList(); + // _refreshController2.callRefresh(); + }, refreshTime: (value) { + if (value != null && value.startDate != null) { + customTimeStr = value.startDate?.toString().substring(0, 10) ?? ''; + setState(() {}); + if (value.endDate != null) { + if (!isPad() && value.startDate!.year == value.endDate!.year) { + customTimeStr = + value.startDate.toString().substring(5, 10) + '~${value.endDate.toString().substring(5, 10)}'; + setState(() {}); + } else { + customTimeStr = '$customTimeStr~${value.endDate?.toString().substring(0, 10)}'; + setState(() {}); + } + } + } + }), + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 10.r), + child: EasyRefresh( + firstRefresh: false, + taskIndependence: true, + controller: refreshController, + header: MaterialHeader(), + footer: TaurusFooter(), + onRefresh: () async { + setState(() { + page = 1; + }); + getList(); + }, + onLoad: () async { + if (page < totalPages) { + setState(() { + page += 1; + }); + getList(); + } + }, + child: dataList.length > 0 + ? ListView.builder( + itemCount: dataList.length, + itemBuilder: (context, index) { + KnowledgePoints item = dataList[index]; + return InkWell( + onTap: () { + RouterManager.router.navigateTo( + context, + RouterManager.jobKnowledgePointsDetailPath + + '?knowledgeName=${Uri.encodeComponent(item.knowledgeName)}&knowledgeId=${item.knowledgeId}', + transition: getTransition(), + ); + }, + child: Container( + margin: EdgeInsets.symmetric(vertical: 5.r, horizontal: 14.r), + padding: EdgeInsets.symmetric(vertical: 14.r, horizontal: 10.r), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10.r)), + color: Colors.white), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: Text( + item.knowledgeName, + style: TextStyle(fontSize: 14.sp, color: Color(0xFF505050)), + )), + + Container( + width: 49.r, + height: 22.r, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.r)), + border: Border.all(width: 1.r, color: Color(0xFF6888FD)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '${item.count}次', + style: TextStyle(fontSize: 10.sp, color: Color(0xFF6888FD)), + ), + Image.asset('assets/images/right_icon_blue.png',width: 8.r,height: 8.r,), + ], + ), + ), + ], + ), + SizedBox(height: 10.r,), + progressBar(context, + title: '正确率:', + color: Color(0xFF90E0BE), + percent: item.correctRate / 100, + padingEdg: EdgeInsets.zero, + marginEdg: EdgeInsets.only(top: 8.h)), + ], + ), + ), + ); + }) + : MyEmptyWidget(), + ), + ), + ), + ], + ), + ); + } +} + +@swidget +Widget progressBar( + BuildContext context, { + double? fontSize, + double? lineHeight, + required String title, + required Color color, + required double percent, + required EdgeInsets padingEdg, + required EdgeInsets marginEdg, + }) { + var percentStr = '${doubleToStringAsFixed(percent * 100)}%'; + fontSize ??= 10.sp; + lineHeight ??= 8.h; + return Container( + margin: marginEdg, + padding: padingEdg, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + quickText(title, color: Color(0xFF8B8B8B), size: fontSize), + Expanded( + flex: 1, + child: Container( + child: Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + /* boxShadow: [ + BoxShadow( + color: color, + spreadRadius: 0.6, + blurRadius: 3, + offset: Offset(0, 0), + ), + ],*/ + ), + child: LinearPercentIndicator( + padding: EdgeInsets.zero, + animation: true, + lineHeight: lineHeight, + animationDuration: 2500, + percent: percent, + /* center: Text( + percentStr, + style: TextStyle(color: Colors.white, fontSize: 4.5.sp), + ),*/ + // linearStrokeCap: LinearStrokeCap.butt, + progressColor: color, + backgroundColor: Color(0xFFE8E8E8), + barRadius: Radius.circular(10.r), + // linearGradient: LinearGradient( + // tileMode: TileMode.mirror, + // stops: [0.0, 1.0], + // colors: [color.withOpacity(0.1), color], + // ), + ), + ), + ), + SizedBox(width: 4.w), + quickText(percentStr, size: fontSize, color: Color(0xFF606060)) + ], + ), + ), + ), + ], + ), + ); +} + +/// 已完成作业条件筛选栏 +@hwidget +Widget jobConditionFilter(BuildContext context, + {required TabController controller, + PickerDateRange? customTime, + required Function refreshTime, + required String customTimeStr, + required Function(String? startTime, String? endTime) onTimeFilter}) { + var customTimeState = PickerDateRange(null, null); + if (customTime != null) { + customTimeState = PickerDateRange(customTime!.startDate != null ? customTime!.startDate : null, + customTime!.endDate != null ? customTime!.endDate : null); + } + + DateTime getMonthStartDate() { + DateTime now = DateTime.now(); + return DateTime(now.year, now.month, 1); // 获取当前月份的第一天 + } + + DateTime getMonthEndDate() { + DateTime now = DateTime.now(); + int nextMonth = now.month + 1; + if (nextMonth > 12) { + nextMonth = 1; + now = now.add(Duration(days: 31 - now.day)); // 跨年了,所以加到当前月的最后一天 + } else { + now = now.add(Duration(days: DateTime(now.year, nextMonth, 0).day - now.day)); // 加到下个月的第一天的前一天,即本月最后一天 + } + return now; + } + + return Container( + // height: 39.h, + // padding: EdgeInsets.only(left: 4.w, right: 12.w), + decoration: BoxDecoration( + color: Color.fromRGBO(244, 244, 244, 1), + // border: Border(bottom: BorderSide(color: Color.fromRGBO(204, 204, 204, 1), width: 1)), + ), + child: Container( + alignment: Alignment.centerLeft, + decoration: BoxDecoration( + border: Border(bottom: BorderSide(width: 1.r,color: Color(0xFFCCCCCC))) + ), + child: TabBar( + controller: controller, + unselectedLabelStyle: TextStyle(fontSize: 12.sp, color: const Color.fromRGBO(102, 102, 102, 1)), + labelStyle: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.bold, + color: Color.fromRGBO(116, 145, 253, 1), + ), + isScrollable: true, + labelColor: Color(0xFF6888FD), + unselectedLabelColor: Color(0xFF666666), + padding: EdgeInsets.symmetric(horizontal: 14.r), + // indicatorSize: TabBarIndicatorSize.label, // 设置指示器高度和标签一样高 + onTap: (int val) async { + switch (val) { + case 0: // 近一周 + onTimeFilter( + CommonUtils.getWeekStartDate().toString().substring(0, 10), + CommonUtils.getWeekEndDate().toString().substring(0, 10), + ); + break; + case 1: // 近一个月 + onTimeFilter( + getMonthStartDate().toString().substring(0, 10), + getMonthEndDate().toString().substring(0, 10), + ); + break; + default: // 自定义 + var dialogData = await showDialog( + context: context, + builder: (BuildContext context1) { + return Center( + child: Container( + color: Colors.white, + width: isPad() ? ScreenUtil().screenWidth / 2 : ScreenUtil().screenWidth / 1.3, + height: ScreenUtil().screenHeight / 2, + child: SfDateRangePicker( + showActionButtons: true, + confirmText: '确定', + cancelText: '取消', + onSubmit: (p0) { + print(p0); + Navigator.of(context1).pop(p0); + refreshTime(p0); + }, + onCancel: () { + Navigator.of(context1).pop(); + }, + selectionMode: DateRangePickerSelectionMode.range, + initialSelectedRange: customTimeState, + ), + ), + ); + }); + // startDate: 2024-03-04 18:47:00.117958, endDate: 2024-03-11 18:47:00.117986 + // if (dialogData != null && (dialogData.startDate != null || dialogData.endDate != null)) {} + onTimeFilter( + dialogData?.startDate?.toString().substring(0, 10), + dialogData?.endDate?.toString().substring(0, 10), + ); + customTimeState = dialogData!; + } + }, + tabs: [ + const Tab(text: '近一周'), + const Tab(text: '近一月'), + Tab(text: customTimeStr), + ], + ), + ), + ); +} diff --git a/marking_app/lib/pages/homework_correction/job_knowledge_points_detail.dart b/marking_app/lib/pages/homework_correction/job_knowledge_points_detail.dart new file mode 100644 index 0000000..96f1b64 --- /dev/null +++ b/marking_app/lib/pages/homework_correction/job_knowledge_points_detail.dart @@ -0,0 +1,465 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_easyrefresh/easy_refresh.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:functional_widget_annotation/functional_widget_annotation.dart'; +import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/common/base_structure_result.dart'; +import 'package:marking_app/common/model/job/job_knowledge_detail_student.dart'; +import 'package:marking_app/common/model/job/job_knowledge_points.dart'; +import 'package:marking_app/common/model/job/job_knowledge_points_detail.dart'; +import 'package:marking_app/common/model/job/job_student_history.dart'; +import 'package:marking_app/components/ReturnToHomepage.dart'; +import 'package:marking_app/pages/homework_correction/widget/personal_detail_topbar.dart'; +import 'package:marking_app/routes/RouterManager.dart'; +import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; +import 'package:marking_app/utils/index.dart'; +import 'package:marking_app/utils/my_text.dart'; +import 'package:marking_app/utils/request/rest_client.dart'; +import 'package:percent_indicator/percent_indicator.dart'; +import 'package:photo_view/photo_view.dart'; +import 'package:syncfusion_flutter_datepicker/datepicker.dart'; +part 'job_knowledge_points_detail.g.dart'; +class JobKnowledgePointsDetail extends StatefulWidget { + final String knowledgeName; + final int knowledgeId; + + const JobKnowledgePointsDetail( + {Key? key, required this.knowledgeName, required this.knowledgeId}) + : super(key: key); + + @override + _JobKnowledgePointsDetailState createState() => + _JobKnowledgePointsDetailState(); +} + +class _JobKnowledgePointsDetailState extends State + with CommonMixin, TickerProviderStateMixin { + @override + List dataList = []; + List studentList = []; + late final EasyRefreshController refreshController; + String paperImg = ''; + + int studentId = 0; + + @override + void initState() { + super.initState(); + refreshController = EasyRefreshController(); + EasyLoading.show(status: 'loading...'); + getList(); + } + + @override + void dispose() { + super.dispose(); + } + + void getList() async { + RestClient _client = await getClient(); + BaseStructureResult> res = + await _client.getKnowledgeReportDetail(widget.knowledgeId); + if (res.success) { + setState(() { + dataList = res.data!; + }); + } + + EasyLoading.dismiss(); + } + + showStudent(questionid, title) async { + await getStudents(questionid); + + showModalBottomSheet( + context: context, + elevation: 10, + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.r), + topRight: Radius.circular(20.r), + ), + ), + builder: (BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 2.w), + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: 14.h), + child: quickText( + title, + size: 18.sp, + fontWeight: FontWeight.bold, + color: Color.fromRGBO(60, 60, 60, 1), + ), + ), + Expanded( + child: ListView( + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w), + children: [ + Wrap( + spacing: 6.r, // 主轴(水平)方向间距 + runSpacing: 4.r, // 纵轴(垂直)方向间距 + alignment: WrapAlignment.spaceAround, //沿主轴方向居中 + children: studentList.map((e) { + return Container( + padding: EdgeInsets.symmetric( + vertical: 4.r, horizontal: 8.r), + decoration: BoxDecoration( + color: e.isAnswer + ? Color(0xFF4CC793) + : Color(0xFFE2E2E2), + borderRadius: BorderRadius.circular(4.r), + ), + child: quickText(e.studentName, + color: + e.isAnswer ? Colors.white : Color(0xFF505E6E), + size: 10.sp), + ); + }).toList(), + ), + ], + ), + ) + ], + ), + ); + }, + ); + EasyLoading.dismiss(); + } + + showImg(int sectionId, String questionNo) async { + await getImg(sectionId, questionNo); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + insetPadding: EdgeInsets.symmetric(vertical: 55.r,horizontal: 45.r), + contentPadding: EdgeInsets.all(0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(15.r))), + content: Container( + width: MediaQuery.of(context).size.width, + // height: MediaQuery.of(context).size.height, + child: Image.network(paperImg), + ), + ); + }, + ); + EasyLoading.dismiss(); + } + + getStudents(questionid) async { + RestClient _client = await getClient(); + BaseStructureResult> res = + await _client.getKnowledgeStudent(questionid); + if (res.success) { + studentList = res.data!; + } else { + studentList = []; + } + } + + getImg(int sectionId, String questionNo) async { + RestClient _client = await getClient(); + BaseStructureResult res = + await _client.getKnowledgeImg(sectionId, questionNo); + if (res.success) { + paperImg = res.data!; + } else { + paperImg = ''; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color.fromRGBO(245, 245, 245, 1), + appBar: AppBar( + backgroundColor: Colors.white, + title: Text(widget.knowledgeName, + style: TextStyle(fontSize: 14.sp, color: Color(0xFF333333))), + centerTitle: true, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.black), + onPressed: () => Navigator.of(context).pop(), + ), + actions: [ + ReturnToHomepage(), + ], + elevation: 0, + ), + body: Padding( + padding: EdgeInsets.symmetric(vertical: 10.r), + child: EasyRefresh( + firstRefresh: false, + taskIndependence: true, + controller: refreshController, + header: MaterialHeader(), + footer: TaurusFooter(), + onRefresh: () async { + getList(); + }, + onLoad: () async { + // getList(); + }, + child: dataList.length > 0 + ? ListView.builder( + itemCount: dataList.length, + itemBuilder: (context, index) { + KnowledgePointsDetail item = dataList[index]; + return InkWell( + onTap: () { + /* RouterManager.router.navigateTo( + context, + RouterManager.quickCheckPersonalPath + + '?jobId=${item.jobName}&studentId=$studentId', + transition: getTransition(), + );*/ + }, + child: Container( + margin: EdgeInsets.symmetric( + vertical: 5.r, horizontal: 14.r), + padding: EdgeInsets.symmetric( + vertical: 14.r, horizontal: 10.r), + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(10.r)), + color: Colors.white), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + item.publishTime.substring(0, 10), + style: TextStyle( + fontSize: 14.sp, + color: Color(0xFF505050)), + ), + SizedBox( + width: 10.r, + ), + Expanded( + child: Text( + item.jobName, + style: TextStyle( + fontSize: 14.sp, + color: Color(0xFF505050)), + )), + InkWell( + onTap: () { + EasyLoading.show(status: 'loading...'); + showImg(item.sectionId, item.questionNo); + }, + child: Container( + width: 49.r, + height: 22.r, + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular(20.r)), + border: Border.all( + width: 1.r, color: Color(0xFF8B8B8B)), + ), + child: Center( + child: Text( + '第${item.questionNo}题', + style: TextStyle( + fontSize: 10.sp, + color: Color(0xFF8B8B8B)), + ), + ), + ), + ), + ], + ), + SizedBox( + height: 10.r, + ), + Container( + margin: EdgeInsets.only(top: 8.h), + padding: EdgeInsets.zero, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + EasyLoading.show(status: 'loading...'); + showStudent( + item.questionId, item.jobName); + }, + child: Container( + margin: EdgeInsets.only(right: 6.r), + width: 56.r, + height: 20.r, + decoration: BoxDecoration( + color: Color(0xFFD4FFED), + borderRadius: + BorderRadius.circular(20.r), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + quickText('正确率', + color: Color(0xFF4CC793), + size: 10.sp), + Image.asset('assets/images/icon_back_green.png',width: 8.r,height: 8.r,) + ], + )), + ), + Expanded( + flex: 1, + child: Container( + child: Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(10.r), + ), + child: LinearPercentIndicator( + padding: EdgeInsets.zero, + animation: true, + lineHeight: 8.h, + animationDuration: 2500, + percent: item.correctRate / 100, + progressColor: + Color(0xFF90E0BE), + backgroundColor: + Color(0xFFE8E8E8), + barRadius: + Radius.circular(10.r), + ), + ), + ), + SizedBox(width: 4.w), + quickText( + '${doubleToStringAsFixed(item.correctRate / 100 * 100)}%', + size: 10.sp, + color: Color(0xFF606060)) + ], + ), + ), + ), + ], + ), + ), + /* progressBar(context, + title: '正确率 >', + color: Color(0xFF90E0BE), + percent: item.correctRate / 100, + padingEdg: EdgeInsets.zero, + marginEdg: EdgeInsets.only(top: 8.h), + studentCall:showStudent(item.questionId,item.jobName), + ),*/ + ], + ), + ), + ); + }) + : MyEmptyWidget(), + ), + ), + ); + } +} + +@swidget +Widget progressBar( + BuildContext context, { + double? fontSize, + double? lineHeight, + required String title, + required Color color, + required double percent, + required EdgeInsets padingEdg, + required EdgeInsets marginEdg, + required Future studentCall, +}) { + var percentStr = '${doubleToStringAsFixed(percent * 100)}%'; + fontSize ??= 10.sp; + lineHeight ??= 8.h; + return Container( + margin: marginEdg, + padding: padingEdg, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + studentCall; + }, + child: Container( + margin: EdgeInsets.only(right: 6.r), + width: 56.r, + height: 20.r, + decoration: BoxDecoration( + color: Color(0xFFD4FFED), + borderRadius: BorderRadius.circular(20.r), + ), + child: Center( + child: quickText(title, + color: Color(0xFF4CC793), size: fontSize))), + ), + Expanded( + flex: 1, + child: Container( + child: Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + /* boxShadow: [ + BoxShadow( + color: color, + spreadRadius: 0.6, + blurRadius: 3, + offset: Offset(0, 0), + ), + ],*/ + ), + child: LinearPercentIndicator( + padding: EdgeInsets.zero, + animation: true, + lineHeight: lineHeight, + animationDuration: 2500, + percent: percent, + /* center: Text( + percentStr, + style: TextStyle(color: Colors.white, fontSize: 4.5.sp), + ),*/ + // linearStrokeCap: LinearStrokeCap.butt, + progressColor: color, + backgroundColor: Color(0xFFE8E8E8), + barRadius: Radius.circular(10.r), + // linearGradient: LinearGradient( + // tileMode: TileMode.mirror, + // stops: [0.0, 1.0], + // colors: [color.withOpacity(0.1), color], + // ), + ), + ), + ), + SizedBox(width: 4.w), + quickText(percentStr, size: fontSize, color: Color(0xFF606060)) + ], + ), + ), + ), + ], + ), + ); +} diff --git a/marking_app/lib/pages/homework_correction/job_personal_detail.dart b/marking_app/lib/pages/homework_correction/job_personal_detail.dart index b40b7cf..9d3202b 100644 --- a/marking_app/lib/pages/homework_correction/job_personal_detail.dart +++ b/marking_app/lib/pages/homework_correction/job_personal_detail.dart @@ -60,6 +60,7 @@ class _JobPersonalDetailState extends State with CommonMixin, } void getList() async { + print(widget.studentId); RestClient _client = await getClient(); BaseStructureResult res = await _client.getStudentJobHistory(widget.studentId, !isJob, startDataTime, endDataTime, page, pageSize); diff --git a/marking_app/lib/pages/homework_correction/job_priority_review_set.dart b/marking_app/lib/pages/homework_correction/job_priority_review_set.dart index ceb876a..1e4585b 100644 --- a/marking_app/lib/pages/homework_correction/job_priority_review_set.dart +++ b/marking_app/lib/pages/homework_correction/job_priority_review_set.dart @@ -16,8 +16,9 @@ import 'package:marking_app/utils/request/rest_client.dart'; class JobPriorityReviewSet extends StatefulWidget { final String groupId; final String title; + final String? page; - const JobPriorityReviewSet({Key? key, required this.groupId,required this.title}) + const JobPriorityReviewSet({Key? key, required this.groupId,required this.title,this.page = ''}) : super(key: key); @override @@ -207,8 +208,27 @@ class _JobPriorityReviewSetState extends State fontSize: 12.sp, color: Color(0xFF6888FD)), )), - item.readLevel == 1 - ? InkWell( + + widget.page == 'answerTrajectory'?Container( + height: 20.r, + width: 70.r, + decoration: BoxDecoration( + border: Border.all(width: 1.r,color: Color(0xFFFFA41E)), + borderRadius: BorderRadius.all(Radius.circular(20.r)), + + ), + child: Center(child: Text('详情',style: TextStyle(fontSize: 10.r,color: Color(0xFFFFA41E))), + )):widget.page == 'history'?Container( + height: 20.r, + width: 70.r, + decoration: BoxDecoration( + color: Color(0xFF6888FD), + borderRadius: BorderRadius.all(Radius.circular(20.r)) + ), + child: Center(child: Text('历史作业',style: TextStyle(fontSize: 10.r,color: Colors.white),)), + ): item.readLevel == 1 + ? + InkWell( onTap: () { setState(() { isClicking = true; @@ -218,7 +238,7 @@ class _JobPriorityReviewSetState extends State EasyLoading.show( status: 'loading...'); }, - child: Container( + child:Container( height: 20.r, width: 70.r, decoration: BoxDecoration( @@ -246,7 +266,7 @@ class _JobPriorityReviewSetState extends State ), ), ) - : InkWell( + :InkWell( onTap: () { setState(() { isClicking = true; @@ -256,7 +276,7 @@ class _JobPriorityReviewSetState extends State EasyLoading.show( status: 'loading...'); }, - child: Container( + child: Container( height: 20.r, width: 70.r, decoration: BoxDecoration( @@ -284,34 +304,6 @@ class _JobPriorityReviewSetState extends State ), ), ), - /* SizedBox( - width: 5.r, - ), - InkWell( - onTap: () { - RouterManager.router.navigateTo(context, - '${RouterManager.jobPersonalDetailPath}?studentId=${item.studentId}&studentName=${Uri.encodeComponent(item.studentName)}'); - }, - child: Container( - height: 20.r, - width: 70.r, - decoration: BoxDecoration( - borderRadius: BorderRadius.all( - Radius.circular(20.r)), - color: Colors.white, - border: Border.all( - width: 1.r, - color: Color(0xFFFCA017))), - child: Center( - child: Text( - '详情', - style: TextStyle( - fontSize: 10.sp, - color: Color(0xFFFCA017)), - ), - ), - ), - )*/ ], ), ), @@ -346,7 +338,24 @@ class _JobPriorityReviewSetState extends State fontSize: 12.sp, color: Color(0xFF6888FD)), )), - item.readLevel == 1 + widget.page == 'answerTrajectory'?Container( + height: 24.r, + width: 72.r, + decoration: BoxDecoration( + border: Border.all(width: 1.r,color: Color(0xFFFFA41E)), + borderRadius: BorderRadius.all(Radius.circular(20.r)), + + ), + child: Center(child: Text('详情',style: TextStyle(fontSize: 10.r,color: Color(0xFFFFA41E))), + )):widget.page == 'history'?Container( + height: 24.r, + width: 82.r, + decoration: BoxDecoration( + color: Color(0xFF6888FD), + borderRadius: BorderRadius.all(Radius.circular(20.r)) + ), + child: Center(child: Text('历史作业',style: TextStyle(fontSize: 10.r,color: Colors.white),)), + ):item.readLevel == 1 ? InkWell( onTap: () { setState(() { @@ -420,34 +429,6 @@ class _JobPriorityReviewSetState extends State ), ), ), - /* SizedBox( - width: 5.r, - ), - InkWell( - onTap: () { - RouterManager.router.navigateTo(context, - '${RouterManager.jobPersonalDetailPath}?studentId=${item.studentId}&studentName=${Uri.encodeComponent(item.studentName)}'); - }, - child: Container( - height: 20.r, - width: 70.r, - decoration: BoxDecoration( - borderRadius: BorderRadius.all( - Radius.circular(20.r)), - color: Colors.white, - border: Border.all( - width: 1.r, - color: Color(0xFFFCA017))), - child: Center( - child: Text( - '详情', - style: TextStyle( - fontSize: 10.sp, - color: Color(0xFFFCA017)), - ), - ), - ), - )*/ ], ), ), diff --git a/marking_app/lib/pages/homework_correction/job_report.dart b/marking_app/lib/pages/homework_correction/job_report.dart index 810008f..98b2730 100644 --- a/marking_app/lib/pages/homework_correction/job_report.dart +++ b/marking_app/lib/pages/homework_correction/job_report.dart @@ -9,6 +9,7 @@ import 'package:marking_app/common/model/job/job_report_join_class.dart'; import 'package:marking_app/common/model/job/job_report_knowledge_model.dart'; import 'package:marking_app/common/model/job/job_report_model.dart'; import 'package:marking_app/components/ReturnToHomepage.dart'; +import 'package:marking_app/pages/homework_correction/components/imgDialog.dart'; import 'package:marking_app/pages/homework_correction/widget/report_table.dart'; import 'package:marking_app/pages/homework_correction/widget/top_count.dart'; import 'package:marking_app/pages/mainPage.dart'; @@ -1445,7 +1446,7 @@ Widget $unitTimeAnsweringSituation(BuildContext context, int jobid, List createState() => _JobStudentGroupState(); @@ -59,7 +60,7 @@ class _JobStudentGroupState extends State with CommonMixin { } void goNextPage(id,title){ - RouterManager.router.navigateTo(context, '${RouterManager.jobPriorityReviewSetPath}?&groupId=$id&title=${Uri.encodeComponent(title)}',transition: getTransition()); + RouterManager.router.navigateTo(context, '${RouterManager.jobPriorityReviewSetPath}?&groupId=$id&title=${Uri.encodeComponent(title)}&page=${widget.page}',transition: getTransition()); } @override diff --git a/marking_app/lib/pages/homework_correction/providers/handwriting_drawing_trajectory_provider.dart b/marking_app/lib/pages/homework_correction/providers/handwriting_drawing_trajectory_provider.dart new file mode 100644 index 0000000..30f40bf --- /dev/null +++ b/marking_app/lib/pages/homework_correction/providers/handwriting_drawing_trajectory_provider.dart @@ -0,0 +1,18 @@ +/// 原稿作业回显 +// 回显批注轨迹 + +import 'package:marking_app/common/model/job/gesture_recording.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:marking_app/common/mixin/common.dart'; + +final jobHandwritingDrawingTrajectoryProvider = + StateNotifierProvider>( + (ref) => JobHandwritingDrawingTrajectoryProviderHandle([])); + +class JobHandwritingDrawingTrajectoryProviderHandle extends StateNotifier> with CommonMixin { + JobHandwritingDrawingTrajectoryProviderHandle(List progress) : super(progress); + + setVal(List val) { + state = val; + } +} diff --git a/marking_app/lib/pages/homework_correction/quick_check_personal.dart b/marking_app/lib/pages/homework_correction/quick_check_personal.dart index e53aef8..426618c 100644 --- a/marking_app/lib/pages/homework_correction/quick_check_personal.dart +++ b/marking_app/lib/pages/homework_correction/quick_check_personal.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:marking_app/common/mixin/common.dart'; import 'package:marking_app/common/model/common/base_structure_result.dart'; import 'package:marking_app/common/model/job/job_data_report.dart'; @@ -8,24 +9,23 @@ import 'package:marking_app/components/ReturnToHomepage.dart'; import 'package:marking_app/pages/homework_correction/widget/student_kg_table.dart'; import 'package:marking_app/pages/homework_correction/widget/student_zg_table.dart'; import 'package:marking_app/routes/RouterManager.dart'; -import 'package:marking_app/utils/common_utils.dart'; import 'package:marking_app/utils/request/rest_client.dart'; import 'package:marking_app/utils/toast_utils.dart'; -class QuickCheckPersonal extends StatefulWidget { +import 'providers/handwriting_drawing_trajectory_provider.dart'; +import 'widget/answer_handwriting.dart'; + +class QuickCheckPersonal extends StatefulHookConsumerWidget { final int jobId; final int studentId; - const QuickCheckPersonal( - {Key? key, required this.jobId, required this.studentId}) - : super(key: key); + const QuickCheckPersonal({Key? key, required this.jobId, required this.studentId}) : super(key: key); @override - State createState() => _QuickCheckPersonalState(); + ConsumerState createState() => _QuickCheckPersonalState(); } -class _QuickCheckPersonalState extends State - with CommonMixin { +class _QuickCheckPersonalState extends ConsumerState with CommonMixin { StudentDetails? studentInfo; void initState() { @@ -40,18 +40,16 @@ class _QuickCheckPersonalState extends State params['jobid'] = widget.jobId; // params['jobid'] = '521646983660101'; params['studentId'] = widget.studentId; - BaseStructureResult data = - await _client.getJobPersonalReport(params); - if(data.data!.studentId != null){ + BaseStructureResult data = await _client.getJobPersonalReport(params); + if (data.data!.studentId != null) { setState(() { studentInfo = data.data; }); - }else{ + } else { Navigator.pop(context); ToastUtils.showError('暂无数据'); } EasyLoading.dismiss(); - } @override @@ -74,7 +72,7 @@ class _QuickCheckPersonalState extends State ), actions: [ ReturnToHomepage(), - /* Title( + /* Title( color: Color(0xFF6888FD), child: Container( child: InkWell( @@ -89,20 +87,19 @@ class _QuickCheckPersonalState extends State alignment: Alignment.center, ), ),*/ - ], ), - body: SingleChildScrollView( child: Column( children: [ Padding( - padding: EdgeInsets.only(top: 14.r,left: 14.r), + padding: EdgeInsets.only(top: 14.r, left: 14.r), child: Row( children: [ InkWell( - onTap: (){ - RouterManager.router.navigateTo(context, '${RouterManager.jobPersonalDetailPath}?studentId=${widget.studentId}&studentName=${Uri.encodeComponent(studentInfo!.studentName!)}'); + onTap: () { + RouterManager.router.navigateTo(context, + '${RouterManager.jobPersonalDetailPath}?studentId=${widget.studentId}&studentName=${Uri.encodeComponent(studentInfo!.studentName!)}'); }, child: Container( width: 93.r, @@ -112,22 +109,37 @@ class _QuickCheckPersonalState extends State borderRadius: BorderRadius.circular(4.r), ), child: Center( - child: Text('历史查询',style: TextStyle(fontSize: 10.r,color: Color(0xFF2080F7)),), + child: Text( + '历史作业', + style: TextStyle(fontSize: 10.r, color: Color(0xFF2080F7)), + ), ), ), ), - SizedBox(width: 10.r,), - /*Container( - width: 93.r, - height: 28.r, - decoration: BoxDecoration( - color: Color(0xFFEDFFF7), - borderRadius: BorderRadius.circular(4.r), + SizedBox( + width: 10.r, + ), + InkWell( + onTap: () { + showAnswerHandwriting(context, jobId: widget.jobId, studentId: widget.studentId).then((value) { + ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal([]); + }); + }, + child: Container( + width: 93.r, + height: 28.r, + decoration: BoxDecoration( + color: Color(0xFFEDFFF7), + borderRadius: BorderRadius.circular(4.r), + ), + child: Center( + child: Text( + '原稿笔迹', + style: TextStyle(fontSize: 10.r, color: Color(0xFF4CC793)), + ), + ), ), - child: Center( - child: Text('原稿笔迹',style: TextStyle(fontSize: 10.r,color: Color(0xFF4CC793)),), - ), - ),*/ + ), ], ), ), @@ -147,31 +159,38 @@ class _QuickCheckPersonalState extends State children: [ Text( '客观题', - style: TextStyle( - fontSize: 14.sp, color: Color(0xFF5C5C5C),fontWeight: FontWeight.w600), + style: TextStyle(fontSize: 14.sp, color: Color(0xFF5C5C5C), fontWeight: FontWeight.w600), ), SizedBox( width: 10.r, ), Text( '${studentInfo!.kgValidRate}%', - style: TextStyle( - fontSize: 14.sp, color: Color(0xFF6888FD),fontWeight: FontWeight.w600), + style: TextStyle(fontSize: 14.sp, color: Color(0xFF6888FD), fontWeight: FontWeight.w600), ), ], ), - SizedBox(height: 10.r,), SizedBox( - height: studentInfo!.kgDetails.length>8?300.r:studentInfo!.kgDetails.length * 40.r + 40.r, + height: 10.r, + ), + SizedBox( + height: studentInfo!.kgDetails.length > 8 ? 300.r : studentInfo!.kgDetails.length * 40.r + 40.r, child: StudentKgTable( headList: ['题号', '学生答案', '标准答案'], bodyList: studentInfo!.kgDetails, + questionNumCall: (no) { + showAnswerHandwriting(context, jobId: widget.jobId, studentId: widget.studentId, questionNo: int.parse(no)).then((value) { + ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal([]); + }); + }, ), ) ], ), ), - SizedBox(height: 15.r,), + SizedBox( + height: 15.r, + ), //主观题 Container( padding: EdgeInsets.symmetric(vertical: 14.r, horizontal: 10.r), @@ -188,25 +207,30 @@ class _QuickCheckPersonalState extends State children: [ Text( '主观题', - style: TextStyle( - fontSize: 14.sp, color: Color(0xFF5C5C5C),fontWeight: FontWeight.w600), + style: TextStyle(fontSize: 14.sp, color: Color(0xFF5C5C5C), fontWeight: FontWeight.w600), ), SizedBox( width: 10.r, ), Text( '${studentInfo!.zgValidRate}%', - style: TextStyle( - fontSize: 14.sp, color: Color(0xFF6888FD),fontWeight: FontWeight.w600), + style: TextStyle(fontSize: 14.sp, color: Color(0xFF6888FD), fontWeight: FontWeight.w600), ), ], ), - SizedBox(height: 10.r,), SizedBox( - height: studentInfo!.zgDetails.length>8?300.r:studentInfo!.zgDetails.length * 40.r + 40.r, + height: 10.r, + ), + SizedBox( + height: studentInfo!.zgDetails.length > 8 ? 300.r : studentInfo!.zgDetails.length * 40.r + 40.r, child: StudentZgTable( - headList: ['题号', '用时', '学生答案','批注结果','批注'], + headList: ['题号', '用时', '学生答案', '批注结果', '批注'], bodyList: studentInfo!.zgDetails, + questionNumCall: (no) { + showAnswerHandwriting(context, jobId: widget.jobId, studentId: widget.studentId, questionNo: int.parse(no)).then((value) { + ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal([]); + }); + }, ), ) ], diff --git a/marking_app/lib/pages/homework_correction/widget/answer_handwriting.dart b/marking_app/lib/pages/homework_correction/widget/answer_handwriting.dart new file mode 100644 index 0000000..c167ebf --- /dev/null +++ b/marking_app/lib/pages/homework_correction/widget/answer_handwriting.dart @@ -0,0 +1,1036 @@ +import 'dart:async'; + +import 'package:json_annotation/json_annotation.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:functional_widget_annotation/functional_widget_annotation.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/job/job_handwriting.dart'; +import 'package:marking_app/common/model/job/test_questions_image_info.dart'; +import 'package:marking_app/pages/common/event_bus_mixin.dart'; +import 'package:marking_app/pages/homework_correction/hooks/use_cached_img_refresh.dart'; +import 'package:marking_app/utils/index.dart'; +import 'package:marking_app/utils/my_text.dart'; +import 'package:marking_app/utils/my_time_util.dart'; + +import '../../../common/model/job/gesture_recording.dart'; +import '../providers/handwriting_drawing_trajectory_provider.dart'; + +part 'answer_handwriting.g.dart'; + +/// 学生答题轨迹 +class AnswerHandwriting extends Dialog { + final int jobId; + final int studentId; + final int? pageNum; + final int? questionNo; + final Function closeCall; + const AnswerHandwriting({required this.jobId, required this.studentId, required this.closeCall, this.pageNum, this.questionNo}); + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + width: ScreenUtil().screenWidth - 60.r, + alignment: Alignment.center, + child: AnswerHandwritingMainBox( + jobId: jobId, + studentId: studentId, + pageNum: pageNum, + questionNo: questionNo, + closeCall: closeCall, + ), + ), + ); + } +} + +Future showAnswerHandwriting(BuildContext context, {required int jobId, required int studentId, int? pageNum, int? questionNo}) async { + return showDialog( + context: context, + builder: (BuildContext context) => AnswerHandwriting( + jobId: jobId, + studentId: studentId, + pageNum: pageNum, + questionNo: questionNo, + closeCall: () => Navigator.of(context).pop(), + ), + ); +} + +// 主图 +class AnswerHandwritingMainBox extends HookWidget { + const AnswerHandwritingMainBox({ + required this.jobId, + required this.studentId, + required this.closeCall, + this.pageNum, + this.questionNo, + Key? key, + }) : super(key: key); + + final int jobId; + final int studentId; + final int? pageNum; + final int? questionNo; + final Function closeCall; + + @override + Widget build(BuildContext context) { + var _useStateModel = UseMainBoxState.use(jobId, studentId, pageNum, questionNo); + + useValueChanged(_useStateModel.handwritingData.value, (_, __) { + var theData = _useStateModel.handwritingData.value; + _useStateModel.pageNum.value = theData?.pageNum; + _useStateModel.pageCount.value = theData?.pageCount ?? 0; + _useStateModel.playPause.value = false; + _useStateModel.constantFastSpeed.value = false; + Future.delayed(Duration.zero, () { + _useStateModel.handwritingKey.currentState?.ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal([]); + }); + _useStateModel.handwritingDetail.value = _useStateModel.getHandwritingDetail(theData); + }); + + useValueChanged(_useStateModel.pageNum.value, (oldVal, __) { + if (oldVal != null && oldVal != _useStateModel.pageNum.value) { + _useStateModel.questionNo = null; + _useStateModel.getData().catchError((e) => closeCall()); + } + }); + + useEffect(() { + _useStateModel.getData().catchError((e) => closeCall()); + return () {}; + }, []); + + JobHandwriting? _data = _useStateModel.handwritingData.value; + HandwritingInfo? _dataDetail = _useStateModel.handwritingDetail.value; + + if (_data == null || _dataDetail == null) return Container(); + + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Stack( + alignment: const FractionalOffset(0, 0.5), + children: [ + // 图片展示主框 + HandwritingDrawBox( + _data.paperPicture, + _dataDetail, + key: _useStateModel.handwritingKey, + ), + $PageNumberBox(_data.pageNum, _data.pageCount), + // 上一页按钮 + $PreviousNutton( + _useStateModel.pageNum.value, + () => _useStateModel.pageNum.value = _useStateModel.pageNum.value! - 1, + ), + // 下一题按钮 + $NextPageButton( + _useStateModel.pageNum.value, + _useStateModel.pageCount.value, + () => _useStateModel.pageNum.value = _useStateModel.pageNum.value! + 1, + ), + ], + ), + $BottomPlaybar(_dataDetail.timeConsuming, _dataDetail.pauseCount, _dataDetail.pauseInterval), + ], + ); + } +} + +class UseMainBoxState with CommonMixin { + final int jobId; + final int studentId; + int? questionNo; + final ValueNotifier pageNum; + final ValueNotifier pageCount; + final ValueNotifier handwritingData; + final ValueNotifier handwritingDetail; + + final ValueNotifier playPause; // 播放暂停 + final ValueNotifier constantFastSpeed; // 原速、快速 默认原速 + + GlobalKey<_HandwritingDrawBoxState> handwritingKey; + + UseMainBoxState._({ + required this.jobId, + required this.studentId, + required this.pageNum, + required this.handwritingData, + required this.questionNo, + required this.pageCount, + required this.playPause, + required this.constantFastSpeed, + required this.handwritingDetail, + required this.handwritingKey, + }); + + // 工厂构造函数 + factory UseMainBoxState.use(int jobId, int studentId, [int? pageNum, int? questionNo]) { + return UseMainBoxState._( + jobId: jobId, + studentId: studentId, + questionNo: questionNo, + pageNum: useState(pageNum), + handwritingData: useState(null), + handwritingDetail: useState(null), + pageCount: useState(0), + playPause: useState(false), + constantFastSpeed: useState(false), + handwritingKey: GlobalKey(), + ); + } + + Future getData() async { + try { + ToastUtils.showLoading(); + var _client = await getClient(); + var res = await _client.getHandwriting(jobId, studentId, questionNo, pageNum.value); + if (res?.success ?? false) { + handwritingData.value = res!.data; + if (handwritingData.value!.lattices.isEmpty) { + Future.delayed(Duration.zero, () => ToastUtils.showInfo('此页试题没有笔迹')); + } + return; + } + Future.delayed(Duration(seconds: 1), () => ToastUtils.showError(res?.message ?? '笔记数据请求失败')); + } catch (e) { + print(e); + } finally { + ToastUtils.dismiss(); + } + } + + HandwritingInfo? getHandwritingDetail(JobHandwriting? theData) { + if (theData == null) return null; + // 笔画分组 + // var lattices = Map>.fromIterable( + // theData.lattices, + // key: (key) => key.stroke, + // value: (value) { + // // return theData.lattices.where((item) => item.stroke == value.stroke).toList()..sort((a, b) => a.time.compareTo(b.time)); + // return theData.lattices.where((item) => item.stroke == value.stroke).toList(); + // }, + // ); + var lattices = Map>(); + var theLattices = theData.lattices; + for (var i = 0; i < theLattices.length; i++) { + Lattices item = theLattices[i]; + if (!lattices.containsKey(item.stroke)) lattices[item.stroke] = []; + lattices[item.stroke]!.add(item); // 添加笔画数据 + } + + List latticeKeys = lattices.keys.toList(); + int timeConsuming = 0; + if (latticeKeys.isNotEmpty) { + List? firstAction = lattices[latticeKeys[0]]; + List? lastAction = lattices[latticeKeys[latticeKeys.length - 1]]; + int firstTime = 0; + int lastTime = 0; + + if (firstAction?.isNotEmpty ?? false) { + // 第一个笔画集合 + firstTime = firstAction![0].time; + } + if (lastAction?.isNotEmpty ?? false) { + // 最后一笔画集合 + lastTime = lastAction![0].time; + } + timeConsuming = lastTime - firstTime; + } + var pauseCount = 0; // 停顿次数 + List pauseInterval = []; + for (var i = 0; i < latticeKeys.length; i++) { + var currentLattices = lattices[latticeKeys[i]]!; // 当前循环笔画集合 + var prevLattices = i == 0 ? null : lattices[latticeKeys[i - 1]]!; // 下一个笔画集合 + for (var j = 0; j < currentLattices.length; j++) { + var intervalTime = 0; + var lattice = currentLattices[j]; + + if (j != 0) { + var prevItem = currentLattices[j - 1]; + var adjacentSpacingTime = lattice.time - prevItem.time; + intervalTime = adjacentSpacingTime + prevItem.intervalTime; + if (adjacentSpacingTime > 5000) { + // 大于5秒算一次停顿 + pauseCount++; + pauseInterval.add(PauseIntervalTime(startTime: prevItem.intervalTime, endTime: intervalTime)); + } + } else { + if (i != 0 && prevLattices != null) { + var prevLatticeLastItem = prevLattices[prevLattices.length - 1]; + var adjacentSpacingTime = lattice.time - prevLatticeLastItem.time; + intervalTime = adjacentSpacingTime + prevLatticeLastItem.intervalTime; + if (adjacentSpacingTime > 5000) { + // 大于5秒算一次停顿 + pauseCount++; + pauseInterval.add(PauseIntervalTime(startTime: prevLatticeLastItem.intervalTime, endTime: intervalTime)); + } + } + } + lattice.intervalTime = intervalTime; + } + } + return HandwritingInfo(pauseCount, timeConsuming, lattices, pauseInterval); + } +} + +class HandwritingInfo { + int pauseCount; // 停顿次数 + List pauseInterval; + int timeConsuming; // 耗时(毫秒) + Map> strokeMap; // 笔画数据 + + HandwritingInfo(this.pauseCount, this.timeConsuming, this.strokeMap, this.pauseInterval); +} + +// 图片展示 +@hwidget +Widget $theCachedNetworkImage(ImageWidgetBuilder imageBuilder, {required String imageUrl}) { + UseCachedImgRefresh _useImgRefsh = UseCachedImgRefresh.use(); + + return CachedNetworkImage( + key: _useImgRefsh.imageKey.value, + fit: BoxFit.contain, + imageUrl: imageUrl, + imageBuilder: imageBuilder, + placeholder: (context, url) => Center(child: SpinKitWaveSpinner(color: Theme.of(context).primaryColor, size: 50.r)), + errorWidget: (context, url, error) { + return GestureDetector( + onTap: () => (_useImgRefsh.imageKey.value = UniqueKey()), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset('assets/images/test_paper_loading_failed.png'), + quickText('加载失败,点击重试', color: Color.fromRGBO(148, 163, 182, 1), size: 12.sp), + ], + ), + ); + }, + ); +} + +/// 上一页 +@swidget +Widget $previousNutton(BuildContext context, int? pageNum, Function call) { + if (pageNum != null && pageNum > 1) + return Positioned( + left: 3.w, + child: FloatingActionButton( + heroTag: '点击前往上一题', + tooltip: '点击前往上一题', + focusColor: Theme.of(context).primaryColor, + backgroundColor: const Color.fromRGBO(24, 32, 32, 0.1), + elevation: 6.r, + onPressed: () => easyThrottle('answer_handwriting_previous', () => call()), + child: Icon(Icons.arrow_back_ios, color: Colors.white, size: 22.sp), + ), + ); + + return SizedBox(); +} + +/// 下一页 +@swidget +Widget $nextPageButton(BuildContext context, int? pageNum, int totalNum, Function call) { + if (pageNum != null && pageNum < totalNum) + return Positioned( + right: 3.w, + child: FloatingActionButton( + heroTag: '点击前往下一题', + tooltip: '点击前往下一题', + elevation: 6.r, + backgroundColor: const Color.fromRGBO(24, 32, 32, 0.1), + onPressed: () => easyThrottle('answer_handwriting_next', () => call()), + child: Icon(Icons.arrow_forward_ios, color: Colors.white, size: 22.sp), + ), + ); + + return SizedBox(); +} + +// 笔记还原主框 +class HandwritingDrawBox extends StatefulHookConsumerWidget { + final String image; + final HandwritingInfo handwritingData; + // final double boxWidth; + // final double boxHeight; + const HandwritingDrawBox(this.image, this.handwritingData, {super.key}); + + @override + ConsumerState createState() => _HandwritingDrawBoxState(); +} + +class _HandwritingDrawBoxState extends ConsumerState with EventBusMixin { + ImageStream? imageStream; // 图片监听数据 + TestQuestionsImageInfo? imagInfoModel; // 试题图片数据 + late ImageStreamListener theImageStreamListener; + + late ValueNotifier> _vnHandWritings; + + late RemoveListener _jobHandwritingDrawingTrajectoryListener; // 批注关闭监听 + List> _packagedHandwritingDatas = []; + List _packagedHandwritingDataAll = []; + List pendingData = []; // 待执行数据 + List timers = []; + int handwritingTime = 0; + int handwritingDuration = 0; + double speed = 1; // 播放速度 + @override + void initState() { + super.initState(); + _vnHandWritings = ValueNotifier>([]); + _jobHandwritingDrawingTrajectoryListener = ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).addListener((state) { + _vnHandWritings.value = state; + }, fireImmediately: false); + eventOn(callback: (e) { + switch (e.runtimeType) { + case JobHandwritingRunTimeBus: + var _model = (e as JobHandwritingRunTimeBus); + var _runtime = _model.runTimeVal; + handwritingDuration = _model.totalVal; + handwritingTime = _runtime; + if (_runtime <= 0) { + pendingData.clear(); + } + break; + case JobHandwritingDragProgressBarBus: + var _model = (e as JobHandwritingDragProgressBarBus); + dragProgressBarInitData(_model.changeVal, _model.totalVal); + break; + case JobHandwritingPlaybarBus: + // 播放 暂停 + var _val = e as JobHandwritingPlaybarBus; + if (_val.play) { + // 播放 + toGoPlay(); + } else { + // 暂停 + toGoPause(_val.recalculate); + } + break; + case PlaybackSpeedBus: + // 播放速度 + var _model = (e as PlaybackSpeedBus); + speed = _model.speed; + dragProgressBarInitData(handwritingDuration - handwritingTime, handwritingDuration); + break; + default: + } + }); + + theImageStreamListener = ImageStreamListener((ImageInfo info, bool _) { + if (imagInfoModel != null) return; + imagInfoModel = TestQuestionsImageInfo( + // 获取图片的宽高 + boxHeight: ScreenUtil().scaleHeight, // 图片展示自适应所以 这里无法获取容器的高度 + boxWidth: ScreenUtil().screenWidth - 60.r, + url: widget.image, + height: info.image.height.toDouble(), + width: info.image.width.toDouble(), + )..calculateStartAndEndHeight(60.h); // 60.h是底部的播放栏高度 + getCalculatedSize(); + }); + } + + @override + void dispose() { + timers.forEach((e) { + if (e.isActive) e.cancel(); + }); + _jobHandwritingDrawingTrajectoryListener(); + _vnHandWritings.dispose(); + try { + imageStream?.removeListener(theImageStreamListener); + eventCancel(); + } catch (e) {} + super.dispose(); + } + + // 暂停播放 + Future toGoPause(bool recalculate) async { + timers.forEach((e) { + if (e.isActive) e.cancel(); + }); + timers = []; + // 总时间-剩余时间=已经执行时间 + if (recalculate && pendingData.isNotEmpty && handwritingTime > 0 && (handwritingDuration - handwritingTime > 0)) { + // 待执行的数据不等于空 每个数据都需要减去当前暂停已经执行的时间 + pendingData = pendingData.map((e) { + return GestureHandwritingRecording( + stroke: e.stroke, + data: e.data, + usageTime: e.usageTime, + intervalTime: e.intervalTime - (handwritingDuration - handwritingTime) * 1000, + ); + }).toList(); + } + } + + /// 拖动进度条后重新初始化数据 + /// @param startTime 起始时间 单位秒 + Future dragProgressBarInitData(int startTime, int totalDuration) async { + eventFire(model: JobHandwritingPlaybarBus(false, false)); + timers.forEach((e) { + if (e.isActive) e.cancel(); + }); + timers = []; + pendingData.clear(); + + if (startTime == 0) { + ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal([]); + pendingData.addAll(_packagedHandwritingDataAll); + } else { + // 待执行的数据不等于空 每个数据都需要减去当前暂停已经执行的时间 + startTime = startTime * 1000; // 转为毫秒 + List executeImmediately = []; // 立即执行数据 + List waitingExecution = []; // 等待执行数据 + + for (var i = 0; i < _packagedHandwritingDataAll.length; i++) { + var item = _packagedHandwritingDataAll[i]; + + if (item.intervalTime < startTime) { + // 需要直接装配到直接打印的容器 + executeImmediately.add(item); + } else { + var intervalTime = item.intervalTime - startTime; + // 需要等待的数据 + waitingExecution.add(GestureHandwritingRecording( + stroke: item.stroke, + data: item.data, + usageTime: item.usageTime, + intervalTime: intervalTime, + )); + } + } + + pendingData = waitingExecution; + ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal(executeImmediately); + } + eventFire(model: JobHandwritingPlaybarBus(true)); + } + + Future zhixinCall(GestureHandwritingRecording e) async { + if (mounted) { + List trajectorys = ref.read(jobHandwritingDrawingTrajectoryProvider)..add(e); + ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal(List.from(trajectorys)); + pendingData.remove(e); // 执行后删除容器中的当前动作 + } + } + + /// 开始播放 + Future toGoPlay() async { + try { + var executableData = _packagedHandwritingDataAll; + if (pendingData.isNotEmpty) { + // 待执行的数据没有执行完成 就继续执行待执行数据 + executableData = pendingData; + } else { + pendingData.addAll(_packagedHandwritingDataAll); + ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal([]); + } + + executableData.forEach((e) { + var ter = Timer(Duration(milliseconds: e.intervalTime ~/ speed), () => zhixinCall(e)); + timers.add(ter); + }); + } catch (e) { + print('播放报错:$e'); + } + } + + // 计算尺寸 + Future getCalculatedSize() async { + if (imagInfoModel == null) return; + var dataInfo = widget.handwritingData; + Map> mapData = dataInfo.strokeMap; + if (mapData.isNotEmpty) { + List keys = mapData.keys.toList(); + + for (var i = 0; i < keys.length; i++) { + List newTrajectoryData = mapData[keys[i]]!.map((e) { + double theX = e.x * imagInfoModel!.scale!; + double theY = e.y * imagInfoModel!.scale!; + + return GestureHandwritingRecording( + data: Offset(theX, theY), + usageTime: e.time.toInt(), + intervalTime: e.intervalTime, + stroke: e.stroke, + ); + }).toList(); + + newTrajectoryData.sort((a, b) => a.usageTime.compareTo(b.usageTime)); + + _packagedHandwritingDatas.add(newTrajectoryData); // 分组数据 + _packagedHandwritingDataAll.addAll(newTrajectoryData); // 不分组数据 + } + Future.delayed(Duration.zero, () => eventFire(model: JobHandwritingGetReadyBus())); // 通知外部可以播放笔迹 + } + } + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.center, + child: RepaintBoundary( + child: CustomPaint( + willChange: true, + isComplex: true, + foregroundPainter: HandWritingDrawingPainter(ctrl: _vnHandWritings), + // size: Size(ScreenUtil().screenWidth - 60.r, widget.boxHeight), + child: RepaintBoundary( + child: $TheCachedNetworkImage( + imageUrl: widget.image, + (context, imageProvider) { + Image imageWidget = Image(image: imageProvider, fit: BoxFit.contain); + if (imagInfoModel == null) { + imageStream?.removeListener(theImageStreamListener); + // 视图中展示图片的尺寸计算获取 + imageStream = imageWidget.image.resolve(ImageConfiguration()); + imageStream?.addListener(theImageStreamListener); + } + + return imageWidget; + }, + ), + ), + ), + ), + ); + } +} + +class HandWritingDrawingPainter extends CustomPainter { + final ValueNotifier> ctrl; + HandWritingDrawingPainter({required this.ctrl}) : super(repaint: ctrl); + //[定义画笔] + final Paint paintBrush = Paint() + //画笔颜色 + ..color = Colors.black + //画笔笔触类型 + ..strokeCap = StrokeCap.round + //是否启动抗锯齿 + ..isAntiAlias = true + //绘画风格,默认为填充 + // ..style = PaintingStyle.fill + //画笔的宽度 + ..style = PaintingStyle.stroke + ..strokeWidth = 0.5.r; + + @override + void paint(Canvas canvas, Size size) { + // canvas.drawPoints(PointMode.points, thePoints, paintBrush); + canvas.save(); + + var points = ctrl.value; + var _length = points.length; + for (int i = 0; i < _length; i++) { + GestureHandwritingRecording item = points[i]; + GestureHandwritingRecording? nextItem = i + 1 < _length ? points[i + 1] : null; + + Offset offsetData = item.data; + Offset? nextOffsetData = nextItem?.data; + if (nextOffsetData != null && item.stroke == nextItem?.stroke) { + canvas.drawLine(offsetData, nextOffsetData, paintBrush); + } + } + canvas.restore(); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + if (oldDelegate is HandWritingDrawingPainter) { + var repaint = ctrl.value.length != oldDelegate.ctrl.value.length || oldDelegate.ctrl.value != ctrl.value; + print('调用是否绘制:$repaint'); + return repaint; + } + return true; // 如果 oldDelegate 不是 MyCustomPainter 的实例,则总是重绘 + } +} + +@swidget +Widget $pageNumberBox(int pageNum, int totalNum) { + return Positioned( + top: 6.h, + right: 4.w, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h), + decoration: BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.47), + borderRadius: BorderRadius.circular(5.r), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + quickText('$pageNum', color: Colors.white, size: 11.sp, align: TextAlign.end), + quickText('/', color: Colors.white, size: 10.sp, align: TextAlign.end), + quickText('$totalNum', color: Colors.white, size: 8.sp, align: TextAlign.end), + ], + )), + ); +} + +@hwidget +Widget $bottomPlaybar(BuildContext context, int timeConsuming, int pauseCount, List pauseIntervals) { + var usePlaybar = UseBottomPlaybar.use(timeConsuming); + useValueChanged(timeConsuming, (_, __) { + usePlaybar.playTimingSuspend(); + usePlaybar.playPause.value = false; + var seds = timeConsuming ~/ 1000; + if ((timeConsuming % 1000) > 500) seds += 1; + usePlaybar.handwritingDuration.value = seds; + usePlaybar.constantFastSpeed.value = PlaybackSpeed.ORIGINAL_SPEED; + }); + + useValueChanged(usePlaybar.handwritingDuration.value, (_, __) { + usePlaybar.useTime.value = usePlaybar.handwritingDuration.value; + }); + // 播放速度 + useValueChanged(usePlaybar.constantFastSpeed.value, (_, __) { + usePlaybar.eventFire(model: PlaybackSpeedBus(usePlaybar.constantFastSpeed.value.speed)); + // 播放速度变化 + usePlaybar.playTimingSuspend(); + usePlaybar.playTimingStarts(); + }); + // 计时结束监听 + useValueChanged(usePlaybar.useTime.value, (_, __) { + var _runtime = usePlaybar.useTime.value; + if (_runtime <= 0 || usePlaybar.handwritingDuration.value == _runtime) { + Future.delayed(Duration.zero, () => (usePlaybar.playPause.value = false)); // 初始化播放按钮 + } + usePlaybar.eventFire(model: JobHandwritingRunTimeBus(_runtime, usePlaybar.handwritingDuration.value)); + }); + + useEffect(() { + usePlaybar.eventOn(callback: (e) { + switch (e.runtimeType) { + case JobHandwritingPlaybarBus: + // 出发播放暂停 + var _val = e as JobHandwritingPlaybarBus; + if (_val.play) { + // 开始播放 + usePlaybar.playTimingStarts(); + if (!usePlaybar.playPause.value) Future.delayed(Duration.zero, () => usePlaybar.playPause.value = true); + } else { + // 暂停播放 + usePlaybar.playTimingSuspend(); + if (usePlaybar.playPause.value) Future.delayed(Duration.zero, () => usePlaybar.playPause.value = false); + } + break; + case JobHandwritingGetReadyBus: + // 作业笔迹已经计算好坐标 可以开始播放 + Future.delayed(Duration.zero, () => (usePlaybar.handWritingReady.value = true)); + break; + default: + } + }); + + return () { + try { + usePlaybar.eventCancel(); + usePlaybar.timer.value?.cancel(); + } catch (e) { + print(e); + } + }; + }, []); + + return Container( + height: 60.h, + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + alignment: Alignment.center, + color: Color.fromRGBO(0, 0, 0, 0.4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (usePlaybar.handWritingReady.value) + InkWell( + onTap: () => easyThrottle('job_handwriting_play_pause', () { + if (usePlaybar.handwritingDuration.value == 0) return ToastUtils.showInfo('没有笔迹'); + + usePlaybar.playPause.value = !usePlaybar.playPause.value; + usePlaybar.eventFire(model: JobHandwritingPlaybarBus(usePlaybar.playPause.value)); + }), + child: Icon( + !usePlaybar.playPause.value ? Icons.play_circle_outline : Icons.pause_circle_outline, + color: Colors.white, + size: 28.r, + ), + ) + else + SpinKitPouringHourGlassRefined(size: 40.sp, color: Colors.white), + SizedBox(width: 6.w), + Expanded( + child: LayoutBuilder(builder: (context, constraints) { + final double containerWidth = constraints.maxWidth; // 展示区域总宽度 + var unitScale = containerWidth / timeConsuming; // 单位刻度 + var pauseIntervalsLength = pauseIntervals.length; + + List pauseTickMarks = pauseIntervals.asMap().keys.map((e) { + bool isLast = e == pauseIntervalsLength - 1; + bool isFirst = e == 0; + var item = pauseIntervals[e]; + return Positioned( + top: 0, + left: unitScale * item.startTime, + child: Container( + width: unitScale * (item.apart ?? 0), + height: 8.h, + decoration: BoxDecoration( + color: Color.fromRGBO(202, 201, 201, 1), + borderRadius: isFirst + ? BorderRadius.only(topLeft: Radius.circular(8.r), bottomLeft: Radius.circular(10.r)) + : (isLast ? BorderRadius.only(topRight: Radius.circular(8.r), bottomRight: Radius.circular(10.r)) : null), + ), + ), + ); + }).toList(); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + children: [ + Container( + height: 8.h, + width: containerWidth, + decoration: BoxDecoration( + // color: Color.fromRGBO(146, 146, 146, 1), + color: Colors.white, + borderRadius: BorderRadius.circular(50.r), + ), + ), + ...pauseTickMarks, + Container( + height: 8.h, + width: containerWidth, + // color: Theme.of(context).primaryColor, + child: SliderTheme( + data: SliderTheme.of(context).copyWith( + trackHeight: 8.h, // 轨道高度 + trackShape: RoundedRectSliderTrackShape(), // 轨道形状,可以自定义 + activeTrackColor: Theme.of(context).primaryColor, // 激活的轨道颜色 + inactiveTrackColor: Colors.transparent, // 未激活的轨道颜色 + thumbShape: RoundSliderThumbShape(enabledThumbRadius: 0, disabledThumbRadius: 0), + thumbColor: Colors.white, // 滑块颜色 + overlayShape: RoundSliderOverlayShape(overlayRadius: 0), + overlayColor: Colors.black54, // 滑块外圈颜色 + // valueIndicatorShape: PaddleSliderValueIndicatorShape(), // 标签形状,可以自定义 + ), + child: Slider( + value: (usePlaybar.handwritingDuration.value - usePlaybar.useTime.value).toDouble(), + min: 0.0, + max: usePlaybar.handwritingDuration.value.toDouble(), + inactiveColor: Colors.transparent, + onChangeEnd: (value) { + if (!usePlaybar.handWritingReady.value) return; + usePlaybar.playTimingSuspend(); // 暂停计时器得暂停 + usePlaybar.eventFire(model: JobHandwritingDragProgressBarBus(value.toInt(), usePlaybar.handwritingDuration.value)); + usePlaybar.useTime.value = usePlaybar.handwritingDuration.value - value.toInt(); + }, + onChanged: (double value) { + if (!usePlaybar.handWritingReady.value) return; + usePlaybar.useTime.value = usePlaybar.handwritingDuration.value - value.toInt(); + }, + ), + ), + ), + ], + ), + SizedBox(height: 4.h), + SizedBox( + width: containerWidth, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + quickText('累计停顿:$pauseCount次', color: Colors.white, size: 7.sp), + quickText(convertSeconds(usePlaybar.useTime.value)?.toString() ?? '', color: Colors.white, size: 7.sp), + ], + ), + ) + ], + ); + }), + ), + SizedBox(width: 16.w), + InkWell( + onTap: () => easyThrottle('job_handwriting_speed', () { + var theIndex = PlaybackSpeed.values.indexOf(usePlaybar.constantFastSpeed.value); + if (theIndex == PlaybackSpeed.values.length - 1) { + theIndex = -1; + } + usePlaybar.constantFastSpeed.value = PlaybackSpeed.values[theIndex + 1]; + }, duration: Duration(milliseconds: 500)), + child: Container( + // alignment: Alignment., + padding: EdgeInsets.symmetric(horizontal: 3.w, vertical: 1.5.h), + decoration: BoxDecoration(color: Color.fromRGBO(182, 197, 250, 1), borderRadius: BorderRadius.circular(4.r)), + child: quickText( + '${usePlaybar.constantFastSpeed.value.name}', + color: Color.fromRGBO(79, 114, 244, 1), + size: 8.sp, + align: TextAlign.center, + ), + ), + ), + ], + ), + ); +} + +class SysjTime extends StatefulWidget { + const SysjTime({super.key}); + + @override + State createState() => _SysjTimeState(); +} + +class _SysjTimeState extends State with EventBusMixin { + int useTime = 0; + @override + void initState() { + super.initState(); + eventOn(callback: (JobHandwritingRunTimeBus e) { + useTime = e.runTimeVal; + toUpState(setState, () {}, mounted); + }); + } + + @override + void dispose() { + eventCancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return quickText(convertSeconds(useTime)?.toString() ?? '', color: Colors.white, size: 7.sp); + } +} + +class UseBottomPlaybar with EventBusMixin { + final ValueNotifier handwritingDuration; // 笔迹总时长 + final ValueNotifier playPause; // 播放暂停 + final ValueNotifier constantFastSpeed; // 原速、快速 默认原速 + final ValueNotifier handWritingReady; + + final ValueNotifier useTime; // 耗时 单位:(秒) + + final ValueNotifier timer; + + UseBottomPlaybar._( + {required this.handWritingReady, + required this.handwritingDuration, + required this.playPause, + required this.constantFastSpeed, + required this.useTime, + required this.timer}); + + // 工厂构造函数 + factory UseBottomPlaybar.use(int milliseconds) { + int handwritingDuration = milliseconds ~/ 1000; + if ((milliseconds % 1000) > 500) handwritingDuration += 1; + return UseBottomPlaybar._( + playPause: useState(false), + constantFastSpeed: useState(PlaybackSpeed.ORIGINAL_SPEED), + useTime: useState(handwritingDuration), + timer: useState(null), + handwritingDuration: useState(handwritingDuration), + handWritingReady: useState(false), + ); + } + + /// 开始计时 + void playTimingStarts() { + if (useTime.value > 0) { + timer.value?.cancel(); + + timer.value = Timer.periodic(Duration(milliseconds: 1000 ~/ constantFastSpeed.value.speed), (theTime) { + useTime.value -= 1; + if (useTime.value < 0) { + theTime.cancel(); + timer.value?.cancel(); + timer.value = null; + useTime.value = handwritingDuration.value; + } + }); + } + } + + /// 暂停 + void playTimingSuspend() { + timer.value?.cancel(); + timer.value = null; + } +} + +// 播放按钮 +class JobHandwritingPlaybarBus { + bool play; + bool recalculate; + JobHandwritingPlaybarBus(this.play, [this.recalculate = true]); +} + +// 笔迹是否已经准备好(笔迹计算好坐标后通知通知栏可以开始播放) +class JobHandwritingGetReadyBus { + JobHandwritingGetReadyBus(); +} + +// 笔记运行时间 +class JobHandwritingRunTimeBus { + int runTimeVal; + int totalVal; + JobHandwritingRunTimeBus(this.runTimeVal, this.totalVal); +} + +// 拖动进度条 +class JobHandwritingDragProgressBarBus { + int changeVal; + int totalVal; + JobHandwritingDragProgressBarBus(this.changeVal, this.totalVal); +} + +// 播放速度 (原速播放/快速播放) +class PlaybackSpeedBus { + double speed; + PlaybackSpeedBus(this.speed); +} + +// 播放倍速 +enum PlaybackSpeed { + ORIGINAL_SPEED(name: '原速播放', speed: 1), + ONE_POINT_FIVE_SPEED(name: '1.5x播放', speed: 1.5), + DOUBLE_SPEED(name: '2.0x播放', speed: 2), + TRIPLE_SPEED(name: '3.0x播放', speed: 3), + FOUR_SPEED(name: '4.0x播放', speed: 4), + FIVE_SPEED(name: '5.0x播放', speed: 5), + SIX_SPEED(name: '6.0x播放', speed: 6); + + const PlaybackSpeed({required this.name, required this.speed}); + final double speed; + final String name; +} + +@JsonSerializable() +class PauseIntervalTime extends Object { + int? apart; + int startTime; + int endTime; + PauseIntervalTime({required this.startTime, required this.endTime}) { + apart = endTime - startTime; + } + + factory PauseIntervalTime.fromJson(Map srcJson) => _$PauseIntervalTimeFromJson(srcJson); + + Map toJson() => _$PauseIntervalTimeToJson(this); +} diff --git a/marking_app/lib/pages/homework_correction/widget/answer_trajectory_job.dart b/marking_app/lib/pages/homework_correction/widget/answer_trajectory_job.dart new file mode 100644 index 0000000..aa19102 --- /dev/null +++ b/marking_app/lib/pages/homework_correction/widget/answer_trajectory_job.dart @@ -0,0 +1,286 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:marking_app/common/model/job/job_task_item.dart'; +import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; +import 'package:marking_app/utils/index.dart'; +import 'package:marking_app/utils/my_text.dart'; + +import '../../../routes/RouterManager.dart'; + +class AnswerTrajectoryJob extends StatelessWidget { + final List jobList; + + const AnswerTrajectoryJob(this.jobList, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return jobList != null && jobList.length > 0 + ? isPad() + ? GridView( + shrinkWrap: true, + physics: ClampingScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, //横轴三个子widget + mainAxisSpacing: 10.h, + crossAxisSpacing: 6.w, + childAspectRatio: 2.4 //宽高比为1时,子widget + ), + children: List.generate(jobList.length, (index) { + JobTaskItem item = jobList[index]; + return InkWell( + onTap: (){ + RouterManager.router.navigateTo(context, + '${RouterManager.answerTrajectoryJobDetailPath}?&jobId=${item.id}&jobName=${Uri.encodeComponent(item.title)}&genderName=${Uri.encodeComponent(item.genderName)}', + transition: getTransition()); + }, + child: Container( + padding: EdgeInsets.only(top: 10.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6.r), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: const Color.fromRGBO(210, 216, 241, 1), + offset: Offset.zero, //阴影y轴偏移量 + blurRadius: 5.8, //阴影模糊程度 + spreadRadius: 0, //阴影扩散程度 + ) + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 顶部任务名称 + Padding( + padding: EdgeInsets.symmetric(horizontal: 6.w), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 32.w, + height: 18.h, + alignment: Alignment.center, + padding: EdgeInsets.only(left: 2.w), + decoration: BoxDecoration( + color: const Color.fromRGBO(104, 136, 253, 1), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(14.r), + topRight: Radius.circular(3.r), + bottomLeft: Radius.circular(4.r), + bottomRight: Radius.circular(4.r), + ), + ), + margin: EdgeInsets.only(right: 4.w), + child: quickText(item.markingTypeEnum.name, + color: Colors.white, size: 10.sp), + ), + Expanded( + child: quickText(item.title, + size: 12.sp, + color: Color.fromRGBO(70, 70, 70, 1), + maxLines: 2), + ), + SizedBox( + width: 5.r, + ), + Container( + padding: EdgeInsets.symmetric( + vertical: 1.r, horizontal: 5.r), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.r), + border: Border.all( + width: 1.r, color: Color(0xFF4CC793)), + ), + child: Center( + child: Text( + item.subjectName, + style: TextStyle( + fontSize: 8.r, + color: Color(0xFF4CC793)), + ), + ), + ) + ], + ), + ), +Padding( + padding: EdgeInsets.symmetric(horizontal: 6.r), + child: Text('时间:${item.createTime.substring(0,10)}',style: TextStyle(fontSize: 10.sp),), +), + Container( + padding: EdgeInsets.symmetric(vertical: 6.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(6.r), + bottomRight: Radius.circular(6.r)), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: const Color.fromRGBO(0, 0, 0, 0.15), + offset: Offset(0, -0.0001), //阴影y轴偏移量 + blurRadius: 4, //阴影模糊程度 + spreadRadius: 0, //阴影扩散程度 + ) + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + alignment: Alignment.center, + child: quickText('详情', + color: Color(0xFFFFA115), size: 10.sp), + ), + Image.asset( + 'assets/images/icon_back_orange.png', + width: 8.r, + height: 8.r, + ), + ]), + ), + ], + ), + ), + ); + }), + ) + : ListView.builder( + shrinkWrap: true, + physics: ClampingScrollPhysics(), + itemBuilder: (context, index) { + JobTaskItem item = jobList[index]; + return InkWell( + onTap: () { + RouterManager.router.navigateTo(context, + '${RouterManager.answerTrajectoryJobDetailPath}?&jobId=${item.id}&jobName=${Uri.encodeComponent(item.title)}&genderName=${Uri.encodeComponent(item.genderName)}', + transition: getTransition()); + }, + child: Container( + margin: EdgeInsets.symmetric(vertical: 10.r), + padding: EdgeInsets.only(top: 10.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6.r), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: const Color.fromRGBO(210, 216, 241, 1), + offset: Offset.zero, //阴影y轴偏移量 + blurRadius: 5.8, //阴影模糊程度 + spreadRadius: 0, //阴影扩散程度 + ) + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 顶部任务名称 + Padding( + padding: EdgeInsets.symmetric(vertical:14.r,horizontal: 14.r), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 32.w, + height: 18.h, + alignment: Alignment.center, + padding: EdgeInsets.only(left: 2.w), + decoration: BoxDecoration( + color: + const Color.fromRGBO(104, 136, 253, 1), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(14.r), + topRight: Radius.circular(3.r), + bottomLeft: Radius.circular(4.r), + bottomRight: Radius.circular(4.r), + ), + ), + margin: EdgeInsets.only(right: 4.w), + child: quickText(item.markingTypeEnum.name, + color: Colors.white, size: 10.sp), + ), + Expanded( + child: quickText(item.title, + size: 14.sp, + color: Color.fromRGBO(70, 70, 70, 1), + maxLines: 2), + ), + SizedBox( + width: 5.r, + ), + Container( + padding: EdgeInsets.symmetric( + vertical: 1.r, horizontal: 5.r), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.r), + border: Border.all( + width: 1.r, color: Color(0xFF4CC793)), + ), + child: Center( + child: Text( + item.subjectName, + style: TextStyle( + fontSize: 12.r, + color: Color(0xFF4CC793)), + ), + ), + ) + ], + ), + ), + SizedBox( + height: 5.r, + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 14.r), + child: Text('时间:${item.createTime.substring(0,10)}',style: TextStyle(fontSize: 10.sp),), + ), + SizedBox( + height: 10.r, + ), + Container(height: 1.r, + color: Color(0xFFF5F5F5),), + Container( + padding: EdgeInsets.symmetric(vertical: 10.r), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(6.r), + bottomRight: Radius.circular(6.r)), + color: Colors.white, + /*boxShadow: [ + BoxShadow( + color: const Color.fromRGBO(0, 0, 0, 0.15), + offset: Offset(0, -0.0001), //阴影y轴偏移量 + blurRadius: 4, //阴影模糊程度 + spreadRadius: 0, //阴影扩散程度 + ) + ],*/ + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + alignment: Alignment.center, + child: quickText('详情', + color: Color(0xFFFFA115), size: 10.sp), + ), + Image.asset( + 'assets/images/icon_back_orange.png', + width: 8.r, + height: 8.r, + ), + ]), + ), + ], + ), + ), + ); + }, + itemCount: jobList.length, + ) + : MyEmptyWidget(); + } +} diff --git a/marking_app/lib/pages/homework_correction/widget/report_table.dart b/marking_app/lib/pages/homework_correction/widget/report_table.dart index cab74d0..840893c 100644 --- a/marking_app/lib/pages/homework_correction/widget/report_table.dart +++ b/marking_app/lib/pages/homework_correction/widget/report_table.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:marking_app/common/model/job/job_report_model.dart'; +import 'package:marking_app/pages/homework_correction/components/imgDialog.dart'; import 'package:marking_app/routes/RouterManager.dart'; import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; import 'package:marking_app/utils/index.dart'; @@ -359,7 +360,7 @@ class _ReportTableState extends State { onTap: () { if (item.questionPicture == null) return ToastUtils.showInfo('当前试题没有原题'); - Navigator.push( + /* Navigator.push( context, MaterialPageRoute(builder: (_) { return Scaffold( @@ -369,7 +370,8 @@ class _ReportTableState extends State { NetworkImage(item.questionPicture!)), ); }), - ); + );*/ + ImageDialog.showImgDialog(context,item.questionPicture!); }, child: Text('原题', style: TextStyle( diff --git a/marking_app/lib/pages/homework_correction/widget/student_group_list.dart b/marking_app/lib/pages/homework_correction/widget/student_group_list.dart index 10f4fec..4af8df9 100644 --- a/marking_app/lib/pages/homework_correction/widget/student_group_list.dart +++ b/marking_app/lib/pages/homework_correction/widget/student_group_list.dart @@ -7,143 +7,155 @@ import 'package:marking_app/utils/index.dart'; class StudentGroupList extends StatelessWidget { final List studentGroups; final Function goNextPage; - const StudentGroupList(this.studentGroups,this.goNextPage,{Key? key}) : super(key: key); + final Widget? rightBtn; + + const StudentGroupList(this.studentGroups, this.goNextPage, + {Key? key,this.rightBtn}) + : super(key: key); @override Widget build(BuildContext context) { return studentGroups != null && studentGroups.length > 0 - ? isPad()?GridView( - shrinkWrap: true, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 10.r, - crossAxisSpacing: 10.r, - childAspectRatio: 556 / 112, - ), - children: List.generate(studentGroups.length, (index) { - JobStudentGroups item = studentGroups[index]; - String classNames = item.classNames.join(" "); - return InkWell( - onTap: (){ - goNextPage(item.groupId,item.groupName); - // RouterManager.router.navigateTo(context, '${RouterManager.jobPriorityReviewSetPath}?&groupId=${item.groupId}',transition: getTransition()); - }, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 10.r), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10.r)), - color: Colors.white, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(right: 8.r), - child: Text( - item.groupName, - style: TextStyle( - fontSize: 10.sp, color: Color(0xFF6888FD)), - ), + ? isPad() + ? GridView( + shrinkWrap: true, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 10.r, + crossAxisSpacing: 10.r, + childAspectRatio: 556 / 112, ), - Expanded( - child: Text( - classNames, - style: TextStyle( - fontSize: 10.sp, - color: Color(0xFF999999), - overflow: TextOverflow.ellipsis, + children: List.generate(studentGroups.length, (index) { + JobStudentGroups item = studentGroups[index]; + String classNames = item.classNames.join(" "); + return InkWell( + onTap: () { + goNextPage(item.groupId, item.groupName); + // RouterManager.router.navigateTo(context, '${RouterManager.jobPriorityReviewSetPath}?&groupId=${item.groupId}',transition: getTransition()); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10.r), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10.r)), + color: Colors.white, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(right: 8.r), + child: Text( + item.groupName, + style: TextStyle( + fontSize: 10.sp, color: Color(0xFF6888FD)), + ), + ), + Expanded( + child: Text( + classNames, + style: TextStyle( + fontSize: 10.sp, + color: Color(0xFF999999), + overflow: TextOverflow.ellipsis, + ), + textAlign: TextAlign.end, + ), + ), + rightBtn != null + ? rightBtn! + : Container( + margin: EdgeInsets.only(left: 5.r), + height: 20.r, + width: 55.r, + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(20.r)), + color: Color(0xFF6888FD), + ), + child: Center( + child: Text( + '详情', + style: TextStyle( + fontSize: 10.sp, color: Colors.white), + ), + ), + ) + ], + ), ), - textAlign: TextAlign.end, - ), - ), - Container( - margin: EdgeInsets.only(left: 5.r), - height: 20.r, - width: 55.r, - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(20.r)), - color: Color(0xFF6888FD), - ), - child: Center( - child: Text( - '详情', - style: TextStyle( - fontSize: 10.sp, color: Colors.white), - ), - ), - ) - ], - ), - ), - ); - }), - ):ListView.builder( - shrinkWrap: true, - itemBuilder: (context,index){ - JobStudentGroups item = studentGroups[index]; - String classNames = item.classNames.join(" "); - return Container( - padding: EdgeInsets.symmetric(vertical:15.r,horizontal: 10.r), - margin: EdgeInsets.only(bottom: 10.r), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10.r)), - color: Colors.white, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(right: 8.r), - child: Text( - item.groupName, - style: TextStyle( - fontSize: 14.sp, color: Color(0xFF6888FD)), - ), - ), - Expanded( - child: Text( - classNames, - style: TextStyle( - fontSize: 12.sp, - color: Color(0xFF999999), - overflow: TextOverflow.ellipsis, - ), - textAlign: TextAlign.end, - ), - ), - InkWell( - onTap: (){ - goNextPage(item.groupId,item.groupName); - // RouterManager.router.navigateTo(context, '${RouterManager.jobPriorityReviewSetPath}?&groupId=${item.groupId}',transition: getTransition()); - }, - child: Container( - margin: EdgeInsets.only(left: 5.r), - height: 24.r, - width: 55.r, - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(20.r)), - color: Color(0xFF6888FD), - ), - child: Center( - child: Text( - '详情', - style: TextStyle( - fontSize: 10.sp, color: Colors.white), - ), - ), - ), + ); + }), + ) + : ListView.builder( + shrinkWrap: true, + itemBuilder: (context, index) { + JobStudentGroups item = studentGroups[index]; + String classNames = item.classNames.join(" "); + return InkWell( + onTap: () { + goNextPage(item.groupId, item.groupName); + // RouterManager.router.navigateTo(context, '${RouterManager.jobPriorityReviewSetPath}?&groupId=${item.groupId}',transition: getTransition()); + }, + child: Container( + padding: + EdgeInsets.symmetric(vertical: 15.r, horizontal: 10.r), + margin: EdgeInsets.only(bottom: 10.r), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10.r)), + color: Colors.white, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(right: 8.r), + child: Text( + item.groupName, + style: TextStyle( + fontSize: 14.sp, color: Color(0xFF6888FD)), + ), + ), + Expanded( + child: Text( + classNames, + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF999999), + overflow: TextOverflow.ellipsis, + ), + textAlign: TextAlign.end, + ), + ), + rightBtn != null + ? rightBtn! + : Container( + margin: EdgeInsets.only(left: 5.r), + height: 24.r, + width: 55.r, + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(20.r)), + color: Color(0xFF6888FD), + ), + child: Center( + child: Text( + '详情', + style: TextStyle( + fontSize: 10.sp, color: Colors.white), + ), + ), + ) + ], + ), + ), + ); + }, + itemCount: studentGroups.length, ) - ], - ), - ); - }, - itemCount: studentGroups.length, - ) : Padding( - padding: EdgeInsets.only(top: MediaQuery.of(context).size.height/2 - 200.r), - child: MyEmptyWidget(), - ); + padding: EdgeInsets.only( + top: MediaQuery.of(context).size.height / 2 - 200.r), + child: MyEmptyWidget(), + ); } } diff --git a/marking_app/lib/pages/homework_correction/widget/student_kg_table.dart b/marking_app/lib/pages/homework_correction/widget/student_kg_table.dart index 7b622af..126455f 100644 --- a/marking_app/lib/pages/homework_correction/widget/student_kg_table.dart +++ b/marking_app/lib/pages/homework_correction/widget/student_kg_table.dart @@ -8,6 +8,7 @@ class StudentKgTable extends StatefulWidget { final List bodyList; final int? fixedRows; final int? fixedCols; + final Function(String)? questionNumCall; const StudentKgTable({ Key? key, @@ -15,6 +16,7 @@ class StudentKgTable extends StatefulWidget { required this.bodyList, this.fixedCols = 0, this.fixedRows = 0, + this.questionNumCall, }) : super(key: key); @override @@ -29,34 +31,51 @@ class _StudentKgTableState extends State { String sortString(String str) { return String.fromCharCodes(str.codeUnits.toList()..sort()); } + DataRow _getRow(int index, [Color? color]) { assert(index >= 0); KgDetails item = widget.bodyList[index]; return DataRow2.byIndex( index: index, - color: color != null ? MaterialStateProperty.all(color): null, + color: color != null ? MaterialStateProperty.all(color) : null, cells: [ - DataCell(Center( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 5.r), - child: Text(item.questionNo, - style: TextStyle(fontSize: 12.sp, color: Color(0xFF6888FD))), + DataCell( + InkWell( + onTap: () { + if (widget.questionNumCall != null) { + widget.questionNumCall!(item.questionNo); + } + }, + child: Center( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 5.r), + child: Text(item.questionNo, style: TextStyle(fontSize: 12.sp, color: Color(0xFF6888FD))), + ), + ), ), - )), + ), DataCell(Center( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 5.r), - child: Text(item.studentAnswer == null?'未作答':item.studentAnswer!, - style: TextStyle(fontSize: 12.sp, color: item.studentAnswer == null?Color(0xFF525252): - item.state == 2?Color(0xFF4CC793): - item.state == 1?Color(0xFFFF7474):item.state == 0?Color(0xFFD3D3D3):Colors.white), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 5.r), + child: Text( + item.studentAnswer == null ? '未作答' : item.studentAnswer!, + style: TextStyle( + fontSize: 12.sp, + color: item.studentAnswer == null + ? Color(0xFF525252) + : item.state == 2 + ? Color(0xFF4CC793) + : item.state == 1 + ? Color(0xFFFF7474) + : item.state == 0 + ? Color(0xFFD3D3D3) + : Colors.white), ), ))), DataCell(Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 5.r), - child: Text(item.questionAnswer == null ?'无':item.questionAnswer!, - style: TextStyle(fontSize: 12.sp, color: Color(0xFF525252))), + child: Text(item.questionAnswer == null ? '无' : item.questionAnswer!, style: TextStyle(fontSize: 12.sp, color: Color(0xFF525252))), ), )), ], @@ -74,14 +93,10 @@ class _StudentKgTableState extends State { dataRowHeight: 40.r, headingRowHeight: 40.r, border: TableBorder( - horizontalInside: BorderSide( - width: 1, color: Colors.white, style: BorderStyle.solid), - bottom: BorderSide( - width: 1, color: Colors.white, style: BorderStyle.solid), - verticalInside: BorderSide( - width: 1, color: Colors.white, style: BorderStyle.solid)), - headingRowColor: MaterialStateProperty.resolveWith((states) => - widget.fixedCols! > 0 ? Colors.white : Colors.transparent), + horizontalInside: BorderSide(width: 1, color: Colors.white, style: BorderStyle.solid), + bottom: BorderSide(width: 1, color: Colors.white, style: BorderStyle.solid), + verticalInside: BorderSide(width: 1, color: Colors.white, style: BorderStyle.solid)), + headingRowColor: MaterialStateProperty.resolveWith((states) => widget.fixedCols! > 0 ? Colors.white : Colors.transparent), headingRowDecoration: BoxDecoration(color: Color(0xFFE6E6E6)), fixedColumnsColor: Color(0xFFE6E6E6), fixedCornerColor: Colors.grey[400], @@ -95,14 +110,12 @@ class _StudentKgTableState extends State { var item = widget.headList[index]; return DataColumn2( label: Center( - child: Text(item, - style: TextStyle(fontSize: 12.sp, color: Color(0xFF505767))), + child: Text(item, style: TextStyle(fontSize: 12.sp, color: Color(0xFF505767))), ), // size: ColumnSize.S, - fixedWidth: (MediaQuery.of(context).size.width - 20.r - 28.r)/3, + fixedWidth: (MediaQuery.of(context).size.width - 20.r - 28.r) / 3, ); }), - rows: List.generate(widget.bodyList.length, - (index) => _getRow(index, Color(0xFFF5F5F5)))); + rows: List.generate(widget.bodyList.length, (index) => _getRow(index, Color(0xFFF5F5F5)))); } } diff --git a/marking_app/lib/pages/homework_correction/widget/student_zg_table.dart b/marking_app/lib/pages/homework_correction/widget/student_zg_table.dart index 8156fba..232284d 100644 --- a/marking_app/lib/pages/homework_correction/widget/student_zg_table.dart +++ b/marking_app/lib/pages/homework_correction/widget/student_zg_table.dart @@ -2,6 +2,8 @@ import 'package:data_table_2/data_table_2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:marking_app/common/model/job/job_data_report.dart'; +import 'package:marking_app/pages/homework_correction/components/imgDialog.dart'; +import 'package:marking_app/pages/homework_correction/widget/answer_handwriting.dart'; import 'package:marking_app/utils/common_utils.dart'; import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; import 'package:photo_view/photo_view.dart'; @@ -11,6 +13,7 @@ class StudentZgTable extends StatefulWidget { final List bodyList; final int? fixedRows; final int? fixedCols; + final Function(String)? questionNumCall; const StudentZgTable({ Key? key, @@ -18,6 +21,7 @@ class StudentZgTable extends StatefulWidget { required this.bodyList, this.fixedCols = 0, this.fixedRows = 0, + this.questionNumCall, }) : super(key: key); @override @@ -28,23 +32,23 @@ class _StudentZgTableState extends State { final ScrollController _controller = ScrollController(); int? _sortColumnIndex; bool _sortAscending = true; - - void showImgDialog(BuildContext context,String imgUrl){ + + /*void showImgDialog(BuildContext context,String imgUrl){ Navigator.push( context, MaterialPageRoute(builder: (_) { return Scaffold( appBar: AppBar(), body: SizedBox( - /* width: MediaQuery.of(context).size.width * 0.6, - height: MediaQuery.of(context).size.height * 0.6,*/ + */ /* width: MediaQuery.of(context).size.width * 0.6, + height: MediaQuery.of(context).size.height * 0.6,*/ /* child: PhotoView( imageProvider: NetworkImage(imgUrl)), ), ); }), - ); + );*/ /* showDialog(context: context, builder: (BuildContext context){ return AlertDialog( // insetPadding: EdgeInsets.symmetric(vertical: 20.r,horizontal: 20.r), @@ -62,64 +66,75 @@ class _StudentZgTableState extends State { ), ); });*/ - } + // } DataRow _getRow(int index, [Color? color]) { assert(index >= 0); KgDetails item = widget.bodyList[index]; return DataRow2.byIndex( index: index, - color: color != null ? MaterialStateProperty.all(color): null, + color: color != null ? MaterialStateProperty.all(color) : null, cells: [ - DataCell(Center( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 5.r), - child: Text(item.questionNo, - style: TextStyle(fontSize: 12.sp, color: Color(0xFF6888FD))), - ), - )), - DataCell(Center( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 5.r), - child: Text(CommonUtils.second2HMS(item.useTime!), - style: TextStyle(fontSize: 12.sp, color: Color(0xFF525252))), - ), - )), DataCell(InkWell( - onTap: (){ - if(item.state != 0){ - showImgDialog(context,item.studentAnswer!); - } - + onTap: () { + if (widget.questionNumCall != null) { + widget.questionNumCall!(item.questionNo); + } }, child: Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 5.r), - child: Text(item.state!=0 ?'查看':'--', - style: TextStyle(fontSize: 12.sp, color: Color(0xFF3661FE))), + child: Text(item.questionNo, style: TextStyle(fontSize: 12.sp, color: Color(0xFF6888FD))), ), ), )), DataCell(Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 5.r), - child: item.state == 2? - Image.asset('assets/images/job_personal_correct_icon.png',width: 18.r,height: 18.r,):item.state == 1? - Image.asset('assets/images/job_personal_error_icon.png',width: 10.r,height: 10.r,):Text('', style: TextStyle(fontSize: 12.sp, color: Color(0xFF525252))), + child: Text(CommonUtils.second2HMS(item.useTime!), style: TextStyle(fontSize: 12.sp, color: Color(0xFF525252))), ), )), DataCell(InkWell( - onTap: (){ - if(item.state==1 || item.state==2){ - showImgDialog(context,item.annotateAnswers!); + onTap: () { + if (item.state != 0) { + ImageDialog.showImgDialog(context, item.studentAnswer!); } - }, child: Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 5.r), - child: Text(item.state==1 || item.state==2?'查看':'--', - style: TextStyle(fontSize: 12.sp, color: Color(0xFF3661FE))), + child: Text(item.state != 0 ? '查看' : '--', style: TextStyle(fontSize: 12.sp, color: Color(0xFF3661FE))), + ), + ), + )), + DataCell(Center( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 5.r), + child: item.state == 2 + ? Image.asset( + 'assets/images/job_personal_correct_icon.png', + width: 18.r, + height: 18.r, + ) + : item.state == 1 + ? Image.asset( + 'assets/images/job_personal_error_icon.png', + width: 10.r, + height: 10.r, + ) + : Text('', style: TextStyle(fontSize: 12.sp, color: Color(0xFF525252))), + ), + )), + DataCell(InkWell( + onTap: () { + if (item.state == 1 || item.state == 2) { + ImageDialog.showImgDialog(context, item.annotateAnswers!); + } + }, + child: Center( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 5.r), + child: Text(item.state == 1 || item.state == 2 ? '查看' : '--', style: TextStyle(fontSize: 12.sp, color: Color(0xFF3661FE))), ), ), )), @@ -138,14 +153,10 @@ class _StudentZgTableState extends State { headingRowHeight: 40.r, dataRowHeight: 40.r, border: TableBorder( - horizontalInside: BorderSide( - width: 1, color: Colors.white, style: BorderStyle.solid), - bottom: BorderSide( - width: 1, color: Colors.white, style: BorderStyle.solid), - verticalInside: BorderSide( - width: 1, color: Colors.white, style: BorderStyle.solid)), - headingRowColor: MaterialStateProperty.resolveWith((states) => - widget.fixedCols! > 0 ? Colors.white : Colors.transparent), + horizontalInside: BorderSide(width: 1, color: Colors.white, style: BorderStyle.solid), + bottom: BorderSide(width: 1, color: Colors.white, style: BorderStyle.solid), + verticalInside: BorderSide(width: 1, color: Colors.white, style: BorderStyle.solid)), + headingRowColor: MaterialStateProperty.resolveWith((states) => widget.fixedCols! > 0 ? Colors.white : Colors.transparent), headingRowDecoration: BoxDecoration(color: Color(0xFFE6E6E6)), fixedColumnsColor: Color(0xFFE6E6E6), fixedCornerColor: Colors.grey[400], @@ -159,14 +170,12 @@ class _StudentZgTableState extends State { var item = widget.headList[index]; return DataColumn2( label: Center( - child: Text(item, - style: TextStyle(fontSize: 12.sp, color: Color(0xFF505767))), + child: Text(item, style: TextStyle(fontSize: 12.sp, color: Color(0xFF505767))), ), // size: ColumnSize.S, - fixedWidth: (MediaQuery.of(context).size.width - 20.r - 28.r)/5, + fixedWidth: (MediaQuery.of(context).size.width - 20.r - 28.r) / 5, ); }), - rows: List.generate(widget.bodyList.length, - (index) => _getRow(index, Color(0xFFF5F5F5)))); + rows: List.generate(widget.bodyList.length, (index) => _getRow(index, Color(0xFFF5F5F5)))); } } diff --git a/marking_app/lib/pages/mainPage.dart b/marking_app/lib/pages/mainPage.dart index e6052aa..a073354 100644 --- a/marking_app/lib/pages/mainPage.dart +++ b/marking_app/lib/pages/mainPage.dart @@ -16,7 +16,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:marking_app/common/mixin/common.dart'; import 'package:marking_app/common/model/common/base_structure_result.dart'; import 'package:marking_app/common/model/user/user_info.dart'; -import 'package:marking_app/pages/homework_correction/index.dart'; import 'package:marking_app/pages/reports/index.dart'; import 'package:marking_app/provider/do_marking_provider.dart'; import 'package:marking_app/provider/upload_file_provider.dart'; @@ -30,6 +29,8 @@ import 'package:marking_app/utils/index.dart'; import 'package:marking_app/utils/request/rest_client.dart'; import 'package:package_info/package_info.dart'; +import 'homework_correction/job_home.dart'; + class TheMainPage extends StatefulHookConsumerWidget { const TheMainPage({Key? key}) : super(key: key); @@ -45,7 +46,13 @@ class TheMainPageState extends ConsumerState with CommonMixin { Timer? _timer; bool showUpgrade = false; - final List _bodyList = [const TheHomePage(), const TheMarking(), const HomeworkCorrection(), const TheReport()]; + final List _bodyList = [ + const TheHomePage(), + const TheMarking(), + // const HomeworkCorrection(), + const JobHome(), + const TheReport() + ]; int tabIndex = 0; /// 获取项目 icon @@ -79,7 +86,7 @@ class TheMainPageState extends ConsumerState with CommonMixin { if (!value) { return toLoginPage(context); } - if(ref.read(userProvider).id == '540117143121989') return; + if (ref.read(userProvider).id == '540117143121989') return; ref.read(userReportProvider.notifier).initUserReport(); // 初始化报告页面用户身份职位 }); // 定值token // 初始化偏好设置 @@ -99,9 +106,7 @@ class TheMainPageState extends ConsumerState with CommonMixin { void getAppUpgrade(UserInfo user) async { try { showUpgrade = true; - // if (user.loginName == '18888888888') return; - // if (user.loginName == 'u0001') return; - if (user.loginName == '13350366646') return; + if (['18888888888'].contains(user.loginName)) return; // 获取设备信息 String deviceInfo; int deviceType; @@ -151,7 +156,8 @@ class TheMainPageState extends ConsumerState with CommonMixin { child: Scaffold( body: PageView( controller: _pageController, - physics: const BouncingScrollPhysics(), + // physics: const BouncingScrollPhysics(), + physics: NeverScrollableScrollPhysics(), onPageChanged: (index) => toUpState(setState, () => tabIndex = index, mounted), children: _bodyList, ), diff --git a/marking_app/lib/routes/RouterManager.dart b/marking_app/lib/routes/RouterManager.dart index 8bad1ec..2c2ca3a 100644 --- a/marking_app/lib/routes/RouterManager.dart +++ b/marking_app/lib/routes/RouterManager.dart @@ -6,14 +6,16 @@ * @FilePath: \marking_app\lib\routes\RouterManager.dart * @Description: 路由 */ - -import 'dart:convert'; - import 'package:fluro/fluro.dart'; import 'package:flutter/material.dart'; import 'package:marking_app/common/model/enum/marking_list_type.dart'; import 'package:marking_app/pages/common/startUpPage.dart'; +import 'package:marking_app/pages/homework_correction/answer_trajectory.dart'; +import 'package:marking_app/pages/homework_correction/answer_trajectory_job_detail.dart'; import 'package:marking_app/pages/homework_correction/do_papers_job_exam.dart'; +import 'package:marking_app/pages/homework_correction/index.dart'; +import 'package:marking_app/pages/homework_correction/job_knowledge_points.dart'; +import 'package:marking_app/pages/homework_correction/job_knowledge_points_detail.dart'; import 'package:marking_app/pages/homework_correction/job_personal_detail.dart'; import 'package:marking_app/pages/homework_correction/job_priority_review_set.dart'; import 'package:marking_app/pages/homework_correction/job_report.dart'; @@ -66,6 +68,8 @@ class RouterManager { static const String jobListParticipateInClassPath = '/job/list/participateInClass'; // 作业 ==> 收藏页面 static const String jobFavoritePagePath = '/job/favorite/index'; +// 作业 ==> 列表页面 + static const String jobMainListPagePath = '/job/mainList/index'; static const String reportClassTeacherPath = 'report/details/reportClassTeacher'; static const String reportSubjectTeacherPath = 'report/details/reportSubjectTeacher'; @@ -81,6 +85,10 @@ class RouterManager { static const String reportHistoryPath = '/report_detail/report_history'; static const String registerPath = '/register/index'; static const String logOffPath = '/mine/log_off'; + static const String jobKnowledgePointsPath = '/homework_correction/job_knowledge_points'; + static const String jobKnowledgePointsDetailPath = '/homework_correction/job_knowledge_points_detail'; + static const String answerTrajectoryPath = '/homework_correction/answer_trajectory'; + static const String answerTrajectoryJobDetailPath = '/homework_correction/answer_trajectory_job_detail'; // TheMine static final FluroRouter router = FluroRouter(); @@ -103,18 +111,14 @@ class RouterManager { } // 启动页 - static final _startUpPageHandler = - Handler(handlerFunc: (BuildContext? context, Map> params) => const StartUpPage()); + static final _startUpPageHandler = Handler(handlerFunc: (BuildContext? context, Map> params) => const StartUpPage()); // 主页 - static final _mainPageHandler = - Handler(handlerFunc: (BuildContext? context, Map> params) => const TheMainPage()); + static final _mainPageHandler = Handler(handlerFunc: (BuildContext? context, Map> params) => const TheMainPage()); // 登录页 - static final _loginPageHandler = - Handler(handlerFunc: (BuildContext? context, Map> params) => const TheLogin()); + static final _loginPageHandler = Handler(handlerFunc: (BuildContext? context, Map> params) => const TheLogin()); // 阅卷进度页面 static final _progressPageHandler = Handler( - handlerFunc: (BuildContext? context, Map> params) => - Progress(int.parse(params['examSubjectId']![0]), params['name']![0])); + handlerFunc: (BuildContext? context, Map> params) => Progress(int.parse(params['examSubjectId']![0]), params['name']![0])); // 回评页面 static final _reviewPageHandler = Handler(handlerFunc: (BuildContext? context, Map> params) { int markingUserId = int.parse(params['markingUserId']![0]); @@ -133,11 +137,8 @@ class RouterManager { // 回评异常页面 static final _reviewAbnormalPageHandler = Handler( - handlerFunc: (BuildContext? context, Map> params) => ProgressAbnormal( - int.parse(params['examSubjectId']![0]), - params['markingId']![0], - params['name']![0], - params['questionNum']![0])); + handlerFunc: (BuildContext? context, Map> params) => + ProgressAbnormal(int.parse(params['examSubjectId']![0]), params['markingId']![0], params['name']![0], params['questionNum']![0])); // 阅卷页面 static final _markingDoPageHandler = Handler(handlerFunc: (BuildContext? context, Map> params) { try { @@ -179,8 +180,7 @@ class RouterManager { } }); // 批阅作业页面 - static final _markingHomeworkDoPageHandler = - Handler(handlerFunc: (BuildContext? context, Map> params) { + static final _markingHomeworkDoPageHandler = Handler(handlerFunc: (BuildContext? context, Map> params) { try { int taskId = int.parse(params['taskId']![0]); int jobId = int.parse(params['jobId']![0]); @@ -224,8 +224,7 @@ class RouterManager { handlerFunc: (BuildContext? context, Map> params) => const OhterPage(), ); - static final _reportClassTeacherPageHandler = - Handler(handlerFunc: (BuildContext? context, Map> params) { + static final _reportClassTeacherPageHandler = Handler(handlerFunc: (BuildContext? context, Map> params) { int examId = int.parse(params['examId']![0]); return ReportClassTeacher(examId: examId); @@ -295,7 +294,7 @@ class RouterManager { if (params['gradeId'] != null && params['gradeId']?[0] != null && params['gradeId']![0] != 'null') { gradeId = int.parse(params['gradeId']![0]); } - return QuickDataCheckPage(jobId: jobId, className: className,gradeName:gradeName,schoolId:schoolId,gradeId:gradeId); + return QuickDataCheckPage(jobId: jobId, className: className, gradeName: gradeName, schoolId: schoolId, gradeId: gradeId); }, ); @@ -313,14 +312,16 @@ class RouterManager { handlerFunc: (BuildContext? context, Map> params) { String groupId = params['groupId']![0]; String title = params['title']![0]; - return JobPriorityReviewSet(groupId: groupId,title:title); + String? page = params['page']?[0]; + return JobPriorityReviewSet(groupId: groupId, title: title, page: page); }, ); //学生分组 static final _jobStudentGroupPageHandler = Handler( handlerFunc: (BuildContext? context, Map> params) { - return JobStudentGroup(); + String page = params['page']![0]; + return JobStudentGroup(page: page); }, ); //作业收藏页面 @@ -337,7 +338,7 @@ class RouterManager { if (params['gradeId'] != null && params['gradeId']?[0] != null && params['gradeId']![0] != 'null') { gradeId = int.parse(params['gradeId']![0]); } - return JobFavorite(jobId: jobId, gradeId: gradeId, schoolId: schoolId, className: className,jobName:jobName); + return JobFavorite(jobId: jobId, gradeId: gradeId, schoolId: schoolId, className: className, jobName: jobName); }, ); @@ -346,14 +347,19 @@ class RouterManager { handlerFunc: (BuildContext? context, Map> params) { String studentName = params['studentName']![0]; int studentId = int.parse(params['studentId']![0]); - return JobPersonalDetail(studentId: studentId,studentName:studentName); + return JobPersonalDetail(studentId: studentId, studentName: studentName); }, ); + // 优先配置个人详情 + static final _jobMainListPathHandler = Handler( + handlerFunc: (BuildContext? context, Map> params) => HomeworkCorrection(), + ); + // 学生报告详情 static final _reportCardDialogPathHandler = Handler( handlerFunc: (BuildContext? context, Map> params) { - /* String studentName = params['studentName']![0]; + /* String studentName = params['studentName']![0]; int studentId = int.parse(params['studentId']![0]); return ReportCardDialog(studentId: studentId,studentName:studentName);*/ }, @@ -361,20 +367,52 @@ class RouterManager { // 学生历史报告 static final _reportHistoryPathHandler = Handler( handlerFunc: (BuildContext? context, Map> params) { - /* String studentName = params['studentName']![0]; + /* String studentName = params['studentName']![0]; int studentId = int.parse(params['studentId']![0]);*/ return ReportHistory(); }, ); // 注册页 - static final _registerPathHandler = - Handler(handlerFunc: (BuildContext? context, Map> params) => const Register()); + static final _registerPathHandler = Handler(handlerFunc: (BuildContext? context, Map> params) => const Register()); // 注销页 - static final _logOffPathHandler = - Handler(handlerFunc: (BuildContext? context, Map> params) => const LogOff()); + static final _logOffPathHandler = Handler(handlerFunc: (BuildContext? context, Map> params) => const LogOff()); + // 知识点掌握 + static final _jobKnowledgePointsPathHandler = Handler( + handlerFunc: (BuildContext? context, Map> params) { + /* String studentName = params['studentName']![0]; + int studentId = int.parse(params['studentId']![0]);*/ + return JobKnowledgePoints(); + }, + ); + + // 知识点掌握详情 + static final _jobKnowledgePointsDetailPathHandler = Handler( + handlerFunc: (BuildContext? context, Map> params) { + String knowledgeName = params['knowledgeName']![0]; + int knowledgeId = int.parse(params['knowledgeId']![0]); + return JobKnowledgePointsDetail(knowledgeName: knowledgeName, knowledgeId: knowledgeId); + }, + ); + +//答题轨迹 + static final _answerTrajectoryPathHandler = Handler( + handlerFunc: (BuildContext? context, Map> params) { + return AnswerTrajectory(); + }, + ); + +//答题轨迹按作业详情 + static final _answerTrajectoryJobDetailPathHandler = Handler( + handlerFunc: (BuildContext? context, Map> params) { + int jobId = int.parse(params['jobId']![0]); + String jobName = params['jobName']![0]; + String genderName = params['genderName']![0]; + return AnswerTrajectoryJobDetail(jobId: jobId, jobName: jobName, genderName: genderName); + }, + ); // 开始阅卷页面 // static final _doMarkingPapers = Handler(handlerFunc: (BuildContext? context, Map> params) => MarkingPapers()); @@ -392,38 +430,34 @@ class RouterManager { router.define(markingReviewPath, handler: _reviewPageHandler, transitionType: TransitionType.material); router.define(markingJobReviewPath, handler: _reviewJobPageHandler, transitionType: TransitionType.material); router.define(markingDoPath, handler: _markingDoPageHandler, transitionType: TransitionType.material); - router.define(markingHomeworkDoPath, - handler: _markingHomeworkDoPageHandler, transitionType: TransitionType.material); - router.define(markingReviewAbnormalPath, - handler: _reviewAbnormalPageHandler, transitionType: TransitionType.material); + router.define(markingHomeworkDoPath, handler: _markingHomeworkDoPageHandler, transitionType: TransitionType.material); + router.define(markingReviewAbnormalPath, handler: _reviewAbnormalPageHandler, transitionType: TransitionType.material); router.define(agreementPath, handler: _agreementPageHandler, transitionType: TransitionType.material); router.define(ohterMainPagePath, handler: _ohterMainPageHandler, transitionType: TransitionType.material); - router.define(reportClassTeacherPath, - handler: _reportClassTeacherPageHandler, transitionType: TransitionType.material); - router.define(reportSubjectTeacherPath, - handler: _reportSubjectTeacherPageHandler, transitionType: TransitionType.material); - router.define(reportPersonalSubjectPath, - handler: _reportPersonalSubjectPageHandler, transitionType: TransitionType.material); + router.define(reportClassTeacherPath, handler: _reportClassTeacherPageHandler, transitionType: TransitionType.material); + router.define(reportSubjectTeacherPath, handler: _reportSubjectTeacherPageHandler, transitionType: TransitionType.material); + router.define(reportPersonalSubjectPath, handler: _reportPersonalSubjectPageHandler, transitionType: TransitionType.material); router.define(userMinePath, handler: _userMinePageHandler, transitionType: TransitionType.material); router.define(reportDetailPath, handler: _reportDetailPath, transitionType: TransitionType.material); router.define(jobReportPagePath, handler: _jobReportPageHandler, transitionType: TransitionType.material); router.define(jobExamPagePath, handler: _jobExamPageHandler, transitionType: TransitionType.material); - router.define(jobListParticipateInClassPath, - handler: _jobListParticipateInClassHandler, transitionType: TransitionType.material); + router.define(jobListParticipateInClassPath, handler: _jobListParticipateInClassHandler, transitionType: TransitionType.material); router.define(quickDataCheckPath, handler: _quickDataCheckPageHandler, transitionType: TransitionType.material); - router.define(quickCheckPersonalPath, - handler: _quickCheckPersonalPageHandler, transitionType: TransitionType.material); - router.define(jobPriorityReviewSetPath, - handler: _jobPriorityReviewSetPageHandler, transitionType: TransitionType.material); + router.define(quickCheckPersonalPath, handler: _quickCheckPersonalPageHandler, transitionType: TransitionType.material); + router.define(jobPriorityReviewSetPath, handler: _jobPriorityReviewSetPageHandler, transitionType: TransitionType.material); router.define(jobStudentGroupPath, handler: _jobStudentGroupPageHandler, transitionType: TransitionType.material); router.define(jobFavoritePagePath, handler: _jobFavoritePagePathHandler, transitionType: TransitionType.material); router.define(jobPersonalDetailPath, handler: _jobPersonalDetailPathHandler, transitionType: TransitionType.material); router.define(reportCardDialogPath, handler: _reportCardDialogPathHandler, transitionType: TransitionType.material); router.define(reportHistoryPath, handler: _reportHistoryPathHandler, transitionType: TransitionType.material); - router.define(registerPath, handler: _registerPathHandler,transitionType: TransitionType.material); - router.define(logOffPath, handler: _logOffPathHandler,transitionType: TransitionType.material); -// getTransition() + router.define(registerPath, handler: _registerPathHandler, transitionType: TransitionType.material); + router.define(logOffPath, handler: _logOffPathHandler, transitionType: TransitionType.material); + router.define(jobMainListPagePath, handler: _jobMainListPathHandler, transitionType: TransitionType.material); + router.define(jobKnowledgePointsPath, handler: _jobKnowledgePointsPathHandler, transitionType: TransitionType.material); + router.define(jobKnowledgePointsDetailPath, handler: _jobKnowledgePointsDetailPathHandler, transitionType: TransitionType.material); + router.define(answerTrajectoryPath, handler: _answerTrajectoryPathHandler, transitionType: TransitionType.material); + router.define(answerTrajectoryJobDetailPath, handler: _answerTrajectoryJobDetailPathHandler, transitionType: TransitionType.material); router.notFoundHandler = Handler(handlerFunc: (BuildContext? context, Map> params) { toPrint(val: "ROUTE WAS NOT FOUND !!!"); diff --git a/marking_app/lib/utils/common_utils.dart b/marking_app/lib/utils/common_utils.dart index 19d129f..a9d31c4 100644 --- a/marking_app/lib/utils/common_utils.dart +++ b/marking_app/lib/utils/common_utils.dart @@ -45,4 +45,26 @@ class CommonUtils { } return hms; } + + static DateTime getWeekStartDate() { + DateTime now = DateTime.now(); + int dayOfWeek = now.weekday; // 获取今天是周几(1代表周一,7代表周日) + int diff = dayOfWeek - 1; // 计算今天距离周一的天数差 + if (diff < 0) { + diff += 7; // 如果是周日,则需要加上一周的天数 + } + return now.subtract(Duration(days: diff)); // 减去天数差,得到本周一的时间 + } + + static DateTime getWeekEndDate() { + DateTime now = DateTime.now(); + int dayOfWeek = now.weekday; // 获取今天是周几 + int diff = 7 - dayOfWeek; // 计算今天距离周日的天数差 + if (diff == 0) { + diff = 7; // 如果是周日,则加上一周的天数 + } + return now.add(Duration(days: diff)); // 加上天数差减一,得到本周日的时间 + } + + } diff --git a/marking_app/lib/utils/my_time_util.dart b/marking_app/lib/utils/my_time_util.dart new file mode 100644 index 0000000..237cfbd --- /dev/null +++ b/marking_app/lib/utils/my_time_util.dart @@ -0,0 +1,65 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'my_time_util.g.dart'; + +// 毫秒转小时、分钟、秒的函数 +TimeUnitModel? convertMilliseconds(int milliseconds) { + try { + int hours = milliseconds ~/ (1000 * 60 * 60); + int minutes = (milliseconds % (1000 * 60 * 60)) ~/ (1000 * 60); + int seconds = (milliseconds % (1000 * 60)) ~/ 1000; + + if ((milliseconds % 1000) > 500) seconds += 1; + + return TimeUnitModel(hours, minutes, seconds); + } catch (e) { + print('时间转换报错'); + } + return null; +} + +// 毫秒转小时、分钟、秒的函数 +TimeUnitModel? convertSeconds(int totalSeconds) { + try { + int hours = totalSeconds ~/ 3600; // 整除3600得到小时数 + int remainingSeconds = totalSeconds % 3600; // 取模3600得到剩余的秒数 + int minutes = remainingSeconds ~/ 60; // 整除60得到分钟数 + int seconds = remainingSeconds % 60; // 取模60得到最终的秒数 + + return TimeUnitModel(hours, minutes, seconds); + } catch (e) { + print('时间转换报错'); + } + return null; +} + +@JsonSerializable() +class TimeUnitModel extends Object { + int hours; + int minutes; + int seconds; + TimeUnitModel(this.hours, this.minutes, this.seconds); + + factory TimeUnitModel.fromJson(Map srcJson) => _$TimeUnitModelFromJson(srcJson); + + Map toJson() => _$TimeUnitModelToJson(this); + + @override + String toString() { + var timeStr = ''; + if (hours > 0) { + timeStr += '${hours > 9 ? hours : '0' + hours.toString()} '; + } + if (minutes > 0) { + timeStr += '${minutes > 9 ? minutes : '0' + minutes.toString()}'; + } + + if (timeStr.length > 0) { + timeStr += ':${seconds > 9 ? seconds : '0' + seconds.toString()}'; + } else { + timeStr += '00:${seconds > 9 ? seconds : '0' + seconds.toString()}'; + } + + return timeStr; + } +} diff --git a/marking_app/lib/utils/request/rest_client.dart b/marking_app/lib/utils/request/rest_client.dart index eb5d1f0..ac772d0 100644 --- a/marking_app/lib/utils/request/rest_client.dart +++ b/marking_app/lib/utils/request/rest_client.dart @@ -18,8 +18,11 @@ import 'package:marking_app/common/model/job/job_concerned_with_student_params.d import 'package:marking_app/common/model/job/job_data_report.dart'; import 'package:marking_app/common/model/job/job_do_marking_status_info.dart'; import 'package:marking_app/common/model/job/job_fav_student.dart'; -import 'package:marking_app/common/model/job/job_favorite_item_model.dart'; import 'package:marking_app/common/model/job/job_favorite_model.dart'; +import 'package:marking_app/common/model/job/job_handwriting.dart'; +import 'package:marking_app/common/model/job/job_knowledge_detail_student.dart'; +import 'package:marking_app/common/model/job/job_knowledge_points.dart'; +import 'package:marking_app/common/model/job/job_knowledge_points_detail.dart'; import 'package:marking_app/common/model/job/job_level_set_params.dart'; import 'package:marking_app/common/model/job/job_note_taking_trajectory.dart'; import 'package:marking_app/common/model/job/job_page_tab.dart'; @@ -33,7 +36,6 @@ import 'package:marking_app/common/model/job/job_student_history.dart'; import 'package:marking_app/common/model/job/job_student_level.dart'; import 'package:marking_app/common/model/job/job_task_item.dart'; import 'package:marking_app/common/model/job/marking_text_question_job.dart'; -import 'package:marking_app/common/model/job/marking_text_question_job_params.dart'; import 'package:marking_app/common/model/job/marking_text_question_job_tab_params.dart'; import 'package:marking_app/common/model/job/review_again_list_params.dart'; import 'package:marking_app/common/model/job/upload_file_interface_config.dart'; @@ -73,8 +75,7 @@ abstract class RestClient { // 最新版本 @the_retrofit.GET("/api/version/latest?mobileTypeEnum={mobileTypeEnum}") - Future> getLatestVersion( - @the_retrofit.Path("mobileTypeEnum") int mobileTypeEnum); // 1 安卓 2 ios + Future> getLatestVersion(@the_retrofit.Path("mobileTypeEnum") int mobileTypeEnum); // 1 安卓 2 ios // 用户登录 /auth/login/exam-marking/user-mobile @the_retrofit.POST("/auth/login/exam-marking/user") @@ -90,8 +91,7 @@ abstract class RestClient { // 阅卷列表 => 分页获取 @the_retrofit.GET("/api/marking/list") - Future>> getMarkingsByPage( - @the_retrofit.Queries() MarkingListParams params); + Future>> getMarkingsByPage(@the_retrofit.Queries() MarkingListParams params); // /api/marking/list @@ -116,13 +116,11 @@ abstract class RestClient { // 阅卷 => 获取考试试题 @the_retrofit.GET("/api/marking/question") - Future> getTestQuestionsOfExam( - @the_retrofit.Queries() MarkingTextQuestionParams params); + Future> getTestQuestionsOfExam(@the_retrofit.Queries() MarkingTextQuestionParams params); // 阅卷 => 获取考试tab(试题批次) @the_retrofit.GET("/api/marking/{markingUserId}/tab") - Future>> getTestQuestionsOfTab( - @the_retrofit.Path("markingUserId") int markingUserId); + Future>> getTestQuestionsOfTab(@the_retrofit.Path("markingUserId") int markingUserId); // 阅卷 => 单个试题Tag阅卷详情 @the_retrofit.GET("/api/marking/{markingUserId}/progress") @@ -136,16 +134,15 @@ abstract class RestClient { // 阅卷 => 获取考试tab下某次试题 @the_retrofit.GET("/api/marking/{markingUserId}/detail") - Future> getTabOfExam(@the_retrofit.Path("markingUserId") int markingUserId, - @the_retrofit.Queries() MarkingTextQuestionTabParams params); + Future> getTabOfExam( + @the_retrofit.Path("markingUserId") int markingUserId, @the_retrofit.Queries() MarkingTextQuestionTabParams params); // 阅卷 => 提交考试试题 @the_retrofit.PUT("/api/marking") Future> submitTestQuestionsOfExam(@the_retrofit.Body() SubmitExamParams params); @the_retrofit.PUT("/api/marking/error") - Future> submitTestQuestionsOfExamAbnormal( - @the_retrofit.Body() SubmitExamAbnormalParams params); + Future> submitTestQuestionsOfExamAbnormal(@the_retrofit.Body() SubmitExamAbnormalParams params); // 阅卷 => 提交考试试题 @the_retrofit.PUT("/api/marking/review") @@ -157,8 +154,7 @@ abstract class RestClient { // 阅卷 => 结束阅卷 @the_retrofit.GET("/api/marking/original-paper") - Future>> getViewOriginalVolume( - @the_retrofit.Query("markingUserDetailId") String markingUserDetailId); + Future>> getViewOriginalVolume(@the_retrofit.Query("markingUserDetailId") String markingUserDetailId); // 阅卷 => 查看答案 @the_retrofit.GET("/api/marking/answer") @@ -184,25 +180,21 @@ abstract class RestClient { // 阅卷 => 获取异常详细信息 @the_retrofit.GET("/api/marking/error-info") - Future> getMarkingQuestionsErrorInfo( - @the_retrofit.Query("markingUserDetailId") int id); + Future> getMarkingQuestionsErrorInfo(@the_retrofit.Query("markingUserDetailId") int id); // 阅卷 => 获取仲裁详细信息 @the_retrofit.GET("/api/marking/history-score") - Future>> getArbitrateOfHistoryScore( - @the_retrofit.Query("markingUserDetailId") int id); + Future>> getArbitrateOfHistoryScore(@the_retrofit.Query("markingUserDetailId") int id); // 阅卷 => 获取仲裁详细信息 @the_retrofit.GET("/api/marking/rating-info") - Future>> getMarkingRatingInfo( - @the_retrofit.Query("markingUserId") int id); + Future>> getMarkingRatingInfo(@the_retrofit.Query("markingUserId") int id); // ------------------------------------------ 作业 ------------------------------------------ // 作业 => 作业列表 @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Task") - Future>> getJobsByPage( - @the_retrofit.Queries() MarkingListParams params); + Future>> getJobsByPage(@the_retrofit.Queries() MarkingListParams params); // 作业 => 批改获取tabs @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Task/tabs") @@ -210,13 +202,11 @@ abstract class RestClient { // 作业 => 批改获取tabs @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Marking/students") - Future>> getJobWithStudents( - @the_retrofit.Queries() JobConcernedWithStudentParams params); + Future>> getJobWithStudents(@the_retrofit.Queries() JobConcernedWithStudentParams params); // 作业 => 获取考试tab下某次试题 @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Task/detail") - Future> getJobTabOfExam( - @the_retrofit.Queries() MarkingTextQuestionJobTabParams params); + Future> getJobTabOfExam(@the_retrofit.Queries() MarkingTextQuestionJobTabParams params); // 作业 => 提交 @the_retrofit.POST("${RequestConfig.hwProxyKeywords}/api/Task") @@ -228,8 +218,7 @@ abstract class RestClient { // 作业 => 获取考试tab下某次试题 @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Task/questions") - Future>> toGoreviewAgainPage( - @the_retrofit.Queries() ReviewAgainListParams params); + Future>> toGoreviewAgainPage(@the_retrofit.Queries() ReviewAgainListParams params); // 作业 => 获取参考答案 @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Task/answer") @@ -240,8 +229,7 @@ abstract class RestClient { // 作业 => 上传图片请求参数 @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Upload") - Future> getUploadFile( - @the_retrofit.Queries() UploadFileInterfaceConfigParams params); + Future> getUploadFile(@the_retrofit.Queries() UploadFileInterfaceConfigParams params); // 作业 => 获取参考答案 @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Dpc/studentAnswerHandwriting") @@ -250,18 +238,16 @@ abstract class RestClient { // 作业 => 查询作业是否收藏 @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Dpc/collect") - Future> getJobCollect(@the_retrofit.Query("taskId") int taskId, - @the_retrofit.Query("studentId") int studentId, @the_retrofit.Query("paperId") int paperId); + Future> getJobCollect( + @the_retrofit.Query("taskId") int taskId, @the_retrofit.Query("studentId") int studentId, @the_retrofit.Query("paperId") int paperId); // 作业 => 作业优先批阅取消 @the_retrofit.POST("/api/read/cancel-job-read-level") - Future> jobPriorityReviewCancel( - @the_retrofit.Field("jobId") int jobId, @the_retrofit.Field("studentId") int studentId); + Future> jobPriorityReviewCancel(@the_retrofit.Field("jobId") int jobId, @the_retrofit.Field("studentId") int studentId); // 作业 => 作业优先批阅加入 @the_retrofit.POST("/api/read/join-read-level") - Future jobPriorityReviewJoin( - @the_retrofit.Field("jobId") int jobId, @the_retrofit.Field("studentId") int studentId); + Future jobPriorityReviewJoin(@the_retrofit.Field("jobId") int jobId, @the_retrofit.Field("studentId") int studentId); // 作业 => 查询作业是否收藏 @the_retrofit.POST("${RequestConfig.hwProxyKeywords}/api/Dpc/collect") @@ -296,13 +282,11 @@ abstract class RestClient { // 作业 => 列表 ==> 参与班级列表 @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Task/tasks") - Future>> getJobListParticipateInClass( - @the_retrofit.Query("markingId") int jobId); + Future>> getJobListParticipateInClass(@the_retrofit.Query("markingId") int jobId); // 作业 => 作业收藏数量 @the_retrofit.GET("/dpc-api/api/read/job-favorite-count-by-class") - Future>> getListOfJobFavoriteNumber( - @the_retrofit.Query("jobid") int jobId); + Future>> getListOfJobFavoriteNumber(@the_retrofit.Query("jobid") int jobId); // 作业 => 作业收藏列表 @the_retrofit.GET("/api/jobs/fav-student-jobs") @@ -315,13 +299,11 @@ abstract class RestClient { // 作业 => 作业收藏列表 @the_retrofit.POST("${RequestConfig.hwProxyKeywords}/dpc-api/api/read/cancel-favorite") - Future> toJobCancelFavorite( - @the_retrofit.Field() int jobId, @the_retrofit.Field() int studentId); + Future> toJobCancelFavorite(@the_retrofit.Field() int jobId, @the_retrofit.Field() int studentId); // 作业 => 数据快查 @the_retrofit.GET("/api/read/job-data-center-report") - Future> getJobDataCenterReport( - @the_retrofit.Queries() Map params); + Future> getJobDataCenterReport(@the_retrofit.Queries() Map params); // 作业 => 数据快查--个人 @the_retrofit.GET("/api/read/job-data-center-student-report") @@ -329,8 +311,7 @@ abstract class RestClient { // 作业 => 优先批阅,学生分组列表 @the_retrofit.GET("/api/read/job-read-level-student-groups") - Future>> getJobLevelStudentGroups( - @the_retrofit.Query("account") String account); + Future>> getJobLevelStudentGroups(@the_retrofit.Query("account") String account); // 作业 => 优先批阅,优先批阅列表 @the_retrofit.GET("/api/read/job-read-level") @@ -343,19 +324,55 @@ abstract class RestClient { // 作业 => 取消收藏 @the_retrofit.POST("/api/jobs/de-fav-student-job") - Future getJobDeFavorites(@the_retrofit.Field("jobId") int jobId, - @the_retrofit.Field("studentId") int studentId, @the_retrofit.Field("questionPage") int questionPage); + Future getJobDeFavorites( + @the_retrofit.Field("jobId") int jobId, @the_retrofit.Field("studentId") int studentId, @the_retrofit.Field("questionPage") int questionPage); // 作业 => 学生作业详情历史 @the_retrofit.GET("/api/read/student-job-history") Future> getStudentJobHistory( - @the_retrofit.Query("StudentId") int studentId, - @the_retrofit.Query("IsPaper") bool isPaper, - @the_retrofit.Query("DateStart") String? dateStart, - @the_retrofit.Query("DateEnd") String? dateEnd, - @the_retrofit.Query("Page") int page, - @the_retrofit.Query("PageSize") int pageSize, - ); + @the_retrofit.Query("StudentId") int studentId, + @the_retrofit.Query("IsPaper") bool isPaper, + @the_retrofit.Query("DateStart") String? dateStart, + @the_retrofit.Query("DateEnd") String? dateEnd, + @the_retrofit.Query("Page") int page, + @the_retrofit.Query("PageSize") int pageSize, + ); + + // 作业 => 知识点掌握 + @the_retrofit.GET("/api/jobs/knowledge-report") + Future>> getKnowledgeReport( + @the_retrofit.Query("dateStart") String? dateStart, + @the_retrofit.Query("dateEnd") String? dateEnd, + @the_retrofit.Query("knowledgeName") String? knowledgeName, + ); + + // 作业 => 知识点掌握详情 + @the_retrofit.GET("/api/jobs/knowledge-detail-report") + Future>> getKnowledgeReportDetail( + @the_retrofit.Query("KnowledgeId") int knowledgeId, + ); + + // 作业 => 知识点掌握详情人员名单 + @the_retrofit.GET("/api/jobs/knowledge-question-detail/{questionid}") + Future>> getKnowledgeStudent( + @the_retrofit.Path("questionid") int questionid, + ); + + // 作业 => 知识点掌握详情原卷图 + @the_retrofit.GET("/api/jobs/question-paper-img/{sectionid}/{questionno}") + Future> getKnowledgeImg( + @the_retrofit.Path("sectionid") int questionid, + @the_retrofit.Path("questionno") String questionno, + ); + + // 作业 => 获取学生原稿笔记 + @the_retrofit.GET("/api/jobs/student-paper-handwriting") + Future?> getHandwriting( + @the_retrofit.Query("jobId") int jobId, + @the_retrofit.Query("studentId") int studentId, + @the_retrofit.Query("questionNo") int? questionNo, + @the_retrofit.Query("pageNum") int? pageNum, + ); // 注册 @the_retrofit.POST("/auth/login/reg") @@ -372,5 +389,4 @@ abstract class RestClient { // 注销账号 @the_retrofit.POST("/auth/login/lg") Future getLogOff(@the_retrofit.Field("loginName") String loginName); - }