Compare commits
10 Commits
78f2d20536
...
80256a6042
| Author | SHA1 | Date |
|---|---|---|
|
|
80256a6042 | |
|
|
e239ad8710 | |
|
|
d1fd1fb1bc | |
|
|
1a6721cbb0 | |
|
|
3efb94a0d2 | |
|
|
382840a3aa | |
|
|
54f27b0d72 | |
|
|
41b29f19f3 | |
|
|
261d1ad4a4 | |
|
|
1ec5c5eb62 |
|
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.6.3-all.zip
|
||||||
|
|
@ -18,8 +18,8 @@ class RequestConfig {
|
||||||
static RequestConfig? _instance;
|
static RequestConfig? _instance;
|
||||||
String baseUrl;
|
String baseUrl;
|
||||||
|
|
||||||
static const connectTimeout = 8000; // 连接超时
|
static const connectTimeout = 8 * 1000; // 连接超时
|
||||||
static const receiveTimeout = 8000; // 接收超时
|
static const receiveTimeout = 8 * 1000; // 接收超时
|
||||||
static const bool requestDataPrinting = true; // 打印返回数据
|
static const bool requestDataPrinting = true; // 打印返回数据
|
||||||
static const bool printSwitch = true; // 打印返回数据
|
static const bool printSwitch = true; // 打印返回数据
|
||||||
static const successCode = [204, 200]; // 返回成功code
|
static const successCode = [204, 200]; // 返回成功code
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import 'package:making_school_asignment_app/common/config/request_config.dart';
|
||||||
|
|
||||||
part 'do_paper_details_result.g.dart';
|
part 'do_paper_details_result.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(checked: true)
|
@JsonSerializable(checked: true, includeIfNull: false)
|
||||||
class DoPaperDetailsResult extends Object {
|
class DoPaperDetailsResult extends Object {
|
||||||
@JsonKey(name: 'templateIds')
|
@JsonKey(name: 'templateIds')
|
||||||
Map<String, bool> templateIds;
|
Map<String, bool> templateIds;
|
||||||
|
|
@ -17,7 +17,7 @@ class DoPaperDetailsResult extends Object {
|
||||||
@JsonKey(name: 'templateIdKeyMap')
|
@JsonKey(name: 'templateIdKeyMap')
|
||||||
Map<int, int>? templateIdKeyMap;
|
Map<int, int>? templateIdKeyMap;
|
||||||
|
|
||||||
@JsonKey(name: 'submitStudents') // 当前页 总提交学生集合
|
@JsonKey(name: 'submitStudents', toJson: _paperStudentsToJson) // 当前页 总提交学生集合
|
||||||
List<PaperStudents> students;
|
List<PaperStudents> students;
|
||||||
|
|
||||||
@JsonKey(name: 'templateId')
|
@JsonKey(name: 'templateId')
|
||||||
|
|
@ -55,17 +55,17 @@ class DoPaperDetailsResult extends Object {
|
||||||
@JsonKey(name: 'isFav')
|
@JsonKey(name: 'isFav')
|
||||||
bool isFav;
|
bool isFav;
|
||||||
|
|
||||||
@JsonKey(name: 'studentQuestions')
|
@JsonKey(name: 'studentQuestions', toJson: _studentQuestionsToJson)
|
||||||
List<StudentQuestions> studentQuestions;
|
List<StudentQuestions> studentQuestions;
|
||||||
|
|
||||||
// 当前页 未提交学生集合
|
// 当前页 未提交学生集合
|
||||||
@JsonKey(name: 'unSubmitStudents')
|
@JsonKey(name: 'unSubmitStudents', toJson: _paperStudentsToJson)
|
||||||
List<PaperStudents> unSubmitStudents;
|
List<PaperStudents> unSubmitStudents;
|
||||||
|
|
||||||
@JsonKey(name: 'lastPage')
|
@JsonKey(name: 'lastPage', toJson: _lastPageToJson)
|
||||||
LastPage? lastPage;
|
LastPage? lastPage;
|
||||||
|
|
||||||
@JsonKey(name: 'nextPage')
|
@JsonKey(name: 'nextPage', toJson: _nextPageToJson)
|
||||||
NextPage? nextPage;
|
NextPage? nextPage;
|
||||||
|
|
||||||
// 所有页 总待提交数量
|
// 所有页 总待提交数量
|
||||||
|
|
@ -112,27 +112,34 @@ class DoPaperDetailsResult extends Object {
|
||||||
var currentStudent = students.firstWhereOrNull((e) => e.id == studentId);
|
var currentStudent = students.firstWhereOrNull((e) => e.id == studentId);
|
||||||
if (currentStudent != null) priority = currentStudent.isPriority;
|
if (currentStudent != null) priority = currentStudent.isPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!zgtAnswer.contains(RequestConfig.imgUrl)) {
|
||||||
zgtAnswer = '${RequestConfig.imgUrl}$zgtAnswer?$lastAnswerTime';
|
zgtAnswer = '${RequestConfig.imgUrl}$zgtAnswer?$lastAnswerTime';
|
||||||
|
}
|
||||||
|
|
||||||
if (zgtAnnotate?.isNotEmpty ?? false) {
|
if (zgtAnnotate?.isNotEmpty ?? false) {
|
||||||
showZgtAnnotate = RequestConfig.imgUrl + zgtAnnotate!; // 批注图片地址赋值
|
showZgtAnnotate = RequestConfig.imgUrl + zgtAnnotate!; // 批注图片地址赋值
|
||||||
if (annotateTime != null)
|
if (annotateTime != null) showZgtAnnotate = '${showZgtAnnotate!}?$annotateTime';
|
||||||
showZgtAnnotate = '${showZgtAnnotate!}?$annotateTime';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断当前试题是否需要批阅
|
// 判断当前试题是否需要批阅
|
||||||
if (annotateTime == null ||
|
if (annotateTime == null || studentQuestions.indexWhere((e) => e.studentScore == null) != -1) {
|
||||||
studentQuestions.indexWhere((e) => e.studentScore == null) != -1) {
|
|
||||||
needAnnotate = true;
|
needAnnotate = true;
|
||||||
}
|
}
|
||||||
// print('学生作答图片:${annotatedCount}');
|
// print('学生作答图片:${annotatedCount}');
|
||||||
// print('老师批注图片提交数量:${submitCount}');
|
// print('老师批注图片提交数量:${submitCount}');
|
||||||
}
|
}
|
||||||
|
|
||||||
factory DoPaperDetailsResult.fromJson(Map<String, dynamic> srcJson) =>
|
factory DoPaperDetailsResult.fromJson(Map<String, dynamic> srcJson) => _$DoPaperDetailsResultFromJson(srcJson);
|
||||||
_$DoPaperDetailsResultFromJson(srcJson);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$DoPaperDetailsResultToJson(this);
|
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()
|
@JsonSerializable()
|
||||||
|
|
@ -153,8 +160,7 @@ class PaperStudents extends Object {
|
||||||
this.isPriority,
|
this.isPriority,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory PaperStudents.fromJson(Map<String, dynamic> srcJson) =>
|
factory PaperStudents.fromJson(Map<String, dynamic> srcJson) => _$PaperStudentsFromJson(srcJson);
|
||||||
_$PaperStudentsFromJson(srcJson);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$PaperStudentsToJson(this);
|
Map<String, dynamic> toJson() => _$PaperStudentsToJson(this);
|
||||||
}
|
}
|
||||||
|
|
@ -206,8 +212,7 @@ class StudentQuestions extends Object {
|
||||||
this.useTime,
|
this.useTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory StudentQuestions.fromJson(Map<String, dynamic> srcJson) =>
|
factory StudentQuestions.fromJson(Map<String, dynamic> srcJson) => _$StudentQuestionsFromJson(srcJson);
|
||||||
_$StudentQuestionsFromJson(srcJson);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$StudentQuestionsToJson(this);
|
Map<String, dynamic> toJson() => _$StudentQuestionsToJson(this);
|
||||||
}
|
}
|
||||||
|
|
@ -225,8 +230,7 @@ class LastPage extends Object {
|
||||||
this.studentId,
|
this.studentId,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory LastPage.fromJson(Map<String, dynamic> srcJson) =>
|
factory LastPage.fromJson(Map<String, dynamic> srcJson) => _$LastPageFromJson(srcJson);
|
||||||
_$LastPageFromJson(srcJson);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$LastPageToJson(this);
|
Map<String, dynamic> toJson() => _$LastPageToJson(this);
|
||||||
}
|
}
|
||||||
|
|
@ -244,8 +248,7 @@ class NextPage extends Object {
|
||||||
this.studentId,
|
this.studentId,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory NextPage.fromJson(Map<String, dynamic> srcJson) =>
|
factory NextPage.fromJson(Map<String, dynamic> srcJson) => _$NextPageFromJson(srcJson);
|
||||||
_$NextPageFromJson(srcJson);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$NextPageToJson(this);
|
Map<String, dynamic> toJson() => _$NextPageToJson(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class TestQuestionsImageInfo extends Object {
|
||||||
@JsonKey(name: 'actualImgHeight') // 实际展示图片高度
|
@JsonKey(name: 'actualImgHeight') // 实际展示图片高度
|
||||||
double actualImgHeight;
|
double actualImgHeight;
|
||||||
|
|
||||||
@JsonKey(name: 'remainingHeight') // 视图剩余高度 实际展示图片展示高度下 剩余高度.(若伟负数就是超出视图的高度值)
|
@JsonKey(name: 'remainingHeight') // 视图剩余高度 实际展示图片展示高度下 剩余高度.(若为负数就是超出视图的高度值)
|
||||||
double remainingHeight;
|
double remainingHeight;
|
||||||
|
|
||||||
@JsonKey(name: 'imageHeightOffsetStart') // 顶部坐标Y
|
@JsonKey(name: 'imageHeightOffsetStart') // 顶部坐标Y
|
||||||
|
|
@ -62,8 +62,8 @@ class TestQuestionsImageInfo extends Object {
|
||||||
}) {
|
}) {
|
||||||
// 图片已视图宽为基准,高度自适应可滑动 图片的实际宽高都需要乘此基准值
|
// 图片已视图宽为基准,高度自适应可滑动 图片的实际宽高都需要乘此基准值
|
||||||
scaleRatio = boxWidth / imageWidth;
|
scaleRatio = boxWidth / imageWidth;
|
||||||
actualImgWidth = imageWidth * scaleRatio;
|
actualImgWidth = imageWidth * scaleRatio * zoom;
|
||||||
actualImgHeight = imageHeight * scaleRatio;
|
actualImgHeight = imageHeight * scaleRatio * zoom;
|
||||||
remainingHeight = boxHeight - actualImgHeight;
|
remainingHeight = boxHeight - actualImgHeight;
|
||||||
imageHeightOffsetStart = remainingHeight / 2;
|
imageHeightOffsetStart = remainingHeight / 2;
|
||||||
imageHeightOffsetend = imageHeightOffsetStart + actualImgHeight;
|
imageHeightOffsetend = imageHeightOffsetStart + actualImgHeight;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:get/get.dart' as getx;
|
import 'package:get/get.dart' as getx;
|
||||||
import 'package:dio/dio.dart';
|
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: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/api/retrofit_client.dart';
|
||||||
import 'package:making_school_asignment_app/common/config/request_config.dart';
|
import 'package:making_school_asignment_app/common/config/request_config.dart';
|
||||||
|
|
@ -114,6 +115,10 @@ class AuthInterceptor extends Interceptor {
|
||||||
class ResponseHandle extends Interceptor {
|
class ResponseHandle extends Interceptor {
|
||||||
@override
|
@override
|
||||||
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
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)) {
|
if (RequestConfig.successCode.contains(response.statusCode)) {
|
||||||
//
|
//
|
||||||
String? token = response.headers.value('access-token');
|
String? token = response.headers.value('access-token');
|
||||||
|
|
@ -180,9 +185,11 @@ class TheError extends Interceptor {
|
||||||
message = '用户登录失效,请重新登录';
|
message = '用户登录失效,请重新登录';
|
||||||
|
|
||||||
Future.delayed(const Duration(seconds: 2), () {
|
Future.delayed(const Duration(seconds: 2), () {
|
||||||
|
if (getx.Get.currentRoute != Routes.login) {
|
||||||
UserStore.to.erase();
|
UserStore.to.erase();
|
||||||
StorageService.to.erase();
|
StorageService.to.erase();
|
||||||
getx.Get.offAllNamed(Routes.login);
|
getx.Get.offAllNamed(Routes.login);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 404:
|
case 404:
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,7 @@ import 'package:flutter/material.dart';
|
||||||
VoidCallback debounce(Function func, [Duration delay = const Duration(milliseconds: 2000)]) {
|
VoidCallback debounce(Function func, [Duration delay = const Duration(milliseconds: 2000)]) {
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
target() {
|
target() {
|
||||||
if (timer?.isActive ?? false) {
|
|
||||||
timer?.cancel();
|
timer?.cancel();
|
||||||
}
|
|
||||||
timer = Timer(delay, () {
|
timer = Timer(delay, () {
|
||||||
func.call();
|
func.call();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ import 'package:functional_widget_annotation/functional_widget_annotation.dart';
|
||||||
part 'cached_network_img.g.dart';
|
part 'cached_network_img.g.dart';
|
||||||
|
|
||||||
@hwidget
|
@hwidget
|
||||||
Widget $theCachedNetworkImage(ImageWidgetBuilder imageBuilder, {required String imageUrl}) {
|
Widget $theCachedNetworkImage(ImageWidgetBuilder imageBuilder, {required String imageUrl, double? imgWidth}) {
|
||||||
UseCachedImgRefresh useImgRefsh = UseCachedImgRefresh.use();
|
UseCachedImgRefresh useImgRefsh = UseCachedImgRefresh.use();
|
||||||
|
|
||||||
return CachedNetworkImage(
|
return CachedNetworkImage(
|
||||||
key: useImgRefsh.imageKey.value,
|
key: useImgRefsh.imageKey.value,
|
||||||
cacheKey: imageUrl,
|
cacheKey: imageUrl,
|
||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
width: double.infinity,
|
width: imgWidth ?? double.infinity,
|
||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
imageBuilder: imageBuilder,
|
imageBuilder: imageBuilder,
|
||||||
placeholder: (context, url) => Center(child: SpinKitWave(color: Theme.of(context).primaryColor, size: 50.r)),
|
placeholder: (context, url) => Center(child: SpinKitWave(color: Theme.of(context).primaryColor, size: 50.r)),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/src/widgets/framework.dart';
|
import 'package:flutter/src/widgets/framework.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.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 'package:making_school_asignment_app/page/home_page/children/quick_data_check/quick_data_check_state.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import '../config/request_config.dart';
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
static Utils? _instance;
|
static Utils? _instance;
|
||||||
Utils._internal();
|
Utils._internal();
|
||||||
|
|
@ -177,9 +181,7 @@ class Utils {
|
||||||
stu.noAnswerCount = data.dtls.where((w) => w.state == 0 && stu.studentId == w.studentId).length;
|
stu.noAnswerCount = data.dtls.where((w) => w.state == 0 && stu.studentId == w.studentId).length;
|
||||||
|
|
||||||
List<Questions> ques = data.questions;
|
List<Questions> ques = data.questions;
|
||||||
stu.queDtls = data.dtls
|
stu.queDtls = data.dtls.where((w) => w.studentId == stu.studentId && ques.indexWhere((q) => w.templateId == q.templateId && w.questionNo == q.questionNo) > -1).toList();
|
||||||
.where((w) => w.studentId == stu.studentId && ques.indexWhere((q) => w.templateId == q.templateId && w.questionNo == q.questionNo) > -1)
|
|
||||||
.toList();
|
|
||||||
int okCount = stu.queDtls!.where((w) => w.state == 3).length;
|
int okCount = stu.queDtls!.where((w) => w.state == 3).length;
|
||||||
int ttlCount = stu.queDtls!.length;
|
int ttlCount = stu.queDtls!.length;
|
||||||
stu.okRate = Utils.calcRate(okCount, ttlCount);
|
stu.okRate = Utils.calcRate(okCount, ttlCount);
|
||||||
|
|
@ -287,23 +289,26 @@ class Utils {
|
||||||
}
|
}
|
||||||
|
|
||||||
static DateTime getWeekStartDate() {
|
static DateTime getWeekStartDate() {
|
||||||
|
// DateTime now = DateTime.now();
|
||||||
|
// int dayOfWeek = now.weekday; // 获取今天是周几(1代表周一,7代表周日)
|
||||||
|
// int diff = dayOfWeek - 1; // 计算今天距离周一的天数差
|
||||||
|
// if (diff < 0) {
|
||||||
|
// diff += 7; // 如果是周日,则需要加上一周的天数
|
||||||
|
// }
|
||||||
|
// return now.subtract(Duration(days: diff)); // 减去天数差,得到本周一的时间
|
||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
int dayOfWeek = now.weekday; // 获取今天是周几(1代表周一,7代表周日)
|
return now.subtract(const Duration(days: 6));
|
||||||
int diff = dayOfWeek - 1; // 计算今天距离周一的天数差
|
|
||||||
if (diff < 0) {
|
|
||||||
diff += 7; // 如果是周日,则需要加上一周的天数
|
|
||||||
}
|
|
||||||
return now.subtract(Duration(days: diff)); // 减去天数差,得到本周一的时间
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static DateTime getWeekEndDate() {
|
static DateTime getWeekEndDate() {
|
||||||
DateTime now = DateTime.now();
|
// DateTime now = DateTime.now();
|
||||||
int dayOfWeek = now.weekday; // 获取今天是周几
|
// int dayOfWeek = now.weekday; // 获取今天是周几
|
||||||
int diff = 7 - dayOfWeek; // 计算今天距离周日的天数差
|
// int diff = 7 - dayOfWeek; // 计算今天距离周日的天数差
|
||||||
if (diff == 0) {
|
// if (diff == 0) {
|
||||||
diff = 7; // 如果是周日,则加上一周的天数
|
// diff = 7; // 如果是周日,则加上一周的天数
|
||||||
}
|
// }
|
||||||
return now.add(Duration(days: diff)); // 加上天数差减一,得到本周日的时间
|
// return now.add(Duration(days: diff)); // 加上天数差减一,得到本周日的时间
|
||||||
|
return DateTime.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,3 +320,24 @@ bool isPad([double mobilePhoneScale = 1.2]) {
|
||||||
void toUpState(Function(void Function()) setState, VoidCallback fn, bool mounted) {
|
void toUpState(Function(void Function()) setState, VoidCallback fn, bool mounted) {
|
||||||
if (mounted) setState(fn);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,10 @@ class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// String? oldRouter;
|
||||||
return ScreenUtilInit(
|
return ScreenUtilInit(
|
||||||
designSize: const Size(AppConfig.UI_WIDTH, AppConfig.UI_HEIGHT),
|
designSize: const Size(AppConfig.UI_WIDTH, AppConfig.UI_HEIGHT),
|
||||||
/* minTextAdapt: true,
|
/* minTextAdapt: true,
|
||||||
|
|
@ -87,6 +89,13 @@ class MyApp extends StatelessWidget {
|
||||||
// systemStatusBarContrastEnforced: false,
|
// systemStatusBarContrastEnforced: false,
|
||||||
// ));
|
// ));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// if (Routes.reviewHomework == currentRouter) {
|
||||||
|
// // SystemChrome.setEnabledSystemUIMode(SystemUiMode.leanBack, overlays: []);
|
||||||
|
// } else if (oldRouter == Routes.reviewHomework) {
|
||||||
|
// // SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]); // 屏幕刘海
|
||||||
|
// }
|
||||||
|
// oldRouter = currentRouter;
|
||||||
},
|
},
|
||||||
// 这里是国际化支持,确保添加flutter_localizations依赖
|
// 这里是国际化支持,确保添加flutter_localizations依赖
|
||||||
supportedLocales: const [
|
supportedLocales: const [
|
||||||
|
|
@ -106,7 +115,7 @@ class MyApp extends StatelessWidget {
|
||||||
return locale;
|
return locale;
|
||||||
},
|
},
|
||||||
//默认专场动画
|
//默认专场动画
|
||||||
defaultTransition: Transition.fade,
|
defaultTransition: getTransition(),
|
||||||
//初始化路由页面
|
//初始化路由页面
|
||||||
initialRoute: Routes.startPage,
|
initialRoute: Routes.startPage,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart';
|
||||||
import 'package:making_school_asignment_app/page/global_widget/show_student_list.dart';
|
import 'package:making_school_asignment_app/page/global_widget/show_student_list.dart';
|
||||||
import 'package:percent_indicator/percent_indicator.dart';
|
import 'package:percent_indicator/percent_indicator.dart';
|
||||||
import 'package:making_school_asignment_app/common/job/annotated_class.dart';
|
import 'package:making_school_asignment_app/common/job/annotated_class.dart';
|
||||||
|
|
@ -81,6 +83,7 @@ class _AnnotateItemState extends State<AnnotateItem> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showStudentList(context, List<AnnotatedStudents> students, [bool submitted = false]) async {
|
void showStudentList(context, List<AnnotatedStudents> students, [bool submitted = false]) async {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -95,6 +98,7 @@ class _AnnotateItemState extends State<AnnotateItem> {
|
||||||
);
|
);
|
||||||
EasyLoading.dismiss();
|
EasyLoading.dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
AnnotatedClass itemData = widget.item;
|
AnnotatedClass itemData = widget.item;
|
||||||
|
|
@ -158,12 +162,7 @@ class _AnnotateItemState extends State<AnnotateItem> {
|
||||||
title: "收藏夹${widget.item.homeworkFavs.isNotEmpty ? '(${widget.item.homeworkFavs.length})' : ''}",
|
title: "收藏夹${widget.item.homeworkFavs.isNotEmpty ? '(${widget.item.homeworkFavs.length})' : ''}",
|
||||||
font: widget.font - 2.sp,
|
font: widget.font - 2.sp,
|
||||||
clickFunction: () {
|
clickFunction: () {
|
||||||
Get.toNamed(Routes.favStudentPage, arguments: {
|
Get.toNamed(Routes.favStudentPage, arguments: {'homeworkName': widget.name, 'classId': widget.item.classId, 'homeworkId': widget.logic.state.homeworkId.value, 'grade': widget.item.grade});
|
||||||
'homeworkName': widget.name,
|
|
||||||
'classId': widget.item.classId,
|
|
||||||
'homeworkId': widget.logic.state.homeworkId.value,
|
|
||||||
'grade': widget.item.grade
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -211,12 +210,7 @@ class _AnnotateItemState extends State<AnnotateItem> {
|
||||||
title: "收藏夹${widget.item.homeworkFavs.isNotEmpty ? '(${widget.item.homeworkFavs.length})' : ''}",
|
title: "收藏夹${widget.item.homeworkFavs.isNotEmpty ? '(${widget.item.homeworkFavs.length})' : ''}",
|
||||||
font: widget.font - 2.sp,
|
font: widget.font - 2.sp,
|
||||||
clickFunction: () {
|
clickFunction: () {
|
||||||
Get.toNamed(Routes.favStudentPage, arguments: {
|
Get.toNamed(Routes.favStudentPage, arguments: {'homeworkName': widget.name, 'classId': widget.item.classId, 'homeworkId': widget.logic.state.homeworkId.value, 'grade': widget.item.grade});
|
||||||
'homeworkName': widget.name,
|
|
||||||
'classId': widget.item.classId,
|
|
||||||
'homeworkId': widget.logic.state.homeworkId.value,
|
|
||||||
'grade': widget.item.grade
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -227,11 +221,9 @@ class _AnnotateItemState extends State<AnnotateItem> {
|
||||||
padding: EdgeInsets.only(top: 10.r, left: 14.r, right: 14.r),
|
padding: EdgeInsets.only(top: 10.r, left: 14.r, right: 14.r),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [quickText('批阅进度', size: widget.font - 2.sp, color: const Color(0xFF8E8E8E)), quickText('${widget.item.annotateRate}%', size: widget.font + 10.sp, color: const Color(0xFF464646))],
|
||||||
quickText('批阅进度', size: widget.font - 2.sp, color: const Color(0xFF8E8E8E)),
|
),
|
||||||
quickText('${widget.item.annotateRate}%', size: widget.font + 10.sp, color: const Color(0xFF464646))
|
),
|
||||||
],
|
|
||||||
),),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 0.r, left: 14.r, right: 14.r, bottom: 10.r),
|
padding: EdgeInsets.only(top: 0.r, left: 14.r, right: 14.r, bottom: 10.r),
|
||||||
child: LinearPercentIndicator(
|
child: LinearPercentIndicator(
|
||||||
|
|
@ -257,27 +249,9 @@ class _AnnotateItemState extends State<AnnotateItem> {
|
||||||
barRadius: Radius.circular(10.r),
|
barRadius: Radius.circular(10.r),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ProgressBar(
|
ProgressBar(title: '客观题正确率:', color: const Color(0xFFADDCA5), percent: widget.item.kgtCorrectRate / 100, marginEdg: EdgeInsets.zero, padingEdg: EdgeInsets.only(top: 8.h, left: 14.r, right: 14.r), fontSize: widget.font - 2.sp),
|
||||||
title: '客观题正确率:',
|
ProgressBar(title: '主观题正确率:', color: const Color(0xFFADDCA5), percent: widget.item.zgtCorrectRate / 100, padingEdg: EdgeInsets.symmetric(horizontal: 10.r), marginEdg: EdgeInsets.only(top: 8.h), fontSize: widget.font - 2.sp),
|
||||||
color: const Color(0xFFADDCA5),
|
ProgressBar(title: '总正确率:', color: const Color(0xFFADDCA5), percent: widget.item.correctRate / 100, padingEdg: EdgeInsets.symmetric(horizontal: 10.r), marginEdg: EdgeInsets.only(top: 8.h), fontSize: widget.font - 2.sp),
|
||||||
percent: widget.item.kgtCorrectRate / 100,
|
|
||||||
marginEdg: EdgeInsets.zero,
|
|
||||||
padingEdg: EdgeInsets.only(top: 8.h, left: 14.r, right: 14.r),
|
|
||||||
fontSize: widget.font - 2.sp),
|
|
||||||
ProgressBar(
|
|
||||||
title: '主观题正确率:',
|
|
||||||
color: const Color(0xFFADDCA5),
|
|
||||||
percent: widget.item.zgtCorrectRate / 100,
|
|
||||||
padingEdg: EdgeInsets.symmetric(horizontal: 10.r),
|
|
||||||
marginEdg: EdgeInsets.only(top: 8.h),
|
|
||||||
fontSize: widget.font - 2.sp),
|
|
||||||
ProgressBar(
|
|
||||||
title: '总正确率:',
|
|
||||||
color: const Color(0xFFADDCA5),
|
|
||||||
percent: widget.item.correctRate / 100,
|
|
||||||
padingEdg: EdgeInsets.symmetric(horizontal: 10.r),
|
|
||||||
marginEdg: EdgeInsets.only(top: 8.h),
|
|
||||||
fontSize: widget.font - 2.sp),
|
|
||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.only(top: 10.r),
|
margin: EdgeInsets.only(top: 10.r),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
@ -303,9 +277,7 @@ class _AnnotateItemState extends State<AnnotateItem> {
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.r),
|
padding: EdgeInsets.symmetric(vertical: 10.r),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(border: Border(right: BorderSide(width: 1.r, color: const Color.fromRGBO(221, 221, 221, 1)))),
|
||||||
border: Border(right: BorderSide(width: 1.r,color:const Color.fromRGBO(221, 221, 221, 1) ))
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: quickText('数据快查', color: const Color.fromRGBO(118, 118, 118, 1), size: widget.font),
|
child: quickText('数据快查', color: const Color.fromRGBO(118, 118, 118, 1), size: widget.font),
|
||||||
),
|
),
|
||||||
|
|
@ -326,20 +298,27 @@ class _AnnotateItemState extends State<AnnotateItem> {
|
||||||
: [
|
: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () => easyThrottle('TO_GO_REVIEWHOMEWORK', () {
|
||||||
print('批阅..........');
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.leanBack, overlays: []).then((_) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
Get.toNamed(Routes.reviewHomework, arguments: {
|
Get.toNamed(Routes.reviewHomework, arguments: {
|
||||||
'homeworkId': widget.homeworkId,
|
'homeworkId': widget.homeworkId,
|
||||||
'homeworkName': widget.name,
|
'homeworkName': widget.name,
|
||||||
'classId': itemData.classId,
|
'classId': itemData.classId,
|
||||||
'subject': widget.logic.state.subject,
|
'subject': widget.logic.state.subject,
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
|
});
|
||||||
|
// Get.toNamed(Routes.reviewHomework, arguments: {
|
||||||
|
// 'homeworkId': widget.homeworkId,
|
||||||
|
// 'homeworkName': widget.name,
|
||||||
|
// 'classId': itemData.classId,
|
||||||
|
// 'subject': widget.logic.state.subject,
|
||||||
|
// });
|
||||||
|
}),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.r),
|
padding: EdgeInsets.symmetric(vertical: 10.r),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(border: Border(right: BorderSide(width: 1.r, color: const Color.fromRGBO(221, 221, 221, 1)))),
|
||||||
border: Border(right: BorderSide(width: 1.r,color:const Color.fromRGBO(221, 221, 221, 1) ))
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: quickText('批阅', color: const Color.fromRGBO(118, 118, 118, 1), size: widget.font),
|
child: quickText('批阅', color: const Color.fromRGBO(118, 118, 118, 1), size: widget.font),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.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: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_param.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.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/toast_utils.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/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/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/page/home_page/children/homework_review/configuration_files/index.dart';
|
||||||
import 'package:making_school_asignment_app/routes/app_pages.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';
|
import 'original_manuscript_handwriting/answer_handwriting_view.dart';
|
||||||
|
|
||||||
part 'dropdown_switch_students_type.g.dart';
|
part 'dropdown_switch_students_type.g.dart';
|
||||||
|
|
||||||
/// 学生和页码切换
|
/// 学生和页码切换
|
||||||
class DropdownSwitchStudentsType extends StatefulWidget {
|
///
|
||||||
|
|
||||||
|
class DropdownSwitchStudentsType extends StatelessWidget {
|
||||||
const DropdownSwitchStudentsType({super.key});
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final logic = Get.find<HomeworkReviewLogic>();
|
||||||
|
final sateData = logic.state.data;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
|
height: 30.h,
|
||||||
padding: EdgeInsets.only(bottom: 2.r, left: 12.r, right: 12.r),
|
padding: EdgeInsets.only(bottom: 2.r, left: 12.r, right: 12.r),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
|
@ -290,7 +269,8 @@ Widget $historyHomework(BuildContext context) {
|
||||||
if (studentId == null || (sateData.value?.students.isEmpty ?? true)) return;
|
if (studentId == null || (sateData.value?.students.isEmpty ?? true)) return;
|
||||||
var currentStudent = sateData.value!.students.firstWhereOrNull((e) => e.id == studentId);
|
var currentStudent = sateData.value!.students.firstWhereOrNull((e) => e.id == studentId);
|
||||||
if (currentStudent == null) return;
|
if (currentStudent == null) return;
|
||||||
Get.toNamed(Routes.studentWorkDetailPage, arguments: {'studentId': studentId, 'studentName': currentStudent.name});
|
var theState = Get.find<HomeworkReviewLogic>().state;
|
||||||
|
Get.toNamed(Routes.studentWorkDetailPage, arguments: {'studentId': studentId, 'studentName': currentStudent.name,'subject':theState.param.value.subject});
|
||||||
}),
|
}),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,25 @@
|
||||||
import 'dart:async';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.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/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';
|
import 'package:making_school_asignment_app/page/home_page/children/homework_review/configuration_files/index.dart';
|
||||||
|
|
||||||
/// 自定义收藏组件
|
/// 自定义收藏组件
|
||||||
/// isFavorite 默认是否收藏
|
/// isFavorite 默认是否收藏
|
||||||
/// favoriteNum 默认收藏次数
|
/// favoriteNum 默认收藏次数
|
||||||
class FavoriteWidget extends StatefulWidget {
|
|
||||||
|
class FavoriteWidget extends StatelessWidget {
|
||||||
const FavoriteWidget({super.key});
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final logic = Get.find<HomeworkReviewLogic>();
|
||||||
|
final sateData = logic.state;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 4.r),
|
padding: EdgeInsets.symmetric(horizontal: 4.r),
|
||||||
child: InkWell(
|
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,
|
splashColor: Colors.transparent,
|
||||||
highlightColor: Colors.transparent,
|
highlightColor: Colors.transparent,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
@ -73,7 +29,7 @@ class _FavoriteState extends State<FavoriteWidget> with RequestToolMixin {
|
||||||
return Icon(
|
return Icon(
|
||||||
const IconData(0xe63c, fontFamily: "AlibabaIcon"),
|
const IconData(0xe63c, fontFamily: "AlibabaIcon"),
|
||||||
size: 18.sp,
|
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),
|
SizedBox(width: 6.w),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,270 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:functional_widget_annotation/functional_widget_annotation.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/utils/anti_shake_throttling.dart';
|
||||||
|
import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
||||||
|
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||||
|
|
||||||
|
import '../configuration_files/index.dart';
|
||||||
|
import '../configuration_files/zoom_logic.dart';
|
||||||
|
|
||||||
|
part 'question_number_view.g.dart';
|
||||||
|
|
||||||
|
/// 试题题号
|
||||||
|
class QuestionNumberView extends GetView<HomeworkReviewLogic> {
|
||||||
|
const QuestionNumberView({super.key});
|
||||||
|
|
||||||
|
HomeworkReviewState get sateData => controller.state;
|
||||||
|
ZoomState get sateZoomData => controller.zoomLogic.zoomState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
decoration: const BoxDecoration(color: Color.fromRGBO(159, 159, 159, 0.97)),
|
||||||
|
child: GestureDetector(
|
||||||
|
onPanDown: (_) => sateData.panQuestView = false,
|
||||||
|
child: Obx(() {
|
||||||
|
var zoomFile = sateZoomData.zoomFile.value;
|
||||||
|
if (zoomFile == null) return const SizedBox();
|
||||||
|
|
||||||
|
var studentQuestions = sateData.studentQuestions.value ?? [];
|
||||||
|
|
||||||
|
return $QuestionNumberScrollView(
|
||||||
|
controller:controller,
|
||||||
|
sateData: sateData,
|
||||||
|
sateZoomData: sateZoomData,
|
||||||
|
studentQuestions: studentQuestions,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 试题题号视图
|
||||||
|
@hwidget
|
||||||
|
Widget $questionNumberScrollView({
|
||||||
|
required HomeworkReviewLogic controller,
|
||||||
|
required ZoomState sateZoomData,
|
||||||
|
required HomeworkReviewState sateData,
|
||||||
|
required List<StudentQuestions> studentQuestions,
|
||||||
|
}) {
|
||||||
|
final scrollControllerNum = useScrollController(); // 试题题号区域
|
||||||
|
var useZoom = useState<double>(sateZoomData.initScale.value ?? 1);
|
||||||
|
var usePiddingTop = useState<double>(sateZoomData.zoomFile.value?.imageHeightOffsetStart ?? 0);
|
||||||
|
useValueChanged<double?, void>(sateZoomData.zoomFile.value?.imageHeightOffsetStart, (_, __) {
|
||||||
|
usePiddingTop.value = sateZoomData.zoomFile.value?.imageHeightOffsetStart ?? 0;
|
||||||
|
});
|
||||||
|
useEffect(() {
|
||||||
|
var stream = sateZoomData.initScale.listen((e) {
|
||||||
|
// print("initScale : $e");
|
||||||
|
useZoom.value = e ?? 1;
|
||||||
|
var zoomFile = sateZoomData.zoomFile.value;
|
||||||
|
if (zoomFile?.actualHeight == null) return;
|
||||||
|
|
||||||
|
// print(zoomFile?.toJson());
|
||||||
|
// print("缩放基数:${useZoom.value}");
|
||||||
|
// print("图片实际高度:${zoomFile!.actualHeight! * useZoom.value}");
|
||||||
|
// print("视图剩余高度:${zoomFile.viewHeight - (zoomFile.actualHeight! * useZoom.value)}");
|
||||||
|
// var topDistance = (zoomFile!.viewHeight - (zoomFile.actualHeight! * useZoom.value)) / 2;
|
||||||
|
var topDistance = zoomFile!.getZoomFileOffsetStart(useZoom.value);
|
||||||
|
// print("视图剩余高度/2:${max(0, topDistance)}");
|
||||||
|
// 使用 max 函数确保 topDistance 不小于 0
|
||||||
|
usePiddingTop.value = max(0, topDistance);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () {
|
||||||
|
stream.cancel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
var actualImgHeight = useState(sateZoomData.zoomFile.value?.actualHeight);
|
||||||
|
useValueChanged<double?, void>(sateZoomData.zoomFile.value?.actualHeight, (_, __) {
|
||||||
|
actualImgHeight.value = sateZoomData.zoomFile.value?.actualHeight ?? 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
scrollControllerNum.addListener(() {
|
||||||
|
if (sateData.panQuestView == false) sateData.slide.value = scrollControllerNum.offset;
|
||||||
|
});
|
||||||
|
var listenVal = sateData.slide.listen((e) {
|
||||||
|
if (sateData.panQuestView != null && sateData.panQuestView == true && e != scrollControllerNum.offset) {
|
||||||
|
print("进来了试题题号视图");
|
||||||
|
scrollControllerNum.jumpTo(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () {
|
||||||
|
listenVal.cancel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// var actualImgHeight = useImageInfo.value?.actualImgHeight ?? 0; // 实际图片高度
|
||||||
|
|
||||||
|
// print("图片高度:$actualImgHeight");
|
||||||
|
|
||||||
|
print("数据长度:${studentQuestions.length}");
|
||||||
|
print("FFFFFFFFF ${usePiddingTop.value}");
|
||||||
|
|
||||||
|
return SingleChildScrollView(
|
||||||
|
controller: scrollControllerNum,
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
padding: EdgeInsets.only(top: usePiddingTop.value > 0 ? usePiddingTop.value : 0),
|
||||||
|
scrollDirection: Axis.vertical, // 设置垂直滚动
|
||||||
|
child: SizedBox(
|
||||||
|
height: (actualImgHeight.value ?? 0) * useZoom.value,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: studentQuestions
|
||||||
|
.map((e) => $ScoringQuestionsView(
|
||||||
|
studentQuestions,
|
||||||
|
e,
|
||||||
|
controller,
|
||||||
|
sateZoomData.zoomFile.value!.scaleRatio,
|
||||||
|
useZoom.value,
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单道题得分框
|
||||||
|
@hwidget
|
||||||
|
Widget $scoringQuestionsView(BuildContext context, List<StudentQuestions>? studentQuestions,StudentQuestions item, HomeworkReviewLogic logic, double scaleRatio, double initScale) {
|
||||||
|
var studentScore = useState<int?>(item.studentScore);
|
||||||
|
|
||||||
|
useValueChanged<int?, void>(studentScore.value, (_, __) {
|
||||||
|
item.studentScore = studentScore.value;
|
||||||
|
|
||||||
|
// 校验是否自动提交 对于已经批阅过的试题 不重复自动提交
|
||||||
|
var annotateTime = logic.state.data.value?.annotateTime;
|
||||||
|
if (annotateTime == null) {
|
||||||
|
var noRatingGiven = studentQuestions!.firstWhereOrNull((e) => e.studentScore == null);
|
||||||
|
if (noRatingGiven == null) logic.submit(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useValueChanged<StudentQuestions, void>(item, (_, __) {
|
||||||
|
studentScore.value = item.studentScore;
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
return () {};
|
||||||
|
}, []);
|
||||||
|
var padinVal = item.correctRate > 0 ? EdgeInsets.only(top: 6.4.h) : EdgeInsets.symmetric(vertical: 2.h);
|
||||||
|
return Container(
|
||||||
|
height: item.height! * scaleRatio * initScale,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: item.useTime == 0
|
||||||
|
? Container()
|
||||||
|
: 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 ? Colors.green : const Color.fromRGBO(114, 114, 114, 1),
|
||||||
|
const IconData(0xe62b, fontFamily: "AlibabaIcon"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
||||||
|
studentScore.value = studentScore.value == 2 ? null : 2;
|
||||||
|
}, duration: const Duration(milliseconds: 222)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 半
|
||||||
|
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 ? Colors.green : const Color.fromRGBO(114, 114, 114, 1),
|
||||||
|
const IconData(0xe62c, fontFamily: "AlibabaIcon"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
||||||
|
studentScore.value = studentScore.value == 1 ? null : 1;
|
||||||
|
}, duration: const Duration(milliseconds: 222)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 错
|
||||||
|
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 ? Colors.green : const Color.fromRGBO(114, 114, 114, 1),
|
||||||
|
const IconData(0xe62a, fontFamily: "AlibabaIcon"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
||||||
|
studentScore.value = studentScore.value == 0 ? null : 0;
|
||||||
|
}, duration: const Duration(milliseconds: 222)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IgnorePointer(
|
||||||
|
// 事件穿透
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 1.1.w),
|
||||||
|
quickText(item.questionNo, color: Theme.of(context).primaryColor.withOpacity(0.7), size: 8.sp),
|
||||||
|
if (item.correctRate > 0) quickText(' 正确率', color: Colors.grey, size: 5.sp),
|
||||||
|
if (item.correctRate > 0)
|
||||||
|
Expanded(
|
||||||
|
child: LinearPercentIndicator(
|
||||||
|
lineHeight: 6.h,
|
||||||
|
percent: item.correctRate / 100,
|
||||||
|
barRadius: Radius.circular(1.2.r),
|
||||||
|
alignment: MainAxisAlignment.center,
|
||||||
|
progressColor: Theme.of(context).primaryColor,
|
||||||
|
backgroundColor: const Color(0xFFB8C7CB).withOpacity(0.35),
|
||||||
|
center: quickText("${item.correctRate}%", size: 5.sp, align: TextAlign.center, color: Colors.white),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -13,24 +13,20 @@ import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.d
|
||||||
import 'package:making_school_asignment_app/common/utils/cached_network_img.dart';
|
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/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/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';
|
||||||
|
import 'question_number_view.dart';
|
||||||
|
|
||||||
part 'question_paper_view.g.dart';
|
part 'question_paper_view.g.dart';
|
||||||
|
|
||||||
// 试题详情
|
// 试题详情视图
|
||||||
class QuestionPaperView extends StatefulWidget {
|
class QuestionPaperView extends GetView<HomeworkReviewLogic> {
|
||||||
const QuestionPaperView({super.key});
|
const QuestionPaperView({super.key});
|
||||||
|
|
||||||
@override
|
HomeworkReviewState get sateData => controller.state;
|
||||||
State<QuestionPaperView> createState() => _QuestionPaperViewState();
|
ZoomState get zoomState => controller.zoomLogic.zoomState;
|
||||||
}
|
HomeworkReviewAnnotationsControlState get annotationState => controller.annotationState;
|
||||||
|
|
||||||
class _QuestionPaperViewState extends State<QuestionPaperView> {
|
|
||||||
final logic = Get.find<HomeworkReviewLogic>();
|
|
||||||
final sateData = Get.find<HomeworkReviewLogic>().state;
|
|
||||||
final annotationState = Get.find<HomeworkReviewLogic>().annotationState;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -41,15 +37,29 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
||||||
// 试题图片视图
|
// 试题图片视图
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 7,
|
flex: 7,
|
||||||
child: LayoutBuilder(
|
child: Obx(() {
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
ZoomFileModel? zoomFileModel = zoomState.zoomFile.value;
|
||||||
var maxWidth = constraints.maxWidth;
|
|
||||||
var maxHeight = constraints.maxHeight;
|
if (zoomFileModel == null) {
|
||||||
|
/// 计算高度
|
||||||
|
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => zoomState.zoomFile.value = ZoomFileModel(
|
||||||
|
viewWidth: constraints.maxWidth,
|
||||||
|
viewHeight: constraints.maxHeight,
|
||||||
|
));
|
||||||
|
return const SizedBox();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 文件未计算实际高度
|
||||||
|
if (zoomFileModel.fileHeight == null) return const SizedBox();
|
||||||
|
|
||||||
|
var maxWidth = zoomFileModel.viewWidth;
|
||||||
|
var maxHeight = zoomFileModel.viewHeight;
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
// 主图
|
// 主图
|
||||||
QuestionImageView(
|
QuestionImageView(maxWidth, maxHeight, annotationState, controller, zoomState: zoomState, sateData: sateData, actualHeight: zoomFileModel.actualHeight!),
|
||||||
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)),
|
||||||
// 上一题按钮
|
// 上一题按钮
|
||||||
|
|
@ -63,19 +73,15 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
||||||
heroTag: '点击前往上一题',
|
heroTag: '点击前往上一题',
|
||||||
tooltip: '点击前往上一题',
|
tooltip: '点击前往上一题',
|
||||||
focusColor: Theme.of(context).primaryColor,
|
focusColor: Theme.of(context).primaryColor,
|
||||||
backgroundColor:
|
backgroundColor: const Color.fromRGBO(24, 32, 32, 0.05),
|
||||||
const Color.fromRGBO(24, 32, 32, 0.05),
|
|
||||||
elevation: 10.r,
|
elevation: 10.r,
|
||||||
onPressed: () =>
|
onPressed: () => easyThrottle('TestQuestionSwitch', () {
|
||||||
easyThrottle('TestQuestionSwitch', () {
|
|
||||||
var param = sateData.param.value;
|
var param = sateData.param.value;
|
||||||
param.studentId = lastPageVal.studentId;
|
param.studentId = lastPageVal.studentId;
|
||||||
param.templateId = lastPageVal.templateId;
|
param.templateId = lastPageVal.templateId;
|
||||||
sateData.param.value =
|
sateData.param.value = DoPaperDetailsParam.fromJson(param.toJson());
|
||||||
DoPaperDetailsParam.fromJson(param.toJson());
|
|
||||||
}),
|
}),
|
||||||
child: Icon(Icons.arrow_back_ios,
|
child: Icon(Icons.arrow_back_ios, color: Colors.white, size: 22.sp),
|
||||||
color: Colors.white, size: 22.sp),
|
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -86,22 +92,19 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
NextPage? nextPageVal = sateData.data.value?.nextPage;
|
NextPage? nextPageVal = sateData.data.value?.nextPage;
|
||||||
if (nextPageVal == null) return const SizedBox();
|
if (nextPageVal == null) return const SizedBox();
|
||||||
|
|
||||||
return FloatingActionButton(
|
return FloatingActionButton(
|
||||||
heroTag: '点击前往下一题',
|
heroTag: '点击前往下一题',
|
||||||
tooltip: '点击前往下一题',
|
tooltip: '点击前往下一题',
|
||||||
elevation: 10.r,
|
elevation: 10.r,
|
||||||
backgroundColor:
|
backgroundColor: const Color.fromRGBO(24, 32, 32, 0.05),
|
||||||
const Color.fromRGBO(24, 32, 32, 0.05),
|
onPressed: () => easyThrottle('TestQuestionSwitch', () {
|
||||||
onPressed: () =>
|
|
||||||
easyThrottle('TestQuestionSwitch', () {
|
|
||||||
var param = sateData.param.value;
|
var param = sateData.param.value;
|
||||||
param.studentId = nextPageVal.studentId;
|
param.studentId = nextPageVal.studentId;
|
||||||
param.templateId = nextPageVal.templateId;
|
param.templateId = nextPageVal.templateId;
|
||||||
sateData.param.value =
|
sateData.param.value = DoPaperDetailsParam.fromJson(param.toJson());
|
||||||
DoPaperDetailsParam.fromJson(param.toJson());
|
|
||||||
}),
|
}),
|
||||||
child: Icon(Icons.arrow_forward_ios,
|
child: Icon(Icons.arrow_forward_ios, color: Colors.white, size: 22.sp),
|
||||||
color: Colors.white, size: 22.sp),
|
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -112,32 +115,44 @@ class _QuestionPaperViewState extends State<QuestionPaperView> {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
// 试题题号视图
|
// 试题题号视图
|
||||||
Expanded(flex: 2, child: $QuestionNumberView(logic, sateData)),
|
const Expanded(flex: 2, child: QuestionNumberView()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Obx(() {
|
|
||||||
|
/// 数据出错再次请求按钮
|
||||||
|
const DataErrorThenRequestAgainButton(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 数据出错再次请求按钮
|
||||||
|
class DataErrorThenRequestAgainButton extends StatelessWidget {
|
||||||
|
const DataErrorThenRequestAgainButton({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GetBuilder<HomeworkReviewLogic>(builder: (logic) {
|
||||||
|
final sateData = logic.state;
|
||||||
|
|
||||||
if (!sateData.getDataError.value) return const SizedBox();
|
if (!sateData.getDataError.value) return const SizedBox();
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: CupertinoButton(
|
child: CupertinoButton(
|
||||||
color: Colors.grey[300],
|
color: Colors.grey[300],
|
||||||
onPressed: () => easyThrottle('home_work_reload_data', () {
|
onPressed: () => easyThrottle('home_work_reload_data', () {
|
||||||
sateData.param.value =
|
sateData.param.value = DoPaperDetailsParam.fromJson(sateData.param.value.toJson());
|
||||||
DoPaperDetailsParam.fromJson(sateData.param.value.toJson());
|
|
||||||
}),
|
}),
|
||||||
child: quickText('重新请求', color: Colors.black38),
|
child: quickText('再次请求', color: Colors.black38),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
});
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 底部已阅数量和待阅数量
|
// 底部已阅数量和待阅数量
|
||||||
@swidget
|
@swidget
|
||||||
Widget $totalSubmitCountView(
|
Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData) {
|
||||||
BuildContext context, HomeworkReviewState sateData) {
|
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
var data = sateData.data.value;
|
var data = sateData.data.value;
|
||||||
if (data == null) return Container();
|
if (data == null) return Container();
|
||||||
|
|
@ -152,10 +167,7 @@ Widget $totalSubmitCountView(
|
||||||
context: context,
|
context: context,
|
||||||
elevation: 10,
|
elevation: 10,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(10.r), topRight: Radius.circular(10.r))),
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(10.r),
|
|
||||||
topRight: Radius.circular(10.r))),
|
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 2.w),
|
padding: EdgeInsets.symmetric(horizontal: 2.w),
|
||||||
|
|
@ -173,8 +185,7 @@ Widget $totalSubmitCountView(
|
||||||
SizedBox(height: 10.h),
|
SizedBox(height: 10.h),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w),
|
||||||
vertical: 8.h, horizontal: 4.w),
|
|
||||||
children: [
|
children: [
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 7.2.w, // 主轴(水平)方向间距
|
spacing: 7.2.w, // 主轴(水平)方向间距
|
||||||
|
|
@ -185,39 +196,27 @@ Widget $totalSubmitCountView(
|
||||||
alignment: const FractionalOffset(0.05, 0.09),
|
alignment: const FractionalOffset(0.05, 0.09),
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(top: 1.2.h, bottom: 1.5.h, left: 13.w, right: 5.w),
|
||||||
top: 1.2.h,
|
|
||||||
bottom: 1.5.h,
|
|
||||||
left: 13.w,
|
|
||||||
right: 5.w),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(4.r),
|
borderRadius: BorderRadius.circular(4.r),
|
||||||
color: const Color.fromRGBO(
|
color: const Color.fromRGBO(239, 242, 255, 1),
|
||||||
239, 242, 255, 1),
|
|
||||||
),
|
),
|
||||||
child: quickText(
|
child: quickText(
|
||||||
e.name,
|
e.name,
|
||||||
size: 12.sp,
|
size: 12.sp,
|
||||||
wordSpacing: 2,
|
wordSpacing: 2,
|
||||||
color:
|
color: const Color.fromRGBO(80, 94, 110, 1),
|
||||||
const Color.fromRGBO(80, 94, 110, 1),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Stack(
|
Stack(
|
||||||
alignment:
|
alignment: const FractionalOffset(0.52, 0.24),
|
||||||
const FractionalOffset(0.52, 0.24),
|
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
const IconData(0xe63d,
|
const IconData(0xe63d, fontFamily: "AlibabaIcon"),
|
||||||
fontFamily: "AlibabaIcon"),
|
|
||||||
size: 12.sp,
|
size: 12.sp,
|
||||||
color: e.isPriority
|
color: e.isPriority ? Theme.of(context).primaryColor : const Color.fromRGBO(164, 164, 164, 1),
|
||||||
? Theme.of(context).primaryColor
|
|
||||||
: const Color.fromRGBO(
|
|
||||||
164, 164, 164, 1),
|
|
||||||
),
|
),
|
||||||
quickText('优先',
|
quickText('优先', size: 4.sp, color: Colors.white),
|
||||||
size: 4.sp, color: Colors.white),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -239,17 +238,11 @@ Widget $totalSubmitCountView(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(bottom: 1.h),
|
padding: EdgeInsets.only(bottom: 1.h),
|
||||||
child: quickText('已阅',
|
child: quickText('已阅', color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
|
||||||
color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
|
|
||||||
),
|
),
|
||||||
quickText(data.annotatedCount,
|
quickText(data.annotatedCount, color: Theme.of(context).primaryColor, size: 12.sp, fontWeight: FontWeight.bold),
|
||||||
color: Theme.of(context).primaryColor,
|
quickText('/', color: const Color.fromRGBO(117, 117, 117, 1), size: 12.sp),
|
||||||
size: 12.sp,
|
quickText(data.submitCount - data.annotatedCount, color: const Color.fromRGBO(117, 117, 117, 1), size: 10.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),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -257,245 +250,16 @@ Widget $totalSubmitCountView(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 试题题号视图
|
|
||||||
@hwidget
|
|
||||||
Widget $questionNumberView(BuildContext context, HomeworkReviewLogic logic,
|
|
||||||
HomeworkReviewState sateData) {
|
|
||||||
final scrollControllerNum = useScrollController(); // 试题题号区域
|
|
||||||
|
|
||||||
useEffect(() {
|
|
||||||
scrollControllerNum.addListener(() {
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () {
|
|
||||||
listenVal.cancel();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
height: double.infinity,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color.fromRGBO(159, 159, 159, 0.97),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: const Color.fromRGBO(46, 91, 255, 0.2),
|
|
||||||
offset: Offset(0, 8.w), //阴影y轴偏移量
|
|
||||||
blurRadius: 1, //阴影模糊程度
|
|
||||||
spreadRadius: 2, //阴影扩散程度
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: GestureDetector(
|
|
||||||
onPanDown: (_) => sateData.panQuestView = false,
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
controller: scrollControllerNum,
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
scrollDirection: Axis.vertical, // 设置垂直滚动
|
|
||||||
child: Obx(() {
|
|
||||||
var imageVal = sateData.imageScale.value;
|
|
||||||
if (imageVal == null) return const SizedBox();
|
|
||||||
var studentQuestions = sateData.studentQuestions.value;
|
|
||||||
var boxHeight = imageVal.boxHeight;
|
|
||||||
var actualImgHeight = imageVal.actualImgHeight; // 实际图片高度
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
height: boxHeight > actualImgHeight ? boxHeight : actualImgHeight,
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: imageVal.remainingHeight > 0
|
|
||||||
? imageVal.remainingHeight / 2
|
|
||||||
: 0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: studentQuestions
|
|
||||||
?.asMap()
|
|
||||||
.keys
|
|
||||||
.map((e) => $ScoringQuestionsView(
|
|
||||||
logic,
|
|
||||||
studentQuestions[e],
|
|
||||||
imageVal.scaleRatio,
|
|
||||||
studentQuestions))
|
|
||||||
.toList() ??
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 单道题得分框
|
|
||||||
@hwidget
|
|
||||||
Widget $scoringQuestionsView(
|
|
||||||
BuildContext context,
|
|
||||||
HomeworkReviewLogic logic,
|
|
||||||
StudentQuestions item,
|
|
||||||
double scaleRatio,
|
|
||||||
List<StudentQuestions>? studentQuestions) {
|
|
||||||
var studentScore = useState<int?>(item.studentScore);
|
|
||||||
|
|
||||||
useValueChanged<int?, void>(studentScore.value, (_, __) {
|
|
||||||
item.studentScore = studentScore.value;
|
|
||||||
|
|
||||||
// 校验是否自动提交 对于已经批阅过的试题 不重复自动提交
|
|
||||||
var annotateTime = logic.state.data.value?.annotateTime;
|
|
||||||
if (annotateTime == null) {
|
|
||||||
var noRatingGiven =
|
|
||||||
studentQuestions!.firstWhereOrNull((e) => e.studentScore == null);
|
|
||||||
if (noRatingGiven == null) logic.submit(context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
useValueChanged<StudentQuestions, void>(item, (_, __) {
|
|
||||||
studentScore.value = item.studentScore;
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() {
|
|
||||||
return () {};
|
|
||||||
}, []);
|
|
||||||
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: item.useTime == 0?Container():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(0xFF54971F)
|
|
||||||
: const Color.fromRGBO(114, 114, 114, 1),
|
|
||||||
const IconData(0xe62b, fontFamily: "AlibabaIcon"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
|
||||||
studentScore.value = studentScore.value == 2 ? null : 2;
|
|
||||||
}, duration: const Duration(milliseconds: 222)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// 半
|
|
||||||
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(0xFF54971F)
|
|
||||||
: const Color.fromRGBO(114, 114, 114, 1),
|
|
||||||
const IconData(0xe62c, fontFamily: "AlibabaIcon"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
|
||||||
studentScore.value = studentScore.value == 1 ? null : 1;
|
|
||||||
}, duration: const Duration(milliseconds: 222)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// 错
|
|
||||||
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(0xFF54971F)
|
|
||||||
: const Color.fromRGBO(114, 114, 114, 1),
|
|
||||||
const IconData(0xe62a, fontFamily: "AlibabaIcon"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () => easyThrottle('scoring_homework_questions', () {
|
|
||||||
studentScore.value = studentScore.value == 0 ? null : 0;
|
|
||||||
}, duration: const Duration(milliseconds: 222)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
IgnorePointer(
|
|
||||||
// 事件穿透
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
SizedBox(width: 1.1.w),
|
|
||||||
quickText('${item.questionNo}',
|
|
||||||
color: Theme.of(context).primaryColor.withOpacity(0.7),
|
|
||||||
size: 8.sp),
|
|
||||||
if (item.correctRate > 0)
|
|
||||||
quickText(' 正确率', color: Colors.grey, size: 5.sp),
|
|
||||||
if (item.correctRate > 0)
|
|
||||||
Expanded(
|
|
||||||
child: LinearPercentIndicator(
|
|
||||||
lineHeight: 6.h,
|
|
||||||
percent: item.correctRate / 100,
|
|
||||||
barRadius: Radius.circular(1.2.r),
|
|
||||||
alignment: MainAxisAlignment.center,
|
|
||||||
progressColor: Theme.of(context).primaryColor,
|
|
||||||
backgroundColor: const Color(0xFFB8C7CB).withOpacity(0.35),
|
|
||||||
center: quickText("${item.correctRate}%",
|
|
||||||
size: 5.sp,
|
|
||||||
align: TextAlign.center,
|
|
||||||
color: Colors.white),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 试题图片视图
|
// 试题图片视图
|
||||||
class QuestionImageView extends HookWidget
|
class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar> {
|
||||||
with EventBusMixin<BottomOperationBar> {
|
|
||||||
final double maxWidth;
|
final double maxWidth;
|
||||||
final double maxHeight;
|
final double maxHeight;
|
||||||
|
final double actualHeight;
|
||||||
final HomeworkReviewLogic logic;
|
final HomeworkReviewLogic logic;
|
||||||
|
final ZoomState zoomState;
|
||||||
final HomeworkReviewState sateData;
|
final HomeworkReviewState sateData;
|
||||||
final HomeworkReviewAnnotationsControlState annotationState;
|
final HomeworkReviewAnnotationsControlState annotationState;
|
||||||
QuestionImageView(this.maxWidth, this.maxHeight, this.sateData,
|
QuestionImageView(this.maxWidth, this.maxHeight, this.annotationState, this.logic, {required this.actualHeight, required this.zoomState, required this.sateData, super.key});
|
||||||
this.annotationState, this.logic,
|
|
||||||
{super.key});
|
|
||||||
|
|
||||||
/// 获取数组指定倒数具体值的下标
|
/// 获取数组指定倒数具体值的下标
|
||||||
int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) {
|
int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) {
|
||||||
|
|
@ -509,36 +273,46 @@ class QuestionImageView extends HookWidget
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int activePointers = 0;
|
int _activePointers = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final scrollControllerQuestion = useScrollController(); // 试题图片区域
|
final theMaxHeight = useState<double>(maxHeight);
|
||||||
|
useValueChanged<double, void>(maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight);
|
||||||
|
|
||||||
|
var zoomKey = useState<GlobalKey>(GlobalKey());
|
||||||
|
useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId, (old, __) {
|
||||||
|
zoomKey.value = GlobalKey();
|
||||||
|
});
|
||||||
|
|
||||||
var vnHandWritings = useValueNotifier<List<dynamic>>([]);
|
var vnHandWritings = useValueNotifier<List<dynamic>>([]);
|
||||||
|
|
||||||
|
/// 滑动高度
|
||||||
|
var initPosition = useState<Offset?>(null);
|
||||||
|
|
||||||
|
// var customPaintSize = useState<Size>(Size.zero);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
var listenStream = sateData.data.listen((e) {
|
var listenStream = sateData.data.listen((e) {
|
||||||
// 数据清空
|
// 数据清空
|
||||||
sateData.handwritings = [];
|
sateData.handwritings = [];
|
||||||
vnHandWritings.value = sateData.handwritings;
|
vnHandWritings.value = sateData.handwritings;
|
||||||
});
|
});
|
||||||
return () => listenStream.cancel();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
ImageStream? imageStream;
|
var streamSubscriptionSlide = sateData.slide.listen((e) {
|
||||||
var imageStreamListener = useState<ImageStreamListener>(
|
if (sateData.panQuestView != null && sateData.panQuestView == false && initPosition.value?.dy.abs().toInt().toDouble() != sateData.slide.value) {
|
||||||
ImageStreamListener((ImageInfo info, bool _) {
|
if (sateData.zoomOffset != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
sateData.zoomOffset = Offset(sateData.zoomOffset!.dx, -sateData.slide.value);
|
||||||
sateData.imageScale.value = TestQuestionsImageInfo(
|
}
|
||||||
templateId: sateData.data.value?.templateId,
|
initPosition.value = sateData.zoomOffset;
|
||||||
boxWidth: maxWidth,
|
print("赋值 initPosition.value ${initPosition.value}");
|
||||||
boxHeight: maxHeight,
|
}
|
||||||
imageWidth: info.image.width.toDouble(),
|
|
||||||
imageHeight: info.image.height.toDouble(),
|
|
||||||
url: sateData.data.value!.zgtAnswer,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}));
|
return () {
|
||||||
|
listenStream.cancel();
|
||||||
|
streamSubscriptionSlide.cancel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 可选:添加滚动监听
|
// 可选:添加滚动监听
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
|
|
@ -548,19 +322,9 @@ class QuestionImageView extends HookWidget
|
||||||
// 情况全部
|
// 情况全部
|
||||||
if (annotationsData.isNotEmpty) {
|
if (annotationsData.isNotEmpty) {
|
||||||
bool? res = await showDialog<bool>(
|
bool? res = await showDialog<bool>(
|
||||||
context: context,
|
context: Get.context ?? context,
|
||||||
builder: (context1) {
|
builder: (context1) {
|
||||||
return AlertDialog(
|
return AlertDialog(content: quickText("是否撤销全部批注痕迹?"), actions: <Widget>[TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)), TextButton(child: quickText("确定", color: Theme.of(context1).primaryColor), onPressed: () => Navigator.pop(context1, true))]);
|
||||||
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 = [];
|
if (res == true) vnHandWritings.value = [];
|
||||||
|
|
@ -570,15 +334,10 @@ class QuestionImageView extends HookWidget
|
||||||
await showDialog<bool>(
|
await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context1) {
|
builder: (context1) {
|
||||||
return AlertDialog(
|
return AlertDialog(content: quickText("是否撤销上次批阅批注痕迹?"), actions: <Widget>[
|
||||||
content: quickText("是否撤销上次批阅批注痕迹?"),
|
TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
TextButton(
|
||||||
child: quickText("取消"),
|
child: quickText("确定", color: Theme.of(context).primaryColor),
|
||||||
onPressed: () => Navigator.pop(context1, false)),
|
|
||||||
TextButton(
|
|
||||||
child: quickText("确定",
|
|
||||||
color: Theme.of(context).primaryColor),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context1, true);
|
Navigator.pop(context1, true);
|
||||||
sateData.data.value?.zgtAnnotate = null;
|
sateData.data.value?.zgtAnnotate = null;
|
||||||
|
|
@ -600,15 +359,10 @@ class QuestionImageView extends HookWidget
|
||||||
await showDialog<bool>(
|
await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context1) {
|
builder: (context1) {
|
||||||
return AlertDialog(
|
return AlertDialog(content: quickText("是否撤销上次批阅批注痕迹?"), actions: <Widget>[
|
||||||
content: quickText("是否撤销上次批阅批注痕迹?"),
|
TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
TextButton(
|
||||||
child: quickText("取消"),
|
child: quickText("确定", color: Theme.of(context).primaryColor),
|
||||||
onPressed: () => Navigator.pop(context1, false)),
|
|
||||||
TextButton(
|
|
||||||
child: quickText("确定",
|
|
||||||
color: Theme.of(context).primaryColor),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context1, true);
|
Navigator.pop(context1, true);
|
||||||
sateData.data.value?.zgtAnnotate = null;
|
sateData.data.value?.zgtAnnotate = null;
|
||||||
|
|
@ -631,114 +385,107 @@ class QuestionImageView extends HookWidget
|
||||||
vnHandWritings.value = annotationsData;
|
vnHandWritings.value = annotationsData;
|
||||||
});
|
});
|
||||||
|
|
||||||
scrollControllerQuestion.addListener(() {
|
|
||||||
sateData.slide.value = scrollControllerQuestion.offset;
|
|
||||||
}); // 执行滚动相关的逻辑,例如打印滚动位置
|
|
||||||
|
|
||||||
var listenVal = sateData.slide.listen((e) {
|
|
||||||
if (e != scrollControllerQuestion.offset &&
|
|
||||||
sateData.panQuestView == false) scrollControllerQuestion.jumpTo(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 返回一个清理函数,在组件销毁时移除监听
|
|
||||||
return () {
|
return () {
|
||||||
activePointers = 0;
|
_activePointers = 0;
|
||||||
listenVal.cancel();
|
|
||||||
imageStream?.removeListener(imageStreamListener.value);
|
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return Container(
|
print("++++++++++++++++++++++ ${theMaxHeight.value}");
|
||||||
height: maxHeight,
|
print("+++++ 位置:${initPosition.value}");
|
||||||
// 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(
|
return Listener(
|
||||||
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,
|
behavior: HitTestBehavior.opaque,
|
||||||
onPointerDown: (PointerDownEvent event) {
|
onPointerDown: (PointerDownEvent event) {
|
||||||
// 判断当前是否已经有触摸点,如果有则忽略该触摸事件
|
// 判断当前是否已经有触摸点,如果有则忽略该触摸事件
|
||||||
|
|
||||||
// 处理单个触摸点按下的逻辑
|
// 处理单个触摸点按下的逻辑
|
||||||
activePointers++;
|
_activePointers++;
|
||||||
|
sateData.panQuestView = true;
|
||||||
},
|
},
|
||||||
onPointerUp: (PointerUpEvent details) {
|
onPointerUp: (PointerUpEvent details) {
|
||||||
// 处理单个触摸点抬起的逻辑
|
// 处理单个触摸点抬起的逻辑
|
||||||
// activePointers--;
|
// activePointers--;
|
||||||
// globalPosition = null;
|
// globalPosition = null;
|
||||||
activePointers--;
|
_activePointers--;
|
||||||
|
if (!annotationState.pen.value) return;
|
||||||
|
|
||||||
var imageScale = sateData.imageScale.value;
|
|
||||||
if (imageScale == null || !annotationState.pen.value) return;
|
|
||||||
vnHandWritings.value.add(null); // 增加空点以分隔不同的线段
|
vnHandWritings.value.add(null); // 增加空点以分隔不同的线段
|
||||||
sateData.handwritings = vnHandWritings.value; // 添加笔迹数据
|
sateData.handwritings = vnHandWritings.value; // 添加笔迹数据
|
||||||
},
|
},
|
||||||
onPointerMove: (PointerMoveEvent event) {
|
onPointerMove: (PointerMoveEvent event) {
|
||||||
if (activePointers != 1) return;
|
if (_activePointers != 1) return;
|
||||||
|
if (!annotationState.pen.value) return;
|
||||||
|
|
||||||
|
Offset localPosition = event.localPosition; // 相对
|
||||||
|
var zoomFile = zoomState.zoomFile.value!;
|
||||||
|
// var imageHeightOffsetStart = zoomFile.imageHeightOffsetStart??0;
|
||||||
|
var imageHeightOffsetStart = zoomState.zoomFile.value!.getZoomFileOffsetStart(zoomState.initScale.value ?? 1);
|
||||||
|
print("位置:$localPosition; 图片所在位置:$imageHeightOffsetStart");
|
||||||
|
if (imageHeightOffsetStart == 0) return;
|
||||||
|
|
||||||
var imageScale = sateData.imageScale.value;
|
|
||||||
if (imageScale == null || !annotationState.pen.value) return;
|
|
||||||
Offset localPosition = event.localPosition;
|
|
||||||
var dy = localPosition.dy;
|
var dy = localPosition.dy;
|
||||||
if (dy > imageScale.actualImgHeight || dy < 0)
|
print(zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1));
|
||||||
return; // 检查笔记是否超出图片范围
|
if (dy < imageHeightOffsetStart || dy > zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1)) return; // 检查笔记是否超出图片范围
|
||||||
|
|
||||||
vnHandWritings.value = List.from(vnHandWritings.value)
|
var theScale = zoomState.initScale.value ?? 1;
|
||||||
..add(localPosition);
|
// if (theScale != 1) {
|
||||||
|
// print("PPPPPPPPPPPPPPPPPPPPPPPP ${(zoomFile.imageHeightOffsetStart ?? 0)}");
|
||||||
|
// localPosition = Offset(localPosition.dx, localPosition.dy + (zoomFile.imageHeightOffsetStart ?? 0));
|
||||||
|
|
||||||
|
// // 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);
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 说明:“- imageHeightOffsetStart” 由于显示笔记的位置是以图片的位置开始,手势坐标是以左上角开始 所以需要减去上面空白位置
|
||||||
|
// if (theScale == 1) {
|
||||||
|
// }
|
||||||
|
localPosition = Offset((localPosition.dx+(sateData.zoomOffset?.dx.abs()??0)) / theScale, (dy / theScale) - (imageHeightOffsetStart/ theScale));
|
||||||
|
|
||||||
|
print("最终位置 : $localPosition");
|
||||||
|
vnHandWritings.value = List.from(vnHandWritings.value)..add(localPosition);
|
||||||
sateData.handwritings = vnHandWritings.value;
|
sateData.handwritings = vnHandWritings.value;
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Obx(() {
|
||||||
children: [
|
var isPen = annotationState.pen.value;
|
||||||
$TheCachedNetworkImage(
|
var showZgtAnnotate = sateData.data.value?.showZgtAnnotate;
|
||||||
imageUrl: imageUrl,
|
|
||||||
(context, imageProvider) {
|
return Container(
|
||||||
Image imageWidget =
|
height: double.infinity,
|
||||||
Image(image: imageProvider, fit: BoxFit.fitWidth);
|
width: double.infinity,
|
||||||
imageStream?.removeListener(imageStreamListener.value);
|
alignment: Alignment.center,
|
||||||
imageStream = imageWidget.image
|
child: IgnorePointer(
|
||||||
.resolve(const ImageConfiguration())
|
ignoring: isPen,
|
||||||
..addListener(imageStreamListener.value);
|
child: Zoom(
|
||||||
return imageWidget;
|
key: zoomKey.value,
|
||||||
},
|
initTotalZoomOut: true, // 展示全部内容 初始化不产生滚动条
|
||||||
),
|
zoomSensibility: 0.05,
|
||||||
RepaintBoundary(
|
scrollWeight: 4.r,
|
||||||
|
doubleTapAnimDuration: Duration.zero,
|
||||||
|
maxZoomWidth: maxWidth,
|
||||||
|
maxZoomHeight: actualHeight,
|
||||||
|
canvasColor: Colors.transparent,
|
||||||
|
// initPosition: initPosition.value,
|
||||||
|
// initScale: logic.zoomLogic.zoomState.initScale.value ?? 1,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
onScaleUpdate: logic.zoomLogic.onScaleUpdate,
|
||||||
|
onPositionUpdate: logic.zoomLogic.onPanUpPosition,
|
||||||
|
child: RepaintBoundary(
|
||||||
key: logic.pictureOverviewKey,
|
key: logic.pictureOverviewKey,
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
|
// isComplex: true,
|
||||||
|
// size: customPaintSize.value,
|
||||||
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
||||||
size: Size(
|
child: $TheCachedNetworkImage(
|
||||||
sateData.imageScale.value?.actualImgWidth ?? 0,
|
imgWidth: maxWidth,
|
||||||
sateData.imageScale.value?.actualImgHeight ?? 0,
|
imageUrl: showZgtAnnotate ?? sateData.data.value!.zgtAnswer,
|
||||||
|
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
||||||
),
|
),
|
||||||
child: showZgtAnnotate != null
|
// child: showZgtAnnotate != null ? $TheCachedNetworkImage(imgWidth: maxWidth, imageUrl: showZgtAnnotate, (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth)) : null,
|
||||||
? $TheCachedNetworkImage(
|
|
||||||
imageUrl: showZgtAnnotate,
|
|
||||||
(_, imageProvider) => Image(
|
|
||||||
image: imageProvider, fit: BoxFit.fitWidth),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -755,7 +502,7 @@ class DrawingPainter extends CustomPainter {
|
||||||
final Paint paintBrush = Paint()
|
final Paint paintBrush = Paint()
|
||||||
..color = Colors.red
|
..color = Colors.red
|
||||||
..strokeCap = StrokeCap.round
|
..strokeCap = StrokeCap.round
|
||||||
..strokeWidth = 0.6.r;
|
..strokeWidth = 0.75.sp;
|
||||||
DrawingPainter({required this.ctrl}) : super(repaint: ctrl);
|
DrawingPainter({required this.ctrl}) : super(repaint: ctrl);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -765,8 +512,7 @@ class DrawingPainter extends CustomPainter {
|
||||||
for (int i = 0; i < pointsLength; i++) {
|
for (int i = 0; i < pointsLength; i++) {
|
||||||
Offset? offsetData = points[i];
|
Offset? offsetData = points[i];
|
||||||
Offset? nextOffsetData = pointsLength - 1 == i ? null : points[i + 1];
|
Offset? nextOffsetData = pointsLength - 1 == i ? null : points[i + 1];
|
||||||
if (offsetData != null && nextOffsetData != null)
|
if (offsetData != null && nextOffsetData != null) canvas.drawLine(offsetData, nextOffsetData, paintBrush);
|
||||||
canvas.drawLine(offsetData, nextOffsetData, paintBrush);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.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_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_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/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/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/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/toast_utils.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/upload_oss_img_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:making_school_asignment_app/page/global_widget/my_text.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
import 'zoom_logic.dart';
|
||||||
|
|
||||||
// 数据
|
// 数据
|
||||||
class HomeworkReviewState {
|
class HomeworkReviewState {
|
||||||
|
|
@ -26,13 +32,18 @@ class HomeworkReviewState {
|
||||||
late Rx<DoPaperDetailsResult?> data;
|
late Rx<DoPaperDetailsResult?> data;
|
||||||
late Rx<List<StudentQuestions>?> studentQuestions;
|
late Rx<List<StudentQuestions>?> studentQuestions;
|
||||||
late Rx<double> slide; // 滑动位置
|
late Rx<double> slide; // 滑动位置
|
||||||
late bool? panQuestView;
|
bool? panQuestView = null;
|
||||||
late Rx<TestQuestionsImageInfo?> imageScale;
|
// late Rx<TestQuestionsImageInfo?> imageScale;
|
||||||
|
// late Rx<TestQuestionsImageInfo?> imageScaleZoom = Rx<TestQuestionsImageInfo?>(null);
|
||||||
|
|
||||||
|
/// 图片放大后的操作存放数据
|
||||||
List<dynamic> handwritings = [];
|
List<dynamic> handwritings = [];
|
||||||
bool needRefresh = false;
|
bool needRefresh = false;
|
||||||
bool lastQuestionPrompt = false; // 最后一题提示
|
bool lastQuestionPrompt = false; // 最后一题提示
|
||||||
Rx<bool> getDataError = false.obs; // 获取作业数据报错
|
Rx<bool> getDataError = false.obs; // 获取作业数据报错
|
||||||
|
|
||||||
|
Offset? zoomOffset;
|
||||||
|
RxBool favorite = false.obs; // 初始化数据
|
||||||
// late String dateEnd = '';
|
// late String dateEnd = '';
|
||||||
// late int knowledgeId = 0;
|
// late int knowledgeId = 0;
|
||||||
// late RxList<KnowledgeReportDetail> dataList = RxList();
|
// late RxList<KnowledgeReportDetail> dataList = RxList();
|
||||||
|
|
@ -53,20 +64,31 @@ class HomeworkReviewAnnotationsControlState {
|
||||||
|
|
||||||
class HomeworkReviewBinding extends Bindings {
|
class HomeworkReviewBinding extends Bindings {
|
||||||
@override
|
@override
|
||||||
void dependencies() => Get.lazyPut(() => HomeworkReviewLogic());
|
void dependencies() {
|
||||||
|
Get.lazyPut(() => HomeworkReviewLogic());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
||||||
|
final zoomLogic = Get.find<ZoomLogic>();
|
||||||
final GlobalKey pictureOverviewKey = GlobalKey();
|
final GlobalKey pictureOverviewKey = GlobalKey();
|
||||||
late StreamSubscription<DoPaperDetailsParam> _paramListen;
|
late StreamSubscription<DoPaperDetailsParam> _paramListen;
|
||||||
late StreamSubscription<DoPaperDetailsResult?> _dataListen;
|
late StreamSubscription<DoPaperDetailsResult?> _dataListen;
|
||||||
final HomeworkReviewState state = HomeworkReviewState();
|
final HomeworkReviewState state = HomeworkReviewState();
|
||||||
final HomeworkReviewAnnotationsControlState annotationState =
|
final HomeworkReviewAnnotationsControlState annotationState = HomeworkReviewAnnotationsControlState();
|
||||||
HomeworkReviewAnnotationsControlState();
|
double appBarHeight = 56;
|
||||||
|
|
||||||
|
StreamSubscription<TestQuestionsImageInfo?>? imageScaleZoomStream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
|
||||||
|
appBarHeight = MediaQuery.of(Get.context!).padding.top;
|
||||||
|
print("appBarHeight :$appBarHeight");
|
||||||
|
// WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); // 屏幕刘海
|
||||||
|
|
||||||
state.param = DoPaperDetailsParam(
|
state.param = DoPaperDetailsParam(
|
||||||
homeworkId: Get.arguments['homeworkId'],
|
homeworkId: Get.arguments['homeworkId'],
|
||||||
homeworkName: Get.arguments['homeworkName'],
|
homeworkName: Get.arguments['homeworkName'],
|
||||||
|
|
@ -75,28 +97,69 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
||||||
).obs;
|
).obs;
|
||||||
state.data = Rx<DoPaperDetailsResult?>(null);
|
state.data = Rx<DoPaperDetailsResult?>(null);
|
||||||
state.studentQuestions = Rx<List<StudentQuestions>?>(null);
|
state.studentQuestions = Rx<List<StudentQuestions>?>(null);
|
||||||
state.imageScale = Rx<TestQuestionsImageInfo?>(null);
|
// state.imageScale = Rx<TestQuestionsImageInfo?>(null);
|
||||||
state.slide = 0.0.obs;
|
state.slide = 0.0.obs;
|
||||||
getData();
|
|
||||||
// 参数变化更新作业详情
|
// 参数变化更新作业详情
|
||||||
_paramListen = state.param.listen((e) => 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;
|
||||||
|
var zoomState = zoomLogic.zoomState;
|
||||||
|
|
||||||
|
state.studentQuestions.value = e.studentQuestions; // 赋值试题集合
|
||||||
|
|
||||||
|
|
||||||
|
final currentTemplateId = zoomState.zoomFile.value?.templateId; // 获取旧题号ID
|
||||||
|
if (currentTemplateId != null && currentTemplateId != e.templateId) {
|
||||||
|
// zoom 题号判断是否有变 有变就需要清空 zoom文件
|
||||||
|
zoomState.initScale.value = null;
|
||||||
|
zoomState.zoomFile.value!.clearZoomFile(e.templateId);
|
||||||
|
zoomState.zoomFile.update((_) {}); // 更新的是对象 需要执行此回调
|
||||||
|
}
|
||||||
|
if (state.favorite.value != e.isFav) state.favorite.value = !state.favorite.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// imageScaleZoomStream = state.imageScale.listen((e) {
|
||||||
|
// // state.imageScaleZoom.value = theImageScale;
|
||||||
|
// var theImageScaleZoom = state.imageScaleZoom.value;
|
||||||
|
// if (theImageScaleZoom == null || theImageScaleZoom.url != e?.url) state.imageScaleZoom.value = e;
|
||||||
|
// });
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => getData());
|
||||||
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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();
|
_dataListen.cancel();
|
||||||
_paramListen.cancel();
|
_paramListen.cancel();
|
||||||
super.disposeId(id);
|
imageScaleZoomStream?.cancel();
|
||||||
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void getData() async {
|
void getData() async {
|
||||||
var timerControl = Timer(
|
var timerControl = Timer(const Duration(milliseconds: 300), () => ToastUtils.showLoading());
|
||||||
const Duration(milliseconds: 300), () => ToastUtils.showLoading());
|
|
||||||
try {
|
try {
|
||||||
DoPaperDetailsResult? data =
|
DoPaperDetailsResult? data = await getClient().getDoPaperDetails(state.param.value);
|
||||||
await getClient().getDoPaperDetails(state.param.value);
|
|
||||||
// var studentQuestions = data.studentQuestions;
|
// var studentQuestions = data.studentQuestions;
|
||||||
// // 第0个的下标数据不需要处理
|
// // 第0个的下标数据不需要处理
|
||||||
// for (var i = 0; i < studentQuestions.length; i++) {
|
// for (var i = 0; i < studentQuestions.length; i++) {
|
||||||
|
|
@ -105,16 +168,14 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
||||||
// item.topHeight = itemPre.height;
|
// item.topHeight = itemPre.height;
|
||||||
// }
|
// }
|
||||||
state.getDataError.value = false;
|
state.getDataError.value = false;
|
||||||
state.data.value = data;
|
|
||||||
state.handwritings = [];
|
state.handwritings = [];
|
||||||
state.studentQuestions.value = data.studentQuestions;
|
state.data.value = data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('获取数据报错了:$e');
|
print('获取数据报错了:$e');
|
||||||
ToastUtils.showError('获取试题数据出错,请重试');
|
// ToastUtils.showError('未获取到试题数据,请重试');
|
||||||
state.getDataError.value = true;
|
state.getDataError.value = true;
|
||||||
} finally {
|
} finally {
|
||||||
if (timerControl.isActive) timerControl.cancel();
|
if (timerControl.isActive) timerControl.cancel();
|
||||||
|
|
||||||
ToastUtils.dismiss();
|
ToastUtils.dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -168,41 +229,29 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
||||||
if (data == null) return null;
|
if (data == null) return null;
|
||||||
|
|
||||||
// 获取OSS 图片url
|
// 获取OSS 图片url
|
||||||
String imgKey = UploadOssImgUtils.getInstance().setImgKey(
|
String imgKey = UploadOssImgUtils.getInstance().setImgKey(param.homeworkId, data.studentId.toString(), data.templateId.toString());
|
||||||
param.homeworkId,
|
|
||||||
data.studentId.toString(),
|
|
||||||
data.templateId.toString());
|
|
||||||
var resUrl = await getClient().getOssPresignedUri(imgKey);
|
var resUrl = await getClient().getOssPresignedUri(imgKey);
|
||||||
if (resUrl == null) return null;
|
if (resUrl == null) return null;
|
||||||
|
|
||||||
// 没有图片就上传图片
|
// 没有图片就上传图片
|
||||||
RenderRepaintBoundary? boundary = pictureOverviewKey.currentContext!
|
RenderRepaintBoundary? boundary = pictureOverviewKey.currentContext!.findRenderObject() as RenderRepaintBoundary?;
|
||||||
.findRenderObject() as RenderRepaintBoundary?;
|
|
||||||
if (boundary == null) return null;
|
if (boundary == null) return null;
|
||||||
// double dpr = MediaQuery.of(context).devicePixelRatio;
|
// double dpr = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
double pixelRatio = MediaQuery.of(context).devicePixelRatio;
|
/// 此图片在设备中的像素比例
|
||||||
var imageScale = state.imageScale.value;
|
double? pixelRatio = zoomLogic.zoomState.zoomFile.value?.scaleRatio ?? MediaQuery.of(context).devicePixelRatio;
|
||||||
if (imageScale != null) {
|
|
||||||
// 还原图片原始比例
|
|
||||||
pixelRatio = imageScale.imageWidth / imageScale.boxWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Image image = await boundary.toImage(pixelRatio: pixelRatio);
|
ui.Image image = await boundary.toImage(pixelRatio: pixelRatio);
|
||||||
ByteData? byteData =
|
ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||||
await image.toByteData(format: ui.ImageByteFormat.png);
|
|
||||||
if (byteData == null) return null;
|
if (byteData == null) return null;
|
||||||
|
|
||||||
Dio dio = Dio();
|
Dio dio = Dio();
|
||||||
dio.options.contentType = null;
|
dio.options.contentType = null;
|
||||||
List<int> bytes = byteData.buffer
|
List<int> bytes = byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
|
||||||
.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
|
|
||||||
await dio.put(
|
await dio.put(
|
||||||
resUrl,
|
resUrl,
|
||||||
data: Stream.fromIterable(bytes.map((e) => [e])),
|
data: Stream.fromIterable(bytes.map((e) => [e])),
|
||||||
options: Options(
|
options: Options(contentType: null, headers: {Headers.contentLengthHeader: bytes.length}),
|
||||||
contentType: null,
|
|
||||||
headers: {Headers.contentLengthHeader: bytes.length}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return imgKey;
|
return imgKey;
|
||||||
|
|
@ -224,8 +273,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
||||||
|
|
||||||
if (state.data.value?.studentQuestions.isEmpty ?? true) return;
|
if (state.data.value?.studentQuestions.isEmpty ?? true) return;
|
||||||
var studentQuestions = state.data.value!.studentQuestions;
|
var studentQuestions = state.data.value!.studentQuestions;
|
||||||
var noRatingElement =
|
var noRatingElement = studentQuestions.firstWhereOrNull((e) => e.studentScore == null);
|
||||||
studentQuestions.firstWhereOrNull((e) => e.studentScore == null);
|
|
||||||
if (noRatingElement != null) {
|
if (noRatingElement != null) {
|
||||||
ToastUtils.showInfo('${noRatingElement.questionNo}题请评分');
|
ToastUtils.showInfo('${noRatingElement.questionNo}题请评分');
|
||||||
return;
|
return;
|
||||||
|
|
@ -293,4 +341,22 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin {
|
||||||
state.param.value = newParams;
|
state.param.value = newParams;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,262 @@
|
||||||
|
import 'dart:ui' show ImmutableBuffer, ImageDescriptor;
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart' as anti_shake_throttling;
|
||||||
|
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 {
|
||||||
|
ZoomState zoomState = ZoomState(
|
||||||
|
initScale: Rx<double?>(null),
|
||||||
|
zoomFile: Rx<ZoomFileModel?>(null),
|
||||||
|
);
|
||||||
|
|
||||||
|
var oldTemplateId;
|
||||||
|
late StreamSubscription _streamHomework;
|
||||||
|
late StreamSubscription _streamZoomState;
|
||||||
|
|
||||||
|
StreamSubscription<double?>? initScaleStream;
|
||||||
|
|
||||||
|
@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 = Get.find<HomeworkReviewLogic>().state.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());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((e) {
|
||||||
|
/// 试题加载后更新尺寸对象(只要题号没有变就不更新尺寸对象)
|
||||||
|
_streamHomework = Get.find<HomeworkReviewLogic>().state.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());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/// 监听缩放
|
||||||
|
initScaleStream = zoomState.initScale.listen((e) {});
|
||||||
|
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
_streamHomework.cancel();
|
||||||
|
_streamZoomState.cancel();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缩放组件 ==> 缩放监听
|
||||||
|
void onScaleUpdate(double scale, double zoom) async {
|
||||||
|
print("$scale $zoom");
|
||||||
|
|
||||||
|
/// 防抖
|
||||||
|
zoomState.initScale.value = zoom;
|
||||||
|
// anti_shake_throttling.debounce(() => zoomState.initScale.value = zoom, const Duration(milliseconds: 100))();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缩放组件 ==> 位置更新
|
||||||
|
void onPanUpPosition(Offset val) async {
|
||||||
|
// 手指在移动 非物体移动的位置
|
||||||
|
var state = Get.find<HomeworkReviewLogic>().state;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HomeworkReviewZoomBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut(() => ZoomLogic());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ZoomState {
|
||||||
|
final Rx<double?> initScale;
|
||||||
|
final Rx<ZoomFileModel?> zoomFile;
|
||||||
|
|
||||||
|
const ZoomState({required this.zoomFile, required this.initScale});
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ZoomFileModel extends Object {
|
||||||
|
double viewWidth; // 容器宽度
|
||||||
|
double viewHeight; // 容器高度
|
||||||
|
|
||||||
|
double scaleRatio; // 基准
|
||||||
|
|
||||||
|
int? templateId; // 页码
|
||||||
|
|
||||||
|
double? pixelRatio; // 像素比率
|
||||||
|
|
||||||
|
double? fileWidth; // 文件宽度
|
||||||
|
double? fileHeight; // 文件高度
|
||||||
|
|
||||||
|
double? actualWidth; // 实际展示宽度
|
||||||
|
double? actualHeight; // 实际展示高度
|
||||||
|
|
||||||
|
double? remainingHeight; // 视图剩余高度 实际展示图片展示高度下 剩余高度.(若为负数就是超出视图的高度值)
|
||||||
|
double? imageHeightOffsetStart; // 顶部坐标Y
|
||||||
|
double? imageHeightOffsetend; // 底部坐标Y
|
||||||
|
|
||||||
|
ZoomFileModel({
|
||||||
|
required this.viewWidth,
|
||||||
|
required this.viewHeight,
|
||||||
|
this.scaleRatio = 1,
|
||||||
|
this.templateId,
|
||||||
|
this.fileWidth,
|
||||||
|
this.fileHeight,
|
||||||
|
this.actualWidth,
|
||||||
|
this.actualHeight,
|
||||||
|
this.remainingHeight,
|
||||||
|
this.imageHeightOffsetStart,
|
||||||
|
this.imageHeightOffsetend,
|
||||||
|
this.pixelRatio,
|
||||||
|
}) {
|
||||||
|
// 图片已视图宽为基准,高度自适应可滑动 图片的实际宽高都需要乘此基准值.
|
||||||
|
if (fileHeight == null || fileWidth == null) return;
|
||||||
|
|
||||||
|
scaleRatio = viewWidth / fileWidth!;
|
||||||
|
actualWidth = fileWidth! * scaleRatio;
|
||||||
|
actualHeight = fileHeight! * scaleRatio;
|
||||||
|
|
||||||
|
pixelRatio = fileWidth! / viewWidth; // 图片在此设备的像素比例
|
||||||
|
|
||||||
|
remainingHeight = viewHeight - actualHeight!;
|
||||||
|
imageHeightOffsetStart = remainingHeight! / 2;
|
||||||
|
imageHeightOffsetend = imageHeightOffsetStart! + actualHeight!;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory ZoomFileModel.fromJson(Map<String, dynamic> srcJson) => _$ZoomFileModelFromJson(srcJson);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$ZoomFileModelToJson(this);
|
||||||
|
|
||||||
|
// 清空zoom文件(templateId 页码变换清空试题图片信息)
|
||||||
|
ZoomFileModel clearZoomFile(int newTemplateId) {
|
||||||
|
templateId = newTemplateId; // 页码
|
||||||
|
fileWidth = null; // 文件宽度
|
||||||
|
fileHeight = null; // 文件高度
|
||||||
|
actualWidth = null; // 实际展示宽度
|
||||||
|
actualHeight = null; // 实际展示高度
|
||||||
|
remainingHeight = null; // 视图剩余高度 实际展示图片展示高度下 剩余高度.(若为负数就是超出视图的高度值)
|
||||||
|
imageHeightOffsetStart = null; // 顶部坐标Y
|
||||||
|
imageHeightOffsetend = null; // 底部坐标Y
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取缩放比例后的空白距离
|
||||||
|
/// zoom 缩放比例
|
||||||
|
double getZoomFileOffsetStart(double zoom) {
|
||||||
|
if (zoom == 1 || actualHeight == null) return imageHeightOffsetStart ?? 0;
|
||||||
|
return (viewHeight - (actualHeight! * zoom)) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取缩放比例后的底部图片位置
|
||||||
|
/// zoom 缩放比例
|
||||||
|
double getZoomFileHeightOffsetEnd(double zoom) {
|
||||||
|
if (zoom == 1 || actualHeight == null) return imageHeightOffsetend ?? 0;
|
||||||
|
var topSpaceDimensions = getZoomFileOffsetStart(zoom);
|
||||||
|
return topSpaceDimensions + (actualHeight! * zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终版
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,59 +5,32 @@ import 'package:get/get.dart';
|
||||||
import 'package:making_school_asignment_app/page/global_widget/ReturnToHomepage.dart';
|
import 'package:making_school_asignment_app/page/global_widget/ReturnToHomepage.dart';
|
||||||
import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
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/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:making_school_asignment_app/page/home_page/home_logic.dart';
|
||||||
|
|
||||||
import 'components/bottom_operation_bar.dart';
|
import 'components/bottom_operation_bar.dart';
|
||||||
import 'components/dropdown_switch_students_type.dart';
|
import 'components/dropdown_switch_students_type.dart';
|
||||||
import 'components/favorite_widget.dart';
|
import 'components/favorite_widget.dart';
|
||||||
import 'components/question_paper_view.dart';
|
|
||||||
import 'configuration_files/index.dart';
|
import 'configuration_files/index.dart';
|
||||||
|
|
||||||
class HomeworkReview extends StatefulWidget {
|
class HomeworkReview extends StatelessWidget {
|
||||||
const HomeworkReview({super.key});
|
const HomeworkReview({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
State<HomeworkReview> createState() => _HomeworkReviewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _HomeworkReviewState extends State<HomeworkReview> {
|
|
||||||
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();
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
Get.delete<HomeworkReviewLogic>();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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(
|
return PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
onPopInvoked: (e) {
|
onPopInvoked: (e) {
|
||||||
if (e && sateData.needRefresh) {
|
if (e && sateData.needRefresh) {
|
||||||
_controller.getList();
|
controller.getList();
|
||||||
_homeLogicController.getList();
|
homeLogicController.getList();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: AnnotatedRegion(
|
|
||||||
value: const SystemUiOverlayStyle(
|
|
||||||
systemNavigationBarColor: Color(0xFF000000),
|
|
||||||
systemNavigationBarDividerColor: null,
|
|
||||||
statusBarColor: Colors.transparent,
|
|
||||||
systemNavigationBarIconBrightness: Brightness.light,
|
|
||||||
statusBarIconBrightness: Brightness.light,
|
|
||||||
statusBarBrightness: Brightness.light,
|
|
||||||
),
|
|
||||||
child: SafeArea(
|
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
// titleSpacing: 0,
|
// titleSpacing: 0,
|
||||||
|
|
@ -68,22 +41,18 @@ class _HomeworkReviewState extends State<HomeworkReview> {
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
actions: [const FavoriteWidget(), SizedBox(width: 5.w), const ReturnToHomepage()],
|
actions: [const FavoriteWidget(), SizedBox(width: 5.w), const ReturnToHomepage()],
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: const Column(
|
||||||
child: Column(
|
|
||||||
children: [
|
children: [
|
||||||
// 下拉切换
|
// 下拉切换
|
||||||
const DropdownSwitchStudentsType(),
|
DropdownSwitchStudentsType(),
|
||||||
SizedBox(height: 1.h),
|
// SizedBox(height: 10),
|
||||||
const Expanded(child: QuestionPaperView()),
|
// 试题展示区和打分区
|
||||||
const BottomAnnotationSwitch()
|
Expanded(child: QuestionPaperView()),
|
||||||
|
// 底部功能区
|
||||||
|
BottomAnnotationSwitch()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// floatingActionButton: const ButtonFloatingAction(),
|
|
||||||
// floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class _JobReportPageState extends State<JobReportPage> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return OrientationBuilder(builder: (BuildContext context, Orientation orientation) {
|
return OrientationBuilder(builder: (BuildContext context, Orientation orientation) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Color.fromRGBO(245, 245, 245, 1),
|
backgroundColor: const Color.fromRGBO(245, 245, 245, 1),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
||||||
|
|
||||||
class PersonnelDataOverview extends StatefulWidget {
|
class PersonnelDataOverview extends StatefulWidget {
|
||||||
final List<Students> studentList;
|
final List<Students> studentList;
|
||||||
const PersonnelDataOverview({Key? key, required this.studentList}) : super(key: key);
|
const PersonnelDataOverview({super.key, required this.studentList});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PersonnelDataOverview> createState() => _PersonnelDataOverviewState();
|
State<PersonnelDataOverview> createState() => _PersonnelDataOverviewState();
|
||||||
|
|
@ -28,13 +28,13 @@ class _PersonnelDataOverviewState extends State<PersonnelDataOverview> {
|
||||||
var student = widget.studentList[i];
|
var student = widget.studentList[i];
|
||||||
TimeUnits timeUnits = convertMilliseconds(student.useTime!);
|
TimeUnits timeUnits = convertMilliseconds(student.useTime!);
|
||||||
String timeerStr = '';
|
String timeerStr = '';
|
||||||
if (timeUnits.hours > 0) timeerStr = timeUnits.hours.toString() + ':';
|
if (timeUnits.hours > 0) timeerStr = '${timeUnits.hours}:';
|
||||||
if (timeUnits.minutes > 0) timeerStr += timeUnits.minutes.toString() + ':';
|
if (timeUnits.minutes > 0) timeerStr += '${timeUnits.minutes}:';
|
||||||
timeerStr += timeUnits.seconds.toString();
|
timeerStr += timeUnits.seconds.toString();
|
||||||
|
|
||||||
names.add(student.studentName);
|
names.add(student.studentName);
|
||||||
useTimes.add(timeerStr);
|
useTimes.add(timeerStr);
|
||||||
correctRates.add(student.okRate!.toStringAsFixed(0) + '%');
|
correctRates.add('${student.okRate!.toStringAsFixed(0)}%');
|
||||||
noAnswerCounts.add(student.noAnswerCount.toString());
|
noAnswerCounts.add(student.noAnswerCount.toString());
|
||||||
rankings.add('${i + 1}名');
|
rankings.add('${i + 1}名');
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +60,7 @@ class _PersonnelDataOverviewState extends State<PersonnelDataOverview> {
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.only(bottom: 20.h),
|
margin: EdgeInsets.only(bottom: 20.h),
|
||||||
child: quickText('人员数据概况', color: Color.fromRGBO(92, 92, 92, 1), size: 14.sp, fontWeight: FontWeight.bold),
|
child: quickText('人员数据概况', color: const Color.fromRGBO(92, 92, 92, 1), size: 14.sp, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
Scrollbar(
|
Scrollbar(
|
||||||
thickness: 8.w,
|
thickness: 8.w,
|
||||||
|
|
@ -88,7 +88,7 @@ class _PersonnelDataOverviewState extends State<PersonnelDataOverview> {
|
||||||
color: const Color.fromRGBO(230, 230, 230, 1),
|
color: const Color.fromRGBO(230, 230, 230, 1),
|
||||||
margin: EdgeInsets.only(bottom: 1.h, right: 1.w),
|
margin: EdgeInsets.only(bottom: 1.h, right: 1.w),
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w),
|
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w),
|
||||||
child: quickText(entrie.key, color: Color.fromRGBO(24, 35, 77, 1), size: 12.sp, maxLines: 2),
|
child: quickText(entrie.key, color: const Color.fromRGBO(24, 35, 77, 1), size: 12.sp, maxLines: 2),
|
||||||
),
|
),
|
||||||
...entrie.value.map((e) {
|
...entrie.value.map((e) {
|
||||||
bool isTransparentChineseNew = isTransparentChinese && (e?.length ?? 0) == 0;
|
bool isTransparentChineseNew = isTransparentChinese && (e?.length ?? 0) == 0;
|
||||||
|
|
@ -97,23 +97,23 @@ class _PersonnelDataOverviewState extends State<PersonnelDataOverview> {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
margin: EdgeInsets.only(bottom: 1.h, right: 1.w),
|
margin: EdgeInsets.only(bottom: 1.h, right: 1.w),
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w),
|
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w),
|
||||||
color: Color.fromRGBO(245, 245, 245, 1),
|
color: const Color.fromRGBO(245, 245, 245, 1),
|
||||||
child: isTransparentChineseNew
|
child: isTransparentChineseNew
|
||||||
? quickText('透明', color: Colors.transparent, size: 12.sp)
|
? quickText('透明', color: Colors.transparent, size: 12.sp)
|
||||||
: RegExp(r'^\d+$').hasMatch(e) || e.contains('%')
|
: RegExp(r'^\d+$').hasMatch(e) || e.contains('%')
|
||||||
? Row(
|
? Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
quickText(e, color: Color.fromRGBO(82, 82, 82, 1), size: 12.sp, maxLines: 2),
|
quickText(e, color: const Color.fromRGBO(82, 82, 82, 1), size: 12.sp, maxLines: 2),
|
||||||
quickText('透', color: Colors.transparent, size: 12.sp),
|
quickText('透', color: Colors.transparent, size: 12.sp),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: quickText(e, color: Color.fromRGBO(82, 82, 82, 1), size: 12.sp, maxLines: 2),
|
: quickText(e, color: const Color.fromRGBO(82, 82, 82, 1), size: 12.sp, maxLines: 2),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}).toList(),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,8 @@ class _ReadOverPageState extends State<ReadOverPage> {
|
||||||
value: const SystemUiOverlayStyle(
|
value: const SystemUiOverlayStyle(
|
||||||
/* systemNavigationBarColor: Color(0xFF000000),
|
/* systemNavigationBarColor: Color(0xFF000000),
|
||||||
systemNavigationBarDividerColor: null,*/
|
systemNavigationBarDividerColor: null,*/
|
||||||
statusBarColor: Colors.transparent,
|
statusBarColor: Colors.white,
|
||||||
|
systemNavigationBarDividerColor: null,
|
||||||
systemNavigationBarIconBrightness: Brightness.light,
|
systemNavigationBarIconBrightness: Brightness.light,
|
||||||
statusBarIconBrightness: Brightness.light,
|
statusBarIconBrightness: Brightness.light,
|
||||||
statusBarBrightness: Brightness.light,
|
statusBarBrightness: Brightness.light,
|
||||||
|
|
@ -177,7 +178,12 @@ class _ReadOverPageState extends State<ReadOverPage> {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
Get.delete<ReadOverLogic>();
|
Get.delete<ReadOverLogic>();
|
||||||
super.dispose();
|
|
||||||
logic.homeController.getList();
|
logic.homeController.getList();
|
||||||
|
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||||||
|
statusBarColor: Colors.transparent, //状态栏背景颜色
|
||||||
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
systemStatusBarContrastEnforced: false,
|
||||||
|
));
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,7 @@ class _TaskListItemState extends State<TaskListItem> {
|
||||||
return widget.completed
|
return widget.completed
|
||||||
? InkWell(
|
? InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Get.toNamed(Routes.annotateClassPage,
|
Get.toNamed(Routes.annotateClassPage, arguments: {'id': widget.jobTaskItem.id, 'name': widget.jobTaskItem.name, 'grade': widget.jobTaskItem.grade, 'subject': widget.jobTaskItem.subject, 'completed': true, 'tabIndex': widget.tabIndex});
|
||||||
arguments: {'id': widget.jobTaskItem.id, 'name': widget.jobTaskItem.name, 'grade': widget.jobTaskItem.grade,'subject': widget.jobTaskItem.subject, 'completed': true,'tabIndex':widget.tabIndex});
|
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
|
@ -143,7 +142,13 @@ class _TaskListItemState extends State<TaskListItem> {
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {},
|
onTap: () {
|
||||||
|
Get.toNamed(Routes.jobReportPage, arguments: {
|
||||||
|
'title': widget.jobTaskItem.name,
|
||||||
|
'homeworkId': widget.jobTaskItem.id,
|
||||||
|
'grade': widget.jobTaskItem.grade,
|
||||||
|
});
|
||||||
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: quickText('查看报告', color: const Color.fromRGBO(118, 118, 118, 1), size: 13.sp),
|
child: quickText('查看报告', color: const Color.fromRGBO(118, 118, 118, 1), size: 13.sp),
|
||||||
|
|
@ -159,8 +164,7 @@ class _TaskListItemState extends State<TaskListItem> {
|
||||||
padding: EdgeInsets.only(top: 10.r),
|
padding: EdgeInsets.only(top: 10.r),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Get.toNamed(Routes.annotateClassPage,
|
Get.toNamed(Routes.annotateClassPage, arguments: {'id': widget.jobTaskItem.id, 'name': widget.jobTaskItem.name, 'grade': widget.jobTaskItem.grade, 'subject': widget.jobTaskItem.subject, 'tabIndex': widget.tabIndex});
|
||||||
arguments: {'id': widget.jobTaskItem.id, 'name': widget.jobTaskItem.name, 'grade': widget.jobTaskItem.grade,'subject':widget.jobTaskItem.subject,'tabIndex':widget.tabIndex});
|
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: EdgeInsets.only(bottom: 16.h),
|
margin: EdgeInsets.only(bottom: 16.h),
|
||||||
|
|
@ -243,8 +247,7 @@ class _TaskListItemState extends State<TaskListItem> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
quickText(' / ', color: const Color.fromRGBO(130, 130, 130, 1), size: 11.sp, fontWeight: FontWeight.w500),
|
quickText(' / ', color: const Color.fromRGBO(130, 130, 130, 1), size: 11.sp, fontWeight: FontWeight.w500),
|
||||||
quickText(DateTime.parse(widget.jobTaskItem.publishTime).toString().substring(0, 10),
|
quickText(DateTime.parse(widget.jobTaskItem.publishTime).toString().substring(0, 10), color: const Color.fromRGBO(97, 97, 97, 1), size: 12.sp),
|
||||||
color: const Color.fromRGBO(97, 97, 97, 1), size: 12.sp),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -274,7 +277,9 @@ class _TaskListItemState extends State<TaskListItem> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 10.r,),
|
SizedBox(
|
||||||
|
width: 10.r,
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.r, horizontal: 9.r),
|
padding: EdgeInsets.symmetric(vertical: 10.r, horizontal: 9.r),
|
||||||
decoration: const BoxDecoration(shape: BoxShape.circle, color: Colors.white),
|
decoration: const BoxDecoration(shape: BoxShape.circle, color: Colors.white),
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import 'package:syncfusion_flutter_datepicker/datepicker.dart';
|
||||||
import 'student_work_detail_logic.dart';
|
import 'student_work_detail_logic.dart';
|
||||||
|
|
||||||
class StudentWorkDetailPage extends StatefulWidget {
|
class StudentWorkDetailPage extends StatefulWidget {
|
||||||
const StudentWorkDetailPage({Key? key}) : super(key: key);
|
const StudentWorkDetailPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StudentWorkDetailPage> createState() => _StudentWorkDetailPageState();
|
State<StudentWorkDetailPage> createState() => _StudentWorkDetailPageState();
|
||||||
|
|
@ -33,14 +33,14 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
return OrientationBuilder(
|
return OrientationBuilder(
|
||||||
builder: (BuildContext context, Orientation orientation) {
|
builder: (BuildContext context, Orientation orientation) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Color.fromRGBO(245, 245, 245, 1),
|
backgroundColor: const Color.fromRGBO(245, 245, 245, 1),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
title: Text('${state.studentName}作业详情',
|
title: Text('${state.studentName}作业详情',
|
||||||
style: TextStyle(fontSize: 14.sp, color: Color(0xFF333333))),
|
style: TextStyle(fontSize: 14.sp, color: const Color(0xFF333333))),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(Icons.arrow_back_ios, color: Colors.black),
|
icon: const Icon(Icons.arrow_back_ios, color: Colors.black),
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
),
|
),
|
||||||
actions: const [
|
actions: const [
|
||||||
|
|
@ -53,7 +53,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
Container(
|
Container(
|
||||||
height: 1.r,
|
height: 1.r,
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
color: Color.fromRGBO(179, 179, 179, 0.3),
|
color: const Color.fromRGBO(179, 179, 179, 0.3),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
|
@ -79,7 +79,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
fontSize: 14.sp,
|
fontSize: 14.sp,
|
||||||
color: state.isJob.value
|
color: state.isJob.value
|
||||||
? Theme.of(context).primaryColor
|
? Theme.of(context).primaryColor
|
||||||
: Color(0xFF505E6E)),
|
: const Color(0xFF505E6E)),
|
||||||
));
|
));
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -87,7 +87,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
Container(
|
Container(
|
||||||
height: 40.r,
|
height: 40.r,
|
||||||
width: 1.r,
|
width: 1.r,
|
||||||
color: Color.fromRGBO(179, 179, 179, 0.3),
|
color: const Color.fromRGBO(179, 179, 179, 0.3),
|
||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|
@ -107,7 +107,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
fontSize: 14.sp,
|
fontSize: 14.sp,
|
||||||
color: !state.isJob.value
|
color: !state.isJob.value
|
||||||
? Theme.of(context).primaryColor
|
? Theme.of(context).primaryColor
|
||||||
: Color(0xFF505E6E)),
|
: const Color(0xFF505E6E)),
|
||||||
);
|
);
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
|
|
@ -223,7 +223,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'注:',
|
'注:',
|
||||||
style: TextStyle(fontSize: 8.sp, color: Color(0xFF8B8B8B)),
|
style: TextStyle(fontSize: 8.sp, color: const Color(0xFF8B8B8B)),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
width: 10.r,
|
width: 10.r,
|
||||||
|
|
@ -239,7 +239,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'正确',
|
'正确',
|
||||||
style: TextStyle(fontSize: 8.sp, color: Color(0xFF8B8B8B)),
|
style: TextStyle(fontSize: 8.sp, color: const Color(0xFF8B8B8B)),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 15.r,
|
width: 15.r,
|
||||||
|
|
@ -258,7 +258,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'错误',
|
'错误',
|
||||||
style: TextStyle(fontSize: 8.sp, color: Color(0xFF8B8B8B)),
|
style: TextStyle(fontSize: 8.sp, color: const Color(0xFF8B8B8B)),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 15.r,
|
width: 15.r,
|
||||||
|
|
@ -269,7 +269,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(5.r)),
|
borderRadius: BorderRadius.all(Radius.circular(5.r)),
|
||||||
// color: Color(0xFF666666),
|
// color: Color(0xFF666666),
|
||||||
border: Border.all(width: 1.r, color: Color(0xFF666666)),
|
border: Border.all(width: 1.r, color: const Color(0xFF666666)),
|
||||||
// border: Border.all(width: 1.r,color: Colors.grey),
|
// border: Border.all(width: 1.r,color: Colors.grey),
|
||||||
/* boxShadow: [
|
/* boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
|
|
@ -286,7 +286,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'已作答未批阅',
|
'已作答未批阅',
|
||||||
style: TextStyle(fontSize: 8.sp, color: Color(0xFF8B8B8B)),
|
style: TextStyle(fontSize: 8.sp, color: const Color(0xFF8B8B8B)),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 15.r,
|
width: 15.r,
|
||||||
|
|
@ -296,7 +296,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
height: 10.r,
|
height: 10.r,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(5.r)),
|
borderRadius: BorderRadius.all(Radius.circular(5.r)),
|
||||||
border: Border.all(width: 1.r, color: Color(0xFFDDDDDD)),
|
border: Border.all(width: 1.r, color: const Color(0xFFDDDDDD)),
|
||||||
// color: Color(0xFFDDDDDD),
|
// color: Color(0xFFDDDDDD),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -305,7 +305,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'未做',
|
'未做',
|
||||||
style: TextStyle(fontSize: 8.sp, color: Color(0xFF8B8B8B)),
|
style: TextStyle(fontSize: 8.sp, color: const Color(0xFF8B8B8B)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -394,7 +394,7 @@ class _StudentWorkDetailPageState extends State<StudentWorkDetailPage> {
|
||||||
item.name,
|
item.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
color: Color(0xFF464646)),
|
color: const Color(0xFF464646)),
|
||||||
)),
|
)),
|
||||||
// SizedBox(width: 5.r,),
|
// SizedBox(width: 5.r,),
|
||||||
// Text('2024.1',style: TextStyle(fontSize: 12.sp,color: Color(0xFF5B5B5B)),),
|
// Text('2024.1',style: TextStyle(fontSize: 12.sp,color: Color(0xFF5B5B5B)),),
|
||||||
|
|
|
||||||
|
|
@ -40,19 +40,21 @@ class _JobConditionFilterState extends State<JobConditionFilter> {
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime getMonthStartDate() {
|
DateTime getMonthStartDate() {
|
||||||
|
// DateTime now = DateTime.now();
|
||||||
|
// return DateTime(now.year, now.month, 1); // 获取当前月份的第一天
|
||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
return DateTime(now.year, now.month, 1); // 获取当前月份的第一天
|
return now.subtract(const Duration(days: 29));
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime getMonthEndDate() {
|
DateTime getMonthEndDate() {
|
||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
int nextMonth = now.month + 1;
|
// int nextMonth = now.month + 1;
|
||||||
if (nextMonth > 12) {
|
// if (nextMonth > 12) {
|
||||||
nextMonth = 1;
|
// nextMonth = 1;
|
||||||
now = now.add(Duration(days: 31 - now.day)); // 跨年了,所以加到当前月的最后一天
|
// now = now.add(Duration(days: 31 - now.day)); // 跨年了,所以加到当前月的最后一天
|
||||||
} else {
|
// } else {
|
||||||
now = now.add(Duration(days: DateTime(now.year, nextMonth, 0).day - now.day)); // 加到下个月的第一天的前一天,即本月最后一天
|
// now = now.add(Duration(days: DateTime(now.year, nextMonth, 0).day - now.day)); // 加到下个月的第一天的前一天,即本月最后一天
|
||||||
}
|
// }
|
||||||
return now;
|
return now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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/storage.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/toast_utils.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/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/routes/app_pages.dart';
|
||||||
import 'package:making_school_asignment_app/common/store/app_storage_key.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 {
|
class LoginLogic extends GetxController with RequestToolMixin {
|
||||||
final LoginState state = LoginState();
|
final LoginState state = LoginState();
|
||||||
|
|
||||||
@override
|
|
||||||
void onReady() {
|
|
||||||
// TODO: implement onReady
|
|
||||||
super.onReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|
@ -37,6 +30,8 @@ class LoginLogic extends GetxController with RequestToolMixin {
|
||||||
state.passwordController.text = pwd;
|
state.passwordController.text = pwd;
|
||||||
state.keepPwd.value = true;
|
state.keepPwd.value = true;
|
||||||
}
|
}
|
||||||
|
state.userNameController.text = "AppleTester";
|
||||||
|
state.passwordController.text = "AppleTester123!";
|
||||||
|
|
||||||
state.pwdFocus = FocusNode();
|
state.pwdFocus = FocusNode();
|
||||||
state.theFocus = FocusNode();
|
state.theFocus = FocusNode();
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,12 @@ class _LoginPageState extends State<LoginPage> {
|
||||||
statusBarIconBrightness: Brightness.light,
|
statusBarIconBrightness: Brightness.light,
|
||||||
statusBarColor: Colors.transparent, //状态栏背景颜色
|
statusBarColor: Colors.transparent, //状态栏背景颜色
|
||||||
));
|
));
|
||||||
Future.delayed(Duration.zero, () => upgradeLogic.getAppUpgrade(context));
|
|
||||||
Future.delayed(Duration.zero, () => sysProtocol(context));
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
void dispose() {
|
await sysProtocol(context);
|
||||||
Get.delete<LoginLogic>();
|
await Future.delayed(Duration.zero, () => upgradeLogic.getAppUpgrade(context));
|
||||||
super.dispose();
|
});
|
||||||
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -231,7 +228,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||||
padding: EdgeInsets.only(right: 0.w),
|
padding: EdgeInsets.only(right: 0.w),
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
return Transform.scale(
|
return Transform.scale(
|
||||||
scale: 1.2,
|
scale: 1.0,
|
||||||
child: Checkbox(
|
child: Checkbox(
|
||||||
// activeColor: Colors.transparent, //去掉勾选时背景颜色
|
// activeColor: Colors.transparent, //去掉勾选时背景颜色
|
||||||
|
|
||||||
|
|
@ -309,8 +306,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 50.h,
|
height: 50.h,
|
||||||
child: Text('登 录', style: TextStyle(fontSize: 16.sp, color: Colors.white)),
|
child: quickText('登 录', size: 18.sp, color: Colors.white));
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -320,7 +316,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||||
padding: EdgeInsets.only(right: 0.w),
|
padding: EdgeInsets.only(right: 0.w),
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
return Transform.scale(
|
return Transform.scale(
|
||||||
scale: 1.2,
|
scale: 1.0,
|
||||||
child: Checkbox(
|
child: Checkbox(
|
||||||
activeColor: Theme.of(context).primaryColor,
|
activeColor: Theme.of(context).primaryColor,
|
||||||
checkColor: Colors.white,
|
checkColor: Colors.white,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import 'package:making_school_asignment_app/page/home_page/children/class_studen
|
||||||
import 'package:making_school_asignment_app/page/home_page/children/fav_student/fav_student_binding.dart';
|
import 'package:making_school_asignment_app/page/home_page/children/fav_student/fav_student_binding.dart';
|
||||||
import 'package:making_school_asignment_app/page/home_page/children/fav_student/fav_student_view.dart';
|
import 'package:making_school_asignment_app/page/home_page/children/fav_student/fav_student_view.dart';
|
||||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/configuration_files/index.dart';
|
import 'package:making_school_asignment_app/page/home_page/children/homework_review/configuration_files/index.dart';
|
||||||
|
import 'package:making_school_asignment_app/page/home_page/children/homework_review/configuration_files/zoom_logic.dart';
|
||||||
import 'package:making_school_asignment_app/page/home_page/children/homework_review/index.dart';
|
import 'package:making_school_asignment_app/page/home_page/children/homework_review/index.dart';
|
||||||
import 'package:making_school_asignment_app/page/home_page/children/job_report/job_report_binding.dart';
|
import 'package:making_school_asignment_app/page/home_page/children/job_report/job_report_binding.dart';
|
||||||
import 'package:making_school_asignment_app/page/home_page/children/job_report/job_report_view.dart';
|
import 'package:making_school_asignment_app/page/home_page/children/job_report/job_report_view.dart';
|
||||||
|
|
@ -44,63 +45,30 @@ part 'app_routes.dart';
|
||||||
|
|
||||||
abstract class AppPages {
|
abstract class AppPages {
|
||||||
static final pages = [
|
static final pages = [
|
||||||
GetPage(name: Routes.login, page: () => const LoginPage(), binding: LoginBinding(), transition: Transition.noTransition),
|
GetPage(name: Routes.login, page: () => const LoginPage(), binding: LoginBinding(), transition: getTransition()),
|
||||||
GetPage(name: Routes.register, page: () => const Register(), transition: getTransition()),
|
GetPage(name: Routes.register, page: () => const Register(), transition: getTransition()),
|
||||||
GetPage(name: Routes.agreementPage, page: () => const AgreementPage(), binding: LoginBinding(), transition: Transition.noTransition),
|
GetPage(name: Routes.agreementPage, page: () => const AgreementPage(), binding: LoginBinding(), transition: getTransition()),
|
||||||
GetPage(name: Routes.home, page: () => const HomePage(), binding: HomeBinding(), transition: Transition.noTransition),
|
GetPage(name: Routes.home, page: () => const HomePage(), binding: HomeBinding(), transition: getTransition()),
|
||||||
GetPage(name: Routes.startPage, page: () => const StartPage(), binding: StartPageIndexBinding(), transition: Transition.noTransition),
|
GetPage(name: Routes.startPage, page: () => const StartPage(), binding: StartPageIndexBinding(), transition: getTransition()),
|
||||||
GetPage(name: Routes.myInfo, page: () => const MyInfo(), transition: Transition.noTransition),
|
GetPage(name: Routes.myInfo, page: () => const MyInfo(), transition: getTransition()),
|
||||||
GetPage(name: Routes.work, page: () => const WorkPage(), binding: WorkBinding(), transition: Transition.noTransition),
|
GetPage(name: Routes.work, page: () => const WorkPage(), binding: WorkBinding(), transition: getTransition()),
|
||||||
GetPage(name: Routes.otherPage, page: () => const OhterPage(), transition: Transition.noTransition),
|
GetPage(name: Routes.otherPage, page: () => const OhterPage(), transition: getTransition()),
|
||||||
GetPage(name: Routes.readOverPage, page: () => const ReadOverPage(), binding: ReadOverBinding(), transition: Transition.noTransition),
|
GetPage(name: Routes.readOverPage, page: () => const ReadOverPage(), binding: ReadOverBinding(), transition: getTransition()),
|
||||||
GetPage(
|
GetPage(name: Routes.studentHistoryWorkPage, page: () => const StudentHistoryWorkPage(), binding: StudentHistoryWorkBinding(), transition: getTransition()),
|
||||||
name: Routes.studentHistoryWorkPage,
|
GetPage(name: Routes.classStudentPage, page: () => const ClassStudentPage(), binding: ClassStudentBinding(), transition: getTransition()),
|
||||||
page: () => const StudentHistoryWorkPage(),
|
GetPage(name: Routes.annotateClassPage, page: () => const AnnotateClassPage(), binding: AnnotateClassBinding(), transition: getTransition()),
|
||||||
binding: StudentHistoryWorkBinding(),
|
GetPage(name: Routes.quickDataCheckPage, page: () => const QuickDataCheckPage(), binding: QuickDataCheckBinding(), transition: getTransition()),
|
||||||
transition: Transition.noTransition),
|
GetPage(name: Routes.jobReportPage, page: () => const JobReportPage(), binding: JobReportBinding(), transition: getTransition()),
|
||||||
GetPage(name: Routes.classStudentPage, page: () => const ClassStudentPage(), binding: ClassStudentBinding(), transition: Transition.noTransition),
|
GetPage(name: Routes.studentPersonalPage, page: () => const StudentPersonalPage(), binding: StudentPersonalBinding(), transition: getTransition()),
|
||||||
GetPage(
|
GetPage(name: Routes.studentWorkDetailPage, page: () => const StudentWorkDetailPage(), binding: StudentWorkDetailBinding(), transition: getTransition()),
|
||||||
name: Routes.annotateClassPage, page: () => const AnnotateClassPage(), binding: AnnotateClassBinding(), transition: Transition.noTransition),
|
GetPage(name: Routes.knowledgePointsGraspPage, page: () => const KnowledgePointsGraspPage(), binding: KnowledgePointsGraspBinding(), transition: getTransition()),
|
||||||
GetPage(
|
GetPage(name: Routes.knowledgePointsGraspDetailPage, page: () => const KnowledgePointsGraspDetailPage(), binding: KnowledgePointsGraspDetailBinding(), transition: getTransition()),
|
||||||
name: Routes.quickDataCheckPage,
|
GetPage(name: Routes.answerTrajectoryPage, page: () => const AnswerTrajectoryPage(), binding: AnswerTrajectoryBinding(), transition: getTransition()),
|
||||||
page: () => const QuickDataCheckPage(),
|
GetPage(name: Routes.answerTrajectoryDetailPage, page: () => const AnswerTrajectoryDetailPage(), binding: AnswerTrajectoryDetailBinding(), transition: getTransition()),
|
||||||
binding: QuickDataCheckBinding(),
|
|
||||||
transition: Transition.noTransition),
|
|
||||||
GetPage(name: Routes.jobReportPage, page: () => const JobReportPage(), binding: JobReportBinding(), transition: Transition.noTransition),
|
|
||||||
GetPage(
|
|
||||||
name: Routes.studentPersonalPage,
|
|
||||||
page: () => const StudentPersonalPage(),
|
|
||||||
binding: StudentPersonalBinding(),
|
|
||||||
transition: Transition.noTransition),
|
|
||||||
GetPage(
|
|
||||||
name: Routes.studentWorkDetailPage,
|
|
||||||
page: () => const StudentWorkDetailPage(),
|
|
||||||
binding: StudentWorkDetailBinding(),
|
|
||||||
transition: Transition.noTransition),
|
|
||||||
GetPage(
|
|
||||||
name: Routes.knowledgePointsGraspPage,
|
|
||||||
page: () => const KnowledgePointsGraspPage(),
|
|
||||||
binding: KnowledgePointsGraspBinding(),
|
|
||||||
transition: Transition.noTransition),
|
|
||||||
GetPage(
|
|
||||||
name: Routes.knowledgePointsGraspDetailPage,
|
|
||||||
page: () => const KnowledgePointsGraspDetailPage(),
|
|
||||||
binding: KnowledgePointsGraspDetailBinding(),
|
|
||||||
transition: Transition.noTransition),
|
|
||||||
GetPage(
|
|
||||||
name: Routes.answerTrajectoryPage,
|
|
||||||
page: () => const AnswerTrajectoryPage(),
|
|
||||||
binding: AnswerTrajectoryBinding(),
|
|
||||||
transition: Transition.noTransition),
|
|
||||||
GetPage(
|
|
||||||
name: Routes.answerTrajectoryDetailPage,
|
|
||||||
page: () => const AnswerTrajectoryDetailPage(),
|
|
||||||
binding: AnswerTrajectoryDetailBinding(),
|
|
||||||
transition: Transition.noTransition),
|
|
||||||
// 批阅主页(作业、考试)
|
// 批阅主页(作业、考试)
|
||||||
GetPage(name: Routes.reviewHomework, page: () => const HomeworkReview(), binding: HomeworkReviewBinding(), transition: getTransition()),
|
GetPage(name: Routes.reviewHomework, page: () => const HomeworkReview(), bindings: [HomeworkReviewBinding(), HomeworkReviewZoomBinding()], transition: getTransition()),
|
||||||
GetPage(name: Routes.reviewExam, page: () => const HomeworkReview(), binding: HomeworkReviewBinding(), transition: getTransition()),
|
// GetPage(name: Routes.reviewExam, page: () => const HomeworkReview(), bindings: [HomeworkReviewBinding(), HomeworkReviewZoomBinding()], transition: getTransition()),
|
||||||
GetPage(name: Routes.favStudentPage, page: () => const FavStudentPage(), binding: FavStudentBinding(), transition: Transition.noTransition),
|
GetPage(name: Routes.favStudentPage, page: () => const FavStudentPage(), binding: FavStudentBinding(), transition: getTransition()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ dependencies:
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
# 路由、依赖、状态插件
|
# 路由、依赖、状态插件
|
||||||
get: ^4.6.5
|
get: ^4.7.2
|
||||||
# loading插件
|
# loading插件
|
||||||
flutter_easyloading: ^3.0.5
|
flutter_easyloading: ^3.0.5
|
||||||
# 日志插件
|
# 日志插件
|
||||||
|
|
@ -65,6 +65,7 @@ dependencies:
|
||||||
flutter_widget_from_html_core: ^0.14.12
|
flutter_widget_from_html_core: ^0.14.12
|
||||||
# APP内存及本地存储插件
|
# APP内存及本地存储插件
|
||||||
get_storage: ^2.0.3
|
get_storage: ^2.0.3
|
||||||
|
zoom_widget: ^2.0.1
|
||||||
|
|
||||||
# start retrofit请求封装
|
# start retrofit请求封装
|
||||||
retrofit: ^4.1.0
|
retrofit: ^4.1.0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue