From a8b8ba5d817e7ce97981073a388a9deb8e012ef9 Mon Sep 17 00:00:00 2001 From: "1147192855@qq.com" <1147192855@qq.com> Date: Mon, 26 Feb 2024 09:48:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BD=9C=E4=B8=9A=E6=8A=A5?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .../assets/images/2.0x/job_report_scale.png | Bin 0 -> 6102 bytes .../assets/images/3.0x/job_report_scale.png | Bin 0 -> 9894 bytes .../assets/images/4.0x/job_report_scale.png | Bin 0 -> 13713 bytes .../assets/images/job_report_scale.png | Bin 0 -> 2661 bytes .../model/job/job_report_join_class.dart | 45 + .../common/model/job/job_report_model.dart | 20 +- .../pages/homework_correction/job_report.dart | 1172 ++++++++++++++++- .../lib/utils/flutter_wave_loading.dart | 181 +++ .../lib/utils/request/rest_client.dart | 11 +- 10 files changed, 1410 insertions(+), 21 deletions(-) create mode 100644 marking_app/assets/images/2.0x/job_report_scale.png create mode 100644 marking_app/assets/images/3.0x/job_report_scale.png create mode 100644 marking_app/assets/images/4.0x/job_report_scale.png create mode 100644 marking_app/assets/images/job_report_scale.png create mode 100644 marking_app/lib/common/model/job/job_report_join_class.dart create mode 100644 marking_app/lib/utils/flutter_wave_loading.dart diff --git a/.gitignore b/.gitignore index 4f58acc..57cd9a1 100644 --- a/.gitignore +++ b/.gitignore @@ -185,3 +185,5 @@ marking_app/lib/pages/homework_correction/eventBus/job_do_papers_switch_operatio marking_app/lib/pages/homework_correction/eventBus/job_notes_view_bus.g.dart marking_app/lib/pages/homework_correction/eventBus/job_notes_view_bus.g.dart marking_app/lib/common/model/job/job_report_model.g.dart +marking_app/lib/common/model/job/job_report_join_class.g.dart +marking_app/lib/pages/homework_correction/job_report.g.dart diff --git a/marking_app/assets/images/2.0x/job_report_scale.png b/marking_app/assets/images/2.0x/job_report_scale.png new file mode 100644 index 0000000000000000000000000000000000000000..189f2923fd587a57d5edf858ea8abc1c3a53f587 GIT binary patch literal 6102 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 literal 0 HcmV?d00001 diff --git a/marking_app/assets/images/3.0x/job_report_scale.png b/marking_app/assets/images/3.0x/job_report_scale.png new file mode 100644 index 0000000000000000000000000000000000000000..74d4d1e8077ec975af670d94637819b2bc71ab65 GIT binary patch literal 9894 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 KsFtW$J^vp~{w|#W literal 0 HcmV?d00001 diff --git a/marking_app/assets/images/job_report_scale.png b/marking_app/assets/images/job_report_scale.png new file mode 100644 index 0000000000000000000000000000000000000000..458b5422c2da961daa91f086de1ac6c84e352d69 GIT binary patch literal 2661 zcmV-r3YztaP)G=Z-ct%KAvc}T$tJ*bqEMbgVJ^ge~4-wdU;^91wM~gXuttTV^fH%M(0q_VIBmf=( zg9N}MV2}WK1PszMSTXIz!^O}(c6-KSV=93@WwCMC@2&jL0&4?g@2y7e}WZeYOeE;aLJ>XR4Pm;;*uVf*)+Wo=yJc;y@tW#DopiF#w`Kq(mz#z-P%4GD#IwHjH{Bf?N7jiwJNKeV4fbua@TMSnv+PE#Ald`G) zw?g0TIpqy!^%ymc4Whqcl*NHTmOvT`KlB}*@WSZ{g^-5Vvz^cGahikQ&%}N+2xH39 z>zB_y%orRu5T44C!|(wph|0qqExy>tZckiFBPZ?W&fH>4;m_h&79Uv0Mt6U>#4i4< zzQA*?0uO2Be#SA;(0;*WXY2_-*^f&+}`~s7& zj9C@Fu#L&7EP?K#@79hx`EYUN8Ut@bdrrG|RAWbruhd}>L(0@c5PY`Bi|M($Bn^~B zea3;zyQX%}$#Eqsxu*o$58rvOT;*MQ3^kgYz@@C3nDfMOOx%^yyzY3&j1%`kWSXogq{Wws)T_?Np+ zY^*bz2-HRbY8l4M#g64e1LY+}`W`tj|1qU^H~jZH@U5b$F52iJ)gRFobEqV>5G==s zP)@gk=rjMlF0GzZH;2BVmeD$cRGv|Yb7#Cr*@>fg8hFHmBFmx1iS-3)rXvgb!B&u$ zUj*Ate02A@jowkR)gIC`W3m@>OOZw^r!j%np#GjhjN2;0ZTXb-z9(1e+tL+UIpwKq zN96L(phO-z&50}Ls55R5RRrGB=?hpTFIS8Lk2O&(G4#rbwG#5`92{2==qy#ml}(2@ zjZRrrE>J3ELHMds=9W*1bR^>nOjWvx>dan2T&W7%o$CB%Xgr2gvbLb>*Nrk9n*5?I zUR)}p?A+pDpou=XiU;&8Om!}Z^Uk1*F%yBgjvB+8N*y)#59p9YcHpX_De@Wso+9H) z*+tKp&!q3h$gdew!+4{z7V(tkHQy+Vu!t4Bit3A$iGQ}m7$sh%Jqj8n#|jif!~uDz z>qCeEQFiQpjbFwn@h%TpD5jM2{Otak0I*<+2L>G4jGW<*hsG%JcC`oMMNk*M8l%MP z+G~I}qi*b;8>2Us47LK~)0QRj(>GjeQ+4Zz0GjhT0@ zSZ)0#i_$x(_RDDjdg6eldDtrytwRCu0MjC7KwSV$sKB;kdH~n1{%=OqkX=m*QxlvJvf4MOQY6F$?hwb42@DK@ zjLJif7JsW2lK_Bbk%rhBr=bEmTCb2-VEPC&Sax1RN{cH5fA_DQ$yfrcZu3n&?R)AVex^jX~;t6={iDYWh(1) zbv1pDx>eA2o~W&TnNg6bKku%4#Fd~cD8&_-1OUJ@-~|Dwhte^m-Zdzi^N~}^hy|Qo z|Em-L*f3h?xu2DdDc>aO7!NsQ!~7t>)yA2gLR|%2#;C1_Y!74pc4o@zN^Ox*McN}~ zb@iAPrs-DH^Z`&Yrd^~gFppVnz4TIT1)wWbQ4jzC004kI3dfL-tsRws6R^7a)06-J zxtN9e%c3!5DN#oSuN1PnN{4)C�^C*lZPm?_m$gyu$S&iin zNLxSDRnU1%ZR-`Ol!w}&XNs1Q05pVATT#UZ0000~PN5i*-S8Pv#KKKDOC~=;zyY8p ziW=mYjgbRZdS}$t8D(^xOm;5_0ANMewq|__0?taPeL?nLOMkp&wMVQ@PGO9ms{pJL zA1&m@JY&*o>o=*k^4C+;w*t@rP+I{20D!8=jUjcsxD&r+ML1)~$DmXxtCSFlTYfn|rZekL%*pgf}l%ulV&sbIcXq44ho=deQ$W)+uK>&bU*!YGNMu#LsUoFbe*@=Wi`cXYv zfXWI$O+0CHBU6M+%q#%_0000$3&>Gf7en(BQ)O_UP>F`G0D$u3sI2LL6{0;>h|EIE zC^iH81OO-mJGL>W3}f|880?v+d&zIRBirkp2FafAm;eB4VvJE1m1k-4_%#3k00000 z006KiK|ET_i@%zttPXu706-z);h%jLb_%zI+#!)36n!xX0sxdjwee8(tcCawDRtIP TYz)Ge00000NkvXXu0mjfhFsy9 literal 0 HcmV?d00001 diff --git a/marking_app/lib/common/model/job/job_report_join_class.dart b/marking_app/lib/common/model/job/job_report_join_class.dart new file mode 100644 index 0000000..9260cfb --- /dev/null +++ b/marking_app/lib/common/model/job/job_report_join_class.dart @@ -0,0 +1,45 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'job_report_join_class.g.dart'; + +@JsonSerializable() +class JobReportJoinClass extends Object { + @JsonKey(name: 'schoolId') + int schoolId; + + @JsonKey(name: 'schoolName') + String schoolName; + + @JsonKey(name: 'gradeId') + int gradeId; + + @JsonKey(name: 'gradeName') + String gradeName; + + @JsonKey(name: 'graduationYear') + String graduationYear; + + @JsonKey(name: 'className') + String className; + + @JsonKey(name: 'toBeSubmitCount') + int toBeSubmitCount; + + @JsonKey(name: 'submitCount') + int submitCount; + + JobReportJoinClass( + this.schoolId, + this.schoolName, + this.gradeId, + this.gradeName, + this.graduationYear, + this.className, + this.toBeSubmitCount, + this.submitCount, + ); + + factory JobReportJoinClass.fromJson(Map srcJson) => _$JobReportJoinClassFromJson(srcJson); + + Map toJson() => _$JobReportJoinClassToJson(this); +} diff --git a/marking_app/lib/common/model/job/job_report_model.dart b/marking_app/lib/common/model/job/job_report_model.dart index b633b13..fc33424 100644 --- a/marking_app/lib/common/model/job/job_report_model.dart +++ b/marking_app/lib/common/model/job/job_report_model.dart @@ -8,13 +8,13 @@ class JobReportModel extends Object { int studentCount; @JsonKey(name: 'finishRate') - int finishRate; + double finishRate; @JsonKey(name: 'correctRate') - int correctRate; + double correctRate; @JsonKey(name: 'errorRate') - int errorRate; + double errorRate; @JsonKey(name: 'validCount') int validCount; @@ -81,7 +81,7 @@ class KnowledgeInfos extends Object { String knowledgeName; @JsonKey(name: 'rate') - int rate; + double rate; KnowledgeInfos( this.knowledgeId, @@ -112,13 +112,13 @@ class QuestionAnswerInfos extends Object { List finishInfos; @JsonKey(name: 'correctRate') - int correctRate; + double correctRate; @JsonKey(name: 'errorRate') - int errorRate; + double errorRate; @JsonKey(name: 'noAnswerRate') - int noAnswerRate; + double noAnswerRate; QuestionAnswerInfos( this.questionId, @@ -145,7 +145,7 @@ class FinishInfos extends Object { int finishCount; @JsonKey(name: 'correctRate') - int correctRate; + double correctRate; FinishInfos( this.title, @@ -170,10 +170,10 @@ class StudentAnswerInfos extends Object { int useTime; @JsonKey(name: 'correctRate') - int correctRate; + double correctRate; @JsonKey(name: 'finishRate') - int finishRate; + double finishRate; @JsonKey(name: 'noAnswerCount') int noAnswerCount; diff --git a/marking_app/lib/pages/homework_correction/job_report.dart b/marking_app/lib/pages/homework_correction/job_report.dart index 3ee26f8..ea04596 100644 --- a/marking_app/lib/pages/homework_correction/job_report.dart +++ b/marking_app/lib/pages/homework_correction/job_report.dart @@ -1,11 +1,19 @@ +import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.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_report_join_class.dart'; import 'package:marking_app/common/model/job/job_report_model.dart'; -import 'package:marking_app/utils/my_future_builder.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/linear_percent_indicator.dart'; + +import '../../utils/flutter_wave_loading.dart'; + +part 'job_report.g.dart'; /// 作业报告 class JobReport extends StatefulWidget { @@ -20,32 +28,1180 @@ class JobReport extends StatefulWidget { class _JobReportState extends State with CommonMixin { late Future _future; // 考试试卷 + List? involveClasses = []; + JobReportJoinClass? classData; + @override void initState() { - _future = getReport(widget.id); + getInvolveClasses(); + _future = getReport(); super.initState(); } - Future getReport(int jobid) async { + Future getInvolveClasses() async { try { RestClient _client = await getClient(); - BaseStructureResult data = await _client.getJobReport(jobid); - if (data.success) { + BaseStructureResult> result = await _client.getJobReportJoinClasses(widget.id); + if (result.success) { + toUpState(setState, () { + involveClasses = [JobReportJoinClass(-1, '全部', -1, '全部', '全部', '全部', -1, -1), ...(result.data ?? [])]; + }, mounted); + } + } catch (e) { + print(e); + } + } + + Future getReport() async { + try { + RestClient _client = await getClient(); + Map param = classData?.toJson() ?? {}; + param['jobid'] = widget.id; + BaseStructureResult data = await _client.getJobReport(param); + if (!data.success) { throw Exception(data.message ?? '获取报告失败'); } return data.data; - } catch (e) {} + } catch (e) { + print(e); + } + return null; } @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: Color.fromRGBO(245, 245, 245, 1), appBar: AppBar( - title: quickText(widget.title, size: 18.sp, color: Colors.white), + backgroundColor: Colors.white, + title: quickText(widget.title, size: 16.sp, color: Color.fromRGBO(51, 51, 51, 1)), + actions: [ + // 下拉框 + $DropdownSelection(involveClasses, classData, call: (JobReportJoinClass _classData) { + classData = _classData; + toUpState(setState, () => _future = getReport(), mounted); + }), + ], ), body: MyFutureBuilder.buildFutureBuilderOfSingleInstance(context, _future, (data) { - return Container(); + if (data == null) + return Container( + alignment: Alignment.center, + child: ElevatedButton( + child: quickText('点击再次发起请求', color: Colors.white), + onPressed: () { + toUpState(setState, () => _future = getReport(), mounted); + }, + ), + ); + + return ListView( + padding: EdgeInsets.symmetric(vertical: 4.h, horizontal: 10.w), + children: [ + // 顶部图形数据 + $TopGraphic(data), + // 掌握知识点的情况 + $MasterKnowledgePoint(data.knowledgeInfos), + // 掌握知识点的情况 + $OverallPerformance(data.studentCount, data.overallTitles), + // 单位时间答题情况 + $UnitTimeAnsweringSituation(data.questionAnswerInfos), + // 人员数据概况 + $PersonnelDataOverview(data.studentAnswerInfos), + ], + ); }), ); } } + +/// 下拉选择框 +@swidget +Widget $dropdownSelection(List? involveClasses, JobReportJoinClass? classData, + {required Function(JobReportJoinClass) call}) { + print('有几条数据+${involveClasses?.length}'); + if (involveClasses == null) return Container(child: quickText('点击重试')); // 点击重试 + return DropdownButton( + value: classData?.gradeId ?? -1, + style: TextStyle(color: Color.fromRGBO(89, 89, 89, 1), fontSize: 12.sp), + items: involveClasses.map((e) { + return DropdownMenuItem( + value: e.gradeId, + child: quickText(e.graduationYear, size: 12.sp, color: Colors.black), + ); + }).toList(), + onChanged: (int? value) { + if (value == null) return; + call(involveClasses.firstWhere((element) => element.gradeId == value)); + }, + ); +} + +/// 顶部图形数据 +@swidget +Widget $topGraphic(JobReportModel data) { + return Container( + margin: EdgeInsets.only(top: 16.h, bottom: 10.h), + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.h), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16.r)), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 整体评价 + Stack( + alignment: const FractionalOffset(0.5, 0.05), + children: [ + Padding( + padding: EdgeInsetsDirectional.all(30.r), + child: Container( + width: 188.r, + height: 188.r, + alignment: Alignment.center, + padding: EdgeInsets.all(20.r), + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, // 设置为圆形 + boxShadow: [ + BoxShadow( + color: const Color.fromRGBO(104, 136, 253, 1), + spreadRadius: 1.r, // 阴影扩散半径 + blurRadius: 8.r, // 阴影模糊半径 + offset: Offset(0.0, 0), // 阴影偏移量 + ) + ], + ), + child: Container( + alignment: Alignment.center, + padding: EdgeInsets.all(13.r), + decoration: BoxDecoration( + shape: BoxShape.circle, // 设置为圆形 + color: Color.fromRGBO(215, 223, 255, 1), + ), + child: Container( + decoration: BoxDecoration( + // color: Color.fromRGBO(104, 136, 253, 1), + shape: BoxShape.circle, // 设置为圆形 + border: Border.all(color: Color.fromRGBO(0, 179, 134, 0)), + gradient: RadialGradient( + center: Alignment.center, // 渐变中心点 + colors: [ + Color.fromRGBO(104, 136, 253, 0.7), + Color.fromRGBO(104, 136, 253, 0.8), + Color.fromRGBO(104, 136, 253, 0.9), + Color.fromRGBO(104, 136, 253, 1), + ], + ), + ), + alignment: Alignment.center, + child: quickText(data.scoreTitle, color: Colors.white, size: 32.sp, fontWeight: FontWeight.bold), + ), + ), + ), + ), + Image.asset("assets/images/job_report_scale.png", width: 230.r, fit: BoxFit.fill), + ], + ), + + // 完成率、正确率、作业数量 + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + width: 188.r, + height: 188.r, + padding: EdgeInsets.all(9.r), + decoration: BoxDecoration( + shape: BoxShape.circle, // 设置为圆形 + color: Color.fromRGBO(104, 136, 253, 1), + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: quickText('${data.validCount}份', + color: Colors.white, size: 10.sp, fontWeight: FontWeight.bold)), + SizedBox(width: 1.2.w), + Expanded( + flex: 7, + child: Container( + padding: EdgeInsets.all(12.r), + decoration: BoxDecoration( + shape: BoxShape.circle, // 设置为圆形 + color: Color.fromRGBO(76, 199, 147, 1), + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: quickText('${doubleToStringAsFixed(data.correctRate)}%', + color: Colors.white, size: 10.sp, fontWeight: FontWeight.bold)), + Expanded( + flex: 5, + child: Container( + width: double.infinity, + height: double.infinity, + alignment: Alignment.center, + decoration: BoxDecoration( + shape: BoxShape.circle, // 设置为圆形 + color: Color.fromRGBO(254, 151, 40, 1), + ), + child: quickText('${doubleToStringAsFixed(data.finishRate)}%', + color: Colors.white, size: 14.sp, fontWeight: FontWeight.bold), + ), + ), + ], + ), + ), + ) + ], + ), + ), + SizedBox(width: 20.w), + Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 10.r, + height: 10.r, + decoration: BoxDecoration( + shape: BoxShape.circle, // 设置为圆形 + color: Color.fromRGBO(254, 151, 40, 1), + ), + ), + SizedBox(width: 5.w), + quickText('作业完成率', color: Color.fromRGBO(114, 114, 114, 1), size: 12.sp), + SizedBox(width: 4.w), + quickText('${doubleToStringAsFixed(data.finishRate)}%', + color: Color.fromRGBO(72, 72, 72, 1), size: 13.sp, fontWeight: FontWeight.bold), + ], + ), + SizedBox(height: 14.h), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 10.r, + height: 10.r, + decoration: BoxDecoration( + shape: BoxShape.circle, // 设置为圆形 + color: Color.fromRGBO(76, 199, 147, 1), + ), + ), + SizedBox(width: 5.w), + quickText('作业正确率', color: Color.fromRGBO(114, 114, 114, 1), size: 12.sp), + SizedBox(width: 4.w), + quickText('${doubleToStringAsFixed(data.correctRate)}%', + color: Color.fromRGBO(72, 72, 72, 1), size: 13.sp, fontWeight: FontWeight.bold), + ], + ), + SizedBox(height: 14.h), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 10.r, + height: 10.r, + decoration: BoxDecoration( + shape: BoxShape.circle, // 设置为圆形 + color: Color.fromRGBO(104, 136, 253, 1), + ), + ), + SizedBox(width: 5.w), + quickText('作业有效份数', color: Color.fromRGBO(114, 114, 114, 1), size: 12.sp), + SizedBox(width: 4.w), + quickText('${data.validCount}份', + color: Color.fromRGBO(72, 72, 72, 1), size: 13.sp, fontWeight: FontWeight.bold), + ], + ), + ], + ), + ) + ], + ), + SizedBox(height: 50.h), + // 全对、及格、不及格、未做 + Wrap( + spacing: 30.r, + runSpacing: 26.r, + children: [ + // 全对 + Container( + child: Column( + children: [ + Stack( + alignment: const FractionalOffset(0.5, 0.94), + children: [ + Container( + width: 140.w, //宽 + height: 170.h, //高 + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + color: Color.fromRGBO(241, 241, 241, 1), + ), + child: FlutterWaveLoading( + width: 140.w, //宽 + height: 170.h, //高 + isOval: false, // 是否椭圆裁切 + progress: data.allCorrect / data.studentCount, // 进度 + waveHeight: 8, //波浪高 + milliseconds: 5000, + color: Color.fromRGBO(0, 179, 134, 1), //颜色 + ), + ), + Positioned( + left: 16.h, + top: 10.h, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + quickText( + data.allCorrect, + size: 32.sp, + fontWeight: FontWeight.bold, + color: Color.fromRGBO(122, 122, 122, 1), + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + quickText('人', size: 18.sp, color: Color.fromRGBO(122, 122, 122, 1)), + SizedBox(height: 4.h) + ], + ) + ], + ), + quickText('全对', color: Color.fromRGBO(164, 164, 164, 1), size: 16.sp), + ], + )), + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + quickText(doubleToStringAsFixed(data.allCorrect / data.studentCount * 100), + size: 34.sp, color: Colors.white, fontWeight: FontWeight.bold), + SizedBox(width: 1.5.w), + quickText('%', size: 22.sp, color: Colors.white) + ], + ), + ], + ), + SizedBox(height: 8.h), + InkWell( + onTap: () {}, + child: Container( + width: 77.w, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(vertical: 4.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(77.r), + border: Border.all(color: Color.fromRGBO(188, 188, 188, 1)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + quickText('查看', size: 11.sp, color: Color.fromRGBO(118, 118, 118, 1)), + Icon(Icons.arrow_forward_ios, size: 11.sp, color: Color.fromRGBO(95, 95, 95, 1)) + ], + ), + ), + ) + ], + ), + ), + // 及格 + Container( + child: Column( + children: [ + Stack( + alignment: const FractionalOffset(0.5, 0.94), + children: [ + Container( + width: 140.w, //宽 + height: 170.h, //高 + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + color: Color.fromRGBO(241, 241, 241, 1), + ), + child: FlutterWaveLoading( + width: 140.w, //宽 + height: 170.h, //高 + isOval: false, // 是否椭圆裁切 + progress: data.passCount / data.studentCount, // 进度 + waveHeight: 8, //波浪高 + milliseconds: 8000, + color: Color.fromRGBO(255, 134, 0, 0.84), //颜色 + ), + ), + Positioned( + left: 16.h, + top: 10.h, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + quickText( + data.passCount, + size: 32.sp, + fontWeight: FontWeight.bold, + color: Color.fromRGBO(122, 122, 122, 1), + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + quickText('人', size: 18.sp, color: Color.fromRGBO(122, 122, 122, 1)), + SizedBox(height: 4.h) + ], + ) + ], + ), + quickText('及格', color: Color.fromRGBO(164, 164, 164, 1), size: 16.sp), + ], + )), + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + quickText(doubleToStringAsFixed(data.passCount / data.studentCount * 100), + size: 34.sp, color: Colors.white, fontWeight: FontWeight.bold), + SizedBox(width: 1.5.w), + quickText('%', size: 22.sp, color: Colors.white) + ], + ), + ], + ), + SizedBox(height: 8.h), + InkWell( + onTap: () {}, + child: Container( + width: 77.w, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(vertical: 4.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(77.r), + border: Border.all(color: Color.fromRGBO(188, 188, 188, 1)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + quickText('查看', size: 11.sp, color: Color.fromRGBO(118, 118, 118, 1)), + Icon(Icons.arrow_forward_ios, size: 11.sp, color: Color.fromRGBO(95, 95, 95, 1)) + ], + ), + ), + ) + ], + ), + ), + // 不及格 + Container( + child: Column( + children: [ + Stack( + alignment: const FractionalOffset(0.5, 0.94), + children: [ + Container( + width: 140.w, //宽 + height: 170.h, //高 + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + color: Color.fromRGBO(241, 241, 241, 1), + ), + child: FlutterWaveLoading( + width: 140.w, //宽 + height: 170.h, //高 + isOval: false, // 是否椭圆裁切 + progress: data.failCount / data.studentCount, // 进度 + waveHeight: 8, //波浪高 + milliseconds: 7000, + color: Color.fromRGBO(255, 106, 106, 1), //颜色 + ), + ), + Positioned( + left: 16.h, + top: 10.h, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + quickText( + data.failCount, + size: 32.sp, + fontWeight: FontWeight.bold, + color: Color.fromRGBO(122, 122, 122, 1), + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + quickText('人', size: 18.sp, color: Color.fromRGBO(122, 122, 122, 1)), + SizedBox(height: 4.h) + ], + ) + ], + ), + quickText('不及格', color: Color.fromRGBO(164, 164, 164, 1), size: 16.sp), + ], + )), + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + quickText(doubleToStringAsFixed(data.failCount / data.studentCount * 100), + size: 34.sp, color: Colors.white, fontWeight: FontWeight.bold), + SizedBox(width: 1.5.w), + quickText('%', size: 22.sp, color: Colors.white) + ], + ), + ], + ), + SizedBox(height: 8.h), + InkWell( + onTap: () {}, + child: Container( + width: 77.w, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(vertical: 4.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(77.r), + border: Border.all(color: Color.fromRGBO(188, 188, 188, 1)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + quickText('查看', size: 11.sp, color: Color.fromRGBO(118, 118, 118, 1)), + Icon(Icons.arrow_forward_ios, size: 11.sp, color: Color.fromRGBO(95, 95, 95, 1)) + ], + ), + ), + ) + ], + ), + ), + // 未做 + Container( + child: Column( + children: [ + Stack( + alignment: const FractionalOffset(0.5, 0.94), + children: [ + Container( + width: 140.w, //宽 + height: 170.h, //高 + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + color: Color.fromRGBO(241, 241, 241, 1), + ), + child: FlutterWaveLoading( + width: 140.w, //宽 + height: 170.h, //高 + isOval: false, // 是否椭圆裁切 + progress: data.noAnswerCount / data.studentCount, // 进度 + waveHeight: 8, //波浪高 + milliseconds: 6000, + color: Color.fromRGBO(96, 96, 96, 1), //颜色 + ), + ), + Positioned( + left: 16.h, + top: 10.h, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + quickText( + data.noAnswerCount, + size: 32.sp, + fontWeight: FontWeight.bold, + color: Color.fromRGBO(122, 122, 122, 1), + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + quickText('人', size: 18.sp, color: Color.fromRGBO(122, 122, 122, 1)), + SizedBox(height: 4.h) + ], + ) + ], + ), + quickText('未做', color: Color.fromRGBO(164, 164, 164, 1), size: 16.sp), + ], + ), + ), + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + quickText(doubleToStringAsFixed(data.noAnswerCount / data.studentCount * 100), + size: 34.sp, color: Colors.white, fontWeight: FontWeight.bold), + SizedBox(width: 1.5.w), + quickText('%', size: 22.sp, color: Colors.white) + ], + ), + ], + ), + SizedBox(height: 8.h), + InkWell( + onTap: () {}, + child: Container( + width: 77.w, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(vertical: 4.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(77.r), + border: Border.all(color: Color.fromRGBO(188, 188, 188, 1)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + quickText('查看', size: 11.sp, color: Color.fromRGBO(118, 118, 118, 1)), + Icon(Icons.arrow_forward_ios, size: 11.sp, color: Color.fromRGBO(95, 95, 95, 1)) + ], + ), + ), + ) + ], + ), + ), + ], + ), + SizedBox(height: 30.h) + ], + ), + ); +} + +/// 掌握知识点的情况 +@swidget +Widget $masterKnowledgePoint(BuildContext context, List knowledgeInfos) { + Widget childItem(int serialNumber, KnowledgeInfos knowItem) => Container( + margin: EdgeInsets.only(bottom: 20.h), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Expanded( + flex: 10, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + quickText('${(serialNumber + 1).toString() + '.' + knowItem.knowledgeName}', + size: 12.sp, color: Color.fromRGBO(152, 152, 152, 1)), + quickText('${doubleToStringAsFixed(knowItem.rate)}%', + size: 14.sp, color: Color.fromRGBO(64, 64, 64, 1), fontWeight: FontWeight.bold), + ], + ), + ), + SizedBox(width: 10.w), + Expanded(flex: 1, child: SizedBox()), + ], + ), + SizedBox(height: 6.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 10, + child: LinearPercentIndicator( + padding: EdgeInsets.zero, + animation: true, + lineHeight: 15.h, + animationDuration: 2500, + percent: 0.1, + center: Text( + '${doubleToStringAsFixed(knowItem.rate)}%', + style: TextStyle(color: Colors.white, fontSize: 10.sp), + ), + progressColor: Theme.of(context).primaryColor, + backgroundColor: const Color.fromRGBO(219, 224, 243, 1), + barRadius: Radius.circular(10.r), + )), + SizedBox(width: 10.w), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + quickText('查看', size: 14.sp, color: Color.fromRGBO(239, 135, 20, 1)), + Icon(Icons.arrow_forward_ios, size: 11.sp, color: Color.fromRGBO(239, 135, 20, 1)), + ], + ) + ], + ) + ], + ), + ); + return Container( + margin: EdgeInsets.only(top: 10.h), + padding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 12.w), + constraints: BoxConstraints(maxHeight: 320.h), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10.r)), + child: Column( + children: [ + Container( + child: quickText('知识点掌握情况', color: Color.fromRGBO(92, 92, 92, 1), size: 14.sp, fontWeight: FontWeight.bold), + margin: EdgeInsets.only(bottom: 24.h), + ), + // ...knowledgeInfos.asMap().keys.map((e) => childItem(e, knowledgeInfos[e])).toList() + Expanded( + child: ListView(children: knowledgeInfos.asMap().keys.map((e) => childItem(e, knowledgeInfos[e])).toList()), + ), + ], + )); +} + +/// 整体表现 +@swidget +Widget $overallPerformance(int totalNumber, List overallTitles) { + Map colorMap = { + '优秀': Color.fromRGBO(104, 136, 253, 1), + '良好': Color.fromRGBO(255, 186, 33, 1), + '一般': Color.fromRGBO(243, 163, 44, 1), + '较差': Color.fromRGBO(211, 211, 211, 1).withOpacity(0.5), + '很差': Color.fromRGBO(211, 211, 211, 1), + }; + return Container( + margin: EdgeInsets.only(top: 20.h), + padding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 16.w), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10.r), + ), + height: 310.h, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + alignment: Alignment.center, + child: quickText('整体表现', color: Color.fromRGBO(92, 92, 92, 1), size: 14.sp, fontWeight: FontWeight.bold), + margin: EdgeInsets.only(bottom: 20.h), + ), + Expanded( + child: PieChart( + PieChartData( + borderData: FlBorderData(show: false), + sectionsSpace: 2, + centerSpaceRadius: 0, + sections: overallTitles.map((e) { + return PieChartSectionData( + color: colorMap[e.title], + value: e.count / totalNumber * 100, + radius: 110, + title: e.title + (doubleToStringAsFixed(e.count / totalNumber * 100) + '%'), + titleStyle: TextStyle(fontSize: 14.sp, color: Colors.white, fontWeight: FontWeight.bold), + ); + }).toList(), + ), + ), + ) + ], + ), + ); +} + +/// 单位时间答题情况 +@swidget +Widget $unitTimeAnsweringSituation(BuildContext context, List questionAnswerInfos) { + List questionNos = []; // 题号 + List questionTypes = []; // 题型 + List completionStatusWithinThirtySeconds = []; // 30s内完成情况 + List completionStatusWithinThirtyOneSixtySeconds = []; // 31s-60s内完成情况 + List completionStatusWithinSixtyOneOneHundredAndTwenty = []; // 61s-120s内完成情况 + List accuracys = []; // 正确率 + List errorRate = []; // 错误率 + List notDone = []; // 未做 + List viewOriginalQuestion = []; // 查看原题 + List operate = []; + Map> mapData = { + '题号': questionNos, + '题型': questionTypes, + '30s内完成情况': completionStatusWithinThirtySeconds, + '31s-60s内完成情况': completionStatusWithinThirtyOneSixtySeconds, + '61s-120s内完成情况': completionStatusWithinSixtyOneOneHundredAndTwenty, + '正确率': accuracys, + '错误率': errorRate, + '未做': notDone, + '查看原题': viewOriginalQuestion, + '操作': operate + }; + + questionAnswerInfos.forEach((e) { + questionNos.add(e.questionNo); // 题号 + questionTypes.add(e.questionType == 2 ? '主观题' : '客观题'); // 题型 + + accuracys.add(doubleToStringAsFixed(e.correctRate) + '%'); + errorRate.add(doubleToStringAsFixed(e.errorRate) + '%'); + notDone.add(doubleToStringAsFixed(e.noAnswerRate) + '%'); + viewOriginalQuestion.add(e.questionId.toString()); + operate.add(e.questionId.toString()); + + e.finishInfos.forEach((element) { + if ('30s内完成情况' == element.title) { + completionStatusWithinThirtySeconds + .add('${element.finishCount}人完成,正确率${doubleToStringAsFixed(element.correctRate)}%'); + } else if ('31-60s内完成情况' == element.title) { + completionStatusWithinThirtyOneSixtySeconds + .add('${element.finishCount}人完成,正确率${doubleToStringAsFixed(element.correctRate)}%'); + } else { + // 61-120s内完成情况 + completionStatusWithinSixtyOneOneHundredAndTwenty + .add('${element.finishCount}人完成,正确率${doubleToStringAsFixed(element.correctRate)}%'); + } + }); + }); + bool containsChinese(String text) { + // 汉字、扩展A、扩展B、兼容汉字等Unicode范围 + const int startRange = 0x4E00; // 汉字范围的起始Unicode码 + const int endRange = 0x9FFF; // 汉字范围的结束Unicode码 + + for (int i = 0; i < text.length; i++) { + int charCode = text.codeUnitAt(i); + if (charCode >= startRange && charCode <= endRange) { + return true; // 找到了中文字符 + } + } + + return false; // 没有找到中文字符 + } + + return Container( + width: double.infinity, + height: 520.h, + margin: EdgeInsets.only(top: 20.h), + padding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 12.w), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10.r)), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + child: quickText('整体表现', color: Color.fromRGBO(92, 92, 92, 1), size: 14.sp, fontWeight: FontWeight.bold), + margin: EdgeInsets.only(bottom: 20.h), + ), + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...mapData.entries.map((entrie) { + bool isViewButton = ['查看原题', '操作'].contains(entrie.key); // 查看按钮 + bool isRatio = ['正确率', '错误率', '未做'].contains(entrie.key); + bool isQuestionNo = entrie.key == '题号'; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 94.r, + alignment: Alignment.center, + margin: EdgeInsets.only(bottom: 1.h, right: 1.w), + padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + color: Color.fromRGBO(230, 230, 230, 1), + child: quickText(entrie.key, color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp, maxLines: 2), + ), + ...entrie.value.map((e) { + bool noHasChineseCharacter = isQuestionNo && !containsChinese(e); + + return Container( + width: 96.r, + alignment: Alignment.center, + margin: EdgeInsets.only(bottom: 1.h, right: 1.w), + padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + color: Color.fromRGBO(245, 245, 245, 1), + child: isViewButton + ? InkWell( + onTap: () {}, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + quickText('查看', color: Color.fromRGBO(239, 135, 20, 1), size: 12.sp), + Container( + padding: EdgeInsets.only(top: 1.h), + child: Icon( + Icons.arrow_forward_ios, + size: 10.sp, + color: Color.fromRGBO(239, 135, 20, 1), + ), + ), + ], + ), + ) + : RegExp(r'^\d+$').hasMatch(e) || (e.contains('%') && e.length < 4) + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + quickText(e, + color: isRatio + ? Theme.of(context).primaryColor + : Color.fromRGBO(82, 82, 82, 1), + size: isRatio || noHasChineseCharacter ? 12.sp : 12.sp, + maxLines: 2), + quickText('透', color: Colors.transparent, size: 12.sp), + ], + ) + : quickText(e, + color: isRatio ? Theme.of(context).primaryColor : Color.fromRGBO(82, 82, 82, 1), + size: isRatio || noHasChineseCharacter ? 12.sp : 12.sp, + maxLines: 2), + ); + }).toList(), + ], + ); + }).toList(), + ], + ), + ), + ), + + // ...mapData.entries.map((entrie) { + // return Row( + // children: [ + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h, right: 1.w), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: quickText(entrie.key, color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp, maxLines: 2), + // ), + // ...entrie.value.asMap().keys.map((e) { + // String questionNo = questionNos[e]; + // return Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(245, 245, 245, 1), + // child: quickText(questionNo, color: Color.fromRGBO(82, 82, 82, 1), size: 12.sp), + // ); + // }).toList(), + // ], + // ); + // }).toList(), + + // Stack( + // children: [ + // if (false) + // Container( + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: quickText('题号', color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp), + // ), + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: quickText('题型', color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp)), + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: quickText('30s内完成情况', maxLines: 2, color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp)), + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: + // quickText('31s-60s内完成情况', maxLines: 2, color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp)), + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: + // quickText('61s-120s内完成情况', maxLines: 2, color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp)), + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: quickText('正确率', color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp)), + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: quickText('错误率', color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp)), + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: quickText('未做', color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp)), + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: quickText('查看原题', color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp)), + // Container( + // width: 56.w, + // alignment: Alignment.center, + // margin: EdgeInsets.only(bottom: 1.h), + // padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + // color: Color.fromRGBO(230, 230, 230, 1), + // child: quickText('操作', color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp)), + // ], + // ), + // ), + // ], + // ), + ], + ), + ); +} + +/// 人员数据概况 +@swidget +Widget $personnelDataOverview(BuildContext context, List studentAnswerInfos) { + List names = []; + List useTimes = []; + List correctRates = []; + List noAnswerCounts = []; + List rankings = []; + Map> mapData = { + '姓名': names, + '答题时长': useTimes, + '正确率': correctRates, + '未答题数': noAnswerCounts, + '班级排名': rankings, + }; + + studentAnswerInfos.forEach((student) { + TimeUnits timeUnits = convertMilliseconds(student.useTime); + String timeerStr = ''; + if (timeUnits.hours > 0) timeerStr = timeUnits.hours.toString() + ':'; + if (timeUnits.minutes > 0) timeerStr += timeUnits.minutes.toString() + ':'; + if (timeUnits.seconds > 0) timeerStr += timeUnits.seconds.toString(); + + names.add(student.studentName); + useTimes.add(timeerStr); + correctRates.add(doubleToStringAsFixed(student.correctRate) + '%'); + noAnswerCounts.add(student.noAnswerCount.toString()); + rankings.add(student.ranking.toString() + '名'); + }); + + return Container( + width: double.infinity, + height: 290.h, + margin: EdgeInsets.only(top: 20.h), + padding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 12.w), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10.r)), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + child: quickText('人员数据概况', color: Color.fromRGBO(92, 92, 92, 1), size: 14.sp, fontWeight: FontWeight.bold), + margin: EdgeInsets.only(bottom: 20.h), + ), + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...mapData.entries.map((entrie) { + bool isTransparentChinese = ['答题时长', '正确率', '未答题数'].contains(entrie.key); // 透明中文 + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 94.r, + alignment: Alignment.center, + color: Color.fromRGBO(230, 230, 230, 1), + margin: EdgeInsets.only(bottom: 1.h, right: 1.w), + padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + child: quickText(entrie.key, color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp, maxLines: 2), + ), + ...entrie.value.map((e) { + bool isTransparentChineseNew = isTransparentChinese && (e?.length ?? 0) == 0; + return Container( + width: 100.r, + alignment: Alignment.center, + margin: EdgeInsets.only(bottom: 1.h, right: 1.w), + padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w), + color: Color.fromRGBO(245, 245, 245, 1), + child: isTransparentChineseNew + ? quickText('透明', color: Colors.transparent, size: 12.sp) + : RegExp(r'^\d+$').hasMatch(e) || e.contains('%') + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + quickText(e, color: Color.fromRGBO(82, 82, 82, 1), size: 12.sp, maxLines: 2), + quickText('透', color: Colors.transparent, size: 12.sp), + ], + ) + : quickText(e, color: Color.fromRGBO(82, 82, 82, 1), size: 12.sp, maxLines: 2), + ); + }).toList(), + ], + ); + }).toList(), + ], + ), + ), + ), + ], + ), + ); +} + +// 定义一个类来保存转换后的时间单位 +class TimeUnits { + int hours; + int minutes; + int seconds; + + TimeUnits(this.hours, this.minutes, this.seconds); +} + +// 毫秒转小时、分钟、秒的函数 +TimeUnits convertMilliseconds(int totalSeconds) { + int hours = totalSeconds ~/ 3600; // 整除得到小时数 + int remainingSeconds = totalSeconds % 3600; // 求余得到剩余的秒数 + int minutes = remainingSeconds ~/ 60; // 整除得到分钟数 + int seconds = remainingSeconds % 60; // 求余得到秒数 + + return TimeUnits(hours, minutes, seconds); +} diff --git a/marking_app/lib/utils/flutter_wave_loading.dart b/marking_app/lib/utils/flutter_wave_loading.dart new file mode 100644 index 0000000..ae4051e --- /dev/null +++ b/marking_app/lib/utils/flutter_wave_loading.dart @@ -0,0 +1,181 @@ +import 'package:flutter/material.dart'; +import 'package:marking_app/utils/index.dart'; + +/// 说明: 贝塞尔曲线测试画布 +class FlutterWaveLoading extends StatefulWidget { + final double width; + final double height; + final double waveHeight; + final Color color; + final double strokeWidth; + final double progress; + final double factor; + final int secondAlpha; + final double borderRadius; + final bool isOval; + final int milliseconds; + + FlutterWaveLoading( + {this.width = 100, + this.height = 100 / 0.618, + this.factor = 1, + this.waveHeight = 5, + this.progress = 0.5, + this.color = Colors.green, + this.strokeWidth = 3, + this.secondAlpha = 88, + this.isOval = false, + this.milliseconds = 3000, + this.borderRadius = 20}); + + @override + _FlutterWaveLoadingState createState() => _FlutterWaveLoadingState(); +} + +class _FlutterWaveLoadingState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _anim; + + @override + void initState() { + _controller = AnimationController(vsync: this, duration: Duration(milliseconds: widget.milliseconds)) + ..addListener(toSetState) + ..repeat(); + _anim = CurveTween(curve: Curves.linear).animate(_controller); + super.initState(); + } + + void toSetState() => toUpState(setState, () {}, mounted); + + @override + void dispose() { + try { + _controller + ..removeListener(toSetState) + ..dispose(); + } catch (e) { + print('报错了.........:'); + print(e); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return UnconstrainedBox( + child: Container( + width: widget.width, + height: widget.height, + child: CustomPaint( + painter: BezierPainter( + factor: _anim.value, + waveHeight: widget.waveHeight, + progress: widget.progress, + color: widget.color, + strokeWidth: widget.strokeWidth, + secondAlpha: widget.secondAlpha, + isOval: widget.isOval, + borderRadius: widget.borderRadius), + ), + ), + ); + } +} + +class BezierPainter extends CustomPainter { + late Paint _mainPaint; + late Path _mainPath; + + double waveWidth = 80; + late double wrapHeight; + + final double waveHeight; + final Color color; + final double strokeWidth; + final double progress; + final double factor; + final int secondAlpha; + final double borderRadius; + final bool isOval; + + BezierPainter( + {this.factor = 1, + this.waveHeight = 8, + this.progress = 0.5, + this.color = Colors.green, + this.strokeWidth = 3, + this.secondAlpha = 88, + this.isOval = false, + this.borderRadius = 20}) { + _mainPaint = Paint() + ..color = Colors.yellow + ..style = PaintingStyle.stroke + ..strokeWidth = 2; + _mainPath = Path(); + } + + @override + void paint(Canvas canvas, Size size) { + // print(size); + waveWidth = size.width / 2; + wrapHeight = size.height; + + Path path = Path(); + if (!isOval) { + path.addRRect(RRect.fromRectXY(Offset(0, 0) & size, borderRadius, borderRadius)); + canvas.clipPath(path); + // 边框 + // canvas.drawPath( + // path, + // _mainPaint + // ..strokeWidth = strokeWidth + // ..color = color); + } + if (isOval) { + path.addOval(Offset(0, 0) & size); + canvas.clipPath(path); + canvas.drawPath( + path, + _mainPaint + ..strokeWidth = strokeWidth + ..color = color); + } + canvas.translate(0, wrapHeight); + canvas.save(); + canvas.translate(0, waveHeight); + canvas.save(); + canvas.translate(-4 * waveWidth + 2 * waveWidth * factor, 0); + drawWave(canvas); + canvas.drawPath( + _mainPath, + _mainPaint + ..style = PaintingStyle.fill + ..color = color.withAlpha(88)); + canvas.restore(); + + canvas.translate(-4 * waveWidth + 2 * waveWidth * factor * 2, 0); + drawWave(canvas); + canvas.drawPath( + _mainPath, + _mainPaint + ..style = PaintingStyle.fill + ..color = color); + canvas.restore(); + } + + void drawWave(Canvas canvas) { + _mainPath.moveTo(0, 0); + _mainPath.relativeLineTo(0, -wrapHeight * progress); + _mainPath.relativeQuadraticBezierTo(waveWidth / 2, -waveHeight * 2, waveWidth, 0); + _mainPath.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0); + _mainPath.relativeQuadraticBezierTo(waveWidth / 2, -waveHeight * 2, waveWidth, 0); + _mainPath.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0); + _mainPath.relativeQuadraticBezierTo(waveWidth / 2, -waveHeight * 2, waveWidth, 0); + _mainPath.relativeQuadraticBezierTo(waveWidth / 2, waveHeight * 2, waveWidth, 0); + _mainPath.relativeLineTo(0, wrapHeight); + _mainPath.relativeLineTo(-waveWidth * 3 * 2.0, 0); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; +} diff --git a/marking_app/lib/utils/request/rest_client.dart b/marking_app/lib/utils/request/rest_client.dart index 3b6d150..9064f08 100644 --- a/marking_app/lib/utils/request/rest_client.dart +++ b/marking_app/lib/utils/request/rest_client.dart @@ -17,6 +17,7 @@ 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_note_taking_trajectory.dart'; import 'package:marking_app/common/model/job/job_page_tab.dart'; +import 'package:marking_app/common/model/job/job_report_join_class.dart'; import 'package:marking_app/common/model/job/job_report_model.dart'; import 'package:marking_app/common/model/job/job_review_submission.dart'; import 'package:marking_app/common/model/job/job_task_item.dart'; @@ -249,7 +250,11 @@ abstract class RestClient { @the_retrofit.POST("${RequestConfig.hwProxyKeywords}/api/Marking/auto") Future> toJobOneClickReview(@the_retrofit.Field() int taskId); - // 作业 => 一键批阅 - @the_retrofit.GET("${RequestConfig.hwProxyKeywords}/api/Marking/auto") - Future> getJobReport(@the_retrofit.Field() int jobId); + // 作业 => 获取作业报告 + @the_retrofit.GET("/api/jobs/job-report") + Future> getJobReport(@the_retrofit.Queries() Map params); + + // 作业 => 获取作业报告 + @the_retrofit.GET("/api/jobs/student-job-for-class") + Future>> getJobReportJoinClasses(@the_retrofit.Query("jobId") int jobId); }