From 09a275b12fb381950b797dc84148ea3d70535964 Mon Sep 17 00:00:00 2001 From: machuanyu <840649825@qq.com> Date: Thu, 22 Feb 2024 17:28:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=A5=E5=91=8A=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- marking_app/assets/images/basic_info.png | Bin 0 -> 1796 bytes marking_app/assets/images/overall_level.png | Bin 0 -> 2320 bytes marking_app/assets/images/report_group_bg.png | Bin 0 -> 3713 bytes marking_app/assets/images/reports_bg.png | Bin 0 -> 48699 bytes marking_app/assets/images/reports_card.png | Bin 0 -> 2148 bytes marking_app/assets/images/reports_focus.png | Bin 0 -> 2171 bytes marking_app/assets/images/reports_score.png | Bin 0 -> 2010 bytes marking_app/assets/images/right_icon.png | Bin 0 -> 192 bytes marking_app/assets/images/school_icon.png | Bin 0 -> 743 bytes marking_app/assets/images/search_icon.png | Bin 0 -> 755 bytes .../common/model/report/detail_base_info.dart | 204 ++++++ .../lib/common/model/report/exam_records.dart | 62 ++ .../common/model/report/exam_records_all.dart | 141 ++++ .../model/report/exam_records_params.dart | 39 ++ .../model/report/question_know_point.dart | 97 +++ .../lib/common/model/report/report_card.dart | 92 +++ .../model/report/report_card_params.dart | 24 + .../model/report/report_marking_detail.dart | 122 ++++ .../report/report_marking_detail_params.dart | 23 + .../lib/pages/report_detail/index.dart | 651 ++++++++++++++++++ .../report_detail/widgets/basic_table.dart | 104 +++ .../widgets/basic_tablecell.dart | 22 + .../report_detail/widgets/card_table.dart | 137 ++++ .../report_detail/widgets/custom_rect.dart | 89 +++ .../widgets/know_point_list.dart | 127 ++++ .../report_detail/widgets/lab_title.dart | 30 + .../pages/report_detail/widgets/no_data.dart | 11 + .../widgets/overall_level_table.dart | 77 +++ .../report_detail/widgets/question_table.dart | 113 +++ .../widgets/report_card_dialog.dart | 371 ++++++++++ marking_app/lib/pages/reports/index.dart | 626 ++++++++++++++--- .../lib/pages/reports/widgets/myDrawer.dart | 59 ++ .../lib/pages/reports/widgets/userInfo.dart | 64 ++ marking_app/lib/routes/RouterManager.dart | 10 + .../lib/utils/request/rest_client_report.dart | 69 ++ marking_app/pubspec.yaml | 2 + 36 files changed, 3284 insertions(+), 82 deletions(-) create mode 100644 marking_app/assets/images/basic_info.png create mode 100644 marking_app/assets/images/overall_level.png create mode 100644 marking_app/assets/images/report_group_bg.png create mode 100644 marking_app/assets/images/reports_bg.png create mode 100644 marking_app/assets/images/reports_card.png create mode 100644 marking_app/assets/images/reports_focus.png create mode 100644 marking_app/assets/images/reports_score.png create mode 100644 marking_app/assets/images/right_icon.png create mode 100644 marking_app/assets/images/school_icon.png create mode 100644 marking_app/assets/images/search_icon.png create mode 100644 marking_app/lib/common/model/report/detail_base_info.dart create mode 100644 marking_app/lib/common/model/report/exam_records.dart create mode 100644 marking_app/lib/common/model/report/exam_records_all.dart create mode 100644 marking_app/lib/common/model/report/exam_records_params.dart create mode 100644 marking_app/lib/common/model/report/question_know_point.dart create mode 100644 marking_app/lib/common/model/report/report_card.dart create mode 100644 marking_app/lib/common/model/report/report_card_params.dart create mode 100644 marking_app/lib/common/model/report/report_marking_detail.dart create mode 100644 marking_app/lib/common/model/report/report_marking_detail_params.dart create mode 100644 marking_app/lib/pages/report_detail/index.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/basic_table.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/basic_tablecell.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/card_table.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/custom_rect.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/know_point_list.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/lab_title.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/no_data.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/overall_level_table.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/question_table.dart create mode 100644 marking_app/lib/pages/report_detail/widgets/report_card_dialog.dart create mode 100644 marking_app/lib/pages/reports/widgets/myDrawer.dart create mode 100644 marking_app/lib/pages/reports/widgets/userInfo.dart diff --git a/marking_app/assets/images/basic_info.png b/marking_app/assets/images/basic_info.png new file mode 100644 index 0000000000000000000000000000000000000000..f7fbc7a65a8c24262188d9a04925c1a91427b322 GIT binary patch literal 1796 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 literal 0 HcmV?d00001 diff --git a/marking_app/assets/images/reports_bg.png b/marking_app/assets/images/reports_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..0a5af028c4b60060c5bdd166275ad53403a67738 GIT binary patch literal 48699 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

&RFX1Nz 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 literal 0 HcmV?d00001 diff --git a/marking_app/assets/images/reports_score.png b/marking_app/assets/images/reports_score.png new file mode 100644 index 0000000000000000000000000000000000000000..c37207b52880365bcb412c8b9c7c8fea29374672 GIT binary patch literal 2010 zcmV<02POE4P)9x}qs(WTO_D$^PN)-Y;0p2 z+t|i7wy}qAX4S)AUYR_Tqj)Zqjq@>T^B4k}7*N#?p^0dkfQh%LJnzYMh^Rdf8)*ly zIU?IfZxGt|#--tBK3u)>i`}1N4a-(NwB9^-@Zb7-0S=89L{Y(14P;OP=Xo75Bj8zO zQP%-=LqO5$Gtb76&%Dps*CMcgT!Jp(EzGxg^6C@2mql^^fD-M1et(QP}0Q2 zbj>;NoD<}=mkG-+I|mc*1t6YyB;W~>)Fqg}!G#9O9O!}&kU)vS^E{D(1ZjUcH%>xV zLatl7bnTXn1j8c%uR$l^1-JyC3?79B6-MI>UUa3t z->R$AhCh0}h=11cJN^40b2W~N!W^sxzM-pCEsRy=5CE1-|HV%aP*-K*k-$+NOJI;T zJqekFz)Box zu-A`a9nbqJ$zr6pLljkkstO}jA((rzM54qT{L;Dp*Xy8%2D-8BJ)_>XCIL^hGRbMa z##-PbBW@OwzXY#vUZfH&OAsSgn|c4#k}@fk+6cJMJ52jGr`kH;t177CNP>}MBhqMH z!j$N{GC`k!Ewk^D7U#Act{>*?JHtuRwWKl&yw;@EKFwB&Y*{y$GJ}>RW=SrWl$OfR z{c<``^|2fol}g#GL3`oJ0v9iA!Q~BQ)QJESpd<{BF6LYOb6|c2`jwt9`SPl9N*RwN zZ?;WPEpP7Iub0)hbTPxhD|c}1`al>?x%Ai{=eRp*G^u{~%M~**@Wx?GNlo)~qEwgo zP9NbzfERhB^Aq1qR0=k{K@v&na8hiL63)&b;z!Sp@%SjiOK(t>M6cO+M3vg&y?1Ux z6a=GTj<0{NkAMCD7}MKq6VXnhdudczb?br;Og0rY(Fx}@un5oj6m0KH^)h5XpW^bR zeT@4Szxn-5y!2)t&Pgweb7w^MENJpbY`{(iksS6Zc8#Q8l7QSrBLr9>Bt z41YNA7-kcE>9bonzGF~UnmlkKYFF3?fG2IJFbg$H^*Wefmft_>2V8!37rR5?${%O= z+snDGjCxSRHsb8Choj>erm}EZD%xt^|I7booLGUqZLU&TV=dZZL}Wu zsUQnlmxolG0d0~=#~xGs`7Mj1LjgWv_Kb6_7Qb%~E&8O=;SnSN(5aD>%1t)JtM5!T zhxCh_QfyMm-6kuns5)1J7s>>qfYV_#aj940;&}a;!xbo)-s4e)!69wh4!I|+8 zqs(jVw$q>D2N%y^iyHaX^$IL~**Bv)r2tP_A5%tO2fXWzetLF%_*ZgY>oK9<)8{5T z=O5eoS*Y?9w96#|K^@+Tc;)pHpZR2ulgMzBDl#kzR4fNR^>|cIns^5np5CUkM%;LN z3YYbf<)o8I8BnW9lUCWSNK+rSIGNkgG(Pp}wc9U?EE~?oIy--nu24uTJ=!M6Nv&~S zuud;<>xhBuyGqhyz_@UkQ$ffno0$Zum1;AjJ%RptvPMqGm@_b zF%4pqXS%$i8*~?S12g805kY2bGZ>@>T5QFFwO-n|WI1$WBC@(%-)MOp-TE2!)K22< zJOS)TIUkoGoJ*yItV&>6TJtEfEbXY!)$OS)wp#flX-xkXRFogT3I`t4N&%lfWu(ni zL`R%7X1nAn(I&~Ierj-OyD05VXeYWPi`J&S0#a5s?Ymc%W@~v~v!`8nJJBVe+da*9 zM`*wE(BpCGlZ{!D!n9pA+HUUg7QD6Q5r9WmgLHL!Is{3o#oK9_cGuL^St&Jh?%npU z{TQSf*)5#W9jT1+4-Gs6_O;2({W=8M7>5=#=II{u!I!^Cw%cCUaoOotf^?n&Ri)9U sTBz&G=C_$={)CNfY-1bS*vHb|16i40C6iayh5!Hn07*qoM6N<$f>GeoEdT%j literal 0 HcmV?d00001 diff --git a/marking_app/assets/images/right_icon.png b/marking_app/assets/images/right_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4697c997f7d23c630ac94a37b6c447b0b140a67b GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eQ!3HG1Sky`ZDb50q$YP+FDhM-r2B~}i3NjW4 zxjQkeJ16rJ$YDu$^mSxl*x1kgCy^D%5A}3$45?sz+v~{7pvc4WQqzLd!%>KDLIaD= z6;7cI4b7Wc{=F0yGP`Nwn--<6BzX3m)BzpsqgsD5YA?lJ+AiF&B6-8Q#N+4rJ=dFG ivH!sk!D@E+1Vdqu(rKxQe|3NsFnGH9xvX?P)N3jiaRJWMi$8yQmwo#3omE#Ys8+0WmqV5}7Hyh5nskn!xd|Hc zO@-9l!@k+Jf&0^rw}TJkxWgQH^YT%;EBgHP9RI830f!?rh1|*nq;rOvpd`B1>e8sjccgjEa4-xS520Yx z%7eqldD60xuW*(=_b3AG9?L?DUKU#PvbdI_r_Q>)?RSiGi&h`wFG4Ka9f3M5YvU|t zRj^Ow8Ii4&;--yV*a#-PxU)RVb*BJ*roMIu{+OlfsM^@Za*^z*94vdEh6|_HXLTd}e~bJVEQZm5Vw(@+g8o z3toX5llJvM!f54`Yk zx=$r8FwlcJ7cnVH>f*Z^@Moec#@v}u)_b$Magj63lUkq~G2wK6Qo{=6vsgMkpXoI= z)cL8tC!CStHvg2zB z>A5Hgo2$?r&J?<)Q2XSOgBK8+ReJ+deJgY~g6iE?s^xt&mIN9^X;7>IP%Z!f literal 0 HcmV?d00001 diff --git a/marking_app/lib/common/model/report/detail_base_info.dart b/marking_app/lib/common/model/report/detail_base_info.dart new file mode 100644 index 0000000..58648a7 --- /dev/null +++ b/marking_app/lib/common/model/report/detail_base_info.dart @@ -0,0 +1,204 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'detail_base_info.g.dart'; + + +@JsonSerializable() +class DetailBaseInfo extends Object { + + @JsonKey(name: 'BaseInfo') + BaseInfo baseInfo; + + @JsonKey(name: 'Module1_CJZL') + Module1_CJZL module1CJZL; + + @JsonKey(name: 'Module2_ZDGZ') + Module2_ZDGZ module2ZDGZ; + + DetailBaseInfo(this.baseInfo,this.module1CJZL,this.module2ZDGZ,); + + factory DetailBaseInfo.fromJson(Map srcJson) => _$DetailBaseInfoFromJson(srcJson); + + Map toJson() => _$DetailBaseInfoToJson(this); + +} + + +@JsonSerializable() +class BaseInfo extends Object { + + @JsonKey(name: 'ExamName') + String examName; + + @JsonKey(name: 'ExamType') + String examType; + + @JsonKey(name: 'Grade') + String grade; + + @JsonKey(name: 'Subject') + String subject; + + @JsonKey(name: 'ExamTime') + String examTime; + + @JsonKey(name: 'TotalScore') + double totalScore; + + @JsonKey(name: 'AvgScore') + double avgScore; + + @JsonKey(name: 'MaxScore') + double maxScore; + + @JsonKey(name: 'RateGood') + String rateGood; + + @JsonKey(name: 'RatePass') + String ratePass; + + BaseInfo(this.examName,this.examType,this.grade,this.subject,this.examTime,this.totalScore,this.avgScore,this.maxScore,this.rateGood,this.ratePass,); + + factory BaseInfo.fromJson(Map srcJson) => _$BaseInfoFromJson(srcJson); + + Map toJson() => _$BaseInfoToJson(this); + +} + + +@JsonSerializable() +class Module1_CJZL extends Object { + + @JsonKey(name: 'BatchNO') + String batchNO; + + @JsonKey(name: 'HeadStartRowIndex') + int headStartRowIndex; + + @JsonKey(name: 'BodyStartRowIndex') + int bodyStartRowIndex; + + @JsonKey(name: 'Sheets') + List sheets; + + Module1_CJZL(this.batchNO,this.headStartRowIndex,this.bodyStartRowIndex,this.sheets,); + + factory Module1_CJZL.fromJson(Map srcJson) => _$Module1_CJZLFromJson(srcJson); + + Map toJson() => _$Module1_CJZLToJson(this); + +} + + +@JsonSerializable() +class Sheets extends Object { + + @JsonKey(name: 'SheetName') + String? sheetName; + + @JsonKey(name: 'HeadData') + List headData; + + @JsonKey(name: 'BodyData') + List bodyData; + + @JsonKey(name: 'BodyRowHeightPx') + double bodyRowHeightPx; + + @JsonKey(name: 'HeadsRowHeightPx') + double headsRowHeightPx; + + Sheets(this.sheetName,this.headData,this.bodyData,this.bodyRowHeightPx,this.headsRowHeightPx,); + + factory Sheets.fromJson(Map srcJson) => _$SheetsFromJson(srcJson); + + Map toJson() => _$SheetsToJson(this); + +} + + +@JsonSerializable() +class HeadData extends Object { + + @JsonKey(name: 'RowData') + List> rowData; + + @JsonKey(name: 'AutoMerge') + bool autoMerge; + + @JsonKey(name: 'TotalMerge') + bool totalMerge; + + @JsonKey(name: 'ColumnWidthPx') + double columnWidthPx; + + HeadData(this.rowData,this.autoMerge,this.totalMerge,this.columnWidthPx,); + + factory HeadData.fromJson(Map srcJson) => _$HeadDataFromJson(srcJson); + + Map toJson() => _$HeadDataToJson(this); + +} + + +@JsonSerializable() +class Module2_ZDGZ extends Object { + + @JsonKey(name: 'TopExcelData') + TopExcelData topExcelData; + + @JsonKey(name: 'BottomExcelData') + BottomExcelData bottomExcelData; + + Module2_ZDGZ(this.topExcelData,this.bottomExcelData,); + + factory Module2_ZDGZ.fromJson(Map srcJson) => _$Module2_ZDGZFromJson(srcJson); + + Map toJson() => _$Module2_ZDGZToJson(this); + +} + + +@JsonSerializable() +class TopExcelData extends Object { + + @JsonKey(name: 'HeadStartRowIndex') + int headStartRowIndex; + + @JsonKey(name: 'BodyStartRowIndex') + int bodyStartRowIndex; + + @JsonKey(name: 'Sheets') + List sheets; + + TopExcelData(this.headStartRowIndex,this.bodyStartRowIndex,this.sheets,); + + factory TopExcelData.fromJson(Map srcJson) => _$TopExcelDataFromJson(srcJson); + + Map toJson() => _$TopExcelDataToJson(this); + +} + + + +@JsonSerializable() +class BottomExcelData extends Object { + + @JsonKey(name: 'HeadStartRowIndex') + int headStartRowIndex; + + @JsonKey(name: 'BodyStartRowIndex') + int bodyStartRowIndex; + + @JsonKey(name: 'Sheets') + List sheets; + + BottomExcelData(this.headStartRowIndex,this.bodyStartRowIndex,this.sheets,); + + factory BottomExcelData.fromJson(Map srcJson) => _$BottomExcelDataFromJson(srcJson); + + Map toJson() => _$BottomExcelDataToJson(this); + +} + + diff --git a/marking_app/lib/common/model/report/exam_records.dart b/marking_app/lib/common/model/report/exam_records.dart new file mode 100644 index 0000000..8eb7fcc --- /dev/null +++ b/marking_app/lib/common/model/report/exam_records.dart @@ -0,0 +1,62 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'exam_records.g.dart'; + + +@JsonSerializable() +class ExamRecords extends Object { + + @JsonKey(name: 'Classes') + String classes; + + @JsonKey(name: 'ClassIds') + Map classIds; + + @JsonKey(name: 'Id') + int id; + + @JsonKey(name: 'UserId') + int userId; + + @JsonKey(name: 'Name') + String name; + + @JsonKey(name: 'Type') + int type; + + @JsonKey(name: 'TypeName') + String typeName; + + @JsonKey(name: 'Grade') + String grade; + + @JsonKey(name: 'Subjects') + String subjects; + + @JsonKey(name: 'SubjectStr') + String subjectStr; + + @JsonKey(name: 'StartTime') + String startTime; + + @JsonKey(name: 'StartTimeStr') + String startTimeStr; + + @JsonKey(name: 'Status') + int status; + + @JsonKey(name: 'StatusName') + String statusName; + + @JsonKey(name: 'CreateTime') + String createTime; + + ExamRecords(this.classes,this.classIds,this.id,this.userId,this.name,this.type,this.typeName,this.grade,this.subjects,this.subjectStr,this.startTime,this.startTimeStr,this.status,this.statusName,this.createTime,); + + factory ExamRecords.fromJson(Map srcJson) => _$ExamRecordsFromJson(srcJson); + + Map toJson() => _$ExamRecordsToJson(this); + +} + + diff --git a/marking_app/lib/common/model/report/exam_records_all.dart b/marking_app/lib/common/model/report/exam_records_all.dart new file mode 100644 index 0000000..b6247dc --- /dev/null +++ b/marking_app/lib/common/model/report/exam_records_all.dart @@ -0,0 +1,141 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'exam_records_all.g.dart'; + +@JsonSerializable() +class ExamRecordsAll extends Object { + @JsonKey(name: 'Type') + Type type; + + @JsonKey(name: 'Grade') + Grade grade; + + @JsonKey(name: 'Subject') + Subject subject; + + @JsonKey(name: 'TimePeriod') + TimePeriod timePeriod; + + ExamRecordsAll( + this.type, + this.grade, + this.subject, + this.timePeriod, + ); + + factory ExamRecordsAll.fromJson(Map srcJson) => + _$ExamRecordsAllFromJson(srcJson); + + Map toJson() => _$ExamRecordsAllToJson(this); +} + +@JsonSerializable() +class Type extends Object { + @JsonKey(name: 'IsDisabled') + bool isDisabled; + + @JsonKey(name: 'DefaultValue') + String defaultValue; + + @JsonKey(name: 'ComboData') + List comboData; + + Type( + this.isDisabled, + this.defaultValue, + this.comboData, + ); + + factory Type.fromJson(Map srcJson) => + _$TypeFromJson(srcJson); + + Map toJson() => _$TypeToJson(this); +} + +@JsonSerializable() +class ComboData extends Object { + @JsonKey(name: 'Value') + dynamic value; + + @JsonKey(name: 'Text') + String text; + + @JsonKey(name: 'isCheck') + bool isCheck; + + ComboData(this.value, this.text, {this.isCheck = false}); + + factory ComboData.fromJson(Map srcJson) => + _$ComboDataFromJson(srcJson); + + Map toJson() => _$ComboDataToJson(this); +} + +@JsonSerializable() +class Grade extends Object { + @JsonKey(name: 'IsDisabled') + bool isDisabled; + + @JsonKey(name: 'DefaultValue') + String defaultValue; + + @JsonKey(name: 'ComboData') + List comboData; + + Grade( + this.isDisabled, + this.defaultValue, + this.comboData, + ); + + factory Grade.fromJson(Map srcJson) => + _$GradeFromJson(srcJson); + + Map toJson() => _$GradeToJson(this); +} + +@JsonSerializable() +class Subject extends Object { + @JsonKey(name: 'IsDisabled') + bool isDisabled; + + @JsonKey(name: 'DefaultValue') + String defaultValue; + + @JsonKey(name: 'ComboData') + List comboData; + + Subject( + this.isDisabled, + this.defaultValue, + this.comboData, + ); + + factory Subject.fromJson(Map srcJson) => + _$SubjectFromJson(srcJson); + + Map toJson() => _$SubjectToJson(this); +} + +@JsonSerializable() +class TimePeriod extends Object { + @JsonKey(name: 'IsDisabled') + bool isDisabled; + + @JsonKey(name: 'DefaultValue') + String defaultValue; + + @JsonKey(name: 'ComboData') + List comboData; + + TimePeriod( + this.isDisabled, + this.defaultValue, + this.comboData, + ); + + factory TimePeriod.fromJson(Map srcJson) => + _$TimePeriodFromJson(srcJson); + + Map toJson() => _$TimePeriodToJson(this); +} diff --git a/marking_app/lib/common/model/report/exam_records_params.dart b/marking_app/lib/common/model/report/exam_records_params.dart new file mode 100644 index 0000000..2c2aa89 --- /dev/null +++ b/marking_app/lib/common/model/report/exam_records_params.dart @@ -0,0 +1,39 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:marking_app/common/model/common/base_page_report.dart'; + +part 'exam_records_params.g.dart'; + + +@JsonSerializable() +class ExamRecordsParams extends BasePageReport { + + @JsonKey(name: 'SearchStr') + String? searchStr; + + @JsonKey(name: 'Grade') + String? grade; + + @JsonKey(name: 'ClassId') + int? classId; + + @JsonKey(name: 'PositionId') + int? positionId; + + @JsonKey(name: 'ReportType') + String? reportType; + + @JsonKey(name: 'Subject') + int? subject; + + @JsonKey(name: 'TimePeriod') + int? timePeriod; + + ExamRecordsParams({this.searchStr = '',this.grade,this.classId = -1,this.positionId = -1,this.reportType,this.subject = -1,this.timePeriod = -1,page,limit}) : super(page, limit); + + factory ExamRecordsParams.fromJson(Map srcJson) => _$ExamRecordsParamsFromJson(srcJson); + + Map toJson() => _$ExamRecordsParamsToJson(this); + +} + + diff --git a/marking_app/lib/common/model/report/question_know_point.dart b/marking_app/lib/common/model/report/question_know_point.dart new file mode 100644 index 0000000..d62279d --- /dev/null +++ b/marking_app/lib/common/model/report/question_know_point.dart @@ -0,0 +1,97 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:marking_app/common/model/report/detail_base_info.dart'; + +part 'question_know_point.g.dart'; + + +@JsonSerializable() +class QuestionKnowPoint extends Object { + + @JsonKey(name: 'QuestionList') + List questionList; + + @JsonKey(name: 'UserQuestion') + UserQuestion userQuestion; + + @JsonKey(name: 'KnowPointList') + List knowPointList; + + QuestionKnowPoint(this.questionList,this.userQuestion,this.knowPointList,); + + factory QuestionKnowPoint.fromJson(Map srcJson) => _$QuestionKnowPointFromJson(srcJson); + + Map toJson() => _$QuestionKnowPointToJson(this); + +} + + +@JsonSerializable() +class UserQuestion extends Object { + + @JsonKey(name: 'BatchNO') + String batchNO; + + @JsonKey(name: 'HeadStartRowIndex') + int headStartRowIndex; + + @JsonKey(name: 'BodyStartRowIndex') + int bodyStartRowIndex; + + @JsonKey(name: 'Sheets') + List sheets; + + UserQuestion(this.batchNO,this.headStartRowIndex,this.bodyStartRowIndex,this.sheets,); + + factory UserQuestion.fromJson(Map srcJson) => _$UserQuestionFromJson(srcJson); + + Map toJson() => _$UserQuestionToJson(this); + +} + + +@JsonSerializable() +class HeadData extends Object { + + @JsonKey(name: 'RowData') + List> rowData; + + @JsonKey(name: 'AutoMerge') + bool autoMerge; + + @JsonKey(name: 'TotalMerge') + bool totalMerge; + + @JsonKey(name: 'ColumnWidthPx') + int columnWidthPx; + + HeadData(this.rowData,this.autoMerge,this.totalMerge,this.columnWidthPx,); + + factory HeadData.fromJson(Map srcJson) => _$HeadDataFromJson(srcJson); + + Map toJson() => _$HeadDataToJson(this); + +} + + +@JsonSerializable() +class KnowPointList extends Object { + + @JsonKey(name: 'Name') + String name; + + @JsonKey(name: 'QuestionNums') + String questionNums; + + @JsonKey(name: 'CorrectRate') + String correctRate; + + @JsonKey(name: 'GradeCorrectRate') + String gradeCorrectRate; + + KnowPointList(this.name,this.questionNums,this.correctRate,this.gradeCorrectRate,); + + factory KnowPointList.fromJson(Map srcJson) => _$KnowPointListFromJson(srcJson); + + Map toJson() => _$KnowPointListToJson(this); + +} diff --git a/marking_app/lib/common/model/report/report_card.dart b/marking_app/lib/common/model/report/report_card.dart new file mode 100644 index 0000000..e03db36 --- /dev/null +++ b/marking_app/lib/common/model/report/report_card.dart @@ -0,0 +1,92 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'report_card.g.dart'; + + +@JsonSerializable() +class ReportCard extends Object { + + @JsonKey(name: 'TotalCount') + int totalCount; + + @JsonKey(name: 'Head') + List head; + + @JsonKey(name: 'Data') + List data; + + ReportCard(this.totalCount,this.head,this.data,); + + factory ReportCard.fromJson(Map srcJson) => _$ReportCardFromJson(srcJson); + + Map toJson() => _$ReportCardToJson(this); + +} + + +@JsonSerializable() +class Data extends Object { + + @JsonKey(name: 'SubjectDetails') + List subjectDetails; + + @JsonKey(name: 'ExamStudentId') + String examStudentId; + + @JsonKey(name: 'ExamNo') + String examNo; + + @JsonKey(name: 'Name') + String name; + + @JsonKey(name: 'ClassName') + String className; + + @JsonKey(name: 'TotalScore') + double totalScore; + + @JsonKey(name: 'TotalRanking') + int totalRanking; + + @JsonKey(name: 'TotalRankingChange') + String totalRankingChange; + + @JsonKey(name: 'TotalGradeRanking') + int totalGradeRanking; + + @JsonKey(name: 'TotalClassRanking') + int totalClassRanking; + + Data(this.subjectDetails,this.examStudentId,this.examNo,this.name,this.className,this.totalScore,this.totalRanking,this.totalRankingChange,this.totalGradeRanking,this.totalClassRanking,); + + factory Data.fromJson(Map srcJson) => _$DataFromJson(srcJson); + + Map toJson() => _$DataToJson(this); + +} + + +@JsonSerializable() +class SubjectDetails extends Object { + + @JsonKey(name: 'Score') + String score; + + @JsonKey(name: 'ExamSubjectId') + int examSubjectId; + + @JsonKey(name: 'GradeRanking') + String gradeRanking; + + @JsonKey(name: 'ClassRanking') + String classRanking; + + SubjectDetails(this.score,this.examSubjectId,this.gradeRanking,this.classRanking,); + + factory SubjectDetails.fromJson(Map srcJson) => _$SubjectDetailsFromJson(srcJson); + + Map toJson() => _$SubjectDetailsToJson(this); + +} + + diff --git a/marking_app/lib/common/model/report/report_card_params.dart b/marking_app/lib/common/model/report/report_card_params.dart new file mode 100644 index 0000000..fcba487 --- /dev/null +++ b/marking_app/lib/common/model/report/report_card_params.dart @@ -0,0 +1,24 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:marking_app/common/model/common/base_page_report.dart'; + +part 'report_card_params.g.dart'; + + +@JsonSerializable() +class ReportCardParams extends BasePageReport { + + @JsonKey(name: 'ExamId') + int examId; + + @JsonKey(name: 'ClassId') + int classId; + + ReportCardParams({required this.examId,required this.classId,page,limit}):super(page,limit); + + factory ReportCardParams.fromJson(Map srcJson) => _$ReportCardParamsFromJson(srcJson); + + Map toJson() => _$ReportCardParamsToJson(this); + +} + + diff --git a/marking_app/lib/common/model/report/report_marking_detail.dart b/marking_app/lib/common/model/report/report_marking_detail.dart new file mode 100644 index 0000000..6786c1f --- /dev/null +++ b/marking_app/lib/common/model/report/report_marking_detail.dart @@ -0,0 +1,122 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'report_marking_detail.g.dart'; + + +@JsonSerializable() +class ReportMarkingDetail extends Object { + + @JsonKey(name: 'MarkingPagerImgs') + List markingPagerImgs; + + @JsonKey(name: 'ExamOriginPapers') + List examOriginPapers; + + @JsonKey(name: 'QuestionAnswers') + List questionAnswers; + + ReportMarkingDetail(this.markingPagerImgs,this.examOriginPapers,this.questionAnswers,); + + factory ReportMarkingDetail.fromJson(Map srcJson) => _$ReportMarkingDetailFromJson(srcJson); + + Map toJson() => _$ReportMarkingDetailToJson(this); + +} + + +@JsonSerializable() +class ExamOriginPapers extends Object { + + @JsonKey(name: 'PageIndex') + int pageIndex; + + @JsonKey(name: 'ImageUrl') + String imageUrl; + + @JsonKey(name: 'Width') + double width; + + @JsonKey(name: 'Height') + double height; + + @JsonKey(name: 'PaperType') + int paperType; + + @JsonKey(name: 'Question') + List question; + + ExamOriginPapers(this.pageIndex,this.imageUrl,this.width,this.height,this.paperType,this.question,); + + factory ExamOriginPapers.fromJson(Map srcJson) => _$ExamOriginPapersFromJson(srcJson); + + Map toJson() => _$ExamOriginPapersToJson(this); + +} + + +@JsonSerializable() +class Question extends Object { + + @JsonKey(name: 'QuestionNum') + String questionNum; + + @JsonKey(name: 'isObj') + bool isObj; + + @JsonKey(name: 'X') + double x; + + @JsonKey(name: 'Y') + double y; + + @JsonKey(name: 'Width') + double width; + + @JsonKey(name: 'Height') + double height; + + @JsonKey(name: 'score') + double score; + + @JsonKey(name: 'totalScore') + double totalScore; + + Question(this.questionNum,this.isObj,this.x,this.y,this.width,this.height,this.score,this.totalScore,); + + factory Question.fromJson(Map srcJson) => _$QuestionFromJson(srcJson); + + Map toJson() => _$QuestionToJson(this); + +} + + +@JsonSerializable() +class QuestionAnswers extends Object { + + @JsonKey(name: 'QuestionNum') + String questionNum; + + @JsonKey(name: 'Answer') + String answer; + + @JsonKey(name: 'YourAnswer') + String yourAnswer; + + @JsonKey(name: 'AnswerType') + int answerType; + + @JsonKey(name: 'TotalScore') + double totalScore; + + @JsonKey(name: 'Score') + double score; + + QuestionAnswers(this.questionNum,this.answer,this.yourAnswer,this.answerType,this.totalScore,this.score,); + + factory QuestionAnswers.fromJson(Map srcJson) => _$QuestionAnswersFromJson(srcJson); + + Map toJson() => _$QuestionAnswersToJson(this); + +} + + diff --git a/marking_app/lib/common/model/report/report_marking_detail_params.dart b/marking_app/lib/common/model/report/report_marking_detail_params.dart new file mode 100644 index 0000000..782e9f1 --- /dev/null +++ b/marking_app/lib/common/model/report/report_marking_detail_params.dart @@ -0,0 +1,23 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'report_marking_detail_params.g.dart'; + + +@JsonSerializable() +class ReportMarkingDetailParams extends Object { + + @JsonKey(name: 'ExamSubjectId') + int examSubjectId; + + @JsonKey(name: 'StudentExamNum') + String studentExamNum; + + ReportMarkingDetailParams(this.examSubjectId,this.studentExamNum,); + + factory ReportMarkingDetailParams.fromJson(Map srcJson) => _$ReportMarkingDetailParamsFromJson(srcJson); + + Map toJson() => _$ReportMarkingDetailParamsToJson(this); + +} + + diff --git a/marking_app/lib/pages/report_detail/index.dart b/marking_app/lib/pages/report_detail/index.dart new file mode 100644 index 0000000..e74f68b --- /dev/null +++ b/marking_app/lib/pages/report_detail/index.dart @@ -0,0 +1,651 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/common/base_structure_result_report.dart'; +import 'package:marking_app/common/model/report/detail_base_info.dart'; +import 'package:marking_app/common/model/report/exam_records_all.dart'; +import 'package:marking_app/common/model/report/question_know_point.dart'; +import 'package:marking_app/common/model/report/report_card.dart'; +import 'package:marking_app/common/model/report/report_card_params.dart'; +import 'package:marking_app/pages/report_detail/widgets/basic_table.dart'; +import 'package:marking_app/pages/report_detail/widgets/card_table.dart'; +import 'package:marking_app/pages/report_detail/widgets/know_point_list.dart'; +import 'package:marking_app/pages/report_detail/widgets/lab_title.dart'; +import 'package:marking_app/pages/report_detail/widgets/no_data.dart'; +import 'package:marking_app/pages/report_detail/widgets/overall_level_table.dart'; +import 'package:marking_app/pages/report_detail/widgets/report_card_dialog.dart'; +import 'package:marking_app/pages/report_detail/widgets/question_table.dart'; +import 'package:marking_app/utils/request/rest_client_report.dart'; + +class ReportDetail extends StatefulHookConsumerWidget { + final int examId; + final bool showGrade; + + const ReportDetail({Key? key, required this.examId, required this.showGrade}) + : super(key: key); + + @override + _ReportDetailState createState() => _ReportDetailState(); +} + +class _ReportDetailState extends ConsumerState with CommonMixin { + bool isGrade = true; + bool isHasData = false; + + BaseInfo? baseInfo; + BaseStructureResultReport? overallLevelRes; + BaseStructureResultReport? questionKnowPointRes; + List subjectList = []; //科目 + List classList = []; //班级 + ComboData currentSubject = ComboData('', ''); + ComboData currentClass = ComboData('', ''); + bool isClass = false; + + // List cardHeadList = ['序号', '考号', '姓名', '班级', '分数', '班次', '校次']; + List cardHeadList = []; //成绩单表头 + List cardBodyList = []; //成绩单表格 + int currentPage = 1; //成绩单当前页 + int pageSize = 10; //成绩单当前页面请求数量 + int totalPage = 1; //成绩单总页数 + bool isRefresh = true; //防止请求未返回多次点击 + List initialList = []; + + void initState() { + super.initState(); + EasyLoading.show(status: 'loading...'); + getClass(); + } + + void getClass() async { + RestClientReport clientReport = await getClientReport(); + BaseStructureResultReport> res = + await clientReport.getClassList(widget.examId); + + BaseStructureResultReport> subjecData = + await clientReport.getExamsubject(widget.examId); + + setState(() { + classList = res.data!; + classList[0].isCheck = true; + currentClass = classList[0]; + + subjectList = subjecData.data!; + subjectList[0].isCheck = true; + currentSubject = subjectList[0]; + }); + getData(); + } + + void getData() { + getBaseInfo(); + getQuestionKnowPointTable(); + getCard(); + EasyLoading.dismiss(); + } + + //基础信息,总体水平,重点关注 + void getBaseInfo() async { + RestClientReport clientReport = await getClientReport(); + BaseStructureResultReport result = + await clientReport.getReportDetail(widget.examId, + widget.showGrade && isGrade ? -1 : currentClass.value); + setState(() { + overallLevelRes = result; + if (result!.data != null) { + baseInfo = result!.data!.baseInfo; + } + }); + } + + //小题得分,知识点 + void getQuestionKnowPointTable() async { + RestClientReport clientReport = await getClientReport(); + BaseStructureResultReport res = + await clientReport.getQuestionKnowPoint( + widget.examId, currentClass.value, currentSubject.value); + setState(() { + questionKnowPointRes = res; + print( + "%%%%%%%%%%%%%%%%%%%widget.examId=${widget.examId}¤tClass.value=${currentClass.value}¤tSubject.value=${currentSubject.value}"); + }); + EasyLoading.dismiss(); + } + + void getCard() async { + RestClientReport clientReport = await getClientReport(); + + ReportCardParams params = ReportCardParams( + examId: widget.examId, + classId: widget.showGrade && isGrade ? -1 : currentClass.value, + page: currentPage, + limit: pageSize); + BaseStructureResultReport result = + await clientReport.getReportCard(params); + print( + "#######${params.examId}¶ms.classId=${params.classId}¶ms.page=${params.page}¶ms.limit=${params.limit}"); + List head = result.data!.head; + head.removeAt(0); + List beforeList = ['序号', '考号', '姓名', '班级', '分数', '班次', '校次']; + beforeList.addAll(head); + cardHeadList = beforeList; + List bodylist = []; + initialList = result.data!.data; + result.data!.data.forEach((item) { + List arr = [ + item.examNo, + item.examStudentId, + item.name, + item.className, + item.totalScore.toString(), + item.totalClassRanking.toString(), + item.totalRanking.toString() + ]; + item.subjectDetails.forEach((subject) { + arr.add(subject.score); + }); + bodylist.add(arr); + }); + totalPage = (result.data!.totalCount / pageSize).ceil(); + setState(() { + cardBodyList = []; + cardBodyList = bodylist; + isRefresh = true; + }); + EasyLoading.dismiss(); + } + +//成绩单详情 + void showAlertDialog(BuildContext context, int index) { + List detailHead = []; + detailHead.addAll(cardHeadList.slice(5)); + detailHead.insert(0, '总分'); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + insetPadding: EdgeInsets.all(25.r), + content: ReportCardDialog(initialList[index], detailHead), + contentPadding: EdgeInsets.all(0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(15.r)))); + }, + ); + } + + void refreshData(item, bl) { + setState(() { + isClass = bl; + if (isClass) { + currentClass = item; + // getBaseInfo(); + } else { + currentSubject = item; + } + }); + getData(); + // getQuestionKnowPointTable(); + } + + void showSubjectDialog( + BuildContext context, List list, bool isclass) { + showModalBottomSheet( + context: context, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(10.r))), + builder: (BuildContext context) { + return StatefulBuilder(builder: (context, setDialogState) { + return Container( + padding: EdgeInsets.only(top: 20.r), + height: list.length * 100.r, + constraints: BoxConstraints(maxHeight: 300.r), + child: ListView.builder( + itemBuilder: (BuildContext context, int index) { + var item = list?[index]; + return InkWell( + onTap: () { + setDialogState(() { + list.forEach((element) { + element.isCheck = false; + }); + item!.isCheck = true; + }); + EasyLoading.show(status: 'loading...'); + refreshData(item, isclass); + Navigator.pop(context); + }, + child: Padding( + padding: EdgeInsets.symmetric(vertical: 10.r), + child: Center( + child: Text( + item!.text, + style: TextStyle( + fontSize: 14.r, + color: item.isCheck + ? Color(0xFF5F81FD) + : Color(0xFF2E2E2E)), + ))), + ); + }, + itemCount: list.length, + ), + ); + }); + }); + } + + @override + Widget build(BuildContext context) { + if (baseInfo == null || overallLevelRes == null) { + return Container(); + } + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + systemNavigationBarIconBrightness: Brightness.light, + statusBarBrightness: Brightness.dark, + statusBarIconBrightness: Brightness.light, + ), + child: Container( + color: Color(0xFFF8F9FF), + child: Column( + children: [ + Padding( + padding: EdgeInsets.only( + top: MediaQuery.of(context).padding.top + 20.h, + bottom: 20.r), + child: Stack( + alignment: const FractionalOffset(0.04, 0.1), + children: [ + Container( + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + baseInfo!.examName, + style: TextStyle( + fontSize: 16.sp, color: Color(0xFF2D384C)), + ), + ], + ), + ), + InkWell( + onTap: () => Navigator.pop(context), + child: Icon(Icons.arrow_back_ios_new_rounded, + color: Color(0xFF505767), size: 24.sp), + ), + ], + ), + ), + if (widget.showGrade) + Container( + margin: + EdgeInsets.only(left: 20.r, right: 20.r, bottom: 10.r), + width: MediaQuery.of(context).size.width - 40.r, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.r), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Color(0x566787FD), + offset: Offset(1.0, 1.0), //阴影xy轴偏移量 + blurRadius: 10.0, //阴影模糊程度 + spreadRadius: 0.1, //阴影扩散程度 + ) + ]), + child: Flex( + direction: Axis.horizontal, + children: [ + Expanded( + flex: 1, + child: InkWell( + onTap: () { + setState(() { + isGrade = true; + }); + EasyLoading.show(status: 'loading...'); + getData(); + }, + child: Container( + padding: EdgeInsets.symmetric(vertical: 10.r), + decoration: BoxDecoration( + color: isGrade ? Color(0xFF6787FD) : Colors.white, + borderRadius: BorderRadius.circular(20.r), + ), + child: Center( + child: Text( + '年级', + style: TextStyle( + fontSize: 14.sp, + color: isGrade ? Colors.white : Colors.black), + )), + ), + ), + ), + Expanded( + flex: 1, + child: InkWell( + onTap: () { + setState(() { + isGrade = false; + }); + EasyLoading.show(status: 'loading...'); + getData(); + }, + child: Container( + padding: EdgeInsets.symmetric(vertical: 10.r), + decoration: BoxDecoration( + color: + !isGrade ? Color(0xFF6787FD) : Colors.white, + borderRadius: BorderRadius.circular(20.r), + ), + child: Center( + child: Text( + '班级', + style: TextStyle( + fontSize: 14.sp, + color: + !isGrade ? Colors.white : Colors.black), + ), + ), + ), + ), + ), + // Text('99'), + ], + ), + ), + //基本信息 + Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!widget.showGrade || !isGrade) + Padding( + padding: EdgeInsets.only(left: 16.r), + child: Row( + children: [ + Text( + '班级:', + style: TextStyle( + fontSize: 14.sp, color: Color(0xFF474747)), + ), + InkWell( + onTap: () { + showSubjectDialog(context, classList, true); + }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: 3.r, horizontal: 30.r), + decoration: BoxDecoration( + color: Color.fromRGBO(197, 223, 255, 0.31), + border: Border.all( + width: 1.r, color: Color(0xFFC0DCFF)), + borderRadius: + BorderRadius.all(Radius.circular(20.r)), + ), + child: Text( + currentClass.text, + style: TextStyle( + fontSize: 14.r, + color: Color(0xFF6787FD), + fontWeight: FontWeight.w500), + ), + ), + ) + ], + ), + ), + LabTitle('基本信息', 'assets/images/basic_info.png'), + BasicTable(baseInfo), + LabTitle('总体水平', 'assets/images/overall_level.png'), + OverallLevelTable( + overallLevelRes!.data!.module1CJZL.sheets[0]), + LabTitle('重点关注学生', 'assets/images/reports_focus.png'), + OverallLevelTable(overallLevelRes! + .data!.module2ZDGZ.topExcelData.sheets[0]), + SizedBox( + height: 20.r, + ), + OverallLevelTable(overallLevelRes! + .data!.module2ZDGZ.bottomExcelData.sheets[0]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LabTitle('小题得分', 'assets/images/reports_score.png'), + Container( + padding: EdgeInsets.symmetric( + vertical: 2.r, horizontal: 10.r), + margin: EdgeInsets.only(right: 14.r), + decoration: BoxDecoration( + color: Color(0xFF6787FD), + borderRadius: + BorderRadius.all(Radius.circular(15.r)), + ), + child: InkWell( + onTap: () { + showSubjectDialog(context, subjectList, false); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + currentSubject.text, + style: TextStyle( + fontSize: 14.sp, + color: Colors.white, + fontWeight: FontWeight.w500), + ), + Padding( + padding: EdgeInsets.only(left: 8.r), + child: Image.asset( + 'assets/images/right_icon.png', + width: 6.r, + height: 10.r, + ), + ) + ], + ), + ), + ) + ], + ), + if (questionKnowPointRes != null) + Container( + // color: Colors.white, + margin: EdgeInsets.symmetric(horizontal: 14.r), + padding: EdgeInsets.only(bottom: 10.r, top: 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (questionKnowPointRes!.data!.userQuestion + .sheets[0].headData.length > + 0 && + questionKnowPointRes!.data!.userQuestion + .sheets[0].bodyData.length > + 0) + ? SizedBox( + height: questionKnowPointRes! + .data! + .userQuestion + .sheets[0] + .bodyData + .length > + 10 + ? 300.r + : questionKnowPointRes! + .data! + .userQuestion + .sheets[0] + .bodyData + .length * + 30.r, + // width: MediaQuery.of(context).size.width, + child: CardList( + headList: questionKnowPointRes!.data! + .userQuestion.sheets[0].headData, + bodyList: questionKnowPointRes!.data! + .userQuestion.sheets[0].bodyData, + isScore: true, + fixedRows: 1, + fixedCols: 3, + ), + + /*QuestionTable( + headList: questionKnowPointRes!.data! + .userQuestion.sheets[0].headData, + bodyList: questionKnowPointRes!.data! + .userQuestion.sheets[0].bodyData, + ),*/ + ) + : NoData(), + Padding( + padding: EdgeInsets.symmetric( + vertical: 10.r, horizontal: 10.r), + child: Text( + '知识点分析', + style: TextStyle( + fontSize: 16.sp, + color: Color(0xFF2D384C), + fontWeight: FontWeight.w500), + ), + ), + questionKnowPointRes!.data!.knowPointList.length > + 0 + ? SizedBox( + height: 300.r, + child: KnowPointTable( + questionKnowPointRes! + .data!.knowPointList), + ) + : NoData(), + /* questionKnowPointRes!.data!.knowPointList.length > + 0 + ? KnowPointTable( + questionKnowPointRes!.data!.knowPointList) + : NoData(),*/ + ], + ), + ), + LabTitle('成绩单', 'assets/images/reports_card.png'), + if (cardBodyList.length > 0) + Container( + // width: MediaQuery.of(context).size.width, + height: 330.r, + margin: EdgeInsets.symmetric(horizontal: 14.r), + child: CardList( + headList: cardHeadList, + bodyList: cardBodyList, + showCardDetail: showAlertDialog, + fixedRows: 1, + fixedCols: 3, + ), + ), + if (cardBodyList.length > 0) + Padding( + padding: EdgeInsets.only(right: 14.r,top: 6.r), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: () { + setState(() { + if (currentPage > 1 && isRefresh) { + isRefresh = false; + currentPage = currentPage - 1; + getCard(); + EasyLoading.show(status: 'loading...'); + } + }); + }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: 2.r, horizontal: 5.r), + decoration: BoxDecoration( + border: Border.all( + color: currentPage == 1 + ? Color(0xFFB3B9B9) + : Color(0xFF6787FD), + width: 1.r), + borderRadius: BorderRadius.all( + Radius.circular(2.r)), + ), + child: Text( + '上一页', + style: TextStyle( + color: currentPage == 1 + ? Color(0xFFB3B9B9) + : Color(0xFF6787FD)), + ), + ) + + /*Icon(Icons.keyboard_arrow_left, + color: currentPage == 1 + ? Color(0xFFB3B9B9) + : Color(0xFF505767), + size: 24.sp),*/ + ), + SizedBox( + width: 15.r, + ), + InkWell( + onTap: () { + setState(() { + if (currentPage < totalPage && + isRefresh) { + isRefresh = false; + currentPage = currentPage + 1; + getCard(); + EasyLoading.show(status: 'loading...'); + } + }); + }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: 2.r, horizontal: 5.r), + decoration: BoxDecoration( + border: Border.all( + color: totalPage - currentPage > 0 + ? Color(0xFF6787FD) + : Color(0xFFB3B9B9), + width: 1.r), + borderRadius: BorderRadius.all( + Radius.circular(2.r)), + ), + child: Text( + '下一页', + style: TextStyle( + color: totalPage - currentPage > 0 + ? Color(0xFF6787FD) + : Color(0xFFB3B9B9)), + ), + ) + /*Icon(Icons.keyboard_arrow_right, + color: totalPage - currentPage > 0 + ? Color(0xFF505767) + : Color(0xFFB3B9B9), + size: 24.sp),*/ + ) + ], + ), + ), + if (cardBodyList.length == 0) NoData(), + SizedBox( + height: 20.r, + ), + ], + ), + ), + ), + ], + ), + )); + } +} diff --git a/marking_app/lib/pages/report_detail/widgets/basic_table.dart b/marking_app/lib/pages/report_detail/widgets/basic_table.dart new file mode 100644 index 0000000..0a0b6b1 --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/basic_table.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:marking_app/common/model/report/detail_base_info.dart'; +import 'package:marking_app/pages/report_detail/widgets/basic_tablecell.dart'; + +class BasicTable extends StatelessWidget { + final BaseInfo? baseInfo; + + const BasicTable(this.baseInfo, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric(horizontal: 14.r), + padding: EdgeInsets.symmetric(vertical: 10.r, horizontal: 10.r), + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(5.r)), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + // color: Color(0xFFF5F7FF), + border: Border( + bottom: BorderSide(width: 1, color: Color(0xFFE8EBFF)), + ) + ), + child: IntrinsicHeight( + child: Row( + children: [ + Expanded(flex: 1, child: BasicTableCell('考试名称', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.examName, true)), + Expanded(flex: 1, child: BasicTableCell('考试类型', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.examType, true)), + ], + ), + ), + ), + Container( + decoration: BoxDecoration( + border: Border(bottom: BorderSide(width: 1, color: Color(0xFFE8EBFF))) + ), + child: IntrinsicHeight( + child: Row( + children: [ + Expanded(flex: 1, child: BasicTableCell('年级', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.grade, true)), + Expanded(flex: 1, child: BasicTableCell('学科', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.subject, true)), + ], + ), + ), + ), + Container( + decoration: BoxDecoration( + border: Border(bottom: BorderSide(width: 1, color: Color(0xFFE8EBFF))) + ), + child: IntrinsicHeight( + child: Row( + children: [ + Expanded(flex: 1, child: BasicTableCell('考试日期', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.examTime, true)), + Expanded(flex: 1, child: BasicTableCell('满分', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.totalScore.toString(), true)), + ], + ), + ), + ), + Container( + decoration: BoxDecoration( + border: Border(bottom: BorderSide(width: 1, color: Color(0xFFE8EBFF))) + ), + child: IntrinsicHeight( + child: Row( + children: [ + Expanded(flex: 1, child: BasicTableCell('平均分', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.avgScore.toString(), true)), + Expanded(flex: 1, child: BasicTableCell('最高分', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.maxScore.toString(), true)), + ], + ), + ), + ), + Container( + decoration: BoxDecoration( + border: Border(bottom: BorderSide(width: 1, color: Color(0xFFE8EBFF))) + ), + child: IntrinsicHeight( + child: Row( + children: [ + Expanded(flex: 1, child: BasicTableCell('优秀率', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.rateGood, true)), + Expanded(flex: 1, child: BasicTableCell('及格率', false)), + Expanded(flex: 1, child: BasicTableCell(baseInfo!.ratePass, true)), + ], + ), + ), + ) + ], + ), + ); + } +} diff --git a/marking_app/lib/pages/report_detail/widgets/basic_tablecell.dart b/marking_app/lib/pages/report_detail/widgets/basic_tablecell.dart new file mode 100644 index 0000000..ab7457e --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/basic_tablecell.dart @@ -0,0 +1,22 @@ +//基本信息表格单元格 +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; + +class BasicTableCell extends StatelessWidget { + final String content; + final bool isBold; + const BasicTableCell(this.content,this.isBold,{Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: double.infinity, + padding: EdgeInsets.symmetric(vertical:6.r,horizontal: 8.r), + decoration: BoxDecoration( + color: isBold?Colors.white:Color(0xFFF5F7FF), + ), + child: Text(content,style: TextStyle(fontSize: 12.sp,color: isBold?Colors.black:Color(0xFF505767),fontWeight: isBold?FontWeight.w500:FontWeight.w400),) + ); + } +} \ No newline at end of file diff --git a/marking_app/lib/pages/report_detail/widgets/card_table.dart b/marking_app/lib/pages/report_detail/widgets/card_table.dart new file mode 100644 index 0000000..6d512d7 --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/card_table.dart @@ -0,0 +1,137 @@ +import 'package:data_table_2/data_table_2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class CardList extends StatefulWidget { + final List headList; + final List bodyList; + final Function? showCardDetail; + final bool? isScore; + final int? fixedRows; + final int? fixedCols; + + const CardList({ + Key? key, + required this.headList, + required this.bodyList, + this.showCardDetail, + this.isScore = false, + this.fixedCols, + this.fixedRows, + }) : super(key: key); + + @override + State createState() => _CardListState(); +} + +class _CardListState extends State { + final ScrollController _controller = ScrollController(); + int? _sortColumnIndex; + bool _sortAscending = true; + + DataRow _getRow(int index, [Color? color]) { + assert(index >= 0); + var body = widget.bodyList[index]; + return DataRow2.byIndex( + index: index, + color: color != null ? MaterialStateProperty.all(color) : null, + cells: List.generate(widget.headList.length, (itemIndex) { + var item = body[itemIndex]; + return + itemIndex == 0 && widget.isScore == false ?DataCell(Center( + child: Text((index + 1).toString(), + style: + TextStyle(fontSize: 12.sp, color: Color(0xFF262626))), + )): + DataCell(itemIndex == 2 && widget.isScore == false + ? InkWell( + onTap: () { + if (widget.showCardDetail != null) { + widget.showCardDetail!(context, index); + } + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: Center( + child: Text(item.toString(), + style: TextStyle( + fontSize: 12.sp, color: Color(0xFF262626))), + ), + ), + Padding( + padding: EdgeInsets.only(right: 5.r), + child: Image.asset( + 'assets/images/search_icon.png', + width: 14.r, + height: 14.r, + ), + ), + ], + ), + ) + : Center( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 5.r), + child: Text(item.toString(), + style: + TextStyle(fontSize: 12.sp, color: Color(0xFF262626))), + ), + )); + }), + ); + } + + @override + Widget build(BuildContext context) { + return DataTable2( + dividerThickness: 0, + scrollController: _controller, + columnSpacing: 0, + horizontalMargin: 0, + dataRowHeight:50.r, + bottomMargin: 0, + border: TableBorder( + horizontalInside: BorderSide( + width: 1, color: Color(0xFFE0E5FF), style: BorderStyle.solid), + bottom: BorderSide( + width: 1, color: Color(0xFFE0E5FF), style: BorderStyle.solid), + verticalInside: BorderSide( + width: 1, color: Color(0xFFE0E5FF), style: BorderStyle.solid)), + headingRowColor: MaterialStateProperty.resolveWith((states) => + widget.fixedCols! > 0 ? Color(0xFFF0F3FF) : Colors.transparent), + /* headingRowDecoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Colors.grey[400]!, + Colors.grey[200]!, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ),*/ + headingRowDecoration: BoxDecoration(color: Color(0xFFF0F3FF)), + fixedColumnsColor: Color(0xFFF0F3FF), + fixedCornerColor: Colors.grey[400], + minWidth: 70.r * widget.headList.length, + fixedTopRows: widget.fixedRows!, + fixedLeftColumns: widget.fixedCols!, + sortColumnIndex: _sortColumnIndex, + sortAscending: _sortAscending, + // onSelectAll: (val) => setState(() => selectAll(val)), + columns: List.generate(widget.headList.length, (index) { + var item = widget.headList[index]; + return DataColumn2( + label: Center( + child: Text(widget.isScore == true ? item.rowData[0][0] : item, + style: TextStyle(fontSize: 12.sp, color: Color(0xFF505767))), + ), + // size: ColumnSize.S, + fixedWidth: index == 0 && widget.isScore == false?50.r:70.r, + ); + }), + rows: List.generate(widget.bodyList.length, + (index) => _getRow(index, Colors.transparent))); + } +} diff --git a/marking_app/lib/pages/report_detail/widgets/custom_rect.dart b/marking_app/lib/pages/report_detail/widgets/custom_rect.dart new file mode 100644 index 0000000..8e6f3c8 --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/custom_rect.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:marking_app/common/model/report/report_marking_detail.dart'; + +class CustomRect extends StatefulWidget { + ExamOriginPapers examOriginItem; + + CustomRect(this.examOriginItem, {Key? key}) : super(key: key); + + @override + _CustomRectState createState() => _CustomRectState(); +} + +class _CustomRectState extends State { + @override + Widget build(BuildContext context) { + return Container( + width: widget.examOriginItem.width, + height: widget.examOriginItem.height, + decoration: BoxDecoration( + image: DecorationImage( + image: NetworkImage(widget.examOriginItem.imageUrl), + fit: BoxFit.cover, + )), + child: CustomPaint( + size: + Size(widget.examOriginItem.width, widget.examOriginItem.height), + painter: MyPainter(widget.examOriginItem.question), + child: Stack( + children: List.generate(widget.examOriginItem.question.length, (index) { + var item = widget.examOriginItem.question[index]; + return Positioned( + left: item.x + item.width - 50, + top: item.y, + child: Row( + children: [ + Text(item.score.toInt().toString(),style: TextStyle(fontSize: 12.sp,color: Colors.red),), + Text('/',style: TextStyle(fontSize: 12.sp,color: Colors.red),), + Text(item.totalScore.toInt().toString(),style: TextStyle(fontSize: 12.sp,color: Colors.red),), + ], + )); + }), + ), + ), + ); + } +} + +///[定义画笔] +Paint _paint = Paint() + ..color = Colors.red //画笔颜色 + ..strokeCap = StrokeCap.round //画笔笔触类型 + ..isAntiAlias = true //是否启动抗锯齿 + ..style = PaintingStyle.stroke //绘画风格,默认为填充 + ..strokeWidth = 3.0; //画笔的宽度 + +class MyPainter extends CustomPainter { + final List questions; + + MyPainter(this.questions); + + + @override + void paint(Canvas canvas, Size size) { + questions.forEach((item) { + Rect rect = Rect.fromLTWH(item.x,item.y + ,item.width, item.height); + //根据上面的矩形,构建一个圆角矩形 + RRect rrect = RRect.fromRectAndRadius(rect, Radius.circular(0.0)); + + canvas.drawRRect(rrect, _paint); + }); + + /* Rect rect1 = Rect.fromCenter(center: Offset(140.0, 491.0), width: 91.0,height: 35.0); + //根据上面的矩形,构建一个圆角矩形 + RRect rrect1 = RRect.fromRectAndRadius(rect1, Radius.circular(0.0)); + canvas.drawRRect(rrect1, _paint);*/ + /* Rect rect2 = Rect.fromLTWH( 209.0, 549.0, 449.0,210.0); + //根据上面的矩形,构建一个圆角矩形 + RRect rrect2 = RRect.fromRectAndRadius(rect2, Radius.circular(0.0)); + canvas.drawRRect(rrect2, _paint);*/ + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + + return true; + } +} diff --git a/marking_app/lib/pages/report_detail/widgets/know_point_list.dart b/marking_app/lib/pages/report_detail/widgets/know_point_list.dart new file mode 100644 index 0000000..2514251 --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/know_point_list.dart @@ -0,0 +1,127 @@ +import 'package:data_table_2/data_table_2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:marking_app/common/model/report/question_know_point.dart'; + +class KnowPointTable extends StatefulWidget { + final List knowPointList; + + KnowPointTable(this.knowPointList, {Key? key}) : super(key: key); + + @override + State createState() => _KnowPointTableState(); +} + +class _KnowPointTableState extends State { + List headList = ['题号', '知识点', '正确率', '得分率']; + + DataRow _getRow(int index, [Color? color]) { + assert(index >= 0); + var bodyItem = widget.knowPointList[index]; + return DataRow2.byIndex( + index: index, + color: color != null ? MaterialStateProperty.all(color) : null, + cells:[ + DataCell(Center( + child: Container( + padding: EdgeInsets.symmetric( + vertical: 6.r, horizontal: 5.r), + child: Text( + bodyItem.questionNums, + style: TextStyle( + fontSize: 12.sp, color: Color(0xFF262626)), + ), + ), + ),), + DataCell(Container( + padding: EdgeInsets.symmetric( + vertical: 6.r, horizontal: 5.r), + child: Text( + bodyItem.name, + style: TextStyle( + fontSize: 12.sp, color: Color(0xFF262626)), + ), + ),), + DataCell(Center( + child: Container( + padding: EdgeInsets.symmetric( + vertical: 6.r, horizontal: 5.r), + child: Text( + bodyItem.correctRate, + style: TextStyle( + fontSize: 12.sp, color: Color(0xFF262626)), + ), + ), + ),), + DataCell( Center( + child: Container( + padding: EdgeInsets.symmetric( + vertical: 6.r, horizontal: 5.r), + child: Text( + bodyItem.gradeCorrectRate, + style: TextStyle( + fontSize: 12.sp, color: Color(0xFF262626)), + ), + ), + ),), + ], + ); + } + + @override + Widget build(BuildContext context) { + int fixedRows = 1; + int fixedCols = 0; + final ScrollController _controller = ScrollController(); + int? _sortColumnIndex; + bool _sortAscending = true; + + return DataTable2( + dividerThickness: 0, + scrollController: _controller, + columnSpacing: 0, + horizontalMargin: 0, + bottomMargin: 0, + border: TableBorder( + horizontalInside: BorderSide( + width: 1, color: Color(0xFFE0E5FF), style: BorderStyle.solid), + bottom: BorderSide( + width: 1, color: Color(0xFFE0E5FF), style: BorderStyle.solid), + verticalInside: BorderSide( + width: 1, color: Color(0xFFE0E5FF), style: BorderStyle.solid)), + headingRowColor: MaterialStateProperty.resolveWith((states) => + fixedCols > 0 ? Color(0xFFF0F3FF) : Colors.transparent), + /* headingRowDecoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Colors.grey[400]!, + Colors.grey[200]!, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ),*/ + headingRowDecoration: BoxDecoration(color: Color(0xFFF0F3FF)), + fixedColumnsColor: Color(0xFFF0F3FF), + fixedCornerColor: Colors.grey[400], + // minWidth: 70.r * headList.length, + fixedTopRows: fixedRows, + fixedLeftColumns: fixedCols, + sortColumnIndex: _sortColumnIndex, + sortAscending: _sortAscending, + // onSelectAll: (val) => setState(() => selectAll(val)), + columns: List.generate(headList.length, (index) { + var item = headList[index]; + return DataColumn2( + label: Center( + child: Text(item, + style: TextStyle(fontSize: 12.sp, color: Color(0xFF505767))), + ), + // size: ColumnSize.S, + fixedWidth: index == 0?60.r:(MediaQuery.of(context).size.width - 90.r)/3, + ); + }), + rows: List.generate(widget.knowPointList.length, + (index) => _getRow(index, Colors.transparent))); + } +} diff --git a/marking_app/lib/pages/report_detail/widgets/lab_title.dart b/marking_app/lib/pages/report_detail/widgets/lab_title.dart new file mode 100644 index 0000000..8ed369c --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/lab_title.dart @@ -0,0 +1,30 @@ +//基本信息、总体水平 +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class LabTitle extends StatelessWidget { + final String name; + final String img; + + const LabTitle(this.name, this.img, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.only(left: 14.r,top: 15.r,bottom: 15.r), + child: Row( + children: [ + Image.asset( + img, + width: 23.r, + height: 23.r, + ), + Text( + name, + style: TextStyle(fontSize: 16.sp, color: Color(0xFF2D384C)), + ) + ], + ), + ); + } +} diff --git a/marking_app/lib/pages/report_detail/widgets/no_data.dart b/marking_app/lib/pages/report_detail/widgets/no_data.dart new file mode 100644 index 0000000..a8ddee3 --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/no_data.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class NoData extends StatelessWidget { + const NoData({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Center(child: Text('-暂无数据-',style: TextStyle(fontSize: 14.sp,color: Color(0xFF505767)),)); + } +} diff --git a/marking_app/lib/pages/report_detail/widgets/overall_level_table.dart b/marking_app/lib/pages/report_detail/widgets/overall_level_table.dart new file mode 100644 index 0000000..5a584b0 --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/overall_level_table.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; +import 'package:marking_app/common/model/report/detail_base_info.dart'; + +class OverallLevelTable extends StatelessWidget { + final Sheets? tableList; + final bool? hasPad; + const OverallLevelTable(this.tableList,{Key? key,this.hasPad = true}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.only(left: hasPad == true?14.r:0,right: hasPad == true?14.r:0), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Table( + defaultVerticalAlignment: TableCellVerticalAlignment.middle, + + defaultColumnWidth: tableList!.headData.length<7?FixedColumnWidth((MediaQuery.of(context).size.width - 28.r) / tableList!.headData.length):IntrinsicColumnWidth(), + // defaultColumnWidth: IntrinsicColumnWidth(), + border: TableBorder(horizontalInside: BorderSide(width: 1, color: Color(0xFFE0E5FF), style: BorderStyle.solid), + bottom: BorderSide(width: 1, color: Color(0xFFE0E5FF), style: BorderStyle.solid), + verticalInside: BorderSide(width: 1, color: Color(0xFFE0E5FF), style: BorderStyle.solid)) + + /*TableBorder.symmetric( + inside: BorderSide(width: 1, color: Color(0xFFE0E5FF)), + // outside: BorderSide.none, + outside: hasPad == true?BorderSide.none:BorderSide(width: 1, color: Color(0xFFE0E5FF)), + )*/, + // borderRadius: BorderRadius.circular(5.r), + children: List.generate(tableList!.bodyData.length + 1, (bodyIndex) { + var bodyItem ; + if(bodyIndex > 0){ + bodyItem = tableList!.bodyData[bodyIndex - 1]; + } + return bodyIndex == 0? TableRow( + decoration: BoxDecoration( + color: Color(0xFFF0F3FF), + ), + children:List.generate(tableList!.headData.length, (index) { + var item = tableList!.headData[index]; + return Center( + child: Container( + padding: EdgeInsets.symmetric(vertical: 6.r,horizontal: 10.r), + child: Text( + item.rowData[0][0], + style: TextStyle( + fontSize: 12.sp, color: Color(0xFF505767)), + ), + ), + ); + }) + ):TableRow( + decoration: BoxDecoration( + color: Colors.white, + ), + children: List.generate(tableList!.headData.length, (cell){ + return Center( + child: Container( + padding: EdgeInsets.symmetric(vertical: 6.r,horizontal: 5.r), + child: cell == 0?Text( + bodyItem[cell].toString(), + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF262626)), + ):HtmlWidget(bodyItem[cell].toString(),textStyle: TextStyle(fontSize: 12.sp,color: Color(0xFF262626)),), + ), + ); + }) + ); + }) + ), + ), + ); + } +} diff --git a/marking_app/lib/pages/report_detail/widgets/question_table.dart b/marking_app/lib/pages/report_detail/widgets/question_table.dart new file mode 100644 index 0000000..e9694c5 --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/question_table.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:horizontal_data_table/horizontal_data_table.dart'; + + +class QuestionTable extends StatefulWidget { + final List headList; + final List bodyList; + const QuestionTable({ + Key? key, required this.headList, required this.bodyList, + }) : super(key: key); + + + @override + State createState() => _QuestionTableState(); +} + +class _QuestionTableState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return HorizontalDataTable( + leftHandSideColumnWidth: 100, + rightHandSideColumnWidth: (80.r * (widget.headList.length - 1)).toDouble(), + isFixedHeader: true, + headerWidgets: _getTitleWidget(), + isFixedFooter: false, + footerWidgets: _getTitleWidget(), + leftSideItemBuilder: _generateFirstColumnRow, + rightSideItemBuilder: _generateRightHandSideColumnRow, + itemCount:widget.bodyList.length, + rowSeparatorWidget: const Divider( + color: Color(0xFFE0E5FF), + height: 1.0, + thickness: 0.0, + ), + // leftHandSideColBackgroundColor: const Color(0xFFFFFFFF), + // rightHandSideColBackgroundColor: const Color(0xFFFFFFFF), + // itemExtent: 31.r, + ); + } + + List _getTitleWidget() { + return List.generate(widget.headList.length, (index){ + var item = widget.headList[index]; + return Container( + height: 30.r, + decoration: BoxDecoration( + color: Color(0xFFF0F3FF), + border:Border(right: BorderSide(width: 0.5.r,color: Color(0xFFE0E5FF))) + ), + width: index == 0?100.r:80.r, + child: Center( + child: Text(item.rowData[0][0],style: TextStyle( + fontSize: 12.sp, color: Color(0xFF505767))), + ),); + }); + } + + Widget _getTitleItemWidget(String label, double width) { + return Container( + width: width, + height: 56, + padding: const EdgeInsets.fromLTRB(5, 0, 0, 0), + alignment: Alignment.centerLeft, + child: Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), + ); + } + + Widget _generateFirstColumnRow(BuildContext context, int index) { + var item = widget.bodyList[index][0]; + return Container( + width: 100.r, + height: 30.r, + decoration: BoxDecoration( + border:Border(right: BorderSide(width: 0.5.r,color: Color(0xFFE0E5FF))) + ), + padding: const EdgeInsets.fromLTRB(5, 0, 0, 0), + alignment: Alignment.centerLeft, + child: Center( + child: Text(item.toString(), style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF262626))), + ), + ); + } + + Widget _generateRightHandSideColumnRow(BuildContext context, int index) { + var bodyItem = widget.bodyList[index]; + return Row( + children: List.generate(widget.headList.length -1, (bodyIndex){ + return Container( + width: 80.r, + height: 30.r, + decoration: BoxDecoration( + border:Border(right: BorderSide(width: 0.5.r,color: Color(0xFFE0E5FF))) + ), + padding: const EdgeInsets.fromLTRB(5, 0, 0, 0), + alignment: Alignment.centerLeft, + child: Center( + child: Text(bodyItem[bodyIndex + 1].toString(), style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF262626))), + ), + ); + }), + ); + } +} \ No newline at end of file diff --git a/marking_app/lib/pages/report_detail/widgets/report_card_dialog.dart b/marking_app/lib/pages/report_detail/widgets/report_card_dialog.dart new file mode 100644 index 0000000..10bd671 --- /dev/null +++ b/marking_app/lib/pages/report_detail/widgets/report_card_dialog.dart @@ -0,0 +1,371 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/common/base_structure_result_report.dart'; +import 'package:marking_app/common/model/report/exam_records_all.dart'; +import 'package:marking_app/common/model/report/report_card.dart'; +import 'package:marking_app/common/model/report/report_marking_detail.dart'; +import 'package:marking_app/common/model/report/report_marking_detail_params.dart'; +import 'package:marking_app/pages/report_detail/widgets/custom_rect.dart'; +import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; +import 'package:marking_app/utils/request/rest_client_report.dart'; + +class ReportCardDialog extends StatefulHookConsumerWidget { + final Data cardItem; + final List headList; + + const ReportCardDialog(this.cardItem, this.headList, {Key? key}) + : super(key: key); + + @override + _ReportCardDialogState createState() => _ReportCardDialogState(); +} + +class _ReportCardDialogState extends ConsumerState + with CommonMixin { + List subjectList = []; + ExamOriginPapers? examOrigin; + List examOriginList = []; + int currentSubjectId = 0; + + void initState() { + super.initState(); + List arr = widget.headList.slice(3); + arr.asMap().forEach((index, name) { + var subject = widget.cardItem.subjectDetails; + ComboData item = + ComboData(subject[index].examSubjectId, name, isCheck: false); + subjectList.add(item); + }); + subjectList[0].isCheck = true; + currentSubjectId = subjectList[0].value; + getImageDetail(); + } + + void getImageDetail() async { + RestClientReport clientReport = await getClientReport(); + ReportMarkingDetailParams params = + ReportMarkingDetailParams(currentSubjectId, widget.cardItem.examStudentId); + BaseStructureResultReport res = + await clientReport.getMarkingDetail(params); + + setState(() { + if(res.data!=null){ + examOrigin = res.data!.examOriginPapers[0]; + examOriginList = res.data!.examOriginPapers; + }else{ + examOriginList = []; + examOrigin = null; + } + }); + EasyLoading.dismiss(); + } + + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Column(children: [ + Padding( + padding: EdgeInsets.only(top: 10.r, left: 20.r, right: 20.r), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.cardItem.name, + style: TextStyle( + fontSize: 14.sp, + color: Color(0xFF6787FD), + fontWeight: FontWeight.w500), + ), + InkWell( + onTap: (){ + Navigator.pop(context); + }, + child: Icon(Icons.close,color: Colors.grey,size: 20.r,)) + ], + ), + Container( + margin: EdgeInsets.only(top: 10.r), + width: double.infinity, + height: 30.r, + decoration: BoxDecoration( + color: Color(0xFFEFF1FF), + borderRadius: BorderRadius.all(Radius.circular(2.r))), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: List.generate( + widget.headList.length > 7 ? 7 : widget.headList.length, + (index) { + var item = widget.headList[index]; + return Container( + width: (MediaQuery.of(context).size.width - 90.r) / 7, + child: _subjectText(item)); + }), + ), + ), + Container( + width: double.infinity, + height: 30.r, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(2.r))), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: (MediaQuery.of(context).size.width - 90.r) / 7, + child: _subjectText( + widget.cardItem.totalScore.toString())), + Container( + width: (MediaQuery.of(context).size.width - 90.r) / 7, + child: _subjectText( + widget.cardItem.totalClassRanking.toString())), + Container( + width: (MediaQuery.of(context).size.width - 90.r) / 7, + child: _subjectText( + widget.cardItem.totalRanking.toString())), + SizedBox( + width: + (MediaQuery.of(context).size.width - 90.r) / 7 * 4, + child: Row( + children: List.generate( + widget.cardItem.subjectDetails.length > 4 + ? 4 + : widget.cardItem.subjectDetails.length, + (index) { + var subject = widget.cardItem.subjectDetails[index]; + return Container( + width: + (MediaQuery.of(context).size.width - 90.r) / + 7, + child: _subjectText(subject.score)); + }), + )), + ], + ), + ), + if (widget.headList.length > 7) + Container( + width: double.infinity, + height: 30.r, + decoration: BoxDecoration( + color: Color(0xFFEFF1FF), + borderRadius: BorderRadius.all(Radius.circular(2.r))), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: + List.generate(widget.headList.length - 7, (index) { + var item = widget.headList[index + 7]; + return Container( + width: (MediaQuery.of(context).size.width - 90.r) / 7, + child: _subjectText(item)); + }), + ), + ), + if (widget.headList.length > 7) + Container( + width: double.infinity, + height: 30.r, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(2.r))), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: List.generate( + widget.cardItem.subjectDetails.length - 4, (index) { + var subject = widget.cardItem.subjectDetails[index + 4]; + return Container( + width: (MediaQuery.of(context).size.width - 90.r) / 7, + child: _subjectText(subject.score)); + }), + ), + ), + ], + ), + ), + Expanded( + child: Container( + margin: EdgeInsets.only(top: 0.r), + padding: EdgeInsets.symmetric(vertical: 5.r, horizontal: 20.r), + width: double.infinity, + decoration: BoxDecoration( + color: Color(0xFFEFF1FF), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(15.r), + bottomRight: Radius.circular(15.r))), + child: Column( + children: [ + SizedBox( + height: 30.r, + width: MediaQuery.of(context).size.width, + child: ListView.builder( + itemBuilder: (BuildContext context, int index) { + var item = subjectList[index]; + return InkWell( + onTap: () { + setState(() { + subjectList.forEach((element) { + element.isCheck = false; + }); + item.isCheck = true; + currentSubjectId = item.value; + EasyLoading.show(status: 'loading...'); + getImageDetail(); + }); + }, + child: Container( + width: 30.r, + height: 30.r, + margin: EdgeInsets.only( + right: index == subjectList.length + ? 0 + : (MediaQuery.of(context).size.width - + 30.r * 9 - + 110.r) / + 8), + decoration: BoxDecoration( + color: item.isCheck + ? Color(0xFF6787FD) + : Color(0xFFFFFFFF), + shape: BoxShape.circle, + // borderRadius: BorderRadius.all(Radius.circular(13.r)), + ), + child: Center( + child: Text( + item.text, + style: TextStyle( + fontSize: 11.sp, + color: item.isCheck + ? Colors.white + : Color(0xFF667095), + fontWeight: FontWeight.w400), + )), + ), + ); + }, + itemCount: subjectList.length, + scrollDirection: Axis.horizontal, + ), + ), + Expanded( + child: examOrigin != null && examOrigin!.imageUrl != '' + ? Padding( + padding: EdgeInsets.only(top:5.r), + child: InteractiveViewer( + constrained: false, + boundaryMargin: EdgeInsets.all(140.r), + minScale: 0.1, // 最小缩放 + maxScale: 4.0, // 最大缩放 + child: CustomRect(examOrigin!)), + ) + : MyEmptyWidget(), + ), + SizedBox(height: 6.r,), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if(examOrigin != null && examOrigin!.pageIndex > 1) + InkWell( + onTap: () { + var num = examOrigin!.pageIndex; + if (num > 1) { + setState(() { + examOrigin = examOriginList[num - 1 - 1]; + }); + } + }, + child:Container( + padding: EdgeInsets.symmetric( + vertical: 2.r, horizontal: 5.r), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xFF6787FD), + width: 1.r), + borderRadius: BorderRadius.all( + Radius.circular(2.r)), + ), + child: Text( + '下一页', + style: TextStyle( + color: Color(0xFF6787FD), + ), + ), + ), + /* Image.asset( + 'assets/images/report_left_icon.png', + width: 33.r, + height: 33.r, + )*/ + ), + SizedBox( + width: 30.r, + ), + if(examOrigin != null && examOrigin!.pageIndex < examOriginList.length) + InkWell( + onTap: () { + var num = examOrigin!.pageIndex; + if (num < examOriginList.length) { + setState(() { + examOrigin = examOriginList[num]; + }); + + } + }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: 2.r, horizontal: 5.r), + decoration: BoxDecoration( + border: Border.all( + color: Color(0xFF6787FD), + width: 1.r), + borderRadius: BorderRadius.all( + Radius.circular(2.r)), + ), + child: Text( + '上一页', + style: TextStyle( + color: Color(0xFF6787FD), + ), + ), + ), + /* Image.asset( + 'assets/images/report_right_icon.png', + width: 33.r, + height: 33.r, + )*/ + ), + ], + ), + ], + ), + )) + ]), + ); + } +} + +class _subjectText extends StatelessWidget { + final String name; + final bool isTitle; + + const _subjectText(this.name, {this.isTitle = true, Key? key}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Center( + child: Text( + name, + style: TextStyle( + fontSize: isTitle ? 11.sp : 12.sp, + color: isTitle ? Color(0xFF667095) : Color(0xFF424242), + fontWeight: FontWeight.w400), + ), + ); + } +} diff --git a/marking_app/lib/pages/reports/index.dart b/marking_app/lib/pages/reports/index.dart index 27cedee..24920d2 100644 --- a/marking_app/lib/pages/reports/index.dart +++ b/marking_app/lib/pages/reports/index.dart @@ -8,21 +8,30 @@ import 'package:functional_widget_annotation/functional_widget_annotation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:marking_app/common/mixin/common.dart'; +import 'package:marking_app/common/model/common/base_page_data_report.dart'; import 'package:marking_app/common/model/common/base_structure_result_report.dart'; import 'package:marking_app/common/model/enum/reportUserIdentity.dart'; +import 'package:marking_app/common/model/report/exam_records.dart'; +import 'package:marking_app/common/model/report/exam_records_all.dart'; +import 'package:marking_app/common/model/report/exam_records_params.dart'; import 'package:marking_app/common/model/report/report_histogram_model.dart'; import 'package:marking_app/common/model/report/report_home_model.dart'; import 'package:marking_app/common/model/user/user_info.dart'; import 'package:marking_app/common/model/user/user_info_report.dart'; +import 'package:marking_app/pages/report_detail/widgets/no_data.dart'; import 'package:marking_app/pages/reports/bar_chart_histogram.dart'; import 'package:marking_app/pages/reports/services/report_home_services.dart'; import 'package:marking_app/provider/user_provider.dart'; import 'package:marking_app/routes/RouterManager.dart'; +import 'package:marking_app/utils/easy_refresh/MyEmptyWidget.dart'; import 'package:marking_app/utils/index.dart'; import 'package:marking_app/utils/my_text.dart'; import 'package:marking_app/utils/request/rest_client_report.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; +import 'widgets/myDrawer.dart'; +import 'widgets/userInfo.dart'; + part 'index.g.dart'; class TheReport extends StatefulHookConsumerWidget { @@ -32,20 +41,34 @@ class TheReport extends StatefulHookConsumerWidget { _TheReportState createState() => _TheReportState(); } -class _TheReportState extends ConsumerState with CommonMixin, AutomaticKeepAliveClientMixin { +class _TheReportState extends ConsumerState + with CommonMixin, AutomaticKeepAliveClientMixin { late RemoveListener _userReportListener; + late RestClientReport ClientReport; late UserInfo _user; late UserInfoReport _userReport; late Future _future; int? theExamId; + List gradeList = []; + List testTypeList = []; + List selectList = []; + bool isGrade = false; + ComboData currentGrade = ComboData('', ''); + ComboData currentTestType = ComboData('', ''); + List reportList = []; + bool showGrade = false; @override bool get wantKeepAlive => true; @override void initState() { - _future = initData(); + getHomeComboExam(); + // getExamTypeList(); + // getGradeEnumList(); + + // _future = initData(); Future.delayed(Duration.zero, () { _user = ref.read(userProvider); _userReportListener = ref.read(userReportProvider.notifier).addListener( @@ -67,6 +90,91 @@ class _TheReportState extends ConsumerState with CommonMixin, Automat super.dispose(); } + //获取考试类别、年级 + void getHomeComboExam() async { + RestClientReport clientReport = await getClientReport(); + BaseStructureResultReport res = + await clientReport.getHomeCombo(); + testTypeList = res.data!.type.comboData; + gradeList = res.data!.grade.comboData; + testTypeList[0].isCheck = true; + gradeList[0].isCheck = true; + setState(() { + currentTestType = testTypeList[0]; + currentGrade = gradeList[0]; + }); + // getExamRecordsList(); + getLevel(); + } + + //获取考试类别 +/* void getExamTypeList() async { + RestClientReport clientReport = await getClientReport(); + BaseStructureResultReport res = await clientReport.getExamType(); + testTypeList = res.data!.comboData; + testTypeList[0].isCheck = true; + setState(() { + currentTestType = testTypeList[0]; + }); + }*/ + /* //获取年级 + void getGradeEnumList() async { + RestClientReport clientReport = await getClientReport(); + BaseStructureResultReport> result = await clientReport.getGradeEnum(); + gradeList = result.data!; + + print(gradeList); + gradeList[0].isCheck = true; + setState(() { + currentGrade = gradeList[0]; + }); + }*/ + //获取列表数据 + void getExamRecordsList() async { + RestClientReport clientReport = await getClientReport(); + ExamRecordsParams params = ExamRecordsParams( + reportType: currentTestType.value, + grade: currentGrade.value, + page: 1, + limit: 10); + BaseStructureResultReport> result = + await clientReport.getExamrecords(params); + setState(() { + reportList = result.data!.items; + }); + + } + + void checkHandle(item) { + setState(() { + if (isGrade) { + gradeList.forEach((element) { + element.isCheck = false; + }); + item.isCheck = true; + currentGrade = item; + } else { + testTypeList.forEach((element) { + element.isCheck = false; + }); + item.isCheck = true; + currentTestType = item; + } + }); + } + + void getLevel() async { + getExamRecordsList(); + RestClientReport clientReport = await getClientReport(); + BaseStructureResultReport res = + await clientReport.getPositionlevel(currentGrade.text); + if (res.data <= 3) { + showGrade = true; + }else{ + showGrade = false; + } + } + /// 初始化数据 Future initData({int? examId}) async { try { @@ -74,12 +182,14 @@ class _TheReportState extends ConsumerState with CommonMixin, Automat UserInfoReport _userReport = ref.read(userReportProvider); if (!_userReport.normal) { // 用户职位数据是否已经请求,在主页已经请求一次 为正常使用 备用 - bool theFlag = await ref.read(userReportProvider.notifier).initUserReport(); + bool theFlag = + await ref.read(userReportProvider.notifier).initUserReport(); if (!theFlag) { return ToastUtils.showError('请求失败请重试'); } } - BaseStructureResultReport res = await clientReport.getReportHomeData(examId); + BaseStructureResultReport res = + await clientReport.getReportHomeData(examId); if (res.success) { return res.data; } else { @@ -99,6 +209,7 @@ class _TheReportState extends ConsumerState with CommonMixin, Automat return null; } + var _scaffoldkey = new GlobalKey(); //将Scaffold设置为全局变量 @override Widget build(BuildContext context) { super.build(context); //调用super.build(返回值始终返回null,应将其忽略) @@ -113,45 +224,334 @@ class _TheReportState extends ConsumerState with CommonMixin, Automat statusBarBrightness: Brightness.light, ), child: Scaffold( - body: Stack( - children: [ - // 渐变背景色 - $ScaffoldBgc(), + key: _scaffoldkey, + drawer: MyDrawer( + list: selectList, + isGrade: isGrade, + onChanged: checkHandle, + refresh: getLevel), + body: Column( + children: [ + Container( + color: Colors.white, + padding: EdgeInsets.only( + top: MediaQuery.of(context).padding.top + 4.h, + left: 14.w, + right: 14.w, + bottom: 19.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: TopUserInfo(), + ), + /* InkWell( + onTap: () {}, + child: Text( + '历史报告', + style: TextStyle( + fontSize: 14.sp, + color: Color.fromRGBO(128, 128, 128, 1)), + ), + ),*/ + ], + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.only(left: 14.w, right: 14.w), + child: Column( + children: [ + SizedBox( + height: 50.r, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + 'assets/images/school_icon.png', + width: 22.w, + height: 22.w, + ), + Text( + '远轩高级中学', + style: TextStyle( + fontSize: 14.sp, + color: Color.fromRGBO(62, 86, 173, 1)), + ) + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + setState(() { + selectList = gradeList; + isGrade = true; + _scaffoldkey.currentState?.openDrawer(); + }); + }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: 8.h, horizontal: 20.r), + width: + MediaQuery.of(context).size.width / 2 - 50.r, + child: Row( + children: [ + Text( + '年级:', + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF585858)), + ), + Text( + currentGrade.text, + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF6686FD)), + ), + ], + ), + decoration: BoxDecoration( + color: Color.fromRGBO(197, 220, 255, 0.3), + borderRadius: BorderRadius.circular(20.r), + border: Border.all( + color: Color(0xFFC0DCFF), width: 1.r), + ), + ), + ), + InkWell( + onTap: () { + setState(() { + selectList = testTypeList; + isGrade = false; + _scaffoldkey.currentState?.openDrawer(); + }); + }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: 8.h, horizontal: 20.r), + width: + MediaQuery.of(context).size.width / 2 - 60.r, + child: Row( + children: [ + Text( + '考试类别:', + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF585858)), + ), + Text( + currentTestType.text, + style: TextStyle( + fontSize: 12.sp, + color: Color(0xFF6686FD)), + ), + ], + ), + decoration: BoxDecoration( + color: Color.fromRGBO(197, 220, 255, 0.3), + borderRadius: BorderRadius.circular(20.r), + border: Border.all( + color: Color(0xFFC0DCFF), width: 1.r), + ), + ), + ) + ], + ), + SizedBox( + height: 20.r, + ), - RefreshIndicator( - displacement: 50, - color: Theme.of(context).primaryColor, - backgroundColor: Colors.white, - child: MyFutureBuilder.buildFutureBuilderOfSingleInstance(context, _future, - (ReportHomeModel? datas) { - return _TheReportBody( - user: _user, - userReport: _userReport, - data: datas, - refreshReport: () { - _future = initData(); - toUpState(setState, () {}, mounted); - }, - switchCall: (int? examId, int positionIndex) { - theExamId = examId; - if (examId != null) { - ref.read(userReportProvider.notifier).setPositionIndex(positionIndex); - - _future = initData(examId: examId); - toUpState(setState, () {}, mounted); - } - }, - ); - }), - onRefresh: () async { - _future = initData(examId: theExamId); - await _future; - await Future.delayed(Duration(seconds: 1), () => toUpState(setState, () => {}, mounted)); - }, - ), - ], - ), - ), + Expanded( + child: reportList.length>0?ListView.builder( + itemBuilder: (BuildContext context, int index) { + var item = reportList[index]; + return Container( + // padding: EdgeInsets.all(6.r), + margin: EdgeInsets.only(bottom: 20.r), + width: double.infinity, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'assets/images/reports_bg.png'), + fit: BoxFit.fill, + ), + border: Border.all( + width: 1.r, + color: Color.fromRGBO(46, 91, 255, 0.2)), + borderRadius: BorderRadius.circular(5.r), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Container( + padding: EdgeInsets.symmetric( + vertical: 20.r, horizontal: 25.r), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'assets/images/report_group_bg.png'), + fit: BoxFit.fill, + )), + child: Text( + item.typeName, + style: TextStyle( + fontSize: 12.sp, + color: Colors.white), + ), + ), + Text( + item.name, + style: TextStyle( + fontSize: 14.sp, + color: Colors.black), + ) + ], + ), + Container( + padding: + EdgeInsets.symmetric(horizontal: 10.r), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5.r), + ), + child: Table( + defaultVerticalAlignment: + TableCellVerticalAlignment.middle, + border: TableBorder.symmetric( + inside: BorderSide( + width: 1, color: Color(0xFFD7DBF0)), + outside: BorderSide.none, + ), + // borderRadius: BorderRadius.circular(5.r), + children: [ + TableRow( + decoration: BoxDecoration( + color: Color(0xFFECECEC), + ), + children: [ + Center( + child: Container( + padding: EdgeInsets.symmetric( + vertical: 6.r), + child: Text( + '年级', + style: TextStyle( + fontSize: 14.sp, + color: Color(0xFF8A8A8A)), + ), + ), + ), + Center( + child: Text( + '科目', + style: TextStyle( + fontSize: 14.sp, + color: Color(0xFF8A8A8A)), + ), + ), + Center( + child: Text( + '考试日期', + style: TextStyle( + fontSize: 14.sp, + color: Color(0xFF8A8A8A)), + ), + ), + ], + ), + TableRow( + decoration: BoxDecoration( + color: Colors.white, + ), + children: [ + Center( + child: Container( + padding: EdgeInsets.symmetric( + vertical: 6.r), + child: Text( + item.grade, + style: TextStyle( + fontSize: 14.sp, + color: + Color(0xFF4A4A4A)), + ), + ), + ), + Center( + child: Text( + item.subjectStr, + style: TextStyle( + fontSize: 14.sp, + color: Color(0xFF4A4A4A)), + ), + ), + Center( + child: Text( + item.startTimeStr, + style: TextStyle( + fontSize: 14.sp, + color: Color(0xFF4A4A4A)), + ), + ), + ]) + ], + ), + ), + InkWell( + onTap: () { + RouterManager.router.navigateTo( + context, + RouterManager.reportDetailPath + + '?examId=${item.id}&showGrade=${showGrade}', + transition: getTransition(), + ); + }, + child: Container( + margin: EdgeInsets.symmetric( + horizontal: 60.r, vertical: 20.r), + padding: + EdgeInsets.symmetric(vertical: 5.r), + width: double.infinity, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(20.r), + gradient: LinearGradient( + begin: Alignment(0.95, -0.30), + end: Alignment(-0.95, 0.3), + colors: [ + Color(0xFF3561FE), + Color(0xFF6A89FC) + ])), + child: Center( + child: Text( + '查看报告', + style: TextStyle( + fontSize: 16.sp, + color: Colors.white), + )), + ), + ) + ], + ), + ); + }, + itemCount: reportList.length, + ):MyEmptyWidget(), + ), + ], + ), + ), + ) + ], + )), ); } } @@ -176,24 +576,33 @@ class _TheReportBody extends HookWidget { @override Widget build(BuildContext context) { ExamData? examData = data?.examData; - List? tagComparisonList = - examData?.tagComparisonList.map((e) => ReportHistogramModel(name: e.name, val: e.count)).toList(); + List? tagComparisonList = examData?.tagComparisonList + .map((e) => ReportHistogramModel(name: e.name, val: e.count)) + .toList(); List? examSubjectDatas = data?.examSubjectDatas; List _positions = userReport.positions; - SwitchIdentityExamsController potnAndExamCotl = SwitchIdentityExamsController.use( - position: userReport.positionIndex, exam: -1, initExamId: examData?.examId, positons: userReport.positions); - Positions? theCurrentPosition = _positions.isEmpty ? null : _positions[potnAndExamCotl.positionIndex.value]; + SwitchIdentityExamsController potnAndExamCotl = + SwitchIdentityExamsController.use( + position: userReport.positionIndex, + exam: -1, + initExamId: examData?.examId, + positons: userReport.positions); + Positions? theCurrentPosition = _positions.isEmpty + ? null + : _positions[potnAndExamCotl.positionIndex.value]; if (examData == null || theCurrentPosition == null) { return Padding( - padding: EdgeInsets.only(left: 16.w, right: 16.w, top: MediaQuery.of(context).padding.top), + padding: EdgeInsets.only( + left: 16.w, right: 16.w, top: MediaQuery.of(context).padding.top), child: Column( children: [ $ReportTitle( titleName: '', - switchCall: () => easyThrottle('reportHomeExamSwitchCall', () async { + switchCall: () => + easyThrottle('reportHomeExamSwitchCall', () async { // switchCall(examId, potnAndExamCotl.positionIndex.value); List positions = userReport.positions; if (positions.isEmpty) { @@ -215,8 +624,10 @@ class _TheReportBody extends HookWidget { //按钮被按下时的不透明度程度,0~1之间 pressedOpacity: 1, //这也可以自己定义Container然后固定大小进行设置等等 - child: quickText('没有数据,请重试', color: Colors.white, size: 14.sp), - onPressed: () => easyThrottle('reportHomeNoDataAgain', () => refreshReport()), + child: + quickText('没有数据,请重试', color: Colors.white, size: 14.sp), + onPressed: () => easyThrottle( + 'reportHomeNoDataAgain', () => refreshReport()), ), ), ), @@ -228,20 +639,24 @@ class _TheReportBody extends HookWidget { // 内容区域 return Padding( - padding: EdgeInsets.only(left: 16.w, right: 16.w, top: MediaQuery.of(context).padding.top), + padding: EdgeInsets.only( + left: 16.w, right: 16.w, top: MediaQuery.of(context).padding.top), child: Column( children: [ SizedBox(height: 6.h), // 考试报告TITLE $ReportTitle( titleName: examData.examName, - switchCall: () => easyThrottle('reportHomeExamSwitchCall', () async { + switchCall: () => + easyThrottle('reportHomeExamSwitchCall', () async { List positions = userReport.positions; if (positions.isEmpty) { return ToastUtils.showInfo('没有数据,请刷新'); } int? examId = await potnAndExamCotl.openSwitch( - context: context, currentExamId: examData.examId, currentPositionIndex: userReport.positionIndex); + context: context, + currentExamId: examData.examId, + currentPositionIndex: userReport.positionIndex); switchCall(examId, potnAndExamCotl.positionIndex.value); }), @@ -254,7 +669,9 @@ class _TheReportBody extends HookWidget { children: [ // 顶部平均分模块 $TopAverageContainer( - averageVal: examData.averageScore, totalVal: examData.totalScore, context: context), + averageVal: examData.averageScore, + totalVal: examData.totalScore, + context: context), // 详细报告入口区域 $DetailedReportEntry( @@ -282,7 +699,8 @@ class _TheReportBody extends HookWidget { $GraphicDataArea( title: '单科概况', titleImg: 'assets/images/report_home_icon_subject.png', - graphicContainer: $TheProgressChart(context, examSubjectDatas!)), + graphicContainer: + $TheProgressChart(context, examSubjectDatas!)), ], ), ), @@ -335,11 +753,13 @@ Widget $reportTitle({required String titleName, required switchCall}) { ), ), InkWell( - onTap: () => easyThrottle('switchRoleExamSheetCall', () => switchCall()), + onTap: () => + easyThrottle('switchRoleExamSheetCall', () => switchCall()), child: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(Icons.swap_vert_rounded, color: Color.fromRGBO(80, 87, 103, 0.8), size: 16.sp), + Icon(Icons.swap_vert_rounded, + color: Color.fromRGBO(80, 87, 103, 0.8), size: 16.sp), quickText('切换', color: Color.fromRGBO(80, 87, 103, 1), size: 12.sp), ], ), @@ -350,7 +770,10 @@ Widget $reportTitle({required String titleName, required switchCall}) { /// 顶部平均分模块 @swidget -Widget $topAverageContainer({required double averageVal, required double totalVal, required BuildContext context}) { +Widget $topAverageContainer( + {required double averageVal, + required double totalVal, + required BuildContext context}) { return Stack( alignment: const FractionalOffset(0.99, 0), children: [ @@ -366,7 +789,8 @@ Widget $topAverageContainer({required double averageVal, required double totalVa borderRadius: BorderRadius.circular(3).r, child: Container( alignment: Alignment.centerLeft, - padding: EdgeInsets.only(left: 4, right: 4, top: 4, bottom: 7).r, + padding: + EdgeInsets.only(left: 4, right: 4, top: 4, bottom: 7).r, child: quickText('平均分', size: 6.sp, color: Colors.white), decoration: ShapeDecoration( shape: BeveledRectangleBorder( @@ -380,22 +804,29 @@ Widget $topAverageContainer({required double averageVal, required double totalVa gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [Theme.of(context).primaryColor, Theme.of(context).primaryColor.withOpacity(0.3)], + colors: [ + Theme.of(context).primaryColor, + Theme.of(context).primaryColor.withOpacity(0.3) + ], ), ), ), ), SizedBox(height: 1.h), quickText(averageVal.toString(), - size: 38.sp, color: Color.fromRGBO(45, 56, 76, 1), fontWeight: FontWeight.bold), + size: 38.sp, + color: Color.fromRGBO(45, 56, 76, 1), + fontWeight: FontWeight.bold), SizedBox(height: 1.h), - quickText('满分' + totalVal.toString(), size: 12.sp, color: Color.fromRGBO(80, 87, 103, 0.7)), + quickText('满分' + totalVal.toString(), + size: 12.sp, color: Color.fromRGBO(80, 87, 103, 0.7)), ], ), ], ), ), - Image.asset("assets/images/report_home_top_img.png", width: 100.r, height: 100.r) + Image.asset("assets/images/report_home_top_img.png", + width: 100.r, height: 100.r) ], ); } @@ -441,9 +872,13 @@ Widget $detailedReportEntry({ mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - quickText('年级', size: 12.sp, color: Color.fromRGBO(80, 87, 103, 1)), + quickText('年级', + size: 12.sp, color: Color.fromRGBO(80, 87, 103, 1)), SizedBox(height: 5.h), - quickText(grade, size: 16.sp, fontWeight: FontWeight.bold, color: Color.fromRGBO(45, 56, 76, 1)), + quickText(grade, + size: 16.sp, + fontWeight: FontWeight.bold, + color: Color.fromRGBO(45, 56, 76, 1)), ], ), SizedBox(width: 23.w), @@ -454,10 +889,14 @@ Widget $detailedReportEntry({ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - quickText('学科', size: 12.sp, color: Color.fromRGBO(80, 87, 103, 1)), + quickText('学科', + size: 12.sp, color: Color.fromRGBO(80, 87, 103, 1)), SizedBox(height: 5.h), quickText(subject, - size: 16.sp, fontWeight: FontWeight.bold, color: Color.fromRGBO(45, 56, 76, 1), maxLines: 2), + size: 16.sp, + fontWeight: FontWeight.bold, + color: Color.fromRGBO(45, 56, 76, 1), + maxLines: 2), ], ) ], @@ -468,9 +907,13 @@ Widget $detailedReportEntry({ mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - quickText('考试日期', size: 12.sp, color: Color.fromRGBO(80, 87, 103, 1)), + quickText('考试日期', + size: 12.sp, color: Color.fromRGBO(80, 87, 103, 1)), SizedBox(height: 5.h), - quickText(examDate, size: 16.sp, fontWeight: FontWeight.bold, color: Color.fromRGBO(45, 56, 76, 1)), + quickText(examDate, + size: 16.sp, + fontWeight: FontWeight.bold, + color: Color.fromRGBO(45, 56, 76, 1)), ], ), ], @@ -485,7 +928,8 @@ Widget $detailedReportEntry({ '?examId=$examId&classId=$classId&subject=$subjectId&subjectName=${Uri.encodeComponent(subjectName)}'; break; case ReportUserIdentity.CLASS: // 班主任 - rootPath = RouterManager.reportClassTeacherPath + '?examId=$examId'; + rootPath = + RouterManager.reportClassTeacherPath + '?examId=$examId'; break; case ReportUserIdentity.GRADE: // 年级 case ReportUserIdentity.SCHOOL_LEVEL: // 校级 @@ -500,7 +944,8 @@ Widget $detailedReportEntry({ color: Theme.of(context).primaryColor, ).show(context); } - RouterManager.router.navigateTo(context, rootPath, transition: getTransition()); + RouterManager.router + .navigateTo(context, rootPath, transition: getTransition()); }), child: Container( width: double.infinity, @@ -512,7 +957,10 @@ Widget $detailedReportEntry({ gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [Color.fromRGBO(54, 86, 255, 1), Color.fromRGBO(93, 128, 255, 1)], + colors: [ + Color.fromRGBO(54, 86, 255, 1), + Color.fromRGBO(93, 128, 255, 1) + ], ), boxShadow: [ BoxShadow( @@ -523,7 +971,8 @@ Widget $detailedReportEntry({ ) ], ), - child: quickText('查看详细报告', color: Colors.white, size: 14.sp, fontWeight: FontWeight.w400), + child: quickText('查看详细报告', + color: Colors.white, size: 14.sp, fontWeight: FontWeight.w400), ), ), ], @@ -533,7 +982,10 @@ Widget $detailedReportEntry({ /// 图形数据模块 @swidget -Widget $graphicDataArea({required String title, required String titleImg, required Widget graphicContainer}) { +Widget $graphicDataArea( + {required String title, + required String titleImg, + required Widget graphicContainer}) { return Container( margin: EdgeInsets.only(bottom: 28.h), child: Column( @@ -587,7 +1039,8 @@ Widget $theProgressChart(context, List datas) { return $theProgressWidget( context: context, isMultiple: isMultiple, - linearGradient: LinearGradient(colors: [Colors.white, Theme.of(context).primaryColor]), + linearGradient: LinearGradient( + colors: [Colors.white, Theme.of(context).primaryColor]), subjectName: e.subjectName + '平均分', suffixTitle: e.totalScoreStr + '分', contentVal: e.averageScoreStr + '分', @@ -603,7 +1056,8 @@ Widget $theProgressChart(context, List datas) { return $theProgressWidget( context: context, isMultiple: isMultiple, - linearGradient: LinearGradient(colors: [Colors.white, Color.fromRGBO(0, 131, 253, 1)]), + linearGradient: LinearGradient( + colors: [Colors.white, Color.fromRGBO(0, 131, 253, 1)]), subjectName: e.subjectName + '及格率', suffixTitle: '100%', contentVal: e.passRateStr + '%', @@ -637,14 +1091,18 @@ Widget $theProgressWidget({ mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - quickText(subjectName, size: 14.sp, color: Color.fromRGBO(45, 56, 76, 1), fontWeight: FontWeight.w400), + quickText(subjectName, + size: 14.sp, + color: Color.fromRGBO(45, 56, 76, 1), + fontWeight: FontWeight.w400), SizedBox(height: 6.h), Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: LinearPercentIndicator( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0), + padding: + const EdgeInsets.symmetric(horizontal: 0, vertical: 0), // width: MediaQuery.of(context).size.width - 50, animation: true, lineHeight: 9.h, @@ -657,7 +1115,8 @@ Widget $theProgressWidget({ // ), // linearStrokeCap: LinearStrokeCap.butt, // progressColor: Theme.of(context).primaryColor, - backgroundColor: const Color.fromRGBO(219, 224, 243, 1).withOpacity(0.45), + backgroundColor: const Color.fromRGBO(219, 224, 243, 1) + .withOpacity(0.45), barRadius: Radius.circular(10.h), ), ), @@ -666,13 +1125,16 @@ Widget $theProgressWidget({ alignment: Alignment.centerRight, // padding: EdgeInsets.only(left: 20.w),suffixTitle child: quickText(suffixTitle, - size: 12.sp, color: Color.fromRGBO(148, 163, 182, 1), fontWeight: FontWeight.w400), + size: 12.sp, + color: Color.fromRGBO(148, 163, 182, 1), + fontWeight: FontWeight.w400), ), ], ), ], ), - quickText(contentVal, size: 12.sp, fontWeight: FontWeight.w700, color: contentColor), + quickText(contentVal, + size: 12.sp, fontWeight: FontWeight.w700, color: contentColor), ], ), ); diff --git a/marking_app/lib/pages/reports/widgets/myDrawer.dart b/marking_app/lib/pages/reports/widgets/myDrawer.dart new file mode 100644 index 0000000..f87d22e --- /dev/null +++ b/marking_app/lib/pages/reports/widgets/myDrawer.dart @@ -0,0 +1,59 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class MyDrawer extends StatefulWidget { + final List list; + final bool isGrade; + final ValueChanged onChanged; + final Function refresh; + + MyDrawer({required this.list, required this.isGrade, required this.onChanged,required this.refresh,Key? key}) : super(key: key); + + @override + State createState() => _MyDrawerState(); +} + +class _MyDrawerState extends State { + @override + void dispose() { + widget.refresh(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + // margin:EdgeInsets.only(top: 100.r), + child: Drawer( + width: MediaQuery.of(context).size.width / 2 - 20.r, + child: Padding( + padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 50.r), + child: Column( + children: [ + Text(widget.isGrade?'年级':'考试类别',style: TextStyle(fontSize: 16.sp,fontWeight: FontWeight.w500),), + Expanded( + child: ListView.builder( + itemBuilder: (BuildContext context, int index) { + var item = widget.list[index]; + return InkWell( + onTap: (){ + widget.onChanged(item); + Navigator.pop(context); + }, + child: Padding( + padding: EdgeInsets.symmetric(vertical: 20.r), + child: Center( + child: Text(item.text,style: TextStyle(fontSize: 14.sp,fontWeight: FontWeight.w400,color: item.isCheck?Color(0xFF5F81FD):Color(0xFF2E2E2E)),)), + ), + ); + }, + itemCount: widget.list.length, + ), + ), + ], + ), + )), + ); + } +} diff --git a/marking_app/lib/pages/reports/widgets/userInfo.dart b/marking_app/lib/pages/reports/widgets/userInfo.dart new file mode 100644 index 0000000..9c8469c --- /dev/null +++ b/marking_app/lib/pages/reports/widgets/userInfo.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +import 'package:marking_app/provider/user_provider.dart'; +import 'package:marking_app/routes/RouterManager.dart'; +import 'package:marking_app/utils/index.dart'; + +class TopUserInfo extends ConsumerWidget { + const TopUserInfo({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final userState = ref.watch(userProvider); + + return InkWell( + onTap: () { + RouterManager.router.navigateTo(context, RouterManager.userMinePath, transition: getTransition()); + }, + child: Row( + children: [ + Container( + decoration: BoxDecoration( + color: Color.fromRGBO(245, 246, 251, 1), + borderRadius: BorderRadius.circular(30.r), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(50.w), + child: Container( + alignment: Alignment.center, + color: Color.fromRGBO(163, 211, 255, 1), + padding: EdgeInsets.all(1.r), + child: Image.asset('assets/images/logo.png', width: 32.w, height: 32.w), + ), + ), + Container( + margin: EdgeInsets.only(left: 10.h), + child: Text( + userState.userName, + style: TextStyle(fontSize: 15.sp, color: const Color.fromRGBO(45, 56, 76, 0.9)), + ), + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 4.w), + child: Icon( + Icons.arrow_forward_ios_rounded, + size: 12.sp, + color: const Color.fromRGBO(45, 56, 76, 0.9), + ), + ), + SizedBox(width: 5.w) + ], + ), + ), + Expanded(child: SizedBox()) + ], + ), + ); + } +} \ No newline at end of file diff --git a/marking_app/lib/routes/RouterManager.dart b/marking_app/lib/routes/RouterManager.dart index c8eb776..5ee4c23 100644 --- a/marking_app/lib/routes/RouterManager.dart +++ b/marking_app/lib/routes/RouterManager.dart @@ -25,6 +25,7 @@ import 'package:marking_app/pages/marking/review.dart'; import 'package:marking_app/pages/mine/index.dart'; import 'package:marking_app/pages/mine/other_pages/index.dart'; import 'package:marking_app/pages/other/agreement_page.dart'; +import 'package:marking_app/pages/report_detail/index.dart'; import 'package:marking_app/pages/reports/report_class_teacher.dart'; import 'package:marking_app/pages/reports/report_personal_subject.dart'; import 'package:marking_app/pages/reports/report_subject_teacher.dart'; @@ -50,6 +51,7 @@ class RouterManager { static const String reportSubjectTeacherPath = 'report/details/reportSubjectTeacher'; static const String reportPersonalSubjectPath = 'report/details/reportPersonalSubject'; static const String userMinePath = 'user/mine/index'; + static const String reportDetailPath = '/report_detail/index'; // TheMine static final FluroRouter router = FluroRouter(); @@ -188,6 +190,13 @@ class RouterManager { static final _userMinePageHandler = Handler( handlerFunc: (BuildContext? context, Map> params) => TheMine(), ); + static final _reportDetailPath = Handler( + handlerFunc: (BuildContext? context, Map> params){ + int examId = int.parse(params['examId']![0]); + bool showGrade = params['showGrade']![0] == 'true'; + return ReportDetail(examId: examId,showGrade:showGrade); + }, + ); static final _reportPersonalSubjectPageHandler = Handler( handlerFunc: (BuildContext? context, Map> params) { @@ -238,6 +247,7 @@ class RouterManager { router.define(reportPersonalSubjectPath, handler: _reportPersonalSubjectPageHandler, transitionType: TransitionType.material); router.define(userMinePath, handler: _userMinePageHandler, transitionType: TransitionType.material); + router.define(reportDetailPath, handler: _reportDetailPath, transitionType: TransitionType.material); // getTransition() diff --git a/marking_app/lib/utils/request/rest_client_report.dart b/marking_app/lib/utils/request/rest_client_report.dart index 64f62b8..9cc5760 100644 --- a/marking_app/lib/utils/request/rest_client_report.dart +++ b/marking_app/lib/utils/request/rest_client_report.dart @@ -10,8 +10,15 @@ import 'package:dio/dio.dart'; import 'package:marking_app/common/model/common/base_page_data_report.dart'; import 'package:marking_app/common/model/common/base_structure_result_report.dart'; +import 'package:marking_app/common/model/report/detail_base_info.dart'; +import 'package:marking_app/common/model/report/exam_records.dart'; +import 'package:marking_app/common/model/report/exam_records_all.dart'; +import 'package:marking_app/common/model/report/exam_records_params.dart'; import 'package:marking_app/common/model/report/marked_item.dart'; import 'package:marking_app/common/model/report/marked_item_params.dart'; +import 'package:marking_app/common/model/report/question_know_point.dart'; +import 'package:marking_app/common/model/report/report_card.dart'; +import 'package:marking_app/common/model/report/report_card_params.dart'; import 'package:marking_app/common/model/report/report_for_class_teacher_model.dart'; import 'package:marking_app/common/model/report/report_for_class_teacher_params.dart'; import 'package:marking_app/common/model/report/report_for_marking_pagerdetail_model.dart'; @@ -21,6 +28,8 @@ import 'package:marking_app/common/model/report/report_for_subject_student_param import 'package:marking_app/common/model/report/report_for_subject_teacher_model.dart'; import 'package:marking_app/common/model/report/report_for_subject_teacher_params.dart'; import 'package:marking_app/common/model/report/report_home_model.dart'; +import 'package:marking_app/common/model/report/report_marking_detail.dart'; +import 'package:marking_app/common/model/report/report_marking_detail_params.dart'; import 'package:marking_app/common/model/user/user_info_report.dart'; import 'package:retrofit/retrofit.dart' as the_retrofit; @@ -67,4 +76,64 @@ abstract class RestClientReport { @the_retrofit.POST("/api/user/template/getmarkingpagerdetails") Future> getMarkingPagerDetails( @the_retrofit.Body() ReportForMarkingPagerdetailParams params); + + /* // 报告 => 获取考试类别 + @the_retrofit.GET("/api/user/exam/getexamtypecombo") + Future> getExamType();*/ + + // 报告 => 获取年级 + @the_retrofit.GET("/api/public/enum/GradeEnum") + Future>> getGradeEnum(); + + // 报告 => 首页列表 + @the_retrofit.GET("/api/user/exam/examrecords") + Future>> getExamrecords( + @the_retrofit.Queries() ExamRecordsParams params + ); + + // 报告 => 获取考试类别、年级 + @the_retrofit.GET("/api/user/exam/mobilehomecombo") + Future> getHomeCombo(); + + // 报告 => 首页详情 + @the_retrofit.GET("/api/exam/report/master_mobile") + Future> getReportDetail( + @the_retrofit.Query("examId") int examId, @the_retrofit.Query("classId") int classId + ); + + // 报告 => 考试学科 + @the_retrofit.GET("/api/user/exam/examsubject") + Future>> getExamsubject( + @the_retrofit.Query("examId") int examId + ); + + // 报告 => 可选下拉考试类型(是否有年级模块的报告权限) + @the_retrofit.GET("/api/user/exam/positionlevel") + Future getPositionlevel( + @the_retrofit.Query("grade") String grade + ); + + // 报告 => 可选班级 + @the_retrofit.GET("/api/exam/report/classList") + Future>> getClassList( + @the_retrofit.Query("examId") int examId + ); + // 报告 => 可选班级 + @the_retrofit.GET("/api/exam/report/master_mobilesubject") + Future> getQuestionKnowPoint( + @the_retrofit.Query("examId") int examId, + @the_retrofit.Query("classId") int classId, + @the_retrofit.Query("subject") int subject + ); + + // 报告 =>成绩单 + @the_retrofit.POST("/api/user/exam/reportcard") + Future> getReportCard( + @the_retrofit.Body() ReportCardParams params); + + // 报告 =>阅卷详情 + @the_retrofit.POST("/api/exam/report/getmarkingpagerdetails") + Future> getMarkingDetail( + @the_retrofit.Body() ReportMarkingDetailParams params + ); } diff --git a/marking_app/pubspec.yaml b/marking_app/pubspec.yaml index 2073a64..1be5a43 100644 --- a/marking_app/pubspec.yaml +++ b/marking_app/pubspec.yaml @@ -106,6 +106,8 @@ dependencies: image: ^4.1.3 awesome_dialog: ^3.1.0 badges: ^3.1.2 + horizontal_data_table: ^4.1.1 + data_table_2: ^2.5.10 dev_dependencies: flutter_test: