mcy_new #1
|
|
@ -4,7 +4,9 @@ import 'package:dio/dio.dart' hide Headers;
|
|||
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_param.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_result.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/favor_param.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/original_manuscript_handwriting_params.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/review_submission_params.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/job_handwriting.dart';
|
||||
import 'package:retrofit/retrofit.dart';
|
||||
import 'package:making_school_asignment_app/common/job/annotated_class.dart';
|
||||
import 'package:making_school_asignment_app/common/job/class_item.dart';
|
||||
|
|
@ -126,12 +128,11 @@ abstract class RetrofitClient {
|
|||
@POST("/api/hms/Annotate/AnnotateSubmit")
|
||||
Future reviewSubmission(@Body() ReviewSubmissionParams param);
|
||||
|
||||
// 获取原稿笔记
|
||||
@GET("/api/hms/HmsReport/GetStudentPaperHandwriting")
|
||||
Future<JobHandwriting> getOriginalManuscriptHandwriting(@Queries() OriginalManuscriptHandwritingParams param);
|
||||
|
||||
// OSS 上传key
|
||||
@GET("/api/infra/Oss/GetPresignedUri")
|
||||
Future getOssPresignedUri(@Query('key') String key);
|
||||
|
||||
// OSS 图片上传
|
||||
@PUT('{theUrl}')
|
||||
@Headers(<String, dynamic>{'Content-Type': ''})
|
||||
Future<void> uploadImag(@Path() String theUrl, @Part() File file);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
// 原稿笔迹参数
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'original_manuscript_handwriting_params.g.dart';
|
||||
|
||||
@JsonSerializable(checked: true, includeIfNull: false)
|
||||
class OriginalManuscriptHandwritingParams extends Object {
|
||||
@JsonKey(name: 'homeworkId')
|
||||
String homeworkId;
|
||||
|
||||
@JsonKey(name: 'studentId')
|
||||
int studentId;
|
||||
|
||||
@JsonKey(name: 'templateId')
|
||||
int? templateId;
|
||||
|
||||
@JsonKey(name: 'questionNo')
|
||||
int? questionNo;
|
||||
|
||||
@JsonKey(name: 'pageNum')
|
||||
int? pageNum;
|
||||
|
||||
OriginalManuscriptHandwritingParams({
|
||||
required this.homeworkId,
|
||||
required this.studentId,
|
||||
this.templateId,
|
||||
this.pageNum,
|
||||
this.questionNo,
|
||||
});
|
||||
|
||||
factory OriginalManuscriptHandwritingParams.fromJson(Map<String, dynamic> srcJson) => _$OriginalManuscriptHandwritingParamsFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$OriginalManuscriptHandwritingParamsToJson(this);
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'my_time_util.g.dart';
|
||||
|
||||
// 毫秒转小时、分钟、秒的函数
|
||||
TimeUnitModel? convertMilliseconds(int milliseconds) {
|
||||
try {
|
||||
int hours = milliseconds ~/ (1000 * 60 * 60);
|
||||
int minutes = (milliseconds % (1000 * 60 * 60)) ~/ (1000 * 60);
|
||||
int seconds = (milliseconds % (1000 * 60)) ~/ 1000;
|
||||
|
||||
if ((milliseconds % 1000) > 500) seconds += 1;
|
||||
|
||||
return TimeUnitModel(hours, minutes, seconds);
|
||||
} catch (e) {
|
||||
print('时间转换报错');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 毫秒转小时、分钟、秒的函数
|
||||
TimeUnitModel? convertSeconds(int totalSeconds) {
|
||||
try {
|
||||
int hours = totalSeconds ~/ 3600; // 整除3600得到小时数
|
||||
int remainingSeconds = totalSeconds % 3600; // 取模3600得到剩余的秒数
|
||||
int minutes = remainingSeconds ~/ 60; // 整除60得到分钟数
|
||||
int seconds = remainingSeconds % 60; // 取模60得到最终的秒数
|
||||
|
||||
return TimeUnitModel(hours, minutes, seconds);
|
||||
} catch (e) {
|
||||
print('时间转换报错');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class TimeUnitModel extends Object {
|
||||
int hours;
|
||||
int minutes;
|
||||
int seconds;
|
||||
TimeUnitModel(this.hours, this.minutes, this.seconds);
|
||||
|
||||
factory TimeUnitModel.fromJson(Map<String, dynamic> srcJson) => _$TimeUnitModelFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$TimeUnitModelToJson(this);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var timeStr = '';
|
||||
if (hours > 0) {
|
||||
timeStr += '${hours > 9 ? hours : '0' + hours.toString()} ';
|
||||
}
|
||||
if (minutes > 0) {
|
||||
timeStr += '${minutes > 9 ? minutes : '0' + minutes.toString()}';
|
||||
}
|
||||
|
||||
if (timeStr.length > 0) {
|
||||
timeStr += ':${seconds > 9 ? seconds : '0' + seconds.toString()}';
|
||||
} else {
|
||||
timeStr += '00:${seconds > 9 ? seconds : '0' + seconds.toString()}';
|
||||
}
|
||||
|
||||
return timeStr;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import 'package:making_school_asignment_app/common/config/colorUtils.dart';
|
|||
// 快捷Text使用
|
||||
Text quickText(text,
|
||||
{double? size,
|
||||
double? wordSpacing,
|
||||
Color color = CommonColors.defaultColor,
|
||||
TextAlign? align,
|
||||
FontWeight? fontWeight,
|
||||
|
|
@ -28,6 +29,7 @@ Text quickText(text,
|
|||
fontSize: size,
|
||||
color: color,
|
||||
fontWeight: fontWeight,
|
||||
wordSpacing: wordSpacing,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,11 +90,42 @@ class _DropdownSwitchStudentsTypeState extends State<DropdownSwitchStudentsType>
|
|||
value: sateData.value?.studentId,
|
||||
underline: Container(),
|
||||
isExpanded: true,
|
||||
onTap: () {
|
||||
print('数据..................');
|
||||
},
|
||||
items: sateData.value?.students.map((e) {
|
||||
return DropdownMenuItem(value: e.id, child: quickText(e.name, color: const Color.fromRGBO(79, 79, 79, 1), size: 14.sp));
|
||||
return DropdownMenuItem(
|
||||
value: e.id,
|
||||
child: Stack(
|
||||
alignment: const FractionalOffset(0, 0.62),
|
||||
children: [
|
||||
Container(
|
||||
padding: sateData.value?.studentId != e.id && e.isPriority ? EdgeInsets.only(left: 14.w) : null,
|
||||
child: quickText(
|
||||
e.name,
|
||||
size: 14.sp,
|
||||
color: const Color.fromRGBO(79, 79, 79, 1),
|
||||
),
|
||||
),
|
||||
if (e.isPriority && sateData.value?.studentId != e.id)
|
||||
Stack(
|
||||
alignment: const FractionalOffset(0.52, 0.24),
|
||||
children: [
|
||||
Icon(
|
||||
const IconData(0xe63d, fontFamily: "AlibabaIcon"),
|
||||
size: 12.sp,
|
||||
color: e.isPriority ? const Color.fromRGBO(76, 199, 147, 1) : const Color.fromRGBO(164, 164, 164, 1),
|
||||
),
|
||||
quickText('优先', size: 4.sp, color: Colors.white),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
hint: const Text('请选择学生'), // 锚点的显示文本
|
||||
onChanged: (value) {
|
||||
print('1111111111');
|
||||
if (logic.state.param.value.studentId == value) return;
|
||||
logic.state.param.value.studentId = value;
|
||||
logic.state.param.value = DoPaperDetailsParam.fromJson(logic.state.param.value.toJson());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:making_school_asignment_app/common/config/request_config.dart';
|
||||
|
||||
part 'job_handwriting.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class JobHandwriting extends Object {
|
||||
@JsonKey(name: 'lattices')
|
||||
List<Lattices> lattices;
|
||||
|
||||
@JsonKey(name: 'paperPicture')
|
||||
String paperPicture;
|
||||
|
||||
@JsonKey(name: 'pageNum')
|
||||
int pageNum;
|
||||
|
||||
@JsonKey(name: 'pageCount')
|
||||
int pageCount;
|
||||
|
||||
JobHandwriting(this.lattices, this.paperPicture, this.pageNum, this.pageCount);
|
||||
|
||||
factory JobHandwriting.fromJson(Map<String, dynamic> srcJson) => _$JobHandwritingFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$JobHandwritingToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class Lattices extends Object {
|
||||
// 笔画
|
||||
@JsonKey(name: 'stroke')
|
||||
int stroke;
|
||||
|
||||
@JsonKey(name: 'x')
|
||||
double x;
|
||||
|
||||
@JsonKey(name: 'y')
|
||||
double y;
|
||||
|
||||
@JsonKey(name: 'time')
|
||||
int time;
|
||||
|
||||
@JsonKey(name: 'intervalTime')
|
||||
int intervalTime;
|
||||
|
||||
@JsonKey(name: 'initialization')
|
||||
bool initialization;
|
||||
|
||||
Lattices(this.stroke, this.x, this.y, this.time, [this.intervalTime = 0, this.initialization = false]);
|
||||
|
||||
factory Lattices.fromJson(Map<String, dynamic> srcJson) => _$LatticesFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LatticesToJson(this);
|
||||
|
||||
/**
|
||||
* 根据基准初始化坐标数据
|
||||
* @param double scaleRatio
|
||||
*/
|
||||
void toInitialization(double scaleRatio) {
|
||||
x = x * scaleRatio;
|
||||
y = y * scaleRatio;
|
||||
initialization = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,969 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:functional_widget_annotation/functional_widget_annotation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/do_test_questions_image_info.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/original_manuscript_handwriting_params.dart';
|
||||
import 'package:making_school_asignment_app/common/mixins/event_bus_mixin.dart';
|
||||
import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/cached_network_img.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/my_time_util.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/utils.dart';
|
||||
import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/job_handwriting.dart';
|
||||
|
||||
import 'configuration_files/logic.dart';
|
||||
import 'configuration_files/states.dart';
|
||||
|
||||
part 'answer_handwriting_view.g.dart';
|
||||
|
||||
/// 学生答题轨迹
|
||||
class AnswerHandwriting extends Dialog {
|
||||
final String homeworkId;
|
||||
final int studentId;
|
||||
final int? templateId;
|
||||
final int? pageNum;
|
||||
final int? questionNo;
|
||||
final Function closeCall;
|
||||
AnswerHandwriting(
|
||||
{super.key, required this.homeworkId, required this.studentId, required this.closeCall, this.templateId, this.pageNum, this.questionNo}) {
|
||||
Get.find<HandwritingLogic>().params.value = OriginalManuscriptHandwritingParams(
|
||||
homeworkId: homeworkId,
|
||||
studentId: studentId,
|
||||
templateId: templateId,
|
||||
pageNum: pageNum,
|
||||
questionNo: questionNo,
|
||||
);
|
||||
}
|
||||
final _handwritingLogic = Get.find<HandwritingLogic>();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Container(
|
||||
height: ScreenUtil().screenHeight / 1.2,
|
||||
width: ScreenUtil().screenWidth - (ScreenUtil().scaleWidth < 1.5 ? 40.r : 40.r),
|
||||
alignment: Alignment.center,
|
||||
child: AnswerHandwritingMainBox(
|
||||
handwritingLogic: _handwritingLogic,
|
||||
homeworkId: homeworkId,
|
||||
templateId: templateId,
|
||||
studentId: studentId,
|
||||
pageNum: pageNum,
|
||||
questionNo: questionNo,
|
||||
closeCall: closeCall,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showAnswerHandwriting(
|
||||
BuildContext context, {
|
||||
required String homeworkId,
|
||||
required int studentId,
|
||||
int? templateId,
|
||||
int? pageNum,
|
||||
int? questionNo,
|
||||
}) async {
|
||||
try {
|
||||
backCall() {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
Get.put(HandwritingLogic(backCall));
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AnswerHandwriting(
|
||||
homeworkId: homeworkId,
|
||||
templateId: templateId,
|
||||
studentId: studentId,
|
||||
pageNum: pageNum,
|
||||
questionNo: questionNo,
|
||||
closeCall: backCall,
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
Get.delete<HandwritingLogic>();
|
||||
}
|
||||
}
|
||||
|
||||
// 主图
|
||||
class AnswerHandwritingMainBox extends HookWidget with EventBusMixin {
|
||||
AnswerHandwritingMainBox({
|
||||
required this.handwritingLogic,
|
||||
required this.homeworkId,
|
||||
required this.studentId,
|
||||
required this.closeCall,
|
||||
this.templateId,
|
||||
this.pageNum,
|
||||
this.questionNo,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final HandwritingLogic handwritingLogic;
|
||||
|
||||
final String homeworkId;
|
||||
final int? templateId;
|
||||
final int studentId;
|
||||
final int? pageNum;
|
||||
final int? questionNo;
|
||||
final Function closeCall;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var _useStateModel = UseMainBoxState.use(homeworkId, studentId, pageNum, questionNo, templateId);
|
||||
|
||||
useValueChanged<JobHandwriting?, void>(_useStateModel.handwritingData.value, (_, __) {
|
||||
var theData = _useStateModel.handwritingData.value;
|
||||
_useStateModel.pageNum.value = theData?.pageNum;
|
||||
_useStateModel.pageCount.value = theData?.pageCount ?? 0;
|
||||
_useStateModel.playPause.value = false;
|
||||
_useStateModel.constantFastSpeed.value = false;
|
||||
// Future.delayed(Duration.zero, () {
|
||||
// _useStateModel.handwritingKey.currentState?.ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal([]);
|
||||
// });
|
||||
// _useStateModel.handwritingDetail.value = _useStateModel.getHandwritingDetail(theData);
|
||||
});
|
||||
|
||||
useValueChanged<int?, void>(_useStateModel.pageNum.value, (oldVal, __) {
|
||||
if (oldVal != null && oldVal != _useStateModel.pageNum.value) {
|
||||
_useStateModel.questionNo = null;
|
||||
// _useStateModel.getData().catchError((e) => closeCall());
|
||||
}
|
||||
});
|
||||
|
||||
JobHandwriting? data = _useStateModel.handwritingData.value;
|
||||
HandwritingInfo? dataDetail = _useStateModel.handwritingDetail.value;
|
||||
|
||||
// if (data == null || dataDetail == null) return Container();
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Stack(
|
||||
alignment: const FractionalOffset(0, 0.5),
|
||||
children: [
|
||||
// 图片展示主框
|
||||
LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return HandwritingDrawBox(
|
||||
boxWidth: constraints.maxWidth,
|
||||
boxHeight: constraints.maxHeight,
|
||||
key: _useStateModel.handwritingKey,
|
||||
);
|
||||
}),
|
||||
PageNumberBox(),
|
||||
PreviousNutton(), // 上一页按钮
|
||||
NextPageButton(), // 下一题按钮
|
||||
],
|
||||
),
|
||||
),
|
||||
$BottomPlaybar(handwritingLogic),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UseMainBoxState with RequestToolMixin {
|
||||
final String homeworkId;
|
||||
final int studentId;
|
||||
final int? templateId;
|
||||
int? questionNo;
|
||||
final ValueNotifier<int?> pageNum;
|
||||
final ValueNotifier<int> pageCount;
|
||||
final ValueNotifier<JobHandwriting?> handwritingData;
|
||||
final ValueNotifier<HandwritingInfo?> handwritingDetail;
|
||||
|
||||
final ValueNotifier<bool> playPause; // 播放暂停
|
||||
final ValueNotifier<bool> constantFastSpeed; // 原速、快速 默认原速
|
||||
|
||||
GlobalKey<_HandwritingDrawBoxState> handwritingKey;
|
||||
|
||||
UseMainBoxState._({
|
||||
required this.homeworkId,
|
||||
required this.templateId,
|
||||
required this.studentId,
|
||||
required this.pageNum,
|
||||
required this.handwritingData,
|
||||
required this.questionNo,
|
||||
required this.pageCount,
|
||||
required this.playPause,
|
||||
required this.constantFastSpeed,
|
||||
required this.handwritingDetail,
|
||||
required this.handwritingKey,
|
||||
});
|
||||
|
||||
// 工厂构造函数
|
||||
factory UseMainBoxState.use(String homeworkId, int studentId, [int? pageNum, int? questionNo, int? templateId]) {
|
||||
return UseMainBoxState._(
|
||||
homeworkId: homeworkId,
|
||||
templateId: templateId,
|
||||
studentId: studentId,
|
||||
questionNo: questionNo,
|
||||
pageNum: useState(pageNum),
|
||||
handwritingData: useState(null),
|
||||
handwritingDetail: useState(null),
|
||||
pageCount: useState(0),
|
||||
playPause: useState(false),
|
||||
constantFastSpeed: useState(false),
|
||||
handwritingKey: GlobalKey(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 上一页
|
||||
class PreviousNutton extends StatelessWidget {
|
||||
PreviousNutton({super.key});
|
||||
|
||||
final HandwritingLogic handwritingLogic = Get.find<HandwritingLogic>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
if (handwritingLogic.resultData.value?.pageNum != null && handwritingLogic.resultData.value!.pageNum > 1) {
|
||||
return Positioned(
|
||||
left: 3.w,
|
||||
child: FloatingActionButton(
|
||||
heroTag: '点击前往上一题',
|
||||
tooltip: '点击前往上一题',
|
||||
focusColor: Theme.of(context).primaryColor,
|
||||
backgroundColor: const Color.fromRGBO(24, 32, 32, 0.1),
|
||||
elevation: 6.r,
|
||||
onPressed: () => easyThrottle('answer_handwriting_previous', () {
|
||||
var resultData = handwritingLogic.resultData.value;
|
||||
var params = handwritingLogic.params.value;
|
||||
if (resultData == null || params == null) return;
|
||||
params.pageNum = resultData.pageNum - 1;
|
||||
handwritingLogic.params.value = OriginalManuscriptHandwritingParams.fromJson(params.toJson());
|
||||
// handwritingLogic.params.value = params;
|
||||
}),
|
||||
child: Icon(Icons.arrow_back_ios, color: Colors.white, size: 22.sp),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return const SizedBox();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 下一页
|
||||
class NextPageButton extends StatelessWidget {
|
||||
final HandwritingLogic handwritingLogic = Get.find<HandwritingLogic>();
|
||||
|
||||
NextPageButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
if (handwritingLogic.resultData.value?.pageNum != null &&
|
||||
handwritingLogic.resultData.value!.pageNum < handwritingLogic.resultData.value!.pageCount) {
|
||||
return Positioned(
|
||||
right: 3.w,
|
||||
child: FloatingActionButton(
|
||||
heroTag: '点击前往下一题',
|
||||
tooltip: '点击前往下一题',
|
||||
elevation: 6.r,
|
||||
backgroundColor: const Color.fromRGBO(24, 32, 32, 0.1),
|
||||
onPressed: () => easyThrottle('answer_handwriting_next', () {
|
||||
var resultData = handwritingLogic.resultData.value;
|
||||
var params = handwritingLogic.params.value;
|
||||
if (resultData == null || params == null) return;
|
||||
params.pageNum = resultData.pageNum + 1;
|
||||
handwritingLogic.params.value = OriginalManuscriptHandwritingParams.fromJson(params.toJson());
|
||||
}),
|
||||
child: Icon(Icons.arrow_forward_ios, color: Colors.white, size: 22.sp),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return const SizedBox();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 笔记还原主框
|
||||
class HandwritingDrawBox extends StatefulWidget {
|
||||
final double boxWidth;
|
||||
final double boxHeight;
|
||||
const HandwritingDrawBox({required this.boxWidth, required this.boxHeight, super.key});
|
||||
|
||||
@override
|
||||
State<HandwritingDrawBox> createState() => _HandwritingDrawBoxState();
|
||||
}
|
||||
|
||||
class _HandwritingDrawBoxState extends State<HandwritingDrawBox> with EventBusMixin {
|
||||
HandwritingLogic handwritingLogic = Get.find<HandwritingLogic>(); // 学生答题笔迹逻辑层
|
||||
|
||||
ImageStream? imageStream; // 图片监听数据
|
||||
late ImageStreamListener theImageStreamListener; // 试题图片数据
|
||||
|
||||
late ValueNotifier<List<GestureHandwritingRecording>> _vnHandWritings;
|
||||
late ValueNotifier<List<GestureHandwritingRecording>> _vnPrimaryHandWritings;
|
||||
|
||||
late StreamSubscription showManuscriptStream;
|
||||
late StreamSubscription executionDataStream;
|
||||
|
||||
List<List<GestureHandwritingRecording>> _packagedHandwritingDatas = [];
|
||||
List<GestureHandwritingRecording> _packagedHandwritingDataAll = []; // 总数据
|
||||
List<GestureHandwritingRecording> pendingData = []; // 待执行数据
|
||||
List<Timer> timers = [];
|
||||
int handwritingTime = 0;
|
||||
int handwritingDuration = 0;
|
||||
double speed = 2.0; // 播放速度
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState(); /* */
|
||||
|
||||
theImageStreamListener = ImageStreamListener((ImageInfo info, bool _) {
|
||||
// 获取图片的宽高
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
//需要创建的小组件
|
||||
handwritingLogic.imagInfoModel.value = TestQuestionsImageInfo(
|
||||
url: handwritingLogic.resultData.value!.paperPicture,
|
||||
boxWidth: widget.boxWidth,
|
||||
boxHeight: widget.boxHeight,
|
||||
imageWidth: info.image.width.toDouble(),
|
||||
imageHeight: info.image.height.toDouble(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
_vnHandWritings = ValueNotifier<List<GestureHandwritingRecording>>([]);
|
||||
_vnPrimaryHandWritings = ValueNotifier<List<GestureHandwritingRecording>>(_packagedHandwritingDataAll);
|
||||
|
||||
handwritingLogic.toolbar.initialization.listen((e) {
|
||||
// 数据初始化完成赋值数据
|
||||
if (e) {
|
||||
_packagedHandwritingDatas = handwritingLogic.packagedHandwritingDatas.value;
|
||||
_packagedHandwritingDataAll = handwritingLogic.packagedHandwritingDataAll.value;
|
||||
} else {
|
||||
_packagedHandwritingDatas = [];
|
||||
_packagedHandwritingDataAll = [];
|
||||
}
|
||||
|
||||
_vnPrimaryHandWritings.value = [..._packagedHandwritingDataAll]; // 总体数据
|
||||
// eventFire(model: JobHandwritingPlaybarBus);
|
||||
});
|
||||
showManuscriptStream = handwritingLogic.toolbar.showManuscript.listen((e) {
|
||||
// 查看原稿控制
|
||||
if (e) {
|
||||
// 查看原稿
|
||||
eventFire(model: JobHandwritingPlaybarBus(false)); // 暂停
|
||||
_vnPrimaryHandWritings.value = [..._packagedHandwritingDataAll];
|
||||
} else {
|
||||
// 清空原稿数据
|
||||
_vnPrimaryHandWritings.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
executionDataStream = handwritingLogic.toolbar.executionData.listen((e) {
|
||||
print('555555555555555555555555555555');
|
||||
_vnHandWritings.value = e;
|
||||
});
|
||||
|
||||
eventOn(callback: (e) {
|
||||
switch (e.runtimeType) {
|
||||
case JobHandwritingRunTimeBus:
|
||||
var _model = (e as JobHandwritingRunTimeBus);
|
||||
var _runtime = _model.runTimeVal;
|
||||
handwritingDuration = _model.totalVal;
|
||||
handwritingTime = _runtime;
|
||||
if (_runtime <= 0) {
|
||||
pendingData.clear();
|
||||
}
|
||||
break;
|
||||
case JobHandwritingDragProgressBarBus:
|
||||
var _model = (e as JobHandwritingDragProgressBarBus);
|
||||
dragProgressBarInitData(_model.changeVal, _model.totalVal);
|
||||
break;
|
||||
case JobHandwritingPlaybarBus:
|
||||
// 播放 暂停
|
||||
var _val = e as JobHandwritingPlaybarBus;
|
||||
if (_val.play) {
|
||||
// 播放
|
||||
toGoPlay();
|
||||
} else {
|
||||
// 暂停
|
||||
toGoPause(_val.recalculate);
|
||||
}
|
||||
break;
|
||||
case PlaybackSpeedBus:
|
||||
// 播放速度
|
||||
var _model = (e as PlaybackSpeedBus);
|
||||
speed = _model.speed;
|
||||
dragProgressBarInitData(handwritingDuration - handwritingTime, handwritingDuration);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
imageStream?.removeListener(theImageStreamListener);
|
||||
showManuscriptStream.cancel();
|
||||
executionDataStream.cancel();
|
||||
eventCancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 暂停播放
|
||||
Future<void> toGoPause(bool recalculate) async {
|
||||
timers.forEach((e) {
|
||||
if (e.isActive) e.cancel();
|
||||
});
|
||||
timers = [];
|
||||
// 总时间-剩余时间=已经执行时间
|
||||
if (recalculate && pendingData.isNotEmpty && handwritingTime > 0 && (handwritingDuration - handwritingTime > 0)) {
|
||||
// 待执行的数据不等于空 每个数据都需要减去当前暂停已经执行的时间
|
||||
pendingData = pendingData.map((e) {
|
||||
return GestureHandwritingRecording(
|
||||
stroke: e.stroke,
|
||||
data: e.data,
|
||||
usageTime: e.usageTime,
|
||||
intervalTime: e.intervalTime - (handwritingDuration - handwritingTime) * 1000,
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
/// 开始播放
|
||||
Future<void> toGoPlay() async {
|
||||
try {
|
||||
var executableData = _packagedHandwritingDataAll;
|
||||
if (pendingData.isNotEmpty) {
|
||||
// 待执行的数据没有执行完成 就继续执行待执行数据
|
||||
executableData = pendingData;
|
||||
} else {
|
||||
// 需要重新添加所有数据进行播放
|
||||
pendingData.addAll(_packagedHandwritingDataAll);
|
||||
handwritingLogic.toolbar.executionData.value = []; // 重置已经执行的数据
|
||||
}
|
||||
handwritingLogic.toolbar.showManuscript.value = false;
|
||||
executableData.forEach((e) {
|
||||
var ter = Timer(Duration(milliseconds: e.intervalTime ~/ speed), () => zhixinCall(e));
|
||||
timers.add(ter);
|
||||
});
|
||||
} catch (e) {
|
||||
print('播放报错:$e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> zhixinCall(GestureHandwritingRecording e) async {
|
||||
if (mounted) {
|
||||
List<GestureHandwritingRecording> trajectorys = handwritingLogic.toolbar.executionData.value..add(e);
|
||||
handwritingLogic.toolbar.executionData.value = List.from(trajectorys);
|
||||
pendingData.remove(e); // 执行后删除容器中的当前动作
|
||||
}
|
||||
}
|
||||
|
||||
/// 拖动进度条后重新初始化数据
|
||||
/// @param startTime 起始时间 单位秒
|
||||
Future<void> dragProgressBarInitData(int startTime, int totalDuration) async {
|
||||
eventFire(model: JobHandwritingPlaybarBus(false, false));
|
||||
timers.forEach((e) {
|
||||
if (e.isActive) e.cancel();
|
||||
});
|
||||
timers = [];
|
||||
pendingData.clear();
|
||||
|
||||
if (startTime == 0) {
|
||||
handwritingLogic.toolbar.executionData.value = []; // 重置已经执行的数据
|
||||
pendingData.addAll(_packagedHandwritingDataAll);
|
||||
} else {
|
||||
// 待执行的数据不等于空 每个数据都需要减去当前暂停已经执行的时间
|
||||
startTime = startTime * 1000; // 转为毫秒
|
||||
List<GestureHandwritingRecording> executeImmediately = []; // 立即执行数据
|
||||
List<GestureHandwritingRecording> waitingExecution = []; // 等待执行数据
|
||||
|
||||
for (var i = 0; i < _packagedHandwritingDataAll.length; i++) {
|
||||
var item = _packagedHandwritingDataAll[i];
|
||||
|
||||
if (item.intervalTime < startTime) {
|
||||
// 需要直接装配到直接打印的容器
|
||||
executeImmediately.add(item);
|
||||
} else {
|
||||
var intervalTime = item.intervalTime - startTime;
|
||||
// 需要等待的数据
|
||||
waitingExecution.add(GestureHandwritingRecording(
|
||||
stroke: item.stroke,
|
||||
data: item.data,
|
||||
usageTime: item.usageTime,
|
||||
intervalTime: intervalTime,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pendingData = waitingExecution;
|
||||
handwritingLogic.toolbar.executionData.value = executeImmediately;
|
||||
}
|
||||
eventFire(model: JobHandwritingPlaybarBus(true));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
print('进入了....................');
|
||||
var paperPicture = handwritingLogic.resultData.value?.paperPicture;
|
||||
if (paperPicture == null) return const SizedBox();
|
||||
|
||||
print('显示原稿:${handwritingLogic.toolbar.showManuscript.value} 数据:${_vnPrimaryHandWritings.value.length}');
|
||||
return RepaintBoundary(
|
||||
child: CustomPaint(
|
||||
foregroundPainter: HandWritingDrawingPainter(
|
||||
ctrl: handwritingLogic.toolbar.showManuscript.value ? _vnPrimaryHandWritings : _vnHandWritings,
|
||||
),
|
||||
child: $TheCachedNetworkImage(
|
||||
imageUrl: paperPicture,
|
||||
(context, imageProvider) {
|
||||
Image imageWidget = Image(image: imageProvider, fit: BoxFit.contain);
|
||||
if (handwritingLogic.imagInfoModel.value == null) {
|
||||
imageStream?.removeListener(theImageStreamListener);
|
||||
imageStream = imageWidget.image.resolve(const ImageConfiguration());
|
||||
imageStream?.addListener(theImageStreamListener);
|
||||
}
|
||||
return imageWidget;
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class HandWritingDrawingPainter extends CustomPainter {
|
||||
final ValueNotifier<List<GestureHandwritingRecording>> ctrl;
|
||||
HandWritingDrawingPainter({required this.ctrl}) : super(repaint: ctrl);
|
||||
//[定义画笔]
|
||||
final Paint paintBrush = Paint()
|
||||
//画笔颜色
|
||||
..color = Colors.black
|
||||
//画笔笔触类型
|
||||
..strokeCap = StrokeCap.round
|
||||
//是否启动抗锯齿
|
||||
..isAntiAlias = true
|
||||
//绘画风格,默认为填充
|
||||
// ..style = PaintingStyle.fill
|
||||
//画笔的宽度
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 0.5.r;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
// canvas.drawPoints(PointMode.points, thePoints, paintBrush);
|
||||
canvas.save();
|
||||
|
||||
var points = ctrl.value;
|
||||
var _length = points.length;
|
||||
for (int i = 0; i < _length; i++) {
|
||||
GestureHandwritingRecording item = points[i];
|
||||
GestureHandwritingRecording? nextItem = i + 1 < _length ? points[i + 1] : null;
|
||||
|
||||
Offset offsetData = item.data;
|
||||
Offset? nextOffsetData = nextItem?.data;
|
||||
if (nextOffsetData != null && item.stroke == nextItem?.stroke) {
|
||||
canvas.drawLine(offsetData, nextOffsetData, paintBrush);
|
||||
}
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
||||
if (oldDelegate is HandWritingDrawingPainter) {
|
||||
var repaint = ctrl.value.length != oldDelegate.ctrl.value.length || oldDelegate.ctrl.value != ctrl.value;
|
||||
print('调用是否绘制:$repaint');
|
||||
return repaint;
|
||||
}
|
||||
return true; // 如果 oldDelegate 不是 MyCustomPainter 的实例,则总是重绘
|
||||
}
|
||||
}
|
||||
|
||||
// 页码
|
||||
class PageNumberBox extends StatelessWidget {
|
||||
PageNumberBox({super.key});
|
||||
|
||||
final HandwritingLogic handwritingLogic = Get.find<HandwritingLogic>(); // 学生答题笔迹逻辑层
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Positioned(
|
||||
top: 6.h,
|
||||
right: 4.w,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.47),
|
||||
borderRadius: BorderRadius.circular(5.r),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Obx(() {
|
||||
return quickText('${handwritingLogic.resultData.value?.pageNum}', color: Colors.white, size: 11.sp, align: TextAlign.end);
|
||||
}),
|
||||
quickText('/', color: Colors.white, size: 10.sp, align: TextAlign.end),
|
||||
Obx(() {
|
||||
return quickText('${handwritingLogic.resultData.value?.pageCount}', color: Colors.white, size: 8.sp, align: TextAlign.end);
|
||||
}),
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@hwidget
|
||||
Widget $bottomPlaybar(BuildContext context, HandwritingLogic handwritingLogic) {
|
||||
var timeConsuming = useState(0);
|
||||
var handwritingInfo = useState<HandwritingInfo?>(null);
|
||||
|
||||
var usePlaybar = UseBottomPlaybar.use(timeConsuming.value);
|
||||
useValueChanged<int, void>(timeConsuming.value, (_, __) {
|
||||
usePlaybar.playTimingSuspend();
|
||||
usePlaybar.playPause.value = false;
|
||||
var seds = timeConsuming.value ~/ 1000;
|
||||
if ((timeConsuming.value % 1000) > 500) seds += 1;
|
||||
usePlaybar.handwritingDuration.value = seds;
|
||||
usePlaybar.constantFastSpeed.value = PlaybackSpeed.DOUBLE_SPEED;
|
||||
});
|
||||
|
||||
useValueChanged<int, void>(usePlaybar.handwritingDuration.value, (_, __) {
|
||||
usePlaybar.useTime.value = usePlaybar.handwritingDuration.value;
|
||||
});
|
||||
// 播放速度
|
||||
useValueChanged<PlaybackSpeed, void>(usePlaybar.constantFastSpeed.value, (_, __) {
|
||||
usePlaybar.eventFire(model: PlaybackSpeedBus(usePlaybar.constantFastSpeed.value.speed));
|
||||
// 播放速度变化
|
||||
usePlaybar.playTimingSuspend();
|
||||
usePlaybar.playTimingStarts();
|
||||
});
|
||||
// 计时结束监听
|
||||
useValueChanged<int, void>(usePlaybar.useTime.value, (_, __) {
|
||||
var _runtime = usePlaybar.useTime.value;
|
||||
if (_runtime <= 0 || usePlaybar.handwritingDuration.value == _runtime) {
|
||||
Future.delayed(Duration.zero, () => (usePlaybar.playPause.value = false)); // 初始化播放按钮
|
||||
}
|
||||
usePlaybar.eventFire(model: JobHandwritingRunTimeBus(_runtime, usePlaybar.handwritingDuration.value));
|
||||
});
|
||||
|
||||
useEffect(() {
|
||||
// 初始化
|
||||
var initialization = handwritingLogic.toolbar.initialization.listen((e) {
|
||||
usePlaybar.handWritingReady.value = e;
|
||||
});
|
||||
var handwritingInfoStream = handwritingLogic.handwritingInfo.listen((e) {
|
||||
// 笔迹数据
|
||||
if (e == null) return;
|
||||
handwritingInfo.value = e;
|
||||
timeConsuming.value = e.timeConsuming;
|
||||
});
|
||||
usePlaybar.eventOn(callback: (e) {
|
||||
switch (e.runtimeType) {
|
||||
case JobHandwritingPlaybarBus:
|
||||
// 出发播放暂停
|
||||
var _val = e as JobHandwritingPlaybarBus;
|
||||
if (_val.play) {
|
||||
// 开始播放
|
||||
usePlaybar.playTimingStarts();
|
||||
if (!usePlaybar.playPause.value) usePlaybar.playPause.value = true;
|
||||
} else {
|
||||
// 暂停播放
|
||||
usePlaybar.playTimingSuspend();
|
||||
if (usePlaybar.playPause.value) usePlaybar.playPause.value = false;
|
||||
}
|
||||
break;
|
||||
case JobHandwritingGetReadyBus:
|
||||
// 作业笔迹已经计算好坐标 可以开始播放
|
||||
Future.delayed(Duration.zero, () => (usePlaybar.handWritingReady.value = true));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
||||
return () {
|
||||
try {
|
||||
initialization.cancel();
|
||||
usePlaybar.eventCancel();
|
||||
handwritingInfoStream.cancel();
|
||||
usePlaybar.timer.value?.cancel();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return Container(
|
||||
height: 62.h,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||
alignment: Alignment.center,
|
||||
color: Color.fromRGBO(0, 0, 0, 0.4),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (usePlaybar.handWritingReady.value)
|
||||
InkWell(
|
||||
onTap: () => easyThrottle('job_handwriting_play_pause', () {
|
||||
if (usePlaybar.handwritingDuration.value == 0) return ToastUtils.showInfo('没有笔迹');
|
||||
|
||||
usePlaybar.playPause.value = !usePlaybar.playPause.value;
|
||||
usePlaybar.eventFire(model: JobHandwritingPlaybarBus(usePlaybar.playPause.value));
|
||||
}),
|
||||
child: Icon(
|
||||
!usePlaybar.playPause.value ? Icons.play_circle_outline : Icons.pause_circle_outline,
|
||||
color: Colors.white,
|
||||
size: 28.r,
|
||||
),
|
||||
)
|
||||
else
|
||||
SpinKitPouringHourGlassRefined(size: 40.sp, color: Colors.white),
|
||||
SizedBox(width: 6.w),
|
||||
Expanded(
|
||||
child: LayoutBuilder(builder: (context, constraints) {
|
||||
final double containerWidth = constraints.maxWidth; // 展示区域总宽度
|
||||
var unitScale = containerWidth / timeConsuming.value; // 单位刻度
|
||||
var pauseIntervalsLength = handwritingInfo.value?.pauseInterval.length ?? 0;
|
||||
|
||||
List<Widget> pauseTickMarks = handwritingInfo.value?.pauseInterval.asMap().keys.map((e) {
|
||||
bool isLast = e == pauseIntervalsLength - 1;
|
||||
bool isFirst = e == 0;
|
||||
var item = handwritingInfo.value!.pauseInterval[e];
|
||||
return Positioned(
|
||||
top: 0,
|
||||
left: unitScale * item.startTime,
|
||||
child: Container(
|
||||
width: unitScale * (item.apart ?? 0),
|
||||
height: 10.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Color.fromRGBO(202, 201, 201, 1),
|
||||
borderRadius: isFirst
|
||||
? BorderRadius.only(topLeft: Radius.circular(8.r), bottomLeft: Radius.circular(10.r))
|
||||
: (isLast ? BorderRadius.only(topRight: Radius.circular(8.r), bottomRight: Radius.circular(10.r)) : null),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList() ??
|
||||
[];
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 10.h,
|
||||
width: containerWidth,
|
||||
decoration: BoxDecoration(
|
||||
// color: Color.fromRGBO(146, 146, 146, 1),
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(50.r),
|
||||
),
|
||||
),
|
||||
...pauseTickMarks,
|
||||
Container(
|
||||
height: 10.h,
|
||||
width: containerWidth,
|
||||
// color: Theme.of(context).primaryColor,
|
||||
child: SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
trackHeight: 10.h, // 轨道高度
|
||||
trackShape: RoundedRectSliderTrackShape(), // 轨道形状,可以自定义
|
||||
activeTrackColor: Theme.of(context).primaryColor, // 激活的轨道颜色
|
||||
inactiveTrackColor: Colors.transparent, // 未激活的轨道颜色
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 0, disabledThumbRadius: 0),
|
||||
thumbColor: Colors.white, // 滑块颜色
|
||||
overlayShape: RoundSliderOverlayShape(overlayRadius: 0),
|
||||
overlayColor: Colors.black54, // 滑块外圈颜色
|
||||
// valueIndicatorShape: PaddleSliderValueIndicatorShape(), // 标签形状,可以自定义
|
||||
),
|
||||
child: Slider(
|
||||
value: (usePlaybar.handwritingDuration.value - usePlaybar.useTime.value).toDouble(),
|
||||
min: 0.0,
|
||||
max: usePlaybar.handwritingDuration.value.toDouble(),
|
||||
inactiveColor: Colors.transparent,
|
||||
onChangeEnd: (value) {
|
||||
if (!usePlaybar.handWritingReady.value) return;
|
||||
usePlaybar.playTimingSuspend(); // 暂停计时器得暂停
|
||||
usePlaybar.eventFire(model: JobHandwritingDragProgressBarBus(value.toInt(), usePlaybar.handwritingDuration.value));
|
||||
usePlaybar.useTime.value = usePlaybar.handwritingDuration.value - value.toInt();
|
||||
},
|
||||
onChanged: (double value) {
|
||||
if (!usePlaybar.handWritingReady.value) return;
|
||||
usePlaybar.useTime.value = usePlaybar.handwritingDuration.value - value.toInt();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
SizedBox(
|
||||
width: containerWidth,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
quickText('累计停顿:${handwritingInfo.value?.pauseCount ?? 0}次', color: Colors.white, size: 7.sp),
|
||||
quickText(convertSeconds(usePlaybar.useTime.value)?.toString() ?? '', color: Colors.white, size: 7.sp),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => easyThrottle('job_handwriting_speed', () {
|
||||
var theIndex = PlaybackSpeed.values.indexOf(usePlaybar.constantFastSpeed.value);
|
||||
if (theIndex == PlaybackSpeed.values.length - 1) {
|
||||
theIndex = -1;
|
||||
}
|
||||
usePlaybar.constantFastSpeed.value = PlaybackSpeed.values[theIndex + 1];
|
||||
}, duration: Duration(milliseconds: 500)),
|
||||
child: Container(
|
||||
// alignment: Alignment.,
|
||||
padding: EdgeInsets.symmetric(horizontal: 3.w, vertical: 1.5.h),
|
||||
decoration: BoxDecoration(color: Color.fromRGBO(182, 197, 250, 1), borderRadius: BorderRadius.circular(4.r)),
|
||||
child: quickText(
|
||||
'${usePlaybar.constantFastSpeed.value.name}',
|
||||
color: Color.fromRGBO(79, 114, 244, 1),
|
||||
size: 8.sp,
|
||||
align: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 7.h),
|
||||
StudentManuscriptBtn(handwritingLogic),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 学生原稿按钮视图
|
||||
class StudentManuscriptBtn extends StatelessWidget {
|
||||
final HandwritingLogic handwritingLogic;
|
||||
const StudentManuscriptBtn(this.handwritingLogic, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
print('显示原噶:${handwritingLogic.toolbar.showManuscript.value}');
|
||||
return InkWell(
|
||||
onTap: () => easyThrottle('job_handwriting_udent_manuscript', () {
|
||||
var showManuscript = handwritingLogic.toolbar.showManuscript.value;
|
||||
handwritingLogic.toolbar.showManuscript.value = !showManuscript;
|
||||
}),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 3.w, vertical: 1.5.h),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
color: handwritingLogic.toolbar.showManuscript.value ? Theme.of(context).primaryColor : Colors.grey,
|
||||
),
|
||||
child: quickText('学生原稿', color: Colors.white, size: 8.sp, align: TextAlign.center),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class SysjTime extends StatefulWidget {
|
||||
const SysjTime({super.key});
|
||||
|
||||
@override
|
||||
State<SysjTime> createState() => _SysjTimeState();
|
||||
}
|
||||
|
||||
class _SysjTimeState extends State<SysjTime> with EventBusMixin<JobHandwritingRunTimeBus> {
|
||||
int useTime = 0;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// eventOn(callback: (JobHandwritingRunTimeBus e) {
|
||||
// useTime = e.runTimeVal;
|
||||
// toUpState(setState, () {}, mounted);
|
||||
// });
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
eventCancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return quickText(convertSeconds(useTime)?.toString() ?? '', color: Colors.white, size: 7.sp);
|
||||
}
|
||||
}
|
||||
|
||||
class UseBottomPlaybar with EventBusMixin {
|
||||
final ValueNotifier<int> handwritingDuration; // 笔迹总时长
|
||||
final ValueNotifier<bool> playPause; // 播放暂停
|
||||
final ValueNotifier<PlaybackSpeed> constantFastSpeed; // 播放速度
|
||||
final ValueNotifier<bool> handWritingReady;
|
||||
|
||||
final ValueNotifier<int> useTime; // 耗时 单位:(秒)
|
||||
|
||||
final ValueNotifier<Timer?> timer;
|
||||
|
||||
UseBottomPlaybar._(
|
||||
{required this.handWritingReady,
|
||||
required this.handwritingDuration,
|
||||
required this.playPause,
|
||||
required this.constantFastSpeed,
|
||||
required this.useTime,
|
||||
required this.timer});
|
||||
|
||||
// 工厂构造函数
|
||||
factory UseBottomPlaybar.use(int milliseconds) {
|
||||
int handwritingDuration = milliseconds ~/ 1000;
|
||||
if ((milliseconds % 1000) > 500) handwritingDuration += 1;
|
||||
return UseBottomPlaybar._(
|
||||
playPause: useState(false),
|
||||
constantFastSpeed: useState(PlaybackSpeed.DOUBLE_SPEED), // 默认两倍速
|
||||
useTime: useState(handwritingDuration),
|
||||
timer: useState(null),
|
||||
handwritingDuration: useState(handwritingDuration),
|
||||
handWritingReady: useState(false),
|
||||
);
|
||||
}
|
||||
|
||||
/// 开始计时
|
||||
void playTimingStarts() {
|
||||
if (useTime.value > 0) {
|
||||
timer.value?.cancel();
|
||||
|
||||
timer.value = Timer.periodic(Duration(milliseconds: 1000 ~/ constantFastSpeed.value.speed), (theTime) {
|
||||
useTime.value -= 1;
|
||||
if (useTime.value < 0) {
|
||||
theTime.cancel();
|
||||
timer.value?.cancel();
|
||||
timer.value = null;
|
||||
useTime.value = handwritingDuration.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 暂停
|
||||
void playTimingSuspend() {
|
||||
timer.value?.cancel();
|
||||
timer.value = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
// 逻辑文件
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/do_test_questions_image_info.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/original_manuscript_handwriting_params.dart';
|
||||
import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/job_handwriting.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/original_manuscript_handwriting/configuration_files/states.dart';
|
||||
|
||||
class HandwritingLogic extends GetxController with RequestToolMixin {
|
||||
Rx<JobHandwriting?> resultData = Rx<JobHandwriting?>(null); // 笔迹数据
|
||||
Rx<HandwritingInfo?> handwritingInfo = Rx<HandwritingInfo?>(null); // 笔迹数据
|
||||
Rx<List<List<GestureHandwritingRecording>>> packagedHandwritingDatas = Rx<List<List<GestureHandwritingRecording>>>([]);
|
||||
Rx<List<GestureHandwritingRecording>> packagedHandwritingDataAll = Rx<List<GestureHandwritingRecording>>([]); // 总数据
|
||||
Rx<OriginalManuscriptHandwritingParams?> params = Rx<OriginalManuscriptHandwritingParams?>(null); // 参数
|
||||
Rx<TestQuestionsImageInfo?> imagInfoModel = Rx<TestQuestionsImageInfo?>(null); // 图片加载后的数据
|
||||
|
||||
late ToolbarControl toolbar;
|
||||
|
||||
final Function backCall;
|
||||
HandwritingLogic(this.backCall);
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
toolbar = ToolbarControl(
|
||||
showManuscript: Rx<bool>(false), // 查看原卷
|
||||
initialization: Rx<bool>(false), // 初始化完成
|
||||
executionData: Rx<List<GestureHandwritingRecording>>([]), // 执行数据
|
||||
);
|
||||
ever(params, (callback) => getData());
|
||||
ever(imagInfoModel, (callback) {
|
||||
// 图像数据初始化后 计算笔迹数据
|
||||
handwritingInfo.value = getHandwritingDetail(resultData.value);
|
||||
getCalculatedSize();
|
||||
});
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getData() async {
|
||||
if (params.value == null) return;
|
||||
try {
|
||||
ToastUtils.showLoading();
|
||||
toolbar.initialization.value = false;
|
||||
imagInfoModel.value = null;
|
||||
var res = await getClient().getOriginalManuscriptHandwriting(params.value!);
|
||||
resultData.value = res;
|
||||
} catch (e) {
|
||||
ToastUtils.showError('获取笔迹数据报错,请重试');
|
||||
backCall();
|
||||
} finally {
|
||||
ToastUtils.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
// 计算尺寸
|
||||
Future<void> getCalculatedSize() async {
|
||||
if (imagInfoModel.value == null || handwritingInfo.value == null) return;
|
||||
Map<int, List<Lattices>> mapData = handwritingInfo.value!.strokeMap;
|
||||
double scaleRatio = imagInfoModel.value!.scaleRatio;
|
||||
if (mapData.isNotEmpty) {
|
||||
List<int> keys = mapData.keys.toList();
|
||||
|
||||
List<List<GestureHandwritingRecording>> _packagedHandwritingDatas = [];
|
||||
List<GestureHandwritingRecording> _packagedHandwritingDataAll = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
List<GestureHandwritingRecording> newTrajectoryData = mapData[keys[i]]!.map((e) {
|
||||
double theX = e.x;
|
||||
double theY = e.y;
|
||||
if (!e.initialization) {
|
||||
// 未初始化基准
|
||||
e.toInitialization(scaleRatio);
|
||||
theX = e.x;
|
||||
theY = e.y;
|
||||
}
|
||||
return GestureHandwritingRecording(
|
||||
data: Offset(theX, theY),
|
||||
usageTime: e.time.toInt(),
|
||||
intervalTime: e.intervalTime,
|
||||
stroke: e.stroke,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
newTrajectoryData.sort((a, b) => a.usageTime.compareTo(b.usageTime));
|
||||
_packagedHandwritingDatas.add(newTrajectoryData);
|
||||
_packagedHandwritingDataAll.addAll(newTrajectoryData);
|
||||
}
|
||||
packagedHandwritingDatas.value = _packagedHandwritingDatas; // 分组数据
|
||||
packagedHandwritingDataAll.value = _packagedHandwritingDataAll; // 不分组数据
|
||||
toolbar.showManuscript.value = true; // 默认原稿展示
|
||||
toolbar.initialization.value = true; // 数据初始化完成
|
||||
|
||||
// Future.delayed(Duration.zero, () => eventFire(model: JobHandwritingGetReadyBus())); // 通知外部可以播放笔迹
|
||||
}
|
||||
}
|
||||
|
||||
HandwritingInfo? getHandwritingDetail(JobHandwriting? theData) {
|
||||
if (theData == null) return null;
|
||||
// 笔画分组
|
||||
// var lattices = Map<int, List<Lattices>>.fromIterable(
|
||||
// theData.lattices,
|
||||
// key: (key) => key.stroke,
|
||||
// value: (value) {
|
||||
// // return theData.lattices.where((item) => item.stroke == value.stroke).toList()..sort((a, b) => a.time.compareTo(b.time));
|
||||
// return theData.lattices.where((item) => item.stroke == value.stroke).toList();
|
||||
// },
|
||||
// );
|
||||
var lattices = <int, List<Lattices>>{};
|
||||
var theLattices = theData.lattices;
|
||||
for (var i = 0; i < theLattices.length; i++) {
|
||||
Lattices item = theLattices[i];
|
||||
if (!lattices.containsKey(item.stroke)) lattices[item.stroke] = [];
|
||||
lattices[item.stroke]!.add(item); // 添加笔画数据
|
||||
}
|
||||
|
||||
List<int> latticeKeys = lattices.keys.toList(); // 笔画集合
|
||||
int timeConsuming = 0;
|
||||
if (latticeKeys.isNotEmpty) {
|
||||
// 计算总时长
|
||||
List<Lattices>? firstAction = lattices[latticeKeys[0]];
|
||||
List<Lattices>? lastAction = lattices[latticeKeys[latticeKeys.length - 1]];
|
||||
int firstTime = 0;
|
||||
int lastTime = 0;
|
||||
|
||||
if (firstAction?.isNotEmpty ?? false) {
|
||||
// 第一个笔画集合
|
||||
firstTime = firstAction![0].time;
|
||||
}
|
||||
if (lastAction?.isNotEmpty ?? false) {
|
||||
// 最后一笔画集合
|
||||
lastTime = lastAction![0].time;
|
||||
}
|
||||
timeConsuming = lastTime - firstTime;
|
||||
}
|
||||
var pauseCount = 0; // 停顿次数
|
||||
List<PauseIntervalTime> pauseInterval = [];
|
||||
for (var i = 0; i < latticeKeys.length; i++) {
|
||||
var currentLattices = lattices[latticeKeys[i]]!; // 当前循环笔画集合
|
||||
var prevLattices = i == 0 ? null : lattices[latticeKeys[i - 1]]!; // 下一个笔画集合
|
||||
for (var j = 0; j < currentLattices.length; j++) {
|
||||
var intervalTime = 0;
|
||||
var lattice = currentLattices[j];
|
||||
|
||||
if (j != 0) {
|
||||
var prevItem = currentLattices[j - 1];
|
||||
var adjacentSpacingTime = lattice.time - prevItem.time;
|
||||
intervalTime = adjacentSpacingTime + prevItem.intervalTime;
|
||||
if (adjacentSpacingTime > 5000) {
|
||||
// 大于5秒算一次停顿
|
||||
pauseCount++;
|
||||
pauseInterval.add(PauseIntervalTime(startTime: prevItem.intervalTime, endTime: intervalTime));
|
||||
}
|
||||
} else {
|
||||
if (i != 0 && prevLattices != null) {
|
||||
var prevLatticeLastItem = prevLattices[prevLattices.length - 1];
|
||||
var adjacentSpacingTime = lattice.time - prevLatticeLastItem.time;
|
||||
intervalTime = adjacentSpacingTime + prevLatticeLastItem.intervalTime;
|
||||
if (adjacentSpacingTime > 5000) {
|
||||
// 大于5秒算一次停顿
|
||||
pauseCount++;
|
||||
pauseInterval.add(PauseIntervalTime(startTime: prevLatticeLastItem.intervalTime, endTime: intervalTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
lattice.intervalTime = intervalTime;
|
||||
}
|
||||
}
|
||||
return HandwritingInfo(pauseCount, timeConsuming, lattices, pauseInterval);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
// 实体文件
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/job_handwriting.dart';
|
||||
|
||||
part 'states.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class HandwritingInfo extends Object {
|
||||
int pauseCount; // 停顿次数
|
||||
List<PauseIntervalTime> pauseInterval;
|
||||
int timeConsuming; // 耗时(毫秒)
|
||||
Map<int, List<Lattices>> strokeMap; // 笔画数据
|
||||
|
||||
HandwritingInfo(this.pauseCount, this.timeConsuming, this.strokeMap, this.pauseInterval);
|
||||
|
||||
factory HandwritingInfo.fromJson(Map<String, dynamic> srcJson) => _$HandwritingInfoFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$HandwritingInfoToJson(this);
|
||||
}
|
||||
|
||||
class ToolbarControl {
|
||||
Rx<bool> showManuscript; // 查看原稿
|
||||
Rx<bool> initialization;
|
||||
Rx<List<GestureHandwritingRecording>> executionData;
|
||||
|
||||
ToolbarControl({
|
||||
required this.executionData,
|
||||
required this.showManuscript,
|
||||
required this.initialization,
|
||||
});
|
||||
}
|
||||
|
||||
// 播放倍速
|
||||
enum PlaybackSpeed {
|
||||
ORIGINAL_SPEED(name: '原速播放', speed: 1),
|
||||
ONE_POINT_FIVE_SPEED(name: '1.5x播放', speed: 1.5),
|
||||
DOUBLE_SPEED(name: '2.0x播放', speed: 2),
|
||||
TRIPLE_SPEED(name: '3.0x播放', speed: 3),
|
||||
FOUR_SPEED(name: '4.0x播放', speed: 4),
|
||||
FIVE_SPEED(name: '5.0x播放', speed: 5),
|
||||
SIX_SPEED(name: '6.0x播放', speed: 6);
|
||||
|
||||
const PlaybackSpeed({required this.name, required this.speed});
|
||||
final double speed;
|
||||
final String name;
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class PauseIntervalTime extends Object {
|
||||
int? apart;
|
||||
int startTime;
|
||||
int endTime;
|
||||
PauseIntervalTime({required this.startTime, required this.endTime}) {
|
||||
apart = endTime - startTime;
|
||||
}
|
||||
|
||||
factory PauseIntervalTime.fromJson(Map<String, dynamic> srcJson) => _$PauseIntervalTimeFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$PauseIntervalTimeToJson(this);
|
||||
}
|
||||
|
||||
// 播放按钮
|
||||
class JobHandwritingPlaybarBus {
|
||||
bool play;
|
||||
bool recalculate;
|
||||
JobHandwritingPlaybarBus(this.play, [this.recalculate = true]);
|
||||
}
|
||||
|
||||
// 笔迹是否已经准备好(笔迹计算好坐标后通知通知栏可以开始播放)
|
||||
class JobHandwritingGetReadyBus {
|
||||
JobHandwritingGetReadyBus();
|
||||
}
|
||||
|
||||
// 笔记运行时间
|
||||
class JobHandwritingRunTimeBus {
|
||||
int runTimeVal;
|
||||
int totalVal;
|
||||
JobHandwritingRunTimeBus(this.runTimeVal, this.totalVal);
|
||||
}
|
||||
|
||||
// 拖动进度条
|
||||
class JobHandwritingDragProgressBarBus {
|
||||
int changeVal;
|
||||
int totalVal;
|
||||
JobHandwritingDragProgressBarBus(this.changeVal, this.totalVal);
|
||||
}
|
||||
|
||||
// 播放速度 (原速播放/快速播放)
|
||||
class PlaybackSpeedBus {
|
||||
double speed;
|
||||
PlaybackSpeedBus(this.speed);
|
||||
}
|
||||
|
||||
// 手势记录(原稿笔记还原)
|
||||
class GestureHandwritingRecording {
|
||||
int stroke;
|
||||
int usageTime; // 用时
|
||||
Offset data;
|
||||
int intervalTime; // 间隔时间
|
||||
GestureHandwritingRecording({
|
||||
required this.stroke,
|
||||
required this.data,
|
||||
required this.usageTime,
|
||||
required this.intervalTime,
|
||||
});
|
||||
}
|
||||
|
|
@ -1,12 +1,9 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:functional_widget_annotation/functional_widget_annotation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:making_school_asignment_app/common/config/request_config.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_bus.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_param.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_result.dart';
|
||||
|
|
@ -52,7 +49,7 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
|||
// 主图
|
||||
QuestionImageView(maxWidth, maxHeight, sateData, annotationState, logic),
|
||||
// 继续批阅按钮
|
||||
Positioned(right: 3.w, bottom: 4.h, child: const $ContinueToReview(isFloatingAction: true)),
|
||||
// Positioned(right: 3.w, bottom: 4.h, child: const $ContinueToReview(isFloatingAction: true)),
|
||||
// 上一题按钮
|
||||
Positioned(
|
||||
left: 2.w,
|
||||
|
|
@ -126,6 +123,7 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
|||
}
|
||||
}
|
||||
|
||||
// 底部已阅数量和待阅数量
|
||||
@swidget
|
||||
Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData) {
|
||||
return Obx(() {
|
||||
|
|
@ -136,75 +134,86 @@ Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData)
|
|||
right: 8.w,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
// List<JobConcernedWithStudent>? students = await getStudents();
|
||||
// if (students == null) return;
|
||||
// students = students..sort((e, e1) => e.studentName.compareTo(e1.studentName));
|
||||
// showModalBottomSheet(
|
||||
// context: context,
|
||||
// elevation: 10,
|
||||
// backgroundColor: Colors.white,
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.only(
|
||||
// topLeft: Radius.circular(10.r),
|
||||
// topRight: Radius.circular(10.r),
|
||||
// ),
|
||||
// ),
|
||||
// builder: (BuildContext context) {
|
||||
// return Padding(
|
||||
// padding: EdgeInsets.symmetric(horizontal: 2.w),
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Container(
|
||||
// margin: EdgeInsets.only(top: 14.h),
|
||||
// child: quickText(
|
||||
// '当前页未提交学生名单',
|
||||
// size: 15.sp,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Color.fromRGBO(60, 60, 60, 1),
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(height: 10.h),
|
||||
// Expanded(
|
||||
// child: ListView(
|
||||
// padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w),
|
||||
// children: [
|
||||
// Wrap(
|
||||
// spacing: 6.0, // 主轴(水平)方向间距
|
||||
// runSpacing: 4.0, // 纵轴(垂直)方向间距
|
||||
// alignment: WrapAlignment.spaceAround, //沿主轴方向居中
|
||||
// children: students!.map((e) {
|
||||
// return Chip(
|
||||
// labelPadding: EdgeInsets.symmetric(vertical: 1.5.h, horizontal: 5.w),
|
||||
// backgroundColor: Color.fromRGBO(239, 242, 255, 1),
|
||||
// // avatar: CircleAvatar(
|
||||
// // backgroundColor: Colors.white,
|
||||
// // child: quickText(e.studentName.substring(0, 1),
|
||||
// // size: 12.sp, color: Theme.of(context).primaryColor),
|
||||
// // ),
|
||||
// label: quickText(e.studentName, color: Color.fromRGBO(80, 94, 110, 1), size: 12.sp),
|
||||
// );
|
||||
// }).toList(),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
var submitStudents = sateData.data.value?.students; // 已提交学生
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
elevation: 10,
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(10.r), topRight: Radius.circular(10.r))),
|
||||
builder: (BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 2.w),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 14.h),
|
||||
child: quickText(
|
||||
'第${sateData.data.value!.templateIdKeyMap?[sateData.data.value!.templateId]}页未提交学生名单',
|
||||
size: 15.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: const Color.fromRGBO(60, 60, 60, 1),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w),
|
||||
children: [
|
||||
Wrap(
|
||||
spacing: 7.2.w, // 主轴(水平)方向间距
|
||||
runSpacing: 12.h, // 纵轴(垂直)方向间距
|
||||
alignment: WrapAlignment.start, //沿主轴方向居中
|
||||
children: submitStudents!.map((e) {
|
||||
return Stack(
|
||||
alignment: const FractionalOffset(0.05, 0.09),
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 1.2.h, bottom: 1.5.h, left: 13.w, right: 5.w),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
color: const Color.fromRGBO(239, 242, 255, 1),
|
||||
),
|
||||
child: quickText(
|
||||
e.name,
|
||||
size: 12.sp,
|
||||
wordSpacing: 2,
|
||||
color: const Color.fromRGBO(80, 94, 110, 1),
|
||||
),
|
||||
),
|
||||
Stack(
|
||||
alignment: const FractionalOffset(0.52, 0.24),
|
||||
children: [
|
||||
Icon(
|
||||
const IconData(0xe63d, fontFamily: "AlibabaIcon"),
|
||||
size: 12.sp,
|
||||
color: e.isPriority ? const Color.fromRGBO(76, 199, 147, 1) : const Color.fromRGBO(164, 164, 164, 1),
|
||||
),
|
||||
quickText('优先', size: 4.sp, color: Colors.white),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
quickText('已阅', color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
|
||||
quickText(data.annotatedCount, color: const Color.fromRGBO(76, 199, 147, 1), size: 12.sp, fontWeight: FontWeight.bold),
|
||||
quickText('/', color: const Color.fromRGBO(117, 117, 117, 1), size: 12.sp),
|
||||
quickText(data.submitCount - data.annotatedCount, color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
quickText('已阅', color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
|
||||
quickText(data.annotatedCount, color: const Color.fromRGBO(76, 199, 147, 1), size: 12.sp, fontWeight: FontWeight.bold),
|
||||
quickText('/', color: const Color.fromRGBO(117, 117, 117, 1), size: 12.sp),
|
||||
quickText(data.submitCount - data.annotatedCount, color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ class _HomeworkReviewState extends State<HomeworkReview> {
|
|||
const DropdownSwitchStudentsType(),
|
||||
SizedBox(height: 1.h),
|
||||
const Expanded(child: QuestionPaperView()),
|
||||
|
||||
const BottomAnnotationSwitch()
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|||
import 'package:get/get.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/utils.dart';
|
||||
import 'package:making_school_asignment_app/page/global_widget/ReturnToHomepage.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/original_manuscript_handwriting/answer_handwriting_view.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/student_personal/widget/student_kg_table.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/student_personal/widget/student_zg_table.dart';
|
||||
import 'package:making_school_asignment_app/routes/app_pages.dart';
|
||||
|
|
@ -74,11 +75,7 @@ class _StudentPersonalPageState extends State<StudentPersonalPage> {
|
|||
width: 10.r,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
/* showAnswerHandwriting(context, jobId: widget.jobId, studentId: widget.studentId).then((value) {
|
||||
ref.read(jobHandwritingDrawingTrajectoryProvider.notifier).setVal([]);
|
||||
});*/
|
||||
},
|
||||
onTap: () => showAnswerHandwriting(context, homeworkId: state.homeworkId, studentId: state.studentId),
|
||||
child: Container(
|
||||
width: 93.r,
|
||||
height: 28.r,
|
||||
|
|
|
|||
Loading…
Reference in New Issue