diff --git a/.gitignore b/.gitignore index 7821b8a..64d9515 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ app.*.map.json **/android/app/debug **/android/app/profile **/android/app/release +making_school_asignment_app/*.g.dart +making_school_asignment_app/pubspec.lock +making_school_asignment_app/lib/common/api/retrofit_client.g.dart diff --git a/making_school_asignment_app/lib/common/api/retrofit_client.dart b/making_school_asignment_app/lib/common/api/retrofit_client.dart index 2bba4c0..4829d91 100644 --- a/making_school_asignment_app/lib/common/api/retrofit_client.dart +++ b/making_school_asignment_app/lib/common/api/retrofit_client.dart @@ -1,4 +1,7 @@ import 'package:dio/dio.dart' hide Headers; +import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_param.dart'; +import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_result.dart'; +import 'package:making_school_asignment_app/common/job/marking_models/favor_param.dart'; import 'package:retrofit/retrofit.dart'; import 'package:making_school_asignment_app/common/job/annotated_class.dart'; import 'package:making_school_asignment_app/common/job/class_item.dart'; @@ -98,4 +101,13 @@ abstract class RetrofitClient { //收集订正 @POST("/api/hms/Homework/Collect") Future getCollect(@Field() String homeworkId); + + // --------------------------- 批阅主页 ---------------------- + // 收藏作业 + @POST("/api/hms/FavStudent/FavStudent") + Future toFavStudent(@Body() FavorParam param); + + // 批阅作业详情 + @GET("/api/hms/Annotate/GetForAnnotate") + Future getDoPaperDetails(@Queries() DoPaperDetailsParam param); } diff --git a/making_school_asignment_app/lib/common/api/retrofit_client.g.dart b/making_school_asignment_app/lib/common/api/retrofit_client.g.dart index ad91f13..b82aabf 100644 --- a/making_school_asignment_app/lib/common/api/retrofit_client.g.dart +++ b/making_school_asignment_app/lib/common/api/retrofit_client.g.dart @@ -590,6 +590,62 @@ class _RetrofitClient implements RetrofitClient { return value; } + @override + Future toFavStudent(FavorParam param) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + final _data = {}; + _data.addAll(param.toJson()); + final _result = await _dio.fetch(_setStreamType(Options( + method: 'POST', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/hms/FavStudent/FavStudent', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + final value = _result.data; + return value; + } + + @override + Future getDoPaperDetails( + DoPaperDetailsParam param) async { + final _extra = {}; + final queryParameters = {}; + queryParameters.addAll(param.toJson()); + final _headers = {}; + const Map? _data = null; + final _result = await _dio.fetch>( + _setStreamType(Options( + method: 'GET', + headers: _headers, + extra: _extra, + ) + .compose( + _dio.options, + '/api/hms/Annotate/GetForAnnotate', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + )))); + final value = DoPaperDetailsResult.fromJson(_result.data!); + return value; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/making_school_asignment_app/lib/common/job/marking_models/do_paper_details_param.dart b/making_school_asignment_app/lib/common/job/marking_models/do_paper_details_param.dart new file mode 100644 index 0000000..ad211c1 --- /dev/null +++ b/making_school_asignment_app/lib/common/job/marking_models/do_paper_details_param.dart @@ -0,0 +1,47 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'do_paper_details_param.g.dart'; + +@JsonSerializable(includeIfNull: false) +class DoPaperDetailsParam extends Object { + // 作业ID + @JsonKey(name: 'homeworkId') + String homeworkId; + + @JsonKey(name: 'homeworkName') + String homeworkName; + + // 班级ID + @JsonKey(name: 'classId') + String classId; + + // 0 作业 1 考试 + @JsonKey(name: 'assessType', defaultValue: 0) + int? assessType; + + // 科目 + @JsonKey(name: 'subject') + int subject; + + // 题型ID + @JsonKey(name: 'templateId') + int? templateId; + + // 学生ID + @JsonKey(name: 'studentId') + int? studentId; + + DoPaperDetailsParam({ + required this.homeworkId, + required this.homeworkName, + required this.classId, + required this.subject, + this.assessType, + this.templateId, + this.studentId, + }); + + factory DoPaperDetailsParam.fromJson(Map srcJson) => _$DoPaperDetailsParamFromJson(srcJson); + + Map toJson() => _$DoPaperDetailsParamToJson(this); +} diff --git a/making_school_asignment_app/lib/common/job/marking_models/do_paper_details_result.dart b/making_school_asignment_app/lib/common/job/marking_models/do_paper_details_result.dart new file mode 100644 index 0000000..065b8a6 --- /dev/null +++ b/making_school_asignment_app/lib/common/job/marking_models/do_paper_details_result.dart @@ -0,0 +1,165 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'do_paper_details_result.g.dart'; + +@JsonSerializable(checked: true) +class DoPaperDetailsResult extends Object { + @JsonKey(name: 'templateIds') + Object templateIds; + + @JsonKey(name: 'students') + List students; + + @JsonKey(name: 'templateId') + int templateId; + + @JsonKey(name: 'studentId') + int studentId; + + @JsonKey(name: 'annotatedCount') + int annotatedCount; + + @JsonKey(name: 'submitCount') + int submitCount; + + @JsonKey(name: 'zgtAnswer') + String zgtAnswer; + + @JsonKey(name: 'zgtAnnotate') + String? zgtAnnotate; + + @JsonKey(name: 'lastAnswerTime') + String lastAnswerTime; + + @JsonKey(name: 'isFav') + bool isFav; + + @JsonKey(name: 'studentQuestions') + List studentQuestions; + + @JsonKey(name: 'unSubmitStudents') + Object unSubmitStudents; + + @JsonKey(name: 'lastPage') + LastPage? lastPage; + + @JsonKey(name: 'nextPage') + NextPage? nextPage; + + DoPaperDetailsResult( + this.templateIds, + this.students, + this.templateId, + this.studentId, + this.annotatedCount, + this.submitCount, + this.zgtAnswer, + this.zgtAnnotate, + this.lastAnswerTime, + this.isFav, + this.studentQuestions, + this.unSubmitStudents, + this.lastPage, + this.nextPage, + ); + + factory DoPaperDetailsResult.fromJson(Map srcJson) => _$DoPaperDetailsResultFromJson(srcJson); + + Map toJson() => _$DoPaperDetailsResultToJson(this); +} + +@JsonSerializable() +class PaperStudents extends Object { + @JsonKey(name: 'id') + int id; + + @JsonKey(name: 'name') + String name; + + @JsonKey(name: 'isPriority') + bool isPriority; + + PaperStudents( + this.id, + this.name, + this.isPriority, + ); + + factory PaperStudents.fromJson(Map srcJson) => _$PaperStudentsFromJson(srcJson); + + Map toJson() => _$PaperStudentsToJson(this); +} + +@JsonSerializable() +class StudentQuestions extends Object { + @JsonKey(name: 'questionNo') + int questionNo; + + @JsonKey(name: 'score') + int? score; + + @JsonKey(name: 'scoreStep') + int? scoreStep; + + @JsonKey(name: 'isNeedAnnotate') + bool isNeedAnnotate; + + @JsonKey(name: 'studentScore') + int? studentScore; + + @JsonKey(name: 'isCorrect') + bool? isCorrect; + + @JsonKey(name: 'correctRate') + int correctRate; + + StudentQuestions( + this.questionNo, + this.score, + this.scoreStep, + this.isNeedAnnotate, + this.studentScore, + this.isCorrect, + this.correctRate, + ); + + factory StudentQuestions.fromJson(Map srcJson) => _$StudentQuestionsFromJson(srcJson); + + Map toJson() => _$StudentQuestionsToJson(this); +} + +@JsonSerializable() +class LastPage extends Object { + @JsonKey(name: 'templateId') + int templateId; + + @JsonKey(name: 'studentId') + int studentId; + + LastPage( + this.templateId, + this.studentId, + ); + + factory LastPage.fromJson(Map srcJson) => _$LastPageFromJson(srcJson); + + Map toJson() => _$LastPageToJson(this); +} + +@JsonSerializable() +class NextPage extends Object { + @JsonKey(name: 'templateId') + int templateId; + + @JsonKey(name: 'studentId') + int studentId; + + NextPage( + this.templateId, + this.studentId, + ); + + factory NextPage.fromJson(Map srcJson) => _$NextPageFromJson(srcJson); + + Map toJson() => _$NextPageToJson(this); +} diff --git a/making_school_asignment_app/lib/common/job/marking_models/favor_param.dart b/making_school_asignment_app/lib/common/job/marking_models/favor_param.dart new file mode 100644 index 0000000..6049f4d --- /dev/null +++ b/making_school_asignment_app/lib/common/job/marking_models/favor_param.dart @@ -0,0 +1,33 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'favor_param.g.dart'; + +@JsonSerializable() +class FavorParam extends Object { + @JsonKey(name: 'homeworkId') + String homeworkId; + + @JsonKey(name: 'studentId') + int studentId; + + @JsonKey(name: 'templateId') + int templateId; + + @JsonKey(name: 'questionNo') + int questionNo; + + @JsonKey(name: 'isFav') + bool isFav; + + FavorParam({ + required this.homeworkId, + required this.studentId, + required this.templateId, + required this.questionNo, + required this.isFav, + }); + + factory FavorParam.fromJson(Map srcJson) => _$FavorParamFromJson(srcJson); + + Map toJson() => _$FavorParamToJson(this); +} diff --git a/making_school_asignment_app/lib/common/utils/anti_shake_throttling.dart b/making_school_asignment_app/lib/common/utils/anti_shake_throttling.dart new file mode 100644 index 0000000..a958172 --- /dev/null +++ b/making_school_asignment_app/lib/common/utils/anti_shake_throttling.dart @@ -0,0 +1,54 @@ +// debounce.dart + +import 'dart:async'; + +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; + +/// 函数防抖 +/// +/// [func]: 要执行的方法 +/// [delay]: 要迟延的时长 +VoidCallback debounce(Function func, [Duration delay = const Duration(milliseconds: 2000)]) { + Timer? timer; + target() { + if (timer?.isActive ?? false) { + timer?.cancel(); + } + timer = Timer(delay, () { + func.call(); + }); + } + + return target; +} + +/// 函数节流 +/// +/// [func]: 要执行的方法 +VoidCallback throttle(Future Function() func) { + bool enable = true; + target() { + if (enable == true) { + enable = false; + func().whenComplete(() { + enable = true; + }); + } + } + + return target; +} + +/// 函数节流 +/// +/// [func]: 要执行的方法 +bool easyThrottle(String tagId, EasyThrottleCallback onExecute, + {Duration duration = const Duration(milliseconds: 300), EasyThrottleCallback? onAfter}) { + return EasyThrottle.throttle( + tagId, // <-- An ID for this particular throttler + duration, // <-- The throttle duration + onExecute, // <-- The target method + onAfter: onAfter // <-- Optional callback, called after the duration has passed + ); +} diff --git a/making_school_asignment_app/lib/common/utils/utils.dart b/making_school_asignment_app/lib/common/utils/utils.dart index 8a098bb..c736669 100644 --- a/making_school_asignment_app/lib/common/utils/utils.dart +++ b/making_school_asignment_app/lib/common/utils/utils.dart @@ -1,3 +1,4 @@ +import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; @@ -203,3 +204,12 @@ class Utils { return now.add(Duration(days: diff)); // 加上天数差减一,得到本周日的时间 } } + +// 是否是平板 +bool isPad([double mobilePhoneScale = 1.2]) { + return ScreenUtil().scaleWidth > mobilePhoneScale; +} + +void toUpState(Function(void Function()) setState, VoidCallback fn, bool mounted) { + if (mounted) setState(fn); +} diff --git a/making_school_asignment_app/lib/page/home_page/children/annotate_class/widget/annotate_item.dart b/making_school_asignment_app/lib/page/home_page/children/annotate_class/widget/annotate_item.dart index a15fb5d..fe39191 100644 --- a/making_school_asignment_app/lib/page/home_page/children/annotate_class/widget/annotate_item.dart +++ b/making_school_asignment_app/lib/page/home_page/children/annotate_class/widget/annotate_item.dart @@ -6,7 +6,6 @@ import 'package:making_school_asignment_app/common/job/annotated_class.dart'; import 'package:making_school_asignment_app/common/utils/enum_untils.dart'; import 'package:making_school_asignment_app/page/global_widget/my_text.dart'; import 'package:making_school_asignment_app/page/home_page/children/annotate_class/annotate_class_logic.dart'; -import 'package:making_school_asignment_app/page/home_page/children/annotate_class/annotate_class_state.dart'; import 'package:making_school_asignment_app/page/home_page/children/annotate_class/widget/item_btn.dart'; import 'package:making_school_asignment_app/page/home_page/widget/progress_bar.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart'; @@ -26,6 +25,7 @@ class AnnotateItem extends StatefulWidget { class _AnnotateItemState extends State { @override Widget build(BuildContext context) { + AnnotatedClass itemData = widget.item; return Container( padding: EdgeInsets.only(top: 10.r), margin: EdgeInsets.only(bottom: 10.r), @@ -218,7 +218,12 @@ class _AnnotateItemState extends State { Expanded( child: InkWell( onTap: () { - Get.toNamed(Routes.reviewHomework, arguments: {'homeworkId': widget.homeworkId, 'homeworkName': widget.name}); + Get.toNamed(Routes.reviewHomework, arguments: { + 'homeworkId': widget.homeworkId, + 'homeworkName': widget.name, + 'classId': itemData.classId, + 'subject': 2, + }); }, child: Container( alignment: Alignment.center, diff --git a/making_school_asignment_app/lib/page/home_page/children/homework_review/components/bottom_operation_bar.dart b/making_school_asignment_app/lib/page/home_page/children/homework_review/components/bottom_operation_bar.dart new file mode 100644 index 0000000..a3934ff --- /dev/null +++ b/making_school_asignment_app/lib/page/home_page/children/homework_review/components/bottom_operation_bar.dart @@ -0,0 +1,146 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart'; +import 'package:making_school_asignment_app/common/utils/utils.dart'; + +import '../configuration_files/index.dart'; + +// 底部操作栏 +class BottomAnnotationSwitch extends StatefulWidget { + const BottomAnnotationSwitch({super.key}); + + @override + State createState() => _BottomAnnotationSwitchJobState(); +} + +class _BottomAnnotationSwitchJobState extends State with SingleTickerProviderStateMixin { + late AnimationController _animationController; // 动画 + final _logicControl = Get.find().annotationState; + + @override + void initState() { + _animationController = AnimationController( + vsync: this, + value: 1, // 设置默认值 + lowerBound: 0, + upperBound: 1, + duration: const Duration(milliseconds: 300), + )..addListener(toUp); + super.initState(); + } + + @override + void dispose() { + _animationController + ..removeListener(toUp) + ..dispose(); + super.dispose(); + } + + void toUp() => toUpState(setState, () {}, mounted); + + @override + Widget build(BuildContext context) { + double iconSize = 24.sp; + Color actionColor = Colors.white; + Color defaultColor = const Color.fromRGBO(132, 146, 163, 1); + + return Container( + width: double.infinity, + height: _animationController.value * 44.h, + color: const Color.fromRGBO(83, 83, 83, 1), + padding: EdgeInsets.symmetric(vertical: 1.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded( + flex: 8, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + InkWell( + onTap: () => easyThrottle('BottomAnnotationSwitch', () { + // if (jobNotesModel != null) { + // return AchievementView( + // elevation: 5, + // duration: Duration(seconds: 1), + // title: "笔记回显提示", + // subTitle: "当前正处于笔记回显", + // color: Theme.of(context).primaryColor, + // ).show(context); + // } + // easyThrottle('setSwitchBrush', () => ref.read(annotationGraffitiSwitchProvider.notifier).setSwitchBrush()); + _logicControl.pen.value = !_logicControl.pen.value; + }), + child: Obx(() { + return Icon( + const IconData(0xe635, fontFamily: "AlibabaIcon"), + size: iconSize, + color: _logicControl.pen.value ? actionColor : defaultColor, + ); + }), + ), + InkWell( + onTap: () => easyThrottle('BottomAnnotationSwitch', () { + // if (jobNotesModel != null) { + // return AchievementView( + // elevation: 5, + // duration: Duration(seconds: 1), + // title: "笔记回显提示", + // subTitle: "当前正处于笔记回显", + // color: Theme.of(context).primaryColor, + // ).show(context); + // } + // easyThrottle('setSwitchBrush', () => ref.read(annotationGraffitiSwitchProvider.notifier).setSwitchBrush()); + _logicControl.pen.value = !_logicControl.pen.value; + }), + child: Icon(const IconData(0xe638, fontFamily: "AlibabaIcon"), size: iconSize, color: defaultColor), + ), + InkWell( + onTap: () => easyThrottle('BottomAnnotationSwitch', () { + _logicControl.gestureMove.value = !_logicControl.gestureMove.value; + }), + // IconData(0xe62f, fontFamily: "AlibabaIcon") + child: Icon(const IconData(0xe636, fontFamily: "AlibabaIcon"), + size: iconSize, color: _logicControl.gestureMove.value ? actionColor : defaultColor), + ), + InkWell( + onTap: () => easyThrottle('BottomAnnotationSwitch', () { + // 清空全部 TODO + }), + child: Icon(const IconData(0xe637, fontFamily: "AlibabaIcon"), size: iconSize, color: defaultColor), + ), + ], + ), + ), + Expanded( + flex: 2, + child: Container( + child: InkWell( + onTap: () => easyThrottle('BottomAnnotationSwitch', () { + // if (jobNotesModel != null) { + // return AchievementView( + // elevation: 5, + // duration: Duration(seconds: 1), + // title: "笔记回显提示", + // subTitle: "当前正处于笔记回显", + // color: Theme.of(context).primaryColor, + // ).show(context); + // } + // easyThrottle('setSwitchBrush', () => ref.read(annotationGraffitiSwitchProvider.notifier).setSwitchBrush()); + _logicControl.opControl.value = !_logicControl.opControl.value; + }), + child: Icon( + const IconData(0xe639, fontFamily: "AlibabaIcon"), + size: iconSize, + color: _logicControl.opControl.value ? actionColor : defaultColor, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/making_school_asignment_app/lib/page/home_page/children/homework_review/components/dropdown_switch_students_type.dart b/making_school_asignment_app/lib/page/home_page/children/homework_review/components/dropdown_switch_students_type.dart new file mode 100644 index 0000000..da4ef23 --- /dev/null +++ b/making_school_asignment_app/lib/page/home_page/children/homework_review/components/dropdown_switch_students_type.dart @@ -0,0 +1,186 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart'; +import 'package:making_school_asignment_app/common/utils/utils.dart'; +import 'package:making_school_asignment_app/page/global_widget/my_text.dart'; +import 'package:making_school_asignment_app/page/home_page/children/homework_review/configuration_files/index.dart'; + +/// 学生和页码切换 +class DropdownSwitchStudentsType extends StatefulWidget { + const DropdownSwitchStudentsType({super.key}); + + @override + State createState() => _DropdownSwitchStudentsTypeState(); +} + +class _DropdownSwitchStudentsTypeState extends State { + final logic = Get.find(); + final sateData = Get.find().state; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(bottom: 2.r, left: 12.r, right: 12.r), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: const Color.fromRGBO(46, 91, 255, 0.2), + offset: Offset(2.w, 2.h), //阴影y轴偏移量 + blurRadius: 14, //阴影模糊程度 + spreadRadius: 0.5, //阴影扩散程度 + ) + ], + ), + child: Row( + children: [ + Expanded( + flex: 3, + child: Container( + padding: EdgeInsets.only(left: 10.w), + decoration: BoxDecoration( + color: const Color.fromRGBO(244, 244, 244, 1), + borderRadius: BorderRadius.circular(4.r), + ), + child: DropdownButton( + isExpanded: true, + underline: Container(), + padding: EdgeInsets.only(right: 4.w), + icon: const Icon(Icons.keyboard_arrow_down_rounded), + value: 1, // TODO 数据 + hint: const Text('请选择作业页码'), // 锚点的显示文本 + items: [1, 2, 3].map((e) { + // 数据 + return DropdownMenuItem( + value: e, + child: quickText('$e页', color: const Color.fromRGBO(79, 79, 79, 1), size: 14.sp), + ); + }).toList(), + onChanged: (value) { + // _useSwitchStudentAndType.currentTab.value = _useSwitchStudentAndType.tabs.value.firstWhere((element) => element.pageIndex == value); + }, + ), + ), + ), + const Expanded(flex: 1, child: SizedBox()), + Expanded( + flex: 4, + child: Stack( + children: [ + Container( + padding: EdgeInsets.only(left: 13.w), + decoration: BoxDecoration( + color: const 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: const Icon(Icons.keyboard_arrow_down_rounded), + value: '汪杨', + underline: Container(), + isExpanded: true, + items: ['汪杨'].map((e) { + return DropdownMenuItem( + value: e, + child: quickText(e, color: const Color.fromRGBO(79, 79, 79, 1), size: 14.sp), + ); + }).toList(), + hint: const 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: true ? const Color.fromRGBO(76, 199, 147, 1) : const Color.fromRGBO(164, 164, 164, 1), + ), + quickText('优先', size: 4.sp, color: Colors.white), + ], + ), + ), + ], + ), + ), + const Expanded(flex: 1, child: SizedBox()), + Expanded( + flex: isPad() ? 4 : 5, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + // if (_useSwitchStudentAndType.isFinish.value && + // _useSwitchStudentAndType.tabs.value.firstWhereOrNull((e) => e.finishCount < e.total) != null) + Expanded( + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.flip_camera_android_outlined, size: 10.sp, color: Theme.of(context).primaryColor.withOpacity(0.8)), + SizedBox(width: 1.w), + InkWell( + onTap: () => easyThrottle( + 'DO_PAPERS_JOB_CONTINUE_TO_REVIEW', + () { + // var _currentTabNew = _useSwitchStudentAndType.tabs.value.firstWhere((e) => e.finishCount < e.total); + // _useSwitchStudentAndType.eventFire(model: MarkingTextQuestionJobTabParamsBus(taskId, _currentTabNew.pageIndex)); + }, + ), + child: quickText( + '继续批阅', + size: 10.sp, + decoration: TextDecoration.underline, + color: Theme.of(context).primaryColor.withOpacity(0.9), + ), + ), + SizedBox(width: 2.w), + ], + ), + ), + Expanded( + child: InkWell( + onTap: () => easyThrottle('DO_PAPERS_JOB_HISTORICAL_HOMEWORK', () { + // JobConcernedWithStudent? _studentModel = _useSwitchStudentAndType.currentStudent.value; + // if (_studentModel == null) return; + // String url = + // '${RouterManager.jobPersonalDetailPath}?studentId=${_studentModel.studentId}&studentName=${Uri.encodeComponent(_studentModel.studentName)}'; + // RouterManager.router.navigateTo(context, url, transition: getTransition()); + }), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Icon(Icons.location_history, size: 12.sp, color: const Color.fromRGBO(104, 103, 103, 1)), + SizedBox(width: 1.w), + quickText( + '历史作业', + size: 10.sp, + decoration: TextDecoration.underline, + color: Theme.of(context).primaryColor.withOpacity(0.8), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/making_school_asignment_app/lib/page/home_page/children/homework_review/components/favorite_widget.dart b/making_school_asignment_app/lib/page/home_page/children/homework_review/components/favorite_widget.dart new file mode 100644 index 0000000..608a118 --- /dev/null +++ b/making_school_asignment_app/lib/page/home_page/children/homework_review/components/favorite_widget.dart @@ -0,0 +1,104 @@ +import 'dart:async'; +import 'package:get/get.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:making_school_asignment_app/common/api/retrofit_client.dart'; +import 'package:making_school_asignment_app/common/job/marking_models/favor_param.dart'; +import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart'; +import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart'; +import 'package:making_school_asignment_app/common/utils/toast_utils.dart'; +import 'package:making_school_asignment_app/page/home_page/children/homework_review/configuration_files/index.dart'; + +/// 自定义收藏组件 +/// isFavorite 默认是否收藏 +/// favoriteNum 默认收藏次数 +class FavoriteWidget extends StatefulWidget { + const FavoriteWidget({super.key}); + + @override + State createState() => _FavoriteState(); +} + +class _FavoriteState extends State with RequestToolMixin { + late StreamSubscription _listen; + final _controller = Get.find(); + final sateData = Get.find().state; + RxBool favorite = false.obs; // 初始化数据 + + @override + void initState() { + _listen = _controller.state.data.listen((e) { + getData(); // 更新数据 + }); + super.initState(); + } + + @override + void dispose() { + _listen.cancel(); + super.dispose(); + } + + Future getData() async { + // int taskId, int studentId, int paperId + // try { + // RestClient _client = await getClient(); + // BaseStructureResult res = await _client.getJobCollect(taskId, studentId, paperId); + // if (res.success) return JobCollectParams(paperId, taskId, studentId, res.data ?? false); + // } catch (e) {} + + // return null; + } + + Future toFavorite() async { + try { + // ToastUtils.showLoading(); + RetrofitClient _client = await getClient(); + // _client.toFavStudent(FavorParam(homeworkId: sateData.homeworkId, studentId: null, templateId: null, questionNo: null, isFav: null)); + // favoriteVal.isCollect = !favoriteVal.isCollect; + // favoriteVal.paperId = _eventModel!.paperId; + // BaseStructureResult res = await _client.toJobFavoriteCancel(favoriteVal); + // if (res.success) { + // _future = getData(favoriteVal.taskId, favoriteVal.studentId, _eventModel!.paperId); + // widget.call(); + // toUpState(setState, () {}, mounted); + // } else { + // ToastUtils.showError(res.message ?? '操作失败,请重试'); + // } + } catch (e) { + } finally { + ToastUtils.dismiss(); + } + } + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 4.r), + child: InkWell( + onTap: () => easyThrottle('homework_review_collect_btn', () => toFavorite(), duration: const Duration(milliseconds: 500)), + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + const IconData(0xe63c, fontFamily: "AlibabaIcon"), + size: 18.sp, + color: favorite.value ? const Color.fromRGBO(255, 172, 48, 1) : const Color.fromRGBO(164, 164, 164, 1), + ), + SizedBox(width: 6.w), + ], + ), + ), + ); + } +} + +class JobQuestionsSwitch extends Object { + final int taskId; + final int studentId; + final int paperId; + + JobQuestionsSwitch(this.taskId, this.studentId, this.paperId); +} diff --git a/making_school_asignment_app/lib/page/home_page/children/homework_review/configuration_files/index.dart b/making_school_asignment_app/lib/page/home_page/children/homework_review/configuration_files/index.dart index 6a23557..1ebfd78 100644 --- a/making_school_asignment_app/lib/page/home_page/children/homework_review/configuration_files/index.dart +++ b/making_school_asignment_app/lib/page/home_page/children/homework_review/configuration_files/index.dart @@ -1,11 +1,20 @@ -import 'package:get/get.dart'; -import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart'; +import 'dart:async'; +import 'package:get/get.dart'; +import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_param.dart'; +import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_result.dart'; +import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart'; +import 'package:making_school_asignment_app/common/utils/toast_utils.dart'; + +// 数据 class HomeworkReviewState { HomeworkReviewState(); - late String homeworkId; - late String homeworkName; + // late String homeworkId; + // late String homeworkName; + late Rx param; + late Rx data; + // late String dateEnd = ''; // late int knowledgeId = 0; // late RxList dataList = RxList(); @@ -13,6 +22,17 @@ class HomeworkReviewState { // late RxString knowledgeName = ''.obs; } +// 批注控制器 +class HomeworkReviewAnnotationsControlState { + HomeworkReviewAnnotationsControlState(); + // late RxBool backStep = false.obs; // 返回上一步 + + RxBool pen = true.obs; // 笔 + RxBool gestureMove = false.obs; // 手势移动 + RxBool clearAll = false.obs; // 清空全部 + RxBool opControl = true.obs; // 批注控制器开关 +} + class HomeworkReviewBinding extends Bindings { @override void dependencies() => Get.lazyPut(() => HomeworkReviewLogic()); @@ -20,18 +40,30 @@ class HomeworkReviewBinding extends Bindings { class HomeworkReviewLogic extends GetxController with RequestToolMixin { final HomeworkReviewState state = HomeworkReviewState(); + final HomeworkReviewAnnotationsControlState annotationState = HomeworkReviewAnnotationsControlState(); @override void onInit() { super.onInit(); - state.homeworkId = Get.arguments['homeworkId']; - state.homeworkName = Get.arguments['homeworkName']; + state.param = DoPaperDetailsParam( + homeworkId: Get.arguments['homeworkId'], + homeworkName: Get.arguments['homeworkName'], + classId: Get.arguments['classId'], + subject: Get.arguments['subject'], + ).obs; + state.data = Rx(null); getList(); } void getList() async { - // List data = await getClient().getKnowledgeReportDetail(state.dateStart, state.dateEnd, state.knowledgeId); - // state.dataList.value = data; - // EasyLoading.dismiss(); + var timerControl = Timer(const Duration(milliseconds: 300), () => ToastUtils.showLoading()); + try { + DoPaperDetailsResult? data = await getClient().getDoPaperDetails(state.param.value); + state.data.value = data; + } finally { + if (timerControl.isActive) timerControl.cancel(); + + ToastUtils.dismiss(); + } } } diff --git a/making_school_asignment_app/lib/page/home_page/children/homework_review/index.dart b/making_school_asignment_app/lib/page/home_page/children/homework_review/index.dart index 0c895ae..1f34bff 100644 --- a/making_school_asignment_app/lib/page/home_page/children/homework_review/index.dart +++ b/making_school_asignment_app/lib/page/home_page/children/homework_review/index.dart @@ -1,7 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:making_school_asignment_app/page/global_widget/ReturnToHomepage.dart'; import 'package:making_school_asignment_app/page/global_widget/my_text.dart'; +import 'components/bottom_operation_bar.dart'; +import 'components/dropdown_switch_students_type.dart'; +import 'components/favorite_widget.dart'; import 'configuration_files/index.dart'; class HomeworkReview extends StatefulWidget { @@ -15,13 +21,47 @@ class _HomeworkReviewState extends State { final logic = Get.find(); final sateData = Get.find().state; + @override + void initState() { + WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + super.initState(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: quickText(sateData.homeworkName), + // titleSpacing: 0, + leading: IconButton(icon: const Icon(Icons.arrow_back_ios), onPressed: () => {}), + iconTheme: const IconThemeData(color: Colors.black), + title: quickText(sateData.param.value.homeworkName), + backgroundColor: Colors.white, + elevation: 0, + actions: [ + const FavoriteWidget(), + SizedBox(width: 5.w), + const ReturnToHomepage(), + ], + ), + body: SafeArea( + child: Column( + children: [ + // 下拉切换 + const DropdownSwitchStudentsType(), + SizedBox(height: 1.h), + // Expanded(child: ExamPaperAndScoringView(taskId: taskId, className: className)), + const Expanded(child: SizedBox()), + const BottomAnnotationSwitch(), + ], + ), ), - body: Container(), ); } } diff --git a/making_school_asignment_app/pubspec.lock b/making_school_asignment_app/pubspec.lock index c319b35..ff1f028 100644 --- a/making_school_asignment_app/pubspec.lock +++ b/making_school_asignment_app/pubspec.lock @@ -265,6 +265,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.3.9" + easy_debounce: + dependency: "direct main" + description: + name: easy_debounce + sha256: f082609cfb8f37defb9e37fc28bc978c6712dedf08d4c5a26f820fa10165a236 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.3" equatable: dependency: transitive description: diff --git a/making_school_asignment_app/pubspec.yaml b/making_school_asignment_app/pubspec.yaml index 7c576c7..a07fdb9 100644 --- a/making_school_asignment_app/pubspec.yaml +++ b/making_school_asignment_app/pubspec.yaml @@ -79,6 +79,7 @@ dependencies: flutter_staggered_grid_view: ^0.7.0 dropdown_button2: ^2.3.9 syncfusion_flutter_datepicker: ^25.2.5 + easy_debounce: ^2.0.3 # 防抖节流 dev_dependencies: flutter_test: