no message
This commit is contained in:
parent
54f27b0d72
commit
382840a3aa
|
|
@ -4,7 +4,7 @@ import 'package:making_school_asignment_app/common/config/request_config.dart';
|
|||
|
||||
part 'do_paper_details_result.g.dart';
|
||||
|
||||
@JsonSerializable(checked: true)
|
||||
@JsonSerializable(checked: true, includeIfNull: false)
|
||||
class DoPaperDetailsResult extends Object {
|
||||
@JsonKey(name: 'templateIds')
|
||||
Map<String, bool> templateIds;
|
||||
|
|
@ -17,7 +17,7 @@ class DoPaperDetailsResult extends Object {
|
|||
@JsonKey(name: 'templateIdKeyMap')
|
||||
Map<int, int>? templateIdKeyMap;
|
||||
|
||||
@JsonKey(name: 'submitStudents') // 当前页 总提交学生集合
|
||||
@JsonKey(name: 'submitStudents', toJson: _paperStudentsToJson) // 当前页 总提交学生集合
|
||||
List<PaperStudents> students;
|
||||
|
||||
@JsonKey(name: 'templateId')
|
||||
|
|
@ -55,17 +55,17 @@ class DoPaperDetailsResult extends Object {
|
|||
@JsonKey(name: 'isFav')
|
||||
bool isFav;
|
||||
|
||||
@JsonKey(name: 'studentQuestions')
|
||||
@JsonKey(name: 'studentQuestions', toJson: _studentQuestionsToJson)
|
||||
List<StudentQuestions> studentQuestions;
|
||||
|
||||
// 当前页 未提交学生集合
|
||||
@JsonKey(name: 'unSubmitStudents')
|
||||
@JsonKey(name: 'unSubmitStudents', toJson: _paperStudentsToJson)
|
||||
List<PaperStudents> unSubmitStudents;
|
||||
|
||||
@JsonKey(name: 'lastPage')
|
||||
@JsonKey(name: 'lastPage', toJson: _lastPageToJson)
|
||||
LastPage? lastPage;
|
||||
|
||||
@JsonKey(name: 'nextPage')
|
||||
@JsonKey(name: 'nextPage', toJson: _nextPageToJson)
|
||||
NextPage? nextPage;
|
||||
|
||||
// 所有页 总待提交数量
|
||||
|
|
@ -112,27 +112,34 @@ class DoPaperDetailsResult extends Object {
|
|||
var currentStudent = students.firstWhereOrNull((e) => e.id == studentId);
|
||||
if (currentStudent != null) priority = currentStudent.isPriority;
|
||||
}
|
||||
zgtAnswer = '${RequestConfig.imgUrl}$zgtAnswer?$lastAnswerTime';
|
||||
|
||||
if (!zgtAnswer.contains(RequestConfig.imgUrl)) {
|
||||
zgtAnswer = '${RequestConfig.imgUrl}$zgtAnswer?$lastAnswerTime';
|
||||
}
|
||||
|
||||
if (zgtAnnotate?.isNotEmpty ?? false) {
|
||||
showZgtAnnotate = RequestConfig.imgUrl + zgtAnnotate!; // 批注图片地址赋值
|
||||
if (annotateTime != null)
|
||||
showZgtAnnotate = '${showZgtAnnotate!}?$annotateTime';
|
||||
if (annotateTime != null) showZgtAnnotate = '${showZgtAnnotate!}?$annotateTime';
|
||||
}
|
||||
|
||||
// 判断当前试题是否需要批阅
|
||||
if (annotateTime == null ||
|
||||
studentQuestions.indexWhere((e) => e.studentScore == null) != -1) {
|
||||
if (annotateTime == null || studentQuestions.indexWhere((e) => e.studentScore == null) != -1) {
|
||||
needAnnotate = true;
|
||||
}
|
||||
// print('学生作答图片:${annotatedCount}');
|
||||
// print('老师批注图片提交数量:${submitCount}');
|
||||
}
|
||||
|
||||
factory DoPaperDetailsResult.fromJson(Map<String, dynamic> srcJson) =>
|
||||
_$DoPaperDetailsResultFromJson(srcJson);
|
||||
factory DoPaperDetailsResult.fromJson(Map<String, dynamic> srcJson) => _$DoPaperDetailsResultFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$DoPaperDetailsResultToJson(this);
|
||||
|
||||
static List<Map<String, dynamic>> _paperStudentsToJson(List<PaperStudents> examples) => examples.map((e) => e.toJson()).toList();
|
||||
|
||||
static List<Map<String, dynamic>> _studentQuestionsToJson(List<StudentQuestions> examples) => examples.map((e) => e.toJson()).toList();
|
||||
|
||||
static Map<String, dynamic>? _nextPageToJson(NextPage? example) => example?.toJson();
|
||||
static Map<String, dynamic>? _lastPageToJson(LastPage? example) => example?.toJson();
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
|
|
@ -153,8 +160,7 @@ class PaperStudents extends Object {
|
|||
this.isPriority,
|
||||
);
|
||||
|
||||
factory PaperStudents.fromJson(Map<String, dynamic> srcJson) =>
|
||||
_$PaperStudentsFromJson(srcJson);
|
||||
factory PaperStudents.fromJson(Map<String, dynamic> srcJson) => _$PaperStudentsFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$PaperStudentsToJson(this);
|
||||
}
|
||||
|
|
@ -206,8 +212,7 @@ class StudentQuestions extends Object {
|
|||
this.useTime,
|
||||
});
|
||||
|
||||
factory StudentQuestions.fromJson(Map<String, dynamic> srcJson) =>
|
||||
_$StudentQuestionsFromJson(srcJson);
|
||||
factory StudentQuestions.fromJson(Map<String, dynamic> srcJson) => _$StudentQuestionsFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$StudentQuestionsToJson(this);
|
||||
}
|
||||
|
|
@ -225,8 +230,7 @@ class LastPage extends Object {
|
|||
this.studentId,
|
||||
);
|
||||
|
||||
factory LastPage.fromJson(Map<String, dynamic> srcJson) =>
|
||||
_$LastPageFromJson(srcJson);
|
||||
factory LastPage.fromJson(Map<String, dynamic> srcJson) => _$LastPageFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LastPageToJson(this);
|
||||
}
|
||||
|
|
@ -244,8 +248,7 @@ class NextPage extends Object {
|
|||
this.studentId,
|
||||
);
|
||||
|
||||
factory NextPage.fromJson(Map<String, dynamic> srcJson) =>
|
||||
_$NextPageFromJson(srcJson);
|
||||
factory NextPage.fromJson(Map<String, dynamic> srcJson) => _$NextPageFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$NextPageToJson(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
import 'package:get/get.dart' as getx;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/utils.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:making_school_asignment_app/common/api/retrofit_client.dart';
|
||||
import 'package:making_school_asignment_app/common/config/request_config.dart';
|
||||
|
|
@ -114,6 +115,10 @@ class AuthInterceptor extends Interceptor {
|
|||
class ResponseHandle extends Interceptor {
|
||||
@override
|
||||
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||
const isProd = bool.fromEnvironment('dart.vm.product');
|
||||
if (!isProd && RequestConfig.requestDataPrinting) {
|
||||
toPrint(val: response.data, toPrintJson: true);
|
||||
}
|
||||
if (RequestConfig.successCode.contains(response.statusCode)) {
|
||||
//
|
||||
String? token = response.headers.value('access-token');
|
||||
|
|
@ -180,9 +185,11 @@ class TheError extends Interceptor {
|
|||
message = '用户登录失效,请重新登录';
|
||||
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
UserStore.to.erase();
|
||||
StorageService.to.erase();
|
||||
getx.Get.offAllNamed(Routes.login);
|
||||
if (getx.Get.currentRoute != Routes.login) {
|
||||
UserStore.to.erase();
|
||||
StorageService.to.erase();
|
||||
getx.Get.offAllNamed(Routes.login);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 404:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
|
@ -6,6 +8,8 @@ import 'package:making_school_asignment_app/common/job/homework_details.dart';
|
|||
import 'package:making_school_asignment_app/page/home_page/children/quick_data_check/quick_data_check_state.dart';
|
||||
import 'dart:math';
|
||||
|
||||
import '../config/request_config.dart';
|
||||
|
||||
class Utils {
|
||||
static Utils? _instance;
|
||||
Utils._internal();
|
||||
|
|
@ -167,21 +171,20 @@ class Utils {
|
|||
|
||||
var stuDtls = data.dtls.where((w) => w.studentId == stu.studentId);
|
||||
var secList = [];
|
||||
for(var sec in stuDtls){
|
||||
if(sec.useTime >= 1){
|
||||
for (var sec in stuDtls) {
|
||||
if (sec.useTime >= 1) {
|
||||
var date = DateTime.parse(sec.lastAnswerTime!);
|
||||
var lastSec =(date.difference(DateTime(1970)).inSeconds).floor();
|
||||
var lastSec = (date.difference(DateTime(1970)).inSeconds).floor();
|
||||
|
||||
secList.add(lastSec);
|
||||
secList.add(lastSec - sec.useTime);
|
||||
}
|
||||
}
|
||||
secList.sort();
|
||||
var maxSec = secList.isNotEmpty?secList.last:0;
|
||||
var minSec = secList.isNotEmpty?secList.first:0;
|
||||
var secTime = secList.isNotEmpty?maxSec-minSec:0;
|
||||
secList.sort();
|
||||
var maxSec = secList.isNotEmpty ? secList.last : 0;
|
||||
var minSec = secList.isNotEmpty ? secList.first : 0;
|
||||
var secTime = secList.isNotEmpty ? maxSec - minSec : 0;
|
||||
stu.ttlSec = second2HMS(secTime);
|
||||
|
||||
}
|
||||
|
||||
data.students.sort((a, b) {
|
||||
|
|
@ -293,3 +296,24 @@ bool isPad([double mobilePhoneScale = 1.2]) {
|
|||
void toUpState(Function(void Function()) setState, VoidCallback fn, bool mounted) {
|
||||
if (mounted) setState(fn);
|
||||
}
|
||||
|
||||
void toPrint({required dynamic val, bool toPrintJson = false}) {
|
||||
bool printSwitch = RequestConfig.printSwitch;
|
||||
if (printSwitch && val != null) toPrintJson ? printJson(val) : print(val);
|
||||
}
|
||||
|
||||
/// 单纯的Json格式输出打印
|
||||
void printJson(Object object) {
|
||||
try {
|
||||
JsonEncoder encoder = const JsonEncoder.withIndent(' ');
|
||||
var encoderString = encoder.convert(object);
|
||||
// print(encoderString);
|
||||
// 不使用print()方法是因为这是单条输出,如果过长无法显示全
|
||||
// 所以使用debugPrint()
|
||||
debugPrint(encoderString);
|
||||
// 下面这语句的效果与debugPrint 相同
|
||||
//encoderString.split('\n').forEach((element) => print(element));
|
||||
} catch (e) {
|
||||
toPrint(val: e);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
|
@ -6,49 +5,29 @@ 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_paper_details_param.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart';
|
||||
import 'package:making_school_asignment_app/common/utils/permission_describe_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/configuration_files/index.dart';
|
||||
import 'package:making_school_asignment_app/routes/app_pages.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import 'original_manuscript_handwriting/answer_handwriting_view.dart';
|
||||
|
||||
part 'dropdown_switch_students_type.g.dart';
|
||||
|
||||
/// 学生和页码切换
|
||||
class DropdownSwitchStudentsType extends StatefulWidget {
|
||||
///
|
||||
|
||||
class DropdownSwitchStudentsType extends StatelessWidget {
|
||||
const DropdownSwitchStudentsType({super.key});
|
||||
|
||||
@override
|
||||
State<DropdownSwitchStudentsType> createState() => _DropdownSwitchStudentsTypeState();
|
||||
}
|
||||
|
||||
class _DropdownSwitchStudentsTypeState extends State<DropdownSwitchStudentsType> {
|
||||
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
||||
final logic = Get.find<HomeworkReviewLogic>();
|
||||
final sateData = Get.find<HomeworkReviewLogic>().state.data;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
deviceInfoPlugin.androidInfo.then((androidInfo) {
|
||||
Permission storagePermission = androidInfo.version.sdkInt >= 33 ? Permission.manageExternalStorage : Permission.storage;
|
||||
PermissionDescribeUtil.instance.toLaunchPermissionRequest(
|
||||
context,
|
||||
title: '储存权限请求',
|
||||
describe: "为了提供更好的服务,需要获取到存储权限用于保存批阅痕迹并上传",
|
||||
permissions: [storagePermission],
|
||||
);
|
||||
});
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final logic = Get.find<HomeworkReviewLogic>();
|
||||
final sateData = logic.state.data;
|
||||
|
||||
return Container(
|
||||
height: 30.h,
|
||||
padding: EdgeInsets.only(bottom: 2.r, left: 12.r, right: 12.r),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
|
|
|
|||
|
|
@ -1,69 +1,25 @@
|
|||
import 'dart:async';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:making_school_asignment_app/common/api/retrofit_client.dart';
|
||||
import 'package:making_school_asignment_app/common/job/marking_models/favor_param.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/toast_utils.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/configuration_files/index.dart';
|
||||
|
||||
/// 自定义收藏组件
|
||||
/// isFavorite 默认是否收藏
|
||||
/// favoriteNum 默认收藏次数
|
||||
class FavoriteWidget extends StatefulWidget {
|
||||
|
||||
class FavoriteWidget extends StatelessWidget {
|
||||
const FavoriteWidget({super.key});
|
||||
|
||||
@override
|
||||
State<FavoriteWidget> createState() => _FavoriteState();
|
||||
}
|
||||
|
||||
class _FavoriteState extends State<FavoriteWidget> with RequestToolMixin {
|
||||
late StreamSubscription _listen;
|
||||
final _controller = Get.find<HomeworkReviewLogic>();
|
||||
final sateData = Get.find<HomeworkReviewLogic>().state;
|
||||
RxBool favorite = false.obs; // 初始化数据
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_listen = _controller.state.data.listen((e) {
|
||||
if (e == null) return;
|
||||
if (favorite.value != e.isFav) favorite.value = !favorite.value;
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_listen.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> toFavorite() async {
|
||||
try {
|
||||
var data = sateData.data.value!;
|
||||
var param = sateData.param.value;
|
||||
await getClient().toFavStudent(FavorParam(
|
||||
homeworkId: param.homeworkId,
|
||||
studentId: data.studentId,
|
||||
templateId: data.templateId,
|
||||
questionNo: data.studentQuestions[0].questionNo,
|
||||
isFav: !data.isFav,
|
||||
));
|
||||
favorite.value = !data.isFav;
|
||||
data.isFav = favorite.value;
|
||||
} catch (e) {
|
||||
ToastUtils.showError('操作失败,请重试');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final logic = Get.find<HomeworkReviewLogic>();
|
||||
final sateData = logic.state;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 4.r),
|
||||
child: InkWell(
|
||||
onTap: () => easyThrottle('homework_review_collect_btn', () => toFavorite(), duration: const Duration(milliseconds: 500)),
|
||||
onTap: () => easyThrottle('homework_review_collect_btn', () => logic.toFavorite(), duration: const Duration(milliseconds: 500)),
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
child: Row(
|
||||
|
|
@ -73,7 +29,7 @@ class _FavoriteState extends State<FavoriteWidget> with RequestToolMixin {
|
|||
return Icon(
|
||||
const IconData(0xe63c, fontFamily: "AlibabaIcon"),
|
||||
size: 18.sp,
|
||||
color: favorite.value ? const Color.fromRGBO(255, 172, 48, 1) : const Color.fromRGBO(164, 164, 164, 1),
|
||||
color: sateData.favorite.value ? const Color.fromRGBO(255, 172, 48, 1) : const Color.fromRGBO(164, 164, 164, 1),
|
||||
);
|
||||
}),
|
||||
SizedBox(width: 6.w),
|
||||
|
|
|
|||
|
|
@ -14,26 +14,25 @@ import 'package:making_school_asignment_app/common/utils/cached_network_img.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/configuration_files/index.dart';
|
||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||
import 'package:zoom_widget/zoom_widget.dart';
|
||||
|
||||
import 'dropdown_switch_students_type.dart';
|
||||
import '../configuration_files/zoom_logic.dart';
|
||||
|
||||
part 'question_paper_view.g.dart';
|
||||
|
||||
// 试题详情
|
||||
class QuestionPaperView extends StatefulWidget {
|
||||
|
||||
class QuestionPaperView extends StatelessWidget {
|
||||
const QuestionPaperView({super.key});
|
||||
|
||||
@override
|
||||
State<QuestionPaperView> createState() => _QuestionPaperViewState();
|
||||
}
|
||||
|
||||
class _QuestionPaperViewState extends State<QuestionPaperView> {
|
||||
final logic = Get.find<HomeworkReviewLogic>();
|
||||
final sateData = Get.find<HomeworkReviewLogic>().state;
|
||||
final annotationState = Get.find<HomeworkReviewLogic>().annotationState;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final zoomLogic = Get.find<ZoomLogic>();
|
||||
final zoomState = zoomLogic.zoomState;
|
||||
final logic = Get.find<HomeworkReviewLogic>();
|
||||
final sateData = logic.state;
|
||||
final annotationState = logic.annotationState;
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
Row(
|
||||
|
|
@ -41,14 +40,40 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
|||
// 试题图片视图
|
||||
Expanded(
|
||||
flex: 7,
|
||||
child: LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
|
||||
var maxWidth = constraints.maxWidth;
|
||||
var maxHeight = constraints.maxHeight;
|
||||
child: Obx(() {
|
||||
ZoomFileModel? zoomFileModel = zoomState.zoomFile.value;
|
||||
|
||||
if (zoomFileModel == null) {
|
||||
/// 计算高度
|
||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
|
||||
var maxWidth = constraints.maxWidth;
|
||||
var maxHeight = constraints.maxHeight;
|
||||
// var maxHeight = ScreenUtil().screenHeight - 95.h - logic.appBarHeight; // 1187.2356785851596
|
||||
// print("原始 视图的高度:${maxHeight1} $maxHeight ${MediaQuery.of(context).padding.top}");
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
zoomState.zoomFile.value = ZoomFileModel(viewWidth: maxWidth, viewHeight: maxHeight);
|
||||
});
|
||||
return SizedBox();
|
||||
});
|
||||
} else if (zoomFileModel.fileHeight == null) {
|
||||
/// 文件未计算实际高度
|
||||
return SizedBox();
|
||||
}
|
||||
|
||||
var maxWidth = zoomFileModel.viewWidth;
|
||||
var maxHeight = zoomFileModel.viewHeight;
|
||||
print("文件的高度:${zoomFileModel.actualHeight!}");
|
||||
return Stack(
|
||||
children: [
|
||||
// 主图
|
||||
QuestionImageView(maxWidth, maxHeight, sateData, annotationState, logic),
|
||||
QuestionImageView(
|
||||
maxWidth,
|
||||
maxHeight,
|
||||
sateData,
|
||||
annotationState,
|
||||
logic,
|
||||
actualHeight: zoomFileModel.actualHeight!,
|
||||
),
|
||||
// 继续批阅按钮
|
||||
// Positioned(right: 3.w, bottom: 4.h, child: const $ContinueToReview(isFloatingAction: true)),
|
||||
// 上一题按钮
|
||||
|
|
@ -81,6 +106,7 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
|||
child: Obx(() {
|
||||
NextPage? nextPageVal = sateData.data.value?.nextPage;
|
||||
if (nextPageVal == null) return const SizedBox();
|
||||
|
||||
return FloatingActionButton(
|
||||
heroTag: '点击前往下一题',
|
||||
tooltip: '点击前往下一题',
|
||||
|
|
@ -108,14 +134,13 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
|||
),
|
||||
Obx(() {
|
||||
if (!sateData.getDataError.value) return const SizedBox();
|
||||
|
||||
return Center(
|
||||
child: CupertinoButton(
|
||||
color: Colors.grey[300],
|
||||
onPressed: () => easyThrottle('home_work_reload_data', () {
|
||||
sateData.param.value = DoPaperDetailsParam.fromJson(sateData.param.value.toJson());
|
||||
}),
|
||||
child: quickText('重新请求', color: Colors.black38),
|
||||
child: quickText('再次请求', color: Colors.black38),
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
|
@ -234,7 +259,10 @@ Widget $questionNumberView(BuildContext context, HomeworkReviewLogic logic, Home
|
|||
if (sateData.panQuestView == false) sateData.slide.value = scrollControllerNum.offset;
|
||||
});
|
||||
var listenVal = sateData.slide.listen((e) {
|
||||
if (e != scrollControllerNum.offset && sateData.panQuestView == true) scrollControllerNum.jumpTo(e);
|
||||
if (sateData.panQuestView != null && sateData.panQuestView == true && e != scrollControllerNum.offset) {
|
||||
print("进来了试题题号视图");
|
||||
scrollControllerNum.jumpTo(e);
|
||||
}
|
||||
});
|
||||
|
||||
return () {
|
||||
|
|
@ -270,7 +298,7 @@ Widget $questionNumberView(BuildContext context, HomeworkReviewLogic logic, Home
|
|||
var actualImgHeight = imageVal.actualImgHeight; // 实际图片高度
|
||||
|
||||
return Container(
|
||||
height: boxHeight > actualImgHeight ? boxHeight : actualImgHeight,
|
||||
height: (boxHeight > actualImgHeight ? boxHeight : actualImgHeight) * (logic.state.initScale.value ?? 1),
|
||||
padding: EdgeInsets.only(top: imageVal.remainingHeight > 0 ? imageVal.remainingHeight / 2 : 0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
|
|
@ -292,6 +320,7 @@ Widget $questionNumberView(BuildContext context, HomeworkReviewLogic logic, Home
|
|||
Widget $scoringQuestionsView(
|
||||
BuildContext context, HomeworkReviewLogic logic, StudentQuestions item, double scaleRatio, List<StudentQuestions>? studentQuestions) {
|
||||
var studentScore = useState<int?>(item.studentScore);
|
||||
var initScale = useState<double>(logic.state.initScale.value ?? 1);
|
||||
|
||||
useValueChanged<int?, void>(studentScore.value, (_, __) {
|
||||
item.studentScore = studentScore.value;
|
||||
|
|
@ -304,6 +333,11 @@ Widget $scoringQuestionsView(
|
|||
}
|
||||
});
|
||||
|
||||
useValueChanged<double?, void>(logic.state.initScale.value, (_, __) {
|
||||
print("数据了啦啦啦啦: ${logic.state.initScale}");
|
||||
initScale.value = logic.state.initScale.value ?? 1;
|
||||
});
|
||||
|
||||
useValueChanged<StudentQuestions, void>(item, (_, __) {
|
||||
studentScore.value = item.studentScore;
|
||||
});
|
||||
|
|
@ -312,8 +346,9 @@ Widget $scoringQuestionsView(
|
|||
return () {};
|
||||
}, []);
|
||||
var padinVal = item.correctRate > 0 ? EdgeInsets.only(top: 6.4.h) : EdgeInsets.symmetric(vertical: 2.h);
|
||||
print("高度: ${item.height! * scaleRatio * initScale.value}");
|
||||
return Container(
|
||||
height: item.height! * scaleRatio,
|
||||
height: item.height! * scaleRatio * initScale.value,
|
||||
padding: EdgeInsets.zero,
|
||||
child: item.useTime == 0
|
||||
? Container()
|
||||
|
|
@ -423,10 +458,11 @@ Widget $scoringQuestionsView(
|
|||
class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar> {
|
||||
final double maxWidth;
|
||||
final double maxHeight;
|
||||
final double actualHeight;
|
||||
final HomeworkReviewLogic logic;
|
||||
final HomeworkReviewState sateData;
|
||||
final HomeworkReviewAnnotationsControlState annotationState;
|
||||
QuestionImageView(this.maxWidth, this.maxHeight, this.sateData, this.annotationState, this.logic, {super.key});
|
||||
QuestionImageView(this.maxWidth, this.maxHeight, this.sateData, this.annotationState, this.logic, {required this.actualHeight, super.key});
|
||||
|
||||
/// 获取数组指定倒数具体值的下标
|
||||
int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) {
|
||||
|
|
@ -443,15 +479,26 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
int activePointers = 0;
|
||||
|
||||
ImageStream? imageStream;
|
||||
var zoomKey = GlobalKey();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theMaxHeight = useState<double>(maxHeight);
|
||||
|
||||
useValueChanged<double, void>(maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight);
|
||||
final scrollControllerQuestion = useScrollController(); // 试题图片区域
|
||||
// final imgActualHeight = useState<double?>(sateData.imageScale.value?.actualImgHeight);
|
||||
// final scrollControllerQuestion = useScrollController(); // 试题图片区域
|
||||
var vnHandWritings = useValueNotifier<List<dynamic>>([]);
|
||||
|
||||
/// 滑动高度
|
||||
var initPosition = useState<Offset?>(null);
|
||||
|
||||
var customPaintSize = useState<Size>(Size.zero);
|
||||
|
||||
// useValueChanged<double?, void>(sateData.imageScale.value?.actualImgHeight, (_, __) {
|
||||
// imgActualHeight.value = sateData.imageScale.value?.actualImgHeight;
|
||||
// });
|
||||
|
||||
useEffect(() {
|
||||
var listenStream = sateData.data.listen((e) {
|
||||
// 数据清空
|
||||
|
|
@ -464,9 +511,21 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
sateData.imageScale.value?.actualImgHeight ?? 0,
|
||||
);
|
||||
});
|
||||
|
||||
var streamSubscriptionSlide = sateData.slide.listen((e) {
|
||||
if (sateData.panQuestView != null &&
|
||||
sateData.panQuestView == false &&
|
||||
initPosition.value?.dy.abs().toInt().toDouble() != sateData.slide.value) {
|
||||
if (sateData.zoomOffset != null) {
|
||||
sateData.zoomOffset = Offset(sateData.zoomOffset!.dx, -sateData.slide.value);
|
||||
}
|
||||
initPosition.value = sateData.zoomOffset;
|
||||
}
|
||||
});
|
||||
return () {
|
||||
listenStream.cancel();
|
||||
listenStreamImageScale.cancel();
|
||||
streamSubscriptionSlide.cancel();
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
@ -480,6 +539,7 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
imageHeight: info.image.height.toDouble(),
|
||||
url: sateData.data.value!.zgtAnswer,
|
||||
);
|
||||
print("这里的高度 ${theMaxHeight.value} $actualHeight ${sateData.imageScale.value?.actualImgHeight}");
|
||||
});
|
||||
}));
|
||||
|
||||
|
|
@ -557,109 +617,153 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
vnHandWritings.value = annotationsData;
|
||||
});
|
||||
|
||||
scrollControllerQuestion.addListener(() {
|
||||
sateData.slide.value = scrollControllerQuestion.offset;
|
||||
}); // 执行滚动相关的逻辑,例如打印滚动位置
|
||||
// scrollControllerQuestion.addListener(() {
|
||||
// sateData.slide.value = scrollControllerQuestion.offset;
|
||||
// }); // 执行滚动相关的逻辑,例如打印滚动位置
|
||||
|
||||
var listenVal = sateData.slide.listen((e) {
|
||||
if (e != scrollControllerQuestion.offset && sateData.panQuestView == false) scrollControllerQuestion.jumpTo(e);
|
||||
});
|
||||
// var listenVal = sateData.slide.listen((e) {
|
||||
// if (e != scrollControllerQuestion.offset && sateData.panQuestView == false) scrollControllerQuestion.jumpTo(e);
|
||||
// });
|
||||
|
||||
// 返回一个清理函数,在组件销毁时移除监听
|
||||
return () {
|
||||
activePointers = 0;
|
||||
listenVal.cancel();
|
||||
// listenVal.cancel();
|
||||
imageStream?.removeListener(imageStreamListener.value);
|
||||
};
|
||||
}, []);
|
||||
|
||||
print("IIIIIII");
|
||||
print("++++++++++++++++++++++ ${theMaxHeight.value}");
|
||||
print("+++++ 位置:${initPosition.value}");
|
||||
print("+++++ 缩放:${sateData.initScale.value}");
|
||||
|
||||
return Container(
|
||||
height: maxHeight,
|
||||
// padding: EdgeInsets.only(bottom: 2.h, top: 2.h),
|
||||
// decoration: BoxDecoration(
|
||||
// boxShadow: [
|
||||
// BoxShadow(
|
||||
// color: Colors.grey.withOpacity(0.2),
|
||||
// offset: Offset(-6.r, 1.r),
|
||||
// blurRadius: 10.r,
|
||||
// spreadRadius: 8.r,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
alignment: Alignment.center,
|
||||
child: Obx(() {
|
||||
var imageUrl = sateData.data.value?.zgtAnswer;
|
||||
var showZgtAnnotate = sateData.data.value?.showZgtAnnotate;
|
||||
width: maxWidth,
|
||||
// height: theMaxHeight.value,
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPointerDown: (PointerDownEvent event) {
|
||||
// 判断当前是否已经有触摸点,如果有则忽略该触摸事件
|
||||
|
||||
if (imageUrl == null) return const SizedBox();
|
||||
// 处理单个触摸点按下的逻辑
|
||||
activePointers++;
|
||||
sateData.panQuestView = true;
|
||||
},
|
||||
onPointerUp: (PointerUpEvent details) {
|
||||
// 处理单个触摸点抬起的逻辑
|
||||
// activePointers--;
|
||||
// globalPosition = null;
|
||||
activePointers--;
|
||||
|
||||
print("KKKKKKKKKKKKKKKKKKKKKK");
|
||||
return 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,
|
||||
onPointerDown: (PointerDownEvent event) {
|
||||
// 判断当前是否已经有触摸点,如果有则忽略该触摸事件
|
||||
var imageScale = sateData.imageScale.value;
|
||||
if (imageScale == null || !annotationState.pen.value) return;
|
||||
vnHandWritings.value.add(null); // 增加空点以分隔不同的线段
|
||||
sateData.handwritings = vnHandWritings.value; // 添加笔迹数据
|
||||
},
|
||||
onPointerMove: (PointerMoveEvent event) {
|
||||
if (activePointers != 1) return;
|
||||
|
||||
// 处理单个触摸点按下的逻辑
|
||||
activePointers++;
|
||||
sateData.panQuestView = true;
|
||||
},
|
||||
onPointerUp: (PointerUpEvent details) {
|
||||
// 处理单个触摸点抬起的逻辑
|
||||
// activePointers--;
|
||||
// globalPosition = null;
|
||||
activePointers--;
|
||||
var imageScale = sateData.imageScale.value;
|
||||
if (imageScale == null || !annotationState.pen.value) return;
|
||||
Offset localPosition = event.localPosition; // 相对
|
||||
|
||||
var imageScale = sateData.imageScale.value;
|
||||
if (imageScale == null || !annotationState.pen.value) return;
|
||||
vnHandWritings.value.add(null); // 增加空点以分隔不同的线段
|
||||
sateData.handwritings = vnHandWritings.value; // 添加笔迹数据
|
||||
},
|
||||
onPointerMove: (PointerMoveEvent event) {
|
||||
if (activePointers != 1) return;
|
||||
print("\\\\\\\\");
|
||||
print(localPosition);
|
||||
print("\\\\\\\\ ${imageScale.actualImgHeight}");
|
||||
|
||||
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; // 检查笔记是否超出图片范围
|
||||
var dy = localPosition.dy;
|
||||
if (dy > imageScale.actualImgHeight || dy < 0) return; // 检查笔记是否超出图片范围
|
||||
|
||||
vnHandWritings.value = List.from(vnHandWritings.value)..add(localPosition);
|
||||
sateData.handwritings = vnHandWritings.value;
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
$TheCachedNetworkImage(
|
||||
imageUrl: imageUrl,
|
||||
imgWidth: maxWidth,
|
||||
(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;
|
||||
return imageWidget;
|
||||
},
|
||||
),
|
||||
RepaintBoundary(
|
||||
key: logic.pictureOverviewKey,
|
||||
child: CustomPaint(
|
||||
isComplex: true,
|
||||
size: customPaintSize.value,
|
||||
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
||||
child: showZgtAnnotate != null
|
||||
? $TheCachedNetworkImage(
|
||||
imgWidth: maxWidth,
|
||||
imageUrl: showZgtAnnotate,
|
||||
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
||||
)
|
||||
: null,
|
||||
var theScale = sateData.initScale.value ?? 1;
|
||||
|
||||
/// 缩放后的基数
|
||||
if (theScale != 1) {
|
||||
localPosition = Offset(localPosition.dx / theScale, localPosition.dy / theScale);
|
||||
var theZoomOffset = sateData.zoomOffset;
|
||||
if (theZoomOffset != null) {
|
||||
var dx = theZoomOffset.dx;
|
||||
var dy = theZoomOffset.dy;
|
||||
localPosition = Offset(localPosition.dx + dx, localPosition.dy + dy);
|
||||
}
|
||||
}
|
||||
|
||||
vnHandWritings.value = List.from(vnHandWritings.value)..add(localPosition);
|
||||
sateData.handwritings = vnHandWritings.value;
|
||||
},
|
||||
child: Obx(() {
|
||||
var isPen = annotationState.pen.value;
|
||||
var showZgtAnnotate = sateData.data.value?.showZgtAnnotate;
|
||||
|
||||
print("55555高度: ${actualHeight} ${initPosition.value}");
|
||||
return IgnorePointer(
|
||||
ignoring: isPen,
|
||||
child: Zoom(
|
||||
key: zoomKey,
|
||||
// initTotalZoomOut: true, // 展示全部内容 初始化不产生滚动条
|
||||
zoomSensibility: 0.05,
|
||||
scrollWeight: 4.r,
|
||||
doubleTapAnimDuration: Duration.zero,
|
||||
maxZoomWidth: maxWidth,
|
||||
maxZoomHeight: actualHeight,
|
||||
canvasColor: Colors.transparent,
|
||||
initPosition: initPosition.value,
|
||||
initScale: sateData.initScale.value,
|
||||
backgroundColor: Colors.transparent,
|
||||
onScaleUpdate: logic.onScaleUpdate,
|
||||
onPositionUpdate: logic.onPanUpPosition,
|
||||
child: Center(
|
||||
child: Stack(
|
||||
children: [
|
||||
$TheCachedNetworkImage(
|
||||
imageUrl: sateData.data.value!.zgtAnswer,
|
||||
imgWidth: maxWidth,
|
||||
(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(
|
||||
isComplex: true,
|
||||
size: customPaintSize.value,
|
||||
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
||||
child: showZgtAnnotate != null
|
||||
? $TheCachedNetworkImage(
|
||||
imgWidth: maxWidth,
|
||||
imageUrl: showZgtAnnotate,
|
||||
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth))
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
// return Obx(() {
|
||||
// var imageUrl = sateData.data.value?.zgtAnswer;
|
||||
|
||||
// if (imageUrl == null) return const SizedBox();
|
||||
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
|
@ -10,11 +11,16 @@ 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';
|
||||
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/favor_param.dart';
|
||||
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/permission_describe_util.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';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import 'zoom_logic.dart';
|
||||
|
||||
// 数据
|
||||
class HomeworkReviewState {
|
||||
|
|
@ -26,13 +32,16 @@ class HomeworkReviewState {
|
|||
late Rx<DoPaperDetailsResult?> data;
|
||||
late Rx<List<StudentQuestions>?> studentQuestions;
|
||||
late Rx<double> slide; // 滑动位置
|
||||
late bool? panQuestView;
|
||||
bool? panQuestView = null;
|
||||
late Rx<TestQuestionsImageInfo?> imageScale;
|
||||
List<dynamic> handwritings = [];
|
||||
bool needRefresh = false;
|
||||
bool lastQuestionPrompt = false; // 最后一题提示
|
||||
Rx<bool> getDataError = false.obs; // 获取作业数据报错
|
||||
|
||||
Rx<double?> initScale = Rx<double?>(null);
|
||||
Offset? zoomOffset;
|
||||
RxBool favorite = false.obs; // 初始化数据
|
||||
// late String dateEnd = '';
|
||||
// late int knowledgeId = 0;
|
||||
// late RxList<KnowledgeReportDetail> dataList = RxList();
|
||||
|
|
@ -53,7 +62,10 @@ class HomeworkReviewAnnotationsControlState {
|
|||
|
||||
class HomeworkReviewBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() => Get.lazyPut(() => HomeworkReviewLogic());
|
||||
void dependencies() {
|
||||
Get.lazyPut(() => HomeworkReviewLogic());
|
||||
Get.lazyPut(() => ZoomLogic());
|
||||
}
|
||||
}
|
||||
|
||||
class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
||||
|
|
@ -62,10 +74,16 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
|||
late StreamSubscription<DoPaperDetailsResult?> _dataListen;
|
||||
final HomeworkReviewState state = HomeworkReviewState();
|
||||
final HomeworkReviewAnnotationsControlState annotationState = HomeworkReviewAnnotationsControlState();
|
||||
double appBarHeight = 56;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
appBarHeight = MediaQuery.of(Get.context!).padding.top;
|
||||
print("appBarHeight :$appBarHeight");
|
||||
// WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); // 屏幕刘海
|
||||
|
||||
state.param = DoPaperDetailsParam(
|
||||
homeworkId: Get.arguments['homeworkId'],
|
||||
homeworkName: Get.arguments['homeworkName'],
|
||||
|
|
@ -76,18 +94,42 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
|||
state.studentQuestions = Rx<List<StudentQuestions>?>(null);
|
||||
state.imageScale = Rx<TestQuestionsImageInfo?>(null);
|
||||
state.slide = 0.0.obs;
|
||||
getData();
|
||||
|
||||
// 参数变化更新作业详情
|
||||
_paramListen = state.param.listen((e) => getData());
|
||||
// 试题数据
|
||||
_dataListen = state.data.listen((e) => state.imageScale.value = null);
|
||||
_dataListen = state.data.listen((e) {
|
||||
if (e == null) return;
|
||||
state.imageScale.value = null;
|
||||
if (state.favorite.value != e.isFav) state.favorite.value = !state.favorite.value;
|
||||
});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => getData());
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@override
|
||||
void disposeId(Object id) {
|
||||
void onReady() {
|
||||
Future.delayed(Duration.zero, () {
|
||||
DeviceInfoPlugin().androidInfo.then((androidInfo) {
|
||||
Permission storagePermission = androidInfo.version.sdkInt >= 33 ? Permission.manageExternalStorage : Permission.storage;
|
||||
PermissionDescribeUtil.instance.toLaunchPermissionRequest(
|
||||
Get.context,
|
||||
title: '储存权限请求',
|
||||
describe: "为了提供更好的服务,需要获取到存储权限用于保存批阅痕迹并上传",
|
||||
permissions: [storagePermission],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]); // 屏幕刘海
|
||||
_dataListen.cancel();
|
||||
_paramListen.cancel();
|
||||
super.disposeId(id);
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void getData() async {
|
||||
|
|
@ -105,6 +147,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
|||
state.data.value = data;
|
||||
state.handwritings = [];
|
||||
state.studentQuestions.value = data.studentQuestions;
|
||||
state.initScale.value = null;
|
||||
} catch (e) {
|
||||
print('获取数据报错了:$e');
|
||||
// ToastUtils.showError('未获取到试题数据,请重试');
|
||||
|
|
@ -280,4 +323,40 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
|||
state.param.value = newParams;
|
||||
});
|
||||
}
|
||||
|
||||
void onPanUpPosition(Offset val) async {
|
||||
// 手指在移动 非物体移动的位置
|
||||
|
||||
if (state.zoomOffset?.dy.toStringAsFixed(2) != val.dy.toStringAsFixed(2) ||
|
||||
state.zoomOffset?.dx.toStringAsFixed(2) != val.dx.toStringAsFixed(2)) {
|
||||
print('**************** 正在移动位置 YYY:${val.dy}');
|
||||
print('**************** 正在移动位置 XXX:${val.dx}');
|
||||
state.zoomOffset = val;
|
||||
state.slide.value = val.dy.abs().toInt().toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
// 缩放组件 ==> 缩放监听
|
||||
void onScaleUpdate(double scale, double zoom) async {
|
||||
state.initScale.value = zoom;
|
||||
// print("$scale $zoom");
|
||||
}
|
||||
|
||||
Future<void> toFavorite() async {
|
||||
try {
|
||||
var data = state.data.value!;
|
||||
var param = state.param.value;
|
||||
await getClient().toFavStudent(FavorParam(
|
||||
homeworkId: param.homeworkId,
|
||||
studentId: data.studentId,
|
||||
templateId: data.templateId,
|
||||
questionNo: data.studentQuestions[0].questionNo,
|
||||
isFav: !data.isFav,
|
||||
));
|
||||
state.favorite.value = !data.isFav;
|
||||
data.isFav = state.favorite.value;
|
||||
} catch (e) {
|
||||
ToastUtils.showError('操作失败,请重试');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
import 'dart:ui' show ImmutableBuffer, ImageDescriptor;
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import 'index.dart';
|
||||
|
||||
part 'zoom_logic.g.dart';
|
||||
|
||||
class ZoomLogic extends GetxController {
|
||||
final logic = Get.find<HomeworkReviewLogic>();
|
||||
HomeworkReviewState get homeworkState => logic.state;
|
||||
|
||||
ZoomState zoomState = ZoomState(zoomFile: Rx<ZoomFileModel?>(null));
|
||||
|
||||
var oldTemplateId = null;
|
||||
late StreamSubscription _streamHomework;
|
||||
late StreamSubscription _streamZoomState;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
oldTemplateId = zoomState.zoomFile.value?.templateId;
|
||||
_streamZoomState = zoomState.zoomFile.listen((e) {
|
||||
var templateId = e?.templateId;
|
||||
print("ZOOMFILE 变化了 $templateId");
|
||||
if (templateId == null) return;
|
||||
|
||||
var homeworkData = homeworkState.data.value;
|
||||
var zgtAnswer = homeworkData?.zgtAnswer;
|
||||
print("333333");
|
||||
if (zgtAnswer == null) return;
|
||||
print("444444 ${homeworkData!.templateId} 和 ${templateId}");
|
||||
if (oldTemplateId == templateId) return;
|
||||
|
||||
// getNetworkImageDimensions(zgtAnswer);
|
||||
|
||||
// 第三方库网络图,图片会被解码并缓存于内存
|
||||
oldTemplateId = templateId;
|
||||
print("获取图片尺寸....");
|
||||
CachedNetworkImageProvider(zgtAnswer).getImageSize().then((s) {
|
||||
// 提取宽度和高度
|
||||
if (s == null) return;
|
||||
var oldVal = zoomState.zoomFile.value!;
|
||||
oldVal.fileWidth = s.width;
|
||||
oldVal.fileHeight = s.height;
|
||||
zoomState.zoomFile.value = ZoomFileModel.fromJson(oldVal.toJson());
|
||||
});
|
||||
});
|
||||
_streamHomework = homeworkState.data.listen((e) {
|
||||
print("HOMEWORKSTATE 变化了");
|
||||
if (e == null || zoomState.zoomFile.value == null) return;
|
||||
zoomState.zoomFile.value!.templateId = e.templateId;
|
||||
print("666666 ${e.templateId}");
|
||||
zoomState.zoomFile.value = ZoomFileModel.fromJson(zoomState.zoomFile.value!.toJson());
|
||||
});
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_streamHomework.cancel();
|
||||
_streamZoomState.cancel();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
class ZoomState {
|
||||
final Rx<ZoomFileModel?> zoomFile;
|
||||
|
||||
const ZoomState({required this.zoomFile});
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class ZoomFileModel extends Object {
|
||||
double viewWidth; // 容器宽度
|
||||
double viewHeight; // 容器高度
|
||||
|
||||
int? templateId; // 页码
|
||||
|
||||
double? fileWidth; // 文件宽度
|
||||
double? fileHeight; // 文件高度
|
||||
|
||||
double? actualWidth; // 实际展示宽度
|
||||
double? actualHeight; // 实际展示高度
|
||||
|
||||
ZoomFileModel({
|
||||
required this.viewWidth,
|
||||
required this.viewHeight,
|
||||
this.templateId,
|
||||
this.fileWidth,
|
||||
this.fileHeight,
|
||||
this.actualWidth,
|
||||
this.actualHeight,
|
||||
}) {
|
||||
// 图片已视图宽为基准,高度自适应可滑动 图片的实际宽高都需要乘此基准值.
|
||||
if (fileHeight == null || fileWidth == null) return;
|
||||
|
||||
var scaleRatio = viewWidth / fileWidth!;
|
||||
actualWidth = fileWidth! * scaleRatio;
|
||||
actualHeight = fileHeight! * scaleRatio;
|
||||
|
||||
// remainingHeight = boxHeight - actualImgHeight;
|
||||
// imageHeightOffsetStart = remainingHeight / 2;
|
||||
// imageHeightOffsetend = imageHeightOffsetStart + actualImgHeight;
|
||||
}
|
||||
|
||||
factory ZoomFileModel.fromJson(Map<String, dynamic> srcJson) => _$ZoomFileModelFromJson(srcJson);
|
||||
|
||||
Map<String, dynamic> toJson() => _$ZoomFileModelToJson(this);
|
||||
}
|
||||
|
||||
// 最终版
|
||||
extension GetImageSize on ImageProvider {
|
||||
Future<Size?> getImageSize({bool avoidDecode = false}) async {
|
||||
if (avoidDecode) {
|
||||
// 是否要免解码
|
||||
final cacheStatus = await obtainCacheStatus(configuration: const ImageConfiguration());
|
||||
final tracked = cacheStatus?.tracked ?? false;
|
||||
// 内存缓存中存在时优先从缓存中取,不在内存缓存中时转换为 ImageDescriptor
|
||||
if (!tracked) {
|
||||
ImmutableBuffer? buffer;
|
||||
if (this is AssetBundleImageProvider) {
|
||||
final key = await obtainKey(const ImageConfiguration()) as AssetBundleImageKey;
|
||||
buffer = await key.bundle.loadBuffer(key.name);
|
||||
} else if (this is FileImage) {
|
||||
final file = (this as FileImage).file;
|
||||
final int lengthInBytes = await file.length();
|
||||
if (lengthInBytes > 0) {
|
||||
buffer = await ImmutableBuffer.fromFilePath(file.path);
|
||||
}
|
||||
} else if (this is MemoryImage) {
|
||||
final bytes = (this as MemoryImage).bytes;
|
||||
buffer = await ImmutableBuffer.fromUint8List(bytes);
|
||||
}
|
||||
if (buffer != null) {
|
||||
final descriptor = await ImageDescriptor.encoded(buffer);
|
||||
final size = Size(descriptor.width.toDouble(), descriptor.height.toDouble());
|
||||
buffer.dispose();
|
||||
descriptor.dispose();
|
||||
if (!size.isEmpty) return size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 免解码末开启或内存缓存已存在,最后再兜底上面未处理的情况
|
||||
final completer = Completer<Size?>();
|
||||
ImageStream imageStream = resolve(const ImageConfiguration());
|
||||
|
||||
ImageStreamListener? listener;
|
||||
listener = ImageStreamListener(
|
||||
(imageInfo, synchronousCall) {
|
||||
if (!completer.isCompleted) {
|
||||
completer.complete(Size(imageInfo.image.width.toDouble(), imageInfo.image.height.toDouble()));
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
imageInfo.dispose();
|
||||
imageStream.removeListener(listener!);
|
||||
});
|
||||
},
|
||||
onError: (exception, stackTrace) {
|
||||
if (!completer.isCompleted) {
|
||||
completer.complete();
|
||||
}
|
||||
imageStream.removeListener(listener!);
|
||||
},
|
||||
);
|
||||
imageStream.addListener(listener);
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
|
|
@ -7,43 +7,22 @@ import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
|||
import 'package:making_school_asignment_app/page/home_page/children/annotate_class/annotate_class_logic.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/question_paper_view.dart';
|
||||
import 'package:making_school_asignment_app/page/home_page/home_logic.dart';
|
||||
import 'package:zoom_widget/zoom_widget.dart';
|
||||
|
||||
import 'components/bottom_operation_bar.dart';
|
||||
import 'components/dropdown_switch_students_type.dart';
|
||||
import 'components/favorite_widget.dart';
|
||||
import 'configuration_files/index.dart';
|
||||
|
||||
class HomeworkReview extends StatefulWidget {
|
||||
class HomeworkReview extends StatelessWidget {
|
||||
const HomeworkReview({super.key});
|
||||
|
||||
@override
|
||||
State<HomeworkReview> createState() => _HomeworkReviewState();
|
||||
}
|
||||
|
||||
class _HomeworkReviewState extends State<HomeworkReview> {
|
||||
final ScrollController controller = ScrollController();
|
||||
final logic = Get.find<HomeworkReviewLogic>();
|
||||
final sateData = Get.find<HomeworkReviewLogic>().state;
|
||||
final AnnotateClassLogic _controller = Get.find<AnnotateClassLogic>();
|
||||
final HomeLogic _homeLogicController = Get.find<HomeLogic>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); // 屏幕刘海
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
Get.delete<HomeworkReviewLogic>();
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]); // 屏幕刘海
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final logic = Get.find<HomeworkReviewLogic>();
|
||||
final sateData = logic.state;
|
||||
final AnnotateClassLogic _controller = Get.find<AnnotateClassLogic>();
|
||||
final HomeLogic _homeLogicController = Get.find<HomeLogic>();
|
||||
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (e) {
|
||||
|
|
@ -65,23 +44,22 @@ class _HomeworkReviewState extends State<HomeworkReview> {
|
|||
),
|
||||
body: Column(
|
||||
children: [
|
||||
// 下拉切换
|
||||
// 下拉切换 30.h
|
||||
const DropdownSwitchStudentsType(),
|
||||
// +1
|
||||
SizedBox(height: 1.h),
|
||||
const Expanded(
|
||||
child: QuestionPaperView(),
|
||||
|
||||
// child: Container(
|
||||
// height: double.infinity,
|
||||
// width: double.infinity,
|
||||
// color: Colors.red,
|
||||
// ),
|
||||
// child: QuestionPaperView(),
|
||||
),
|
||||
const Expanded(child: QuestionPaperView()),
|
||||
// +64.h
|
||||
const BottomAnnotationSwitch()
|
||||
],
|
||||
),
|
||||
// floatingActionButton: const ButtonFloatingAction(),
|
||||
// floatingActionButton: FloatingActionButton(
|
||||
// elevation: 8,
|
||||
// tooltip: "点击打开工具栏",
|
||||
// backgroundColor: Colors.white,
|
||||
// onPressed: () {},
|
||||
// child: Icon(Icons.edit, size: 20.sp, color: Theme.of(context).primaryColor),
|
||||
// ),
|
||||
// floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import 'package:making_school_asignment_app/common/store/user_store.dart';
|
|||
import 'package:making_school_asignment_app/common/utils/storage.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/login_page/children/sys_protocol.dart';
|
||||
import 'package:making_school_asignment_app/routes/app_pages.dart';
|
||||
import 'package:making_school_asignment_app/common/store/app_storage_key.dart';
|
||||
|
||||
|
|
@ -16,12 +15,6 @@ import 'login_state.dart';
|
|||
class LoginLogic extends GetxController with RequestToolMixin {
|
||||
final LoginState state = LoginState();
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
// TODO: implement onReady
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
|
|
@ -37,6 +30,8 @@ class LoginLogic extends GetxController with RequestToolMixin {
|
|||
state.passwordController.text = pwd;
|
||||
state.keepPwd.value = true;
|
||||
}
|
||||
state.userNameController.text = "AppleTester";
|
||||
state.passwordController.text = "AppleTester123!";
|
||||
|
||||
state.pwdFocus = FocusNode();
|
||||
state.theFocus = FocusNode();
|
||||
|
|
|
|||
|
|
@ -29,15 +29,12 @@ class _LoginPageState extends State<LoginPage> {
|
|||
statusBarIconBrightness: Brightness.light,
|
||||
statusBarColor: Colors.transparent, //状态栏背景颜色
|
||||
));
|
||||
Future.delayed(Duration.zero, () => upgradeLogic.getAppUpgrade(context));
|
||||
Future.delayed(Duration.zero, () => sysProtocol(context));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
Get.delete<LoginLogic>();
|
||||
super.dispose();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await sysProtocol(context);
|
||||
await Future.delayed(Duration.zero, () => upgradeLogic.getAppUpgrade(context));
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -231,7 +228,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
padding: EdgeInsets.only(right: 0.w),
|
||||
child: Obx(() {
|
||||
return Transform.scale(
|
||||
scale: 1.2,
|
||||
scale: 1.0,
|
||||
child: Checkbox(
|
||||
// activeColor: Colors.transparent, //去掉勾选时背景颜色
|
||||
|
||||
|
|
@ -290,10 +287,10 @@ class _LoginPageState extends State<LoginPage> {
|
|||
},
|
||||
child: Obx(() {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 10.h),
|
||||
decoration: BoxDecoration(
|
||||
color: state.canLogin.value ? const Color(0xFF8C68FF) : const Color(0xFFdddddd),
|
||||
/*boxShadow: [
|
||||
margin: EdgeInsets.symmetric(vertical: 10.h),
|
||||
decoration: BoxDecoration(
|
||||
color: state.canLogin.value ? const Color(0xFF8C68FF) : const Color(0xFFdddddd),
|
||||
/*boxShadow: [
|
||||
BoxShadow(
|
||||
color:
|
||||
const Color.fromRGBO(76, 199, 147, 0.5),
|
||||
|
|
@ -302,15 +299,14 @@ class _LoginPageState extends State<LoginPage> {
|
|||
spreadRadius: 0.5, //阴影扩散程度
|
||||
)
|
||||
],*/
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(17.w),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(17.w),
|
||||
),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
width: double.infinity,
|
||||
height: 50.h,
|
||||
child: Text('登 录', style: TextStyle(fontSize: 16.sp, color: Colors.white)),
|
||||
);
|
||||
alignment: Alignment.center,
|
||||
width: double.infinity,
|
||||
height: 50.h,
|
||||
child: quickText('登 录', size: 18.sp, color: Colors.white));
|
||||
}),
|
||||
),
|
||||
Row(
|
||||
|
|
@ -320,7 +316,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
padding: EdgeInsets.only(right: 0.w),
|
||||
child: Obx(() {
|
||||
return Transform.scale(
|
||||
scale: 1.2,
|
||||
scale: 1.0,
|
||||
child: Checkbox(
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
checkColor: Colors.white,
|
||||
|
|
@ -372,7 +368,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
child: quickText(
|
||||
'《隐私协议》',
|
||||
size: 12.sp,
|
||||
color:Theme.of(context).primaryColor,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.1+2
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=3.4.1 <4.0.0'
|
||||
|
|
@ -48,7 +48,7 @@ dependencies:
|
|||
# http请求插件
|
||||
dio: ^5.4.2+1
|
||||
# 网络缓存图片
|
||||
cached_network_image: ^3.2.1
|
||||
cached_network_image: ^3.3.1
|
||||
# 上拉加载和下拉刷新的组件
|
||||
flutter_easyrefresh: ^2.2.2
|
||||
photo_view: ^0.15.0
|
||||
|
|
@ -98,7 +98,8 @@ dependencies:
|
|||
app_settings: ^5.1.1
|
||||
device_info_plus: ^11.1.0
|
||||
zoom_widget: ^2.0.1
|
||||
|
||||
zoom_pinch_overlay: ^1.4.3
|
||||
flutter_html_2: ^0.1.0
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
|
@ -109,6 +110,7 @@ dev_dependencies:
|
|||
json_serializable: ^6.6.2
|
||||
# 分离样式
|
||||
functional_widget: ^0.10.2
|
||||
analyzer: ^6.4.1
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
|
|
@ -120,6 +122,9 @@ dev_dependencies:
|
|||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
# dependency_overrides:
|
||||
# collection: ^1.19.0
|
||||
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
|
|
|
|||
Loading…
Reference in New Issue