mcy_new #1
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
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';
|
||||
|
|
@ -123,4 +125,13 @@ abstract class RetrofitClient {
|
|||
// 批阅提交
|
||||
@POST("/api/hms/Annotate/AnnotateSubmit")
|
||||
Future reviewSubmission(@Body() ReviewSubmissionParams 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:get/get_connect/http/src/request/request.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:making_school_asignment_app/common/config/request_config.dart';
|
||||
|
||||
part 'do_paper_details_result.g.dart';
|
||||
|
||||
|
|
@ -39,12 +40,19 @@ class DoPaperDetailsResult extends Object {
|
|||
@JsonKey(name: 'zgtAnswer') // 主观题作答图片
|
||||
String zgtAnswer;
|
||||
|
||||
@JsonKey(name: 'zgtAnnotate') // 主观题批注图片
|
||||
@JsonKey(name: 'showZgtAnnotate') // 主观题老师批注图片
|
||||
String? showZgtAnnotate;
|
||||
|
||||
@JsonKey(name: 'zgtAnnotate') // 主观题老师批注图片
|
||||
String? zgtAnnotate;
|
||||
|
||||
@JsonKey(name: 'lastAnswerTime')
|
||||
@JsonKey(name: 'lastAnswerTime') // 学生提交作答试题时间
|
||||
String lastAnswerTime;
|
||||
|
||||
// 批注时间
|
||||
@JsonKey(name: 'annotateTime')
|
||||
String? annotateTime;
|
||||
|
||||
@JsonKey(name: 'isFav')
|
||||
bool isFav;
|
||||
|
||||
|
|
@ -78,6 +86,8 @@ class DoPaperDetailsResult extends Object {
|
|||
this.templateIdKeys,
|
||||
this.templateIdKeyMap,
|
||||
this.priority,
|
||||
this.annotateTime,
|
||||
this.showZgtAnnotate,
|
||||
) {
|
||||
if (templateIds.keys.isNotEmpty) {
|
||||
templateIdKeys = templateIds.keys.map((e) => int.parse(e)).toList();
|
||||
|
|
@ -94,6 +104,15 @@ class DoPaperDetailsResult extends Object {
|
|||
var currentStudent = students.firstWhereOrNull((e) => e.id == studentId);
|
||||
if (currentStudent != null) priority = currentStudent.isPriority;
|
||||
}
|
||||
zgtAnswer = '${RequestConfig.imgUrl}$zgtAnswer?$lastAnswerTime';
|
||||
|
||||
if (zgtAnnotate?.isNotEmpty ?? false) {
|
||||
showZgtAnnotate = RequestConfig.imgUrl + zgtAnnotate!; // 批注图片地址赋值
|
||||
if (annotateTime != null) showZgtAnnotate = '${showZgtAnnotate!}?$annotateTime';
|
||||
}
|
||||
|
||||
print('学生作答图片:${zgtAnswer}');
|
||||
print('老师批注图片:${showZgtAnnotate}');
|
||||
}
|
||||
|
||||
factory DoPaperDetailsResult.fromJson(Map<String, dynamic> srcJson) => _$DoPaperDetailsResultFromJson(srcJson);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class ReviewSubmissionParams extends Object {
|
|||
@JsonKey(name: 'studentScores')
|
||||
List<StudentScores> studentScores;
|
||||
|
||||
@JsonKey(name: 'pictureBytes')
|
||||
@JsonKey(name: 'zgtAnnotate')
|
||||
String? zgtAnnotate;
|
||||
|
||||
ReviewSubmissionParams({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import '../mixins/request_tool_mixin.dart';
|
||||
|
||||
// OSS 图片上传
|
||||
class UploadOssImgUtils with RequestToolMixin {
|
||||
static UploadOssImgUtils? _singleton;
|
||||
static UploadOssImgUtils getInstance() => _singleton ??= UploadOssImgUtils._internal();
|
||||
|
||||
UploadOssImgUtils._internal();
|
||||
|
||||
Future<void> getPresignedUri(String key) async {
|
||||
var res = await getClient().getOssPresignedUri(key);
|
||||
print(res);
|
||||
}
|
||||
|
||||
String setImgKey(String homeworkId, String studentId, String templateId) {
|
||||
// $"hms-annotate/{input.HomeworkId}/{input.StudentId}/{input.TemplateId}.png"6月12日 13:54
|
||||
return 'hms-annotate/$homeworkId/$studentId/$templateId.png';
|
||||
}
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
|||
return Stack(
|
||||
children: [
|
||||
// 主图
|
||||
QuestionImageView(maxWidth, maxHeight, sateData, annotationState),
|
||||
QuestionImageView(maxWidth, maxHeight, sateData, annotationState, logic),
|
||||
// 继续批阅按钮
|
||||
Positioned(right: 3.w, bottom: 4.h, child: const $ContinueToReview(isFloatingAction: true)),
|
||||
// 上一题按钮
|
||||
|
|
@ -183,86 +183,85 @@ Widget $scoringQuestionsView(BuildContext context, StudentQuestions item, double
|
|||
}, []);
|
||||
var padinVal = item.correctRate > 0 ? EdgeInsets.only(top: 6.4.h) : EdgeInsets.symmetric(vertical: 2.h);
|
||||
return Container(
|
||||
height: item.height * scaleRatio,
|
||||
padding: EdgeInsets.zero,
|
||||
child: Stack(
|
||||
alignment: const FractionalOffset(0, 0),
|
||||
children: [
|
||||
Container(
|
||||
color: Colors.yellow,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 对
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
backgroundColor: const Color.fromRGBO(237, 237, 237, 1), // 设置背景色
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), // 去除圆角
|
||||
),
|
||||
child: Padding(
|
||||
padding: padinVal,
|
||||
child: Icon(
|
||||
size: 22.sp,
|
||||
color: studentScore.value == 2 ? const Color.fromRGBO(255, 152, 0, 1) : const Color.fromRGBO(114, 114, 114, 1),
|
||||
const IconData(0xe62b, fontFamily: "AlibabaIcon"),
|
||||
),
|
||||
),
|
||||
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
||||
studentScore.value = studentScore.value == 2 ? null : 2;
|
||||
}),
|
||||
height: item.height * scaleRatio,
|
||||
padding: EdgeInsets.zero,
|
||||
child: Stack(
|
||||
alignment: const FractionalOffset(0, 0),
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 对
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
backgroundColor: const Color.fromRGBO(237, 237, 237, 1), // 设置背景色
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), // 去除圆角
|
||||
),
|
||||
child: Padding(
|
||||
padding: padinVal,
|
||||
child: Icon(
|
||||
size: 22.sp,
|
||||
color: studentScore.value == 2 ? const Color.fromRGBO(255, 152, 0, 1) : const Color.fromRGBO(114, 114, 114, 1),
|
||||
const IconData(0xe62b, fontFamily: "AlibabaIcon"),
|
||||
),
|
||||
),
|
||||
// 半
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
backgroundColor: const Color.fromRGBO(237, 237, 237, 1), // 设置背景色
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), // 去除圆角
|
||||
),
|
||||
child: Padding(
|
||||
padding: padinVal,
|
||||
child: Icon(
|
||||
size: 22.sp,
|
||||
color: studentScore.value == 1 ? const Color.fromRGBO(255, 152, 0, 1) : const Color.fromRGBO(114, 114, 114, 1),
|
||||
const IconData(0xe62c, fontFamily: "AlibabaIcon"),
|
||||
),
|
||||
),
|
||||
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
||||
studentScore.value = studentScore.value == 1 ? null : 1;
|
||||
}),
|
||||
),
|
||||
),
|
||||
// 错
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
backgroundColor: const Color.fromRGBO(237, 237, 237, 1), // 设置背景色
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), // 去除圆角
|
||||
),
|
||||
child: Padding(
|
||||
padding: padinVal,
|
||||
child: Icon(
|
||||
size: 22.sp,
|
||||
color: studentScore.value == 0 ? const Color.fromRGBO(255, 152, 0, 1) : const Color.fromRGBO(114, 114, 114, 1),
|
||||
const IconData(0xe62a, fontFamily: "AlibabaIcon"),
|
||||
),
|
||||
),
|
||||
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
||||
studentScore.value = studentScore.value == 0 ? null : 0;
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
||||
studentScore.value = studentScore.value == 2 ? null : 2;
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
// 半
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
backgroundColor: const Color.fromRGBO(237, 237, 237, 1), // 设置背景色
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), // 去除圆角
|
||||
),
|
||||
child: Padding(
|
||||
padding: padinVal,
|
||||
child: Icon(
|
||||
size: 22.sp,
|
||||
color: studentScore.value == 1 ? const Color.fromRGBO(255, 152, 0, 1) : const Color.fromRGBO(114, 114, 114, 1),
|
||||
const IconData(0xe62c, fontFamily: "AlibabaIcon"),
|
||||
),
|
||||
),
|
||||
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
||||
studentScore.value = studentScore.value == 1 ? null : 1;
|
||||
}),
|
||||
),
|
||||
),
|
||||
// 错
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
backgroundColor: const Color.fromRGBO(237, 237, 237, 1), // 设置背景色
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), // 去除圆角
|
||||
),
|
||||
child: Padding(
|
||||
padding: padinVal,
|
||||
child: Icon(
|
||||
size: 22.sp,
|
||||
color: studentScore.value == 0 ? const Color.fromRGBO(255, 152, 0, 1) : const Color.fromRGBO(114, 114, 114, 1),
|
||||
const IconData(0xe62a, fontFamily: "AlibabaIcon"),
|
||||
),
|
||||
),
|
||||
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
||||
studentScore.value = studentScore.value == 0 ? null : 0;
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
IgnorePointer(
|
||||
// 事件穿透
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(width: 1.1.w),
|
||||
|
|
@ -281,18 +280,21 @@ Widget $scoringQuestionsView(BuildContext context, StudentQuestions item, double
|
|||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
));
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 试题图片视图
|
||||
class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar> {
|
||||
final double maxWidth;
|
||||
final double maxHeight;
|
||||
final HomeworkReviewLogic logic;
|
||||
final HomeworkReviewState sateData;
|
||||
final HomeworkReviewAnnotationsControlState annotationState;
|
||||
QuestionImageView(this.maxWidth, this.maxHeight, this.sateData, this.annotationState, {super.key});
|
||||
QuestionImageView(this.maxWidth, this.maxHeight, this.sateData, this.annotationState, this.logic, {super.key});
|
||||
|
||||
/// 获取数组指定倒数具体值的下标
|
||||
int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) {
|
||||
|
|
@ -311,6 +313,15 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
final scrollControllerQuestion = useScrollController(); // 试题图片区域
|
||||
var vnHandWritings = useValueNotifier<List<dynamic>>([]);
|
||||
|
||||
useEffect(() {
|
||||
var listenStream = sateData.data.listen((e) {
|
||||
// 数据清空
|
||||
sateData.handwritings = [];
|
||||
vnHandWritings.value = sateData.handwritings;
|
||||
});
|
||||
return () => listenStream.cancel();
|
||||
}, []);
|
||||
|
||||
ImageStream? imageStream;
|
||||
var imageStreamListener = useState<ImageStreamListener>(ImageStreamListener((ImageInfo info, bool _) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
|
|
@ -335,24 +346,66 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
bool? res = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context1) {
|
||||
return AlertDialog(content: quickText("是否撤销上次批阅批注痕迹"), actions: <Widget>[
|
||||
return AlertDialog(content: quickText("是否撤销全部批注痕迹?"), actions: <Widget>[
|
||||
TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
|
||||
TextButton(child: quickText("确定", color: Theme.of(context).primaryColor), onPressed: () => Navigator.pop(context1, true))
|
||||
]);
|
||||
},
|
||||
);
|
||||
if (res == true) vnHandWritings.value = [];
|
||||
} else {
|
||||
// 数据已经是空了 检查是否有上次批注图片 如果有就弹框询问清除
|
||||
if (sateData.data.value?.showZgtAnnotate != null) {
|
||||
await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context1) {
|
||||
return AlertDialog(content: quickText("是否撤销上次批阅批注痕迹?"), actions: <Widget>[
|
||||
TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
|
||||
TextButton(
|
||||
child: quickText("确定", color: Theme.of(context).primaryColor),
|
||||
onPressed: () {
|
||||
Navigator.pop(context1, true);
|
||||
sateData.data.value?.zgtAnnotate = null;
|
||||
sateData.data.value?.showZgtAnnotate = null;
|
||||
},
|
||||
)
|
||||
]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 返回上一步
|
||||
var index = _findTargetIndex(annotationsData, null);
|
||||
if (index != -1) {
|
||||
annotationsData = annotationsData.sublist(0, index + 1);
|
||||
annotationsData = List.from(annotationsData);
|
||||
if (annotationsData.isEmpty) {
|
||||
// 数据已经是空了 检查是否有上次批注图片 如果有就弹框询问清除
|
||||
if (sateData.data.value?.showZgtAnnotate != null) {
|
||||
await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context1) {
|
||||
return AlertDialog(content: quickText("是否撤销上次批阅批注痕迹?"), actions: <Widget>[
|
||||
TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
|
||||
TextButton(
|
||||
child: quickText("确定", color: Theme.of(context).primaryColor),
|
||||
onPressed: () {
|
||||
Navigator.pop(context1, true);
|
||||
sateData.data.value?.zgtAnnotate = null;
|
||||
sateData.data.value?.showZgtAnnotate = null;
|
||||
},
|
||||
)
|
||||
]);
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
annotationsData = [];
|
||||
var index = _findTargetIndex(annotationsData, null);
|
||||
if (index != -1) {
|
||||
annotationsData = annotationsData.sublist(0, index + 1);
|
||||
annotationsData = List.from(annotationsData);
|
||||
} else {
|
||||
annotationsData = [];
|
||||
}
|
||||
}
|
||||
vnHandWritings.value = annotationsData;
|
||||
});
|
||||
|
|
@ -373,63 +426,80 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
}, []);
|
||||
|
||||
return Container(
|
||||
height: maxHeight,
|
||||
// padding: EdgeInsets.only(bottom: 2.h, top: 2.h),
|
||||
alignment: Alignment.center,
|
||||
child: Obx(() {
|
||||
var imageUrl = sateData.data.value?.zgtAnswer;
|
||||
if (imageUrl == null) return const SizedBox();
|
||||
height: maxHeight,
|
||||
// padding: EdgeInsets.only(bottom: 2.h, top: 2.h),
|
||||
alignment: Alignment.center,
|
||||
child: Obx(() {
|
||||
var imageUrl = sateData.data.value?.zgtAnswer;
|
||||
var showZgtAnnotate = sateData.data.value?.showZgtAnnotate;
|
||||
if (imageUrl == null) return const SizedBox();
|
||||
|
||||
return GestureDetector(
|
||||
onPanDown: (_) => sateData.panQuestView = true,
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollControllerQuestion,
|
||||
physics: !annotationState.pen.value ? const BouncingScrollPhysics() : const NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.zero,
|
||||
scrollDirection: Axis.vertical, // 设置垂直滚动
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.2), offset: Offset(-6.r, 1.r), blurRadius: 10.r, spreadRadius: 8.r)]),
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPointerUp: (PointerUpEvent details) {
|
||||
// 处理单个触摸点抬起的逻辑
|
||||
// activePointers--;
|
||||
// globalPosition = null;
|
||||
var imageScale = sateData.imageScale.value;
|
||||
if (imageScale == null || !annotationState.pen.value) return;
|
||||
vnHandWritings.value.add(null); // 增加空点以分隔不同的线段
|
||||
},
|
||||
onPointerMove: (PointerMoveEvent event) {
|
||||
var imageScale = sateData.imageScale.value;
|
||||
if (imageScale == null || !annotationState.pen.value) return;
|
||||
Offset localPosition = event.localPosition;
|
||||
var dy = localPosition.dy;
|
||||
if (dy > imageScale.actualImgHeight || dy < 0) return; // 检查笔记是否超出图片范围
|
||||
return GestureDetector(
|
||||
onPanDown: (_) => sateData.panQuestView = true,
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollControllerQuestion,
|
||||
physics: !annotationState.pen.value ? const BouncingScrollPhysics() : const NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.zero,
|
||||
scrollDirection: Axis.vertical, // 设置垂直滚动
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.2), offset: Offset(-6.r, 1.r), blurRadius: 10.r, spreadRadius: 8.r)]),
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPointerUp: (PointerUpEvent details) {
|
||||
// 处理单个触摸点抬起的逻辑
|
||||
// activePointers--;
|
||||
// globalPosition = null;
|
||||
var imageScale = sateData.imageScale.value;
|
||||
if (imageScale == null || !annotationState.pen.value) return;
|
||||
vnHandWritings.value.add(null); // 增加空点以分隔不同的线段
|
||||
sateData.handwritings = vnHandWritings.value; // 添加笔迹数据
|
||||
},
|
||||
onPointerMove: (PointerMoveEvent event) {
|
||||
var imageScale = sateData.imageScale.value;
|
||||
if (imageScale == null || !annotationState.pen.value) return;
|
||||
Offset localPosition = event.localPosition;
|
||||
var dy = localPosition.dy;
|
||||
if (dy > imageScale.actualImgHeight || dy < 0) return; // 检查笔记是否超出图片范围
|
||||
|
||||
vnHandWritings.value = List.from(vnHandWritings.value)..add(localPosition);
|
||||
},
|
||||
child: RepaintBoundary(
|
||||
child: CustomPaint(
|
||||
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
||||
child: RepaintBoundary(
|
||||
child: $TheCachedNetworkImage(
|
||||
(context, imageProvider) {
|
||||
Image imageWidget = Image(image: imageProvider, fit: BoxFit.fitWidth);
|
||||
imageStream?.removeListener(imageStreamListener.value);
|
||||
imageStream = imageWidget.image.resolve(const ImageConfiguration())..addListener(imageStreamListener.value);
|
||||
return imageWidget;
|
||||
},
|
||||
imageUrl: RequestConfig.imgUrl + imageUrl,
|
||||
vnHandWritings.value = List.from(vnHandWritings.value)..add(localPosition);
|
||||
sateData.handwritings = vnHandWritings.value;
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
$TheCachedNetworkImage(
|
||||
imageUrl: imageUrl,
|
||||
(context, imageProvider) {
|
||||
Image imageWidget = Image(image: imageProvider, fit: BoxFit.fitWidth);
|
||||
imageStream?.removeListener(imageStreamListener.value);
|
||||
imageStream = imageWidget.image.resolve(const ImageConfiguration())..addListener(imageStreamListener.value);
|
||||
return imageWidget;
|
||||
},
|
||||
),
|
||||
RepaintBoundary(
|
||||
key: logic.pictureOverviewKey,
|
||||
child: CustomPaint(
|
||||
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
||||
size: Size(
|
||||
sateData.imageScale.value?.actualImgWidth ?? 0,
|
||||
sateData.imageScale.value?.actualImgHeight ?? 0,
|
||||
),
|
||||
child: showZgtAnnotate != null
|
||||
? $TheCachedNetworkImage(
|
||||
imageUrl: showZgtAnnotate,
|
||||
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}));
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_param.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_result.dart';
|
||||
|
|
@ -9,6 +14,7 @@ import 'package:making_school_asignment_app/common/job/marking_models/do_test_qu
|
|||
import 'package:making_school_asignment_app/common/job/marking_models/review_submission_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/common/utils/upload_oss_img_utils.dart';
|
||||
import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
||||
|
||||
// 数据
|
||||
|
|
@ -23,6 +29,7 @@ class HomeworkReviewState {
|
|||
late Rx<double> slide; // 滑动位置
|
||||
late bool? panQuestView;
|
||||
late Rx<TestQuestionsImageInfo?> imageScale;
|
||||
List<dynamic> handwritings = [];
|
||||
|
||||
// late String dateEnd = '';
|
||||
// late int knowledgeId = 0;
|
||||
|
|
@ -48,6 +55,7 @@ class HomeworkReviewBinding extends Bindings {
|
|||
}
|
||||
|
||||
class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
||||
final GlobalKey pictureOverviewKey = GlobalKey();
|
||||
late StreamSubscription<DoPaperDetailsParam> _paramListen;
|
||||
late StreamSubscription<DoPaperDetailsResult?> _dataListen;
|
||||
final HomeworkReviewState state = HomeworkReviewState();
|
||||
|
|
@ -92,6 +100,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
|||
// item.topHeight = itemPre.height;
|
||||
// }
|
||||
state.data.value = data;
|
||||
state.handwritings = [];
|
||||
state.studentQuestions.value = data.studentQuestions;
|
||||
} finally {
|
||||
if (timerControl.isActive) timerControl.cancel();
|
||||
|
|
@ -140,11 +149,63 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
|||
await submit(context);
|
||||
}
|
||||
|
||||
// 上传图片
|
||||
Future<String?> saveImage(context) async {
|
||||
if (state.handwritings.isEmpty) return null;
|
||||
try {
|
||||
var data = state.data.value;
|
||||
var param = state.param.value;
|
||||
if (data == null) return null;
|
||||
|
||||
// 获取OSS 图片url
|
||||
String imgKey = UploadOssImgUtils.getInstance().setImgKey(param.homeworkId, data.studentId.toString(), data.templateId.toString());
|
||||
var resUrl = await getClient().getOssPresignedUri(imgKey);
|
||||
if (resUrl == null) return null;
|
||||
|
||||
// 没有图片就上传图片
|
||||
RenderRepaintBoundary? boundary = pictureOverviewKey.currentContext!.findRenderObject() as RenderRepaintBoundary?;
|
||||
if (boundary == null) return null;
|
||||
// double dpr = MediaQuery.of(context).devicePixelRatio;
|
||||
|
||||
double pixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||
var imageScale = state.imageScale.value;
|
||||
if (imageScale != null) {
|
||||
// 还原图片原始比例
|
||||
pixelRatio = imageScale.imageWidth / imageScale.boxWidth;
|
||||
}
|
||||
|
||||
ui.Image image = await boundary.toImage(pixelRatio: pixelRatio);
|
||||
ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
if (byteData == null) return null;
|
||||
|
||||
Dio dio = Dio();
|
||||
dio.options.contentType = null;
|
||||
List<int> bytes = byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
|
||||
await dio.put(
|
||||
resUrl,
|
||||
data: Stream.fromIterable(bytes.map((e) => [e])),
|
||||
options: Options(contentType: null, headers: {Headers.contentLengthHeader: bytes.length}),
|
||||
);
|
||||
|
||||
// var res = await getClient().uploadImag(resUrl, file);
|
||||
|
||||
return imgKey;
|
||||
} catch (e) {
|
||||
print('图片上传失败');
|
||||
print(e.toString());
|
||||
ToastUtils.getFluttertoast(msg: '图片上传失败', context: context);
|
||||
} finally {
|
||||
// ToastUtils.dismiss();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 提交打分
|
||||
/// allPairs
|
||||
Future<void> submit(BuildContext context) async {
|
||||
var data = state.data.value;
|
||||
if (data == null) return;
|
||||
|
||||
if (state.data.value?.studentQuestions.isEmpty ?? true) return;
|
||||
var studentQuestions = state.data.value!.studentQuestions;
|
||||
var noRatingElement = studentQuestions.firstWhereOrNull((e) => e.studentScore == null);
|
||||
|
|
@ -152,12 +213,19 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
|||
ToastUtils.showInfo('${noRatingElement.questionNo}题请评分');
|
||||
return;
|
||||
}
|
||||
|
||||
// 图片上传
|
||||
String? zgtAnnotate = state.data.value?.zgtAnnotate;
|
||||
String? newzgtAnnotate = await saveImage(context);
|
||||
if (newzgtAnnotate != null) zgtAnnotate = newzgtAnnotate;
|
||||
|
||||
// TODO 请求提交加载框是否需要 如何防止重复提交
|
||||
await getClient()
|
||||
.reviewSubmission(ReviewSubmissionParams(
|
||||
homeworkId: state.param.value.homeworkId,
|
||||
templateId: data.templateId,
|
||||
studentId: data.studentId,
|
||||
zgtAnnotate: zgtAnnotate,
|
||||
studentScores: studentQuestions.map((e) {
|
||||
var studentScore = e.studentScore!;
|
||||
return StudentScores(
|
||||
|
|
@ -191,7 +259,10 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
|||
);
|
||||
return;
|
||||
}
|
||||
state.param.value = DoPaperDetailsParam.fromJson(state.param.value.toJson());
|
||||
var newParams = DoPaperDetailsParam.fromJson(state.param.value.toJson());
|
||||
newParams.templateId = null;
|
||||
newParams.studentId = null;
|
||||
state.param.value = newParams;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ class _HomeworkReviewState extends State<HomeworkReview> {
|
|||
@override
|
||||
void initState() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
// SystemChrome.setPreferredOrientations([DeviceOrientation.portraitDown]);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ dependencies:
|
|||
flutter_hooks: ^0.20.5
|
||||
flutter_spinkit: ^5.2.1
|
||||
event_bus: ^2.0.0
|
||||
path_provider: ^2.1.3
|
||||
uuid: ^3.0.7
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in New Issue