diff --git a/marking_app/lib/common/model/job/job_student_history.dart b/marking_app/lib/common/model/job/job_student_history.dart new file mode 100644 index 0000000..c9081be --- /dev/null +++ b/marking_app/lib/common/model/job/job_student_history.dart @@ -0,0 +1,144 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'job_student_history.g.dart'; + + +@JsonSerializable() +class JobStudentHistory extends Object { + + @JsonKey(name: 'correctRate') + int correctRate; + + @JsonKey(name: 'objectiveCorrectRate') + int objectiveCorrectRate; + + @JsonKey(name: 'subjectiveCorrectRate') + int subjectiveCorrectRate; + + @JsonKey(name: 'pagedList') + PagedList pagedList; + + JobStudentHistory(this.correctRate,this.objectiveCorrectRate,this.subjectiveCorrectRate,this.pagedList,); + + factory JobStudentHistory.fromJson(Map srcJson) => _$JobStudentHistoryFromJson(srcJson); + + Map toJson() => _$JobStudentHistoryToJson(this); + +} + + +@JsonSerializable() +class PagedList extends Object { + + @JsonKey(name: 'page') + int page; + + @JsonKey(name: 'pageSize') + int pageSize; + + @JsonKey(name: 'total') + int total; + + @JsonKey(name: 'totalPages') + int totalPages; + + @JsonKey(name: 'items') + List items; + + @JsonKey(name: 'hasPrevPage') + bool hasPrevPage; + + @JsonKey(name: 'hasNextPage') + bool hasNextPage; + + PagedList(this.page,this.pageSize,this.total,this.totalPages,this.items,this.hasPrevPage,this.hasNextPage,); + + factory PagedList.fromJson(Map srcJson) => _$PagedListFromJson(srcJson); + + Map toJson() => _$PagedListToJson(this); + +} + + +@JsonSerializable() +class Items extends Object { + + @JsonKey(name: 'id') + int id; + + @JsonKey(name: 'name') + String name; + + @JsonKey(name: 'subject') + int subject; + + @JsonKey(name: 'subjectName') + String subjectName; + + @JsonKey(name: 'publishTime') + String publishTime; + + @JsonKey(name: 'isPaper') + bool isPaper; + + @JsonKey(name: 'studentJobId') + int studentJobId; + + @JsonKey(name: 'correctRate') + int correctRate; + + @JsonKey(name: 'objectiveCorrectRate') + int objectiveCorrectRate; + + @JsonKey(name: 'objectiveDtls') + List objectiveDtls; + + @JsonKey(name: 'subjectiveCorrectRate') + int subjectiveCorrectRate; + + @JsonKey(name: 'subjectiveDtls') + List subjectiveDtls; + + Items(this.id,this.name,this.subject,this.subjectName,this.publishTime,this.isPaper,this.studentJobId,this.correctRate,this.objectiveCorrectRate,this.objectiveDtls,this.subjectiveCorrectRate,this.subjectiveDtls,); + + factory Items.fromJson(Map srcJson) => _$ItemsFromJson(srcJson); + + Map toJson() => _$ItemsToJson(this); + +} + + +@JsonSerializable() +class SubjectiveDtls extends Object { + + @JsonKey(name: 'studentJobId') + int studentJobId; + + @JsonKey(name: 'questionId') + int questionId; + + @JsonKey(name: 'questionNo') + String questionNo; + + @JsonKey(name: 'questionType') + int questionType; + + @JsonKey(name: 'useTime') + int useTime; + + @JsonKey(name: 'score') + double score; + + @JsonKey(name: 'annotateTime') + String? annotateTime; + + @JsonKey(name: 'state') + int state; + + SubjectiveDtls(this.studentJobId,this.questionId,this.questionNo,this.questionType,this.useTime,this.score,this.annotateTime,this.state,); + + factory SubjectiveDtls.fromJson(Map srcJson) => _$SubjectiveDtlsFromJson(srcJson); + + Map toJson() => _$SubjectiveDtlsToJson(this); + +} diff --git a/marking_app/lib/pages/homework_correction/job_personal_detail.dart b/marking_app/lib/pages/homework_correction/job_personal_detail.dart new file mode 100644 index 0000000..6a58867 --- /dev/null +++ b/marking_app/lib/pages/homework_correction/job_personal_detail.dart @@ -0,0 +1,705 @@ +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: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_student_history.dart'; +import 'package:marking_app/pages/homework_correction/widget/personal_detail_topbar.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'; + +class JobPersonalDetail extends StatefulWidget { + final String studentName; + final int studentId; + + const JobPersonalDetail( + {Key? key, required this.studentName, required this.studentId}) + : super(key: key); + + @override + _JobPersonalDetailState createState() => _JobPersonalDetailState(); +} + +class _JobPersonalDetailState extends State + with CommonMixin, TickerProviderStateMixin { + @override + int page = 1; + int pageSize = 10; + int totalPages = 0; + JobStudentHistory? studentData; + bool isJob = true; + late TabController tabController; + String startDataTime = ''; + String endDataTime = ''; + String customTimeStr = '自定义'; + List dataList = []; + late final EasyRefreshController refreshController; + + @override + void initState() { + super.initState(); + refreshController = EasyRefreshController(); + tabController = TabController(length: 4, vsync: this); + EasyLoading.show(status: 'loading...'); + getList(); + } + + @override + void dispose() { + super.dispose(); + tabController.dispose(); + } + + void getList() async { + RestClient _client = await getClient(); + BaseStructureResult res = + await _client.getStudentJobHistory(widget.studentId, !isJob, + startDataTime, endDataTime, page, pageSize); + setState(() { + studentData = res.data!; + if (page == 1) { + dataList = studentData!.pagedList.items; + } else { + dataList = [...dataList, ...studentData!.pagedList.items]; + } + + totalPages = res.data!.pagedList.totalPages; + print('dataLists=${dataList.length}'); + print('totalpages=$totalPages'); + }); + EasyLoading.dismiss(); + } + + @override + Widget build(BuildContext context) { + bool isPadFlag = isPad(); + if (studentData == null) return Container(); + return Scaffold( + backgroundColor: Color.fromRGBO(245, 245, 245, 1), + appBar: AppBar( + backgroundColor: Colors.white, + title: Text('${widget.studentName}作业详情', + 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(), + ), + elevation: 0, + ), + body: Column( + children: [ + Container( + height: 1.r, + width: MediaQuery.of(context).size.width, + color: Color.fromRGBO(179, 179, 179, 0.3), + ), + Container( + color: Colors.white, + padding: EdgeInsets.symmetric(vertical: 2.r), + child: Row( + children: [ + InkWell( + onTap: () { + EasyLoading.show(status: 'loading...'); + setState(() { + isJob = true; + page = 1; + totalPages = 0; + }); + getList(); + }, + child: Container( + width: (MediaQuery.of(context).size.width - 1.r) / 2, + height: 40.r, + child: Center( + child: Text( + '作业', + style: TextStyle( + fontSize: 14.sp, + color: isJob ? Color(0xFF7491FD) : Color(0xFF505E6E)), + )), + ), + ), + Container( + height: 40.r, + width: 1.r, + color: Color.fromRGBO(179, 179, 179, 0.3), + ), + InkWell( + onTap: () { + EasyLoading.show(status: 'loading...'); + setState(() { + isJob = false; + page = 1; + totalPages = 0; + }); + getList(); + }, + child: Container( + width: (MediaQuery.of(context).size.width - 1.r) / 2, + height: 40.r, + child: Center( + child: Text( + '考试', + style: TextStyle( + fontSize: 14.sp, + color: + !isJob ? Color(0xFF7491FD) : Color(0xFF505E6E)), + )), + ), + ), + ], + ), + ), + progressBar(context, + title: '客观题正确率:', + color: Color(0xFFB8C7FF), + percent: studentData!.objectiveCorrectRate / 100, + padingEdg: EdgeInsets.symmetric(horizontal: 14.r), + marginEdg: EdgeInsets.only(top: 8.h)), + progressBar(context, + title: '主观题正确率:', + color: Color(0xFFB8C7FF), + percent: studentData!.subjectiveCorrectRate / 100, + padingEdg: EdgeInsets.symmetric(horizontal: 14.r), + marginEdg: EdgeInsets.only(top: 8.h)), + progressBar(context, + title: '总正确率:', + color: Color(0xFFB8C7FF), + percent: studentData!.correctRate / 100, + padingEdg: EdgeInsets.symmetric(horizontal: 14.r), + marginEdg: EdgeInsets.only(top: 8.h)), + SizedBox( + height: 10.r, + ), + jobConditionFilter(context, + controller: tabController, + jobType: isJob ? 1 : 2, + 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 { + print('startTime=$startTime'); + print('endDataTime=$endTime'); + 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) { + Items item = dataList[index]; + return 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: [ + Container( + width: 32.w, + height: 18.h, + alignment: Alignment.center, + padding: EdgeInsets.only(left: 2.w), + decoration: BoxDecoration( + color: isJob + ? const Color.fromRGBO( + 104, 136, 253, 1) + : Color(0xFFFFA116), + 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: Text( + isJob ? '作业' : '考试', + style: TextStyle( + fontSize: 10.sp, + color: Colors.white), + ), + ), + Expanded( + child: Text( + item.name, + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF464646)), + )), + // SizedBox(width: 5.r,), + // Text('2024.1',style: TextStyle(fontSize: 12.sp,color: Color(0xFF5B5B5B)),), + + Container( + width: 40.r, + height: 20.r, + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular(4.r)), + border: Border.all( + width: 1.r, + color: Color(0xFF4CC793)), + ), + child: Center( + child: Text( + item.subjectName, + style: TextStyle( + fontSize: 10.sp, + color: Color(0xFF4CC793)), + )), + ), + ], + ), + SizedBox( + height: 10.r, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '客:', + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF5B5B5B)), + ), + SizedBox( + width: 5.r, + ), + item.objectiveDtls.length > 0 + ? Expanded( + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.start, + spacing: 8, + runSpacing: 5, + children: List.generate( + item.objectiveDtls.length, + (i) { + SubjectiveDtls subjective = + item.objectiveDtls[i]; + return Container( + width: 20.r, + height: 20.r, + decoration: BoxDecoration( + color: Colors.transparent, + border: Border.all( + width: 1.r, + color: Color( + 0xFF4CC793)), + borderRadius: + BorderRadius.all( + Radius.circular( + 10.r))), + child: Center( + child: Text( + subjective.questionNo, + style: TextStyle( + fontSize: 10.r, + color: + Color(0xFF4CC793)), + )), + ); + }), + ), + ) + : Text( + '无', + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF5B5B5B)), + ), + ], + ), + SizedBox( + height: 10.r, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '主:', + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF5B5B5B)), + ), + SizedBox( + width: 5.r, + ), + item.subjectiveDtls.length > 0 + ? Expanded( + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.start, + spacing: 8, + runSpacing: 5, + children: List.generate( + item.subjectiveDtls.length, + (i) { + SubjectiveDtls subjective = + item.subjectiveDtls[i]; + return Container( + width: 20.r, + height: 20.r, + decoration: BoxDecoration( + color: Colors.transparent, + border: Border.all( + width: 1.r, + color: subjective + .state == + 0 + ? Color( + 0xFFDDDDDD) + : subjective.state == + 3 + ? Color( + 0xFF666666) + : subjective.state == + 1 + ? Color( + 0xFFFF7474) + : Color( + 0xFF4CC793)), + borderRadius: + BorderRadius.all( + Radius.circular( + 10.r))), + child: Center( + child: Text( + subjective.questionNo, + style: TextStyle( + fontSize: 10.r, + color: subjective + .state == + 0 + ? Color(0xFFDDDDDD) + : subjective.state == + 3 + ? Color( + 0xFF666666) + : subjective.state == + 1 + ? Color( + 0xFFFF7474) + : Color( + 0xFF4CC793)), + )), + ); + }), + ), + ) + : Text( + '无', + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF5B5B5B)), + ), + ], + ), + progressBar(context, + title: '客观题正确率:', + color: Color(0xFF90E0BE), + percent: item.objectiveCorrectRate / 100, + padingEdg: EdgeInsets.zero, + marginEdg: EdgeInsets.only(top: 8.h)), + progressBar(context, + title: '主观题正确率:', + color: Color(0xFF90E0BE), + percent: item.subjectiveCorrectRate / 100, + padingEdg: EdgeInsets.zero, + marginEdg: EdgeInsets.only(top: 8.h)), + 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: [ + if (title == '总正确率:') + quickText('确率', color: Colors.transparent, size: fontSize), + 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(0xFF464646)) + ], + ), + ), + ), + ], + ), + ); +} + +/// 已完成作业条件筛选栏 +@hwidget +Widget jobConditionFilter(BuildContext context, + {required TabController controller, + required int jobType, + PickerDateRange? customTime, + required Function refreshTime, + required String customTimeStr, + required Function(String? startTime, String? endTime) onTimeFilter}) { + var customTimeState = PickerDateRange(null, null); + print('customTime=${customTime}'); + if (customTime != null) { + customTimeState = PickerDateRange( + customTime!.startDate != null ? customTime!.startDate : null, + customTime!.endDate != null ? customTime!.endDate : null); + } + + 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)); // 减去天数差,得到本周一的时间 + } + + 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)); // 加上天数差减一,得到本周日的时间 + } + + 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: PersonalDetailTopBar( + controller: controller, + customTimeStr: customTimeStr, + onTap: (int val) async { + switch (val) { + case 0: // 全部 + onTimeFilter(null, null); + break; + case 1: // 近一周 + onTimeFilter( + getWeekStartDate().toString().substring(0, 10), + getWeekEndDate().toString().substring(0, 10), + ); + break; + case 2: // 近一个月 + 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!; + } + }, + ), + ); +} 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 b0aa0e1..90e8c70 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 @@ -7,6 +7,7 @@ import 'package:marking_app/common/model/common/base_structure_result.dart'; import 'package:marking_app/common/model/job/job_level_set_params.dart'; import 'package:marking_app/common/model/job/job_review_submission.dart'; import 'package:marking_app/common/model/job/job_student_level.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'; @@ -153,7 +154,7 @@ class _JobPriorityReviewSetState extends State ),children: List.generate(levelList.length, (index) { JobStudentLevel item = levelList[index]; return Container( - padding: EdgeInsets.symmetric(horizontal: 15.r), + padding: EdgeInsets.symmetric(horizontal: 10.r), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(10.r)), color: Colors.white, @@ -161,7 +162,8 @@ class _JobPriorityReviewSetState extends State child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(item.studentName,style: TextStyle(fontSize: 12.sp,color: Color(0xFF6888FD)),), + Expanded(child: Text(item.studentName,style: TextStyle(fontSize: 12.sp,color: Color(0xFF6888FD)),)), + tabIndex == 0?InkWell( onTap: (){ setState(() { @@ -201,6 +203,24 @@ class _JobPriorityReviewSetState extends State child:Text('设为优先',style: TextStyle(fontSize: 10.sp,color: isClicking?Color(0xFFFFFFFF):Color(0xFF6888FD)),), ), ), + ), + 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)),), + ), + ), ) ], ), @@ -217,7 +237,7 @@ class _JobPriorityReviewSetState extends State child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(item.studentName,style: TextStyle(fontSize: 12.sp,color: Color(0xFF6888FD)),), + Expanded(child: Text(item.studentName,style: TextStyle(fontSize: 12.sp,color: Color(0xFF6888FD)),)), tabIndex == 0?InkWell( onTap: (){ setState(() { @@ -254,6 +274,24 @@ class _JobPriorityReviewSetState extends State child:Text('设为优先',style: TextStyle(fontSize: 10.sp,color: Color(0xFF6888FD)),), ), ), + ), + 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/quick_check_personal.dart b/marking_app/lib/pages/homework_correction/quick_check_personal.dart index 7bd6324..e514672 100644 --- a/marking_app/lib/pages/homework_correction/quick_check_personal.dart +++ b/marking_app/lib/pages/homework_correction/quick_check_personal.dart @@ -6,6 +6,7 @@ import 'package:marking_app/common/model/common/base_structure_result.dart'; import 'package:marking_app/common/model/job/job_data_report.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/request/rest_client.dart'; import 'package:marking_app/utils/toast_utils.dart'; @@ -69,6 +70,25 @@ class _QuickCheckPersonalState extends State icon: Icon(Icons.arrow_back_ios, color: Colors.black), onPressed: () => Navigator.of(context).pop(), ), + actions: [ + Title( + + color: Color(0xFF6888FD), + child: Container( + child: InkWell( + onTap: (){ + RouterManager.router.navigateTo(context, '${RouterManager.jobPersonalDetailPath}?studentId=${widget.studentId}&studentName=${Uri.encodeComponent(studentInfo!.studentName!)}'); + }, + child: Padding( + padding: EdgeInsets.only(right: 16.r), + child: Text('历史作业',style: TextStyle(fontSize: 12.sp,color: Color(0xFF6888FD)),), + ), + ), + alignment: Alignment.center, + ), + ), + + ], ), body: SingleChildScrollView( diff --git a/marking_app/lib/pages/homework_correction/widget/personal_detail_topbar.dart b/marking_app/lib/pages/homework_correction/widget/personal_detail_topbar.dart new file mode 100644 index 0000000..a691559 --- /dev/null +++ b/marking_app/lib/pages/homework_correction/widget/personal_detail_topbar.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class PersonalDetailTopBar extends StatefulWidget { + final TabController controller; + final ValueChanged? onTap; + final String customTimeStr; + const PersonalDetailTopBar({Key? key,required this.controller, this.onTap, required this.customTimeStr}) : super(key: key); + + @override + State createState() => _PersonalDetailTopBarState(); +} + +class _PersonalDetailTopBarState extends State { + @override + void initState(){ + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + decoration: BoxDecoration( + border: Border(bottom: BorderSide(width: 1.r,color: Color(0xFFCCCCCC))) + ), + child: TabBar( + controller: widget.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: widget.onTap, + tabs: [ + const Tab(text: '全部'), + const Tab(text: '近一周'), + const Tab(text: '近一月'), + Tab(text: widget.customTimeStr), + ], + ), + ); + } +} diff --git a/marking_app/lib/routes/RouterManager.dart b/marking_app/lib/routes/RouterManager.dart index cd32be6..986c0a4 100644 --- a/marking_app/lib/routes/RouterManager.dart +++ b/marking_app/lib/routes/RouterManager.dart @@ -14,6 +14,7 @@ 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/do_papers_job_exam.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'; import 'package:marking_app/pages/homework_correction/job_student_group.dart'; @@ -72,6 +73,7 @@ class RouterManager { static const String quickCheckPersonalPath = '/homework_correction/quick_check_personal'; static const String jobPriorityReviewSetPath = '/homework_correction/job_priority_review_set'; static const String jobStudentGroupPath = '/homework_correction/job_student_group'; + static const String jobPersonalDetailPath = '/homework_correction/job_personal_detail'; // TheMine @@ -332,6 +334,14 @@ class RouterManager { }, ); + // 优先配置个人详情 + static final _jobPersonalDetailPathHandler = Handler( + handlerFunc: (BuildContext? context, Map> params) { + String studentName = params['studentName']![0]; + int studentId = int.parse(params['studentId']![0]); + return JobPersonalDetail(studentId: studentId,studentName:studentName); + }, + ); // 开始阅卷页面 // static final _doMarkingPapers = Handler(handlerFunc: (BuildContext? context, Map> params) => MarkingPapers()); @@ -375,6 +385,7 @@ class RouterManager { 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); // getTransition() diff --git a/marking_app/lib/utils/request/rest_client.dart b/marking_app/lib/utils/request/rest_client.dart index d4420d0..8fc35b9 100644 --- a/marking_app/lib/utils/request/rest_client.dart +++ b/marking_app/lib/utils/request/rest_client.dart @@ -29,6 +29,7 @@ import 'package:marking_app/common/model/job/job_report_model.dart'; import 'package:marking_app/common/model/job/job_report_question_deatil_model.dart'; import 'package:marking_app/common/model/job/job_review_submission.dart'; import 'package:marking_app/common/model/job/job_student_goups.dart'; +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'; @@ -344,4 +345,17 @@ 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); + + // 作业 => 学生作业详情历史 + @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, + ); + + }