Compare commits

...

7 Commits

Author SHA1 Message Date
DESKTOP-I3JPKHK\wy 8fd6c57874 Merge branch 'mcy_new' 2025-09-12 14:00:16 +08:00
DESKTOP-I3JPKHK\wy db04323e36 修复升级后的BUG 2025-09-11 17:23:14 +08:00
DESKTOP-I3JPKHK\wy e34234da09 补充提交处理BUG 2025-09-11 17:15:15 +08:00
DESKTOP-I3JPKHK\wy 8209b12459 补充提交 2025-09-11 14:38:22 +08:00
DESKTOP-I3JPKHK\wy b7478fd785 修复全部BUG 2025-09-11 14:25:23 +08:00
DESKTOP-I3JPKHK\wy b6af1f57a4 修复优化页面,修复部分问题。暂时提交 2025-09-10 16:49:25 +08:00
DESKTOP-I3JPKHK\wy 70f658f87d 优化完成批阅试图和视图视图滚动条联动 2025-09-09 15:58:30 +08:00
79 changed files with 3166 additions and 1722 deletions

View File

@ -0,0 +1,3 @@
{
"flutter": "3.32.0"
}

View File

@ -42,3 +42,6 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release
# FVM Version Cache
.fvm/

View File

@ -0,0 +1,3 @@
{
"dart.flutterSdkPath": ".fvm/versions/3.32.0"
}

View File

@ -37,6 +37,10 @@ android {
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
} }
kotlinOptions {
jvmTarget = "1.8"
}
signingConfigs { signingConfigs {
release { release {
keyAlias keystoreProperties['keyAlias'] keyAlias keystoreProperties['keyAlias']

View File

@ -4,6 +4,7 @@
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true"> android:enableOnBackInvokedCallback="true">
<!-- windowTranslucentStatus 设置顶部状态栏使用-->
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@ -12,6 +13,7 @@
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowTranslucentStatus="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 341 KiB

View File

@ -3,10 +3,10 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:forceDarkAllowed">false</item> <item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#8C68FF</item> <item name="android:windowSplashScreenBackground">#FFFFFF</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item> <item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -6,8 +6,8 @@
the Flutter engine draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item> <item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -3,10 +3,10 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:forceDarkAllowed">false</item> <item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#8C68FF</item> <item name="android:windowSplashScreenBackground">#FFFFFF</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item> <item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -6,8 +6,8 @@
the Flutter engine draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item> <item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -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\://mirrors.cloud.tencent.com/gradle/gradle-7.6.3-all.zip distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.10.2-all.zip

View File

@ -18,8 +18,8 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false id "com.android.application" version "8.8.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false id "org.jetbrains.kotlin.android" version "1.8.22" apply false
} }
include ":app" include ":app"

View File

@ -0,0 +1,109 @@
@echo off
SETLOCAL EnableDelayedExpansion
:: 设置 UTF-8 代码页以支持中文显示
chcp 65001 > nul
:: 获取项目根目录
for %%I in ("%~dp0.") do set "PROJECT_DIR=%%~fI"
:: 1. 强制终止所有Java进程Gradle相关
echo Terminating processes...
taskkill /f /im java.exe 2>nul
taskkill /f /im gradlew.exe 2>nul
timeout /t 2 /nobreak >nul
:: 修复后的Flutter构建脚本
echo Running flutter clean...
call flutter clean
if !ERRORLEVEL! neq 0 (
echo flutter clean failed with error: !ERRORLEVEL!
pause
exit /b 1
)
:: 3. 强制删除构建目录(带重试机制)
echo Removing residual files
call :ForceDelete "build"
call :ForceDelete ".dart_tool"
call :ForceDelete "pubspec.lock"
echo Running flutter pub get...
call flutter pub get
if !ERRORLEVEL! neq 0 (
echo flutter pub get failed with error: !ERRORLEVEL!
pause
exit /b 1
)
echo Running build_runner...
call flutter packages pub run build_runner build --delete-conflicting-outputs
if !ERRORLEVEL! neq 0 (
echo build_runner failed with error: !ERRORLEVEL!
pause
exit /b 1
)
echo Building release APK...
call flutter build apk --release
if !ERRORLEVEL! neq 0 (
echo APK build failed with error: !ERRORLEVEL!
pause
exit /b 1
)
:: 新增打开APK所在文件夹
echo Opening APK directory
if exist "build\app\outputs\flutter-apk\" (
explorer "build\app\outputs\flutter-apk\"
) else (
echo APK directory does not exist
)
echo.
echo All steps completed successfully!
echo APK file generated at: %PROJECT_DIR%\build\app\outputs\flutter-apk\
echo.
timeout /t 3 /nobreak >nul
exit /b 0
:: 函数区 -------------------------------------------------
:ForceDelete
setlocal
set "target=%~1"
set "max_retries=3"
set "retry_delay=1"
:: 检查目标是否存在
if not exist "%target%" (
endlocal
goto :eof
)
:: 尝试多次删除
for /l %%i in (1,1,%max_retries%) do (
echo Attempting to delete %target% (try %%i of %max_retries%)
:: 先尝试删除文件
del /f /q /s "%target%\*" 2>nul
:: 再尝试删除目录
rmdir /s /q "%target%" 2>nul
:: 检查是否删除成功
if not exist "%target%" (
echo Successfully deleted %target%
endlocal
goto :eof
)
:: 如果未成功,等待后重试
if %%i lss %max_retries% (
timeout /t %retry_delay% /nobreak >nul
set /a retry_delay*=2 :: 指数退避策略
)
)
:: 如果所有尝试都失败
echo Warning: Could not completely delete %target%, some files may be in use
endlocal
goto :eof

View File

@ -5,13 +5,15 @@ flutter_native_splash:
# 如需恢复默认的白屏,执行如下命令 # 如需恢复默认的白屏,执行如下命令
# flutter pub run flutter_native_splash:remove # flutter pub run flutter_native_splash:remove
# 设置闪屏页的默认态logo或背景图片路径 # 设置闪屏页的默认态logo或背景图片路径
color: "#8C68FF" background_image: assets/images/splash_native.png
image_ios: assets/images/splash_native.png
background_image_android: assets/images/splash_native.png # 全屏模式
fullscreen: true
android_12: android_12:
color: "#FFFFFF"
image: assets/images/logo_splash.png image: assets/images/logo_splash.png
android: true android: true
ios: true ios: true
android_gravity: clip_vertical # android_gravity: clip_vertical
ios_content_mode: scaleToFill # ios_content_mode: scaleToFill

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 69 B

View File

@ -17,7 +17,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView> <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
@ -38,7 +38,7 @@
</scene> </scene>
</scenes> </scenes>
<resources> <resources>
<image name="LaunchImage" width="751" height="1734"/> <image name="LaunchImage" width="168" height="185"/>
<image name="LaunchBackground" width="1" height="1"/> <image name="LaunchBackground" width="1" height="1"/>
</resources> </resources>
</document> </document>

View File

@ -38,7 +38,7 @@
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>UIStatusBarHidden</key> <key>UIStatusBarHidden</key>
<false/> <true/>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>

View File

@ -1,14 +1,6 @@
import 'package:dio/dio.dart' hide Headers; import 'package:dio/dio.dart' hide Headers;
import 'package:making_school_asignment_app/common/job/app_version.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/favor_param.dart';
import 'package:making_school_asignment_app/common/job/marking_models/original_manuscript_handwriting_params.dart';
import 'package:making_school_asignment_app/common/job/marking_models/review_submission_params.dart';
import 'package:making_school_asignment_app/common/job/user_register_params.dart';
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/job_handwriting.dart';
import 'package:retrofit/retrofit.dart';
import 'package:making_school_asignment_app/common/job/annotated_class.dart'; import 'package:making_school_asignment_app/common/job/annotated_class.dart';
import 'package:making_school_asignment_app/common/job/app_version.dart';
import 'package:making_school_asignment_app/common/job/class_item.dart'; import 'package:making_school_asignment_app/common/job/class_item.dart';
import 'package:making_school_asignment_app/common/job/common/app_version_model.dart'; import 'package:making_school_asignment_app/common/job/common/app_version_model.dart';
import 'package:making_school_asignment_app/common/job/common/base_app_version.dart'; import 'package:making_school_asignment_app/common/job/common/base_app_version.dart';
@ -17,13 +9,21 @@ import 'package:making_school_asignment_app/common/job/enum_subject.dart';
import 'package:making_school_asignment_app/common/job/homework_details.dart'; import 'package:making_school_asignment_app/common/job/homework_details.dart';
import 'package:making_school_asignment_app/common/job/knowledge_points_grasp.dart'; import 'package:making_school_asignment_app/common/job/knowledge_points_grasp.dart';
import 'package:making_school_asignment_app/common/job/knowledge_report_detail.dart'; import 'package:making_school_asignment_app/common/job/knowledge_report_detail.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/favor_param.dart';
import 'package:making_school_asignment_app/common/job/marking_models/original_manuscript_handwriting_params.dart';
import 'package:making_school_asignment_app/common/job/marking_models/review_submission_params.dart';
import 'package:making_school_asignment_app/common/job/student_history.dart'; import 'package:making_school_asignment_app/common/job/student_history.dart';
import 'package:making_school_asignment_app/common/job/student_history_params.dart'; import 'package:making_school_asignment_app/common/job/student_history_params.dart';
import 'package:making_school_asignment_app/common/job/student_item.dart'; import 'package:making_school_asignment_app/common/job/student_item.dart';
import 'package:making_school_asignment_app/common/job/student_personal_info.dart'; import 'package:making_school_asignment_app/common/job/student_personal_info.dart';
import 'package:making_school_asignment_app/common/job/user_info_detail.dart'; import 'package:making_school_asignment_app/common/job/user_info_detail.dart';
import 'package:making_school_asignment_app/common/job/user_register_params.dart';
import 'package:making_school_asignment_app/common/job/work_student.dart'; import 'package:making_school_asignment_app/common/job/work_student.dart';
import 'package:making_school_asignment_app/common/job/work_student_params.dart'; import 'package:making_school_asignment_app/common/job/work_student_params.dart';
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/job_handwriting.dart';
import 'package:retrofit/retrofit.dart';
part 'retrofit_client.g.dart'; part 'retrofit_client.g.dart';
@ -64,7 +64,7 @@ abstract class RetrofitClient {
// //
@GET("/api/rbac/Student/GetPriorityAnnotateStudents") @GET("/api/rbac/Student/GetPriorityAnnotateStudents")
Future<List<StudentItem>> getStudentList(@Query('ClassId') String classId, @Query('Subject') int subject); Future<List<StudentItem>> getStudentList(@Query('ClassId') String classId, @Query('Subject') int? subject);
// //
@GET("/api/hms/Annotate/GetUnAnnotateList") @GET("/api/hms/Annotate/GetUnAnnotateList")
@ -88,16 +88,22 @@ abstract class RetrofitClient {
// //
@POST("/api/rbac/Student/PriorityAnnotateStudent") @POST("/api/rbac/Student/PriorityAnnotateStudent")
Future getAnnotateStudent(@Field() String classId, @Field() int studentId, @Field() bool priorityAnnotate, @Field() int subject); Future getAnnotateStudent(
@Field() String classId, @Field() int studentId, @Field() bool priorityAnnotate, @Field() int? subject);
// //
@GET("/api/hms/HmsReport/GetStudentHomework") @GET("/api/hms/HmsReport/GetStudentHomework")
Future<StudentPersonalInfo> getStudentHomework(@Query('HomeworkId') String homeworkId, @Query('StudentId') int studentId); Future<StudentPersonalInfo> getStudentHomework(
@Query('HomeworkId') String homeworkId, @Query('StudentId') int studentId);
// //
@GET("/api/hms/HmsReport/GetKnowledgeReport") @GET("/api/hms/HmsReport/GetKnowledgeReport")
Future<List<KnowledgePointsGrasp>> getKnowledgeReport( Future<List<KnowledgePointsGrasp>> getKnowledgeReport(
@Query('DateStart') String dateStart, @Query('DateEnd') String dateEnd, @Query('KnowledgeName') String knowledgeName,@Query('ClassId') String classId,@Query('Subject') int subject); @Query('DateStart') String dateStart,
@Query('DateEnd') String dateEnd,
@Query('KnowledgeName') String knowledgeName,
@Query('ClassId') String classId,
@Query('Subject') int? subject);
// //
@GET("/api/hms/HmsReport/GetKnowledgeReportDetail") @GET("/api/hms/HmsReport/GetKnowledgeReportDetail")
@ -106,8 +112,8 @@ abstract class RetrofitClient {
// //
@GET("/api/hms/HmsReport/GetQuestionStudentState") @GET("/api/hms/HmsReport/GetQuestionStudentState")
Future<List<Students>> getQuestionStudentState( Future<List<Students>> getQuestionStudentState(@Query('HomeworkId') String homeworkId,
@Query('HomeworkId') String homeworkId, @Query('TemplateId') int templateId, @Query('QuestionNo') String questionNo); @Query('TemplateId') int templateId, @Query('QuestionNo') String questionNo);
// //
@POST("/api/hms/Homework/Collect") @POST("/api/hms/Homework/Collect")
@ -124,7 +130,8 @@ abstract class RetrofitClient {
// //
@GET("/api/hms/FavStudent/GetList") @GET("/api/hms/FavStudent/GetList")
Future<BasePageData<HomeworkFavs>> getFavList(@Query('HomeworkName') String homeworkName, @Query('ClassId') String classId); Future<BasePageData<HomeworkFavs>> getFavList(
@Query('HomeworkName') String homeworkName, @Query('ClassId') String classId);
// () // ()
@POST("/api/hms/Annotate/AllCorrect") @POST("/api/hms/Annotate/AllCorrect")
@ -148,5 +155,6 @@ abstract class RetrofitClient {
// APP // APP
@GET("/api/infra/AppVersion/Get") @GET("/api/infra/AppVersion/Get")
Future<AppVersion?> getLastAppVersion(@Query('appName') String appName, @Query('ftuType') int ftuType); // ftuType 1 2IOS 3windows Future<AppVersion?> getLastAppVersion(
@Query('appName') String appName, @Query('ftuType') int ftuType); // ftuType 1 2IOS 3windows
} }

View File

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
///
class AppStateController extends GetxController with WidgetsBindingObserver {
//
final appLifecycleState = AppLifecycleState.resumed.obs;
// 便
bool get isAppInForeground => appLifecycleState.value == AppLifecycleState.resumed;
@override
void onInit() {
super.onInit();
WidgetsBinding.instance.addObserver(this); //
}
@override
void onClose() {
WidgetsBinding.instance.removeObserver(this); //
super.onClose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
appLifecycleState.value = state; //
// if (isAppInForeground) {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// Get.find<UpgradeLogic>().initiateVersionCheck(Get.context!);
// });
// }
}
}

View File

@ -1,52 +1,61 @@
import 'package:get/get.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:making_school_asignment_app/common/config/request_config.dart'; 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, includeIfNull: false) @JsonSerializable(checked: true, includeIfNull: false)
class DoPaperDetailsResult extends Object { class DoPaperDetailsResult {
@JsonKey(name: 'templateIds') @JsonKey(name: 'templateIds')
Map<String, bool> templateIds; final Map<String, bool> templateIds;
// /// ID键列表
@JsonKey(name: 'templateIdKeys') @JsonKey(name: 'templateIdKeys')
List<int>? templateIdKeys; List<int>? templateIdKeys;
// /// ID键映射表
@JsonKey(name: 'templateIdKeyMap') @JsonKey(name: 'templateIdKeyMap')
Map<int, int>? templateIdKeyMap; Map<int, int>? templateIdKeyMap;
@JsonKey(name: 'submitStudents', toJson: _paperStudentsToJson) // ///
List<PaperStudents> students; @JsonKey(name: 'submitStudents', toJson: _paperStudentsToJson)
final List<PaperStudents> students;
/// ID
@JsonKey(name: 'templateId') @JsonKey(name: 'templateId')
int templateId; final int templateId;
/// ID
@JsonKey(name: 'studentId') @JsonKey(name: 'studentId')
int studentId; final int studentId;
// ///
@JsonKey(name: 'priority', defaultValue: false) @JsonKey(name: 'priority', defaultValue: false)
bool priority; bool priority;
@JsonKey(name: 'annotatedCount') // ///
int annotatedCount; @JsonKey(name: 'annotatedCount')
final int annotatedCount;
@JsonKey(name: 'submitCount') // ///
int submitCount; @JsonKey(name: 'submitCount')
final int submitCount;
@JsonKey(name: 'zgtAnswer') // ///
@JsonKey(name: 'zgtAnswer')
String zgtAnswer; String zgtAnswer;
@JsonKey(name: 'showZgtAnnotate') // ///
@JsonKey(name: 'showZgtAnnotate')
String? showZgtAnnotate; String? showZgtAnnotate;
@JsonKey(name: 'zgtAnnotate') // ///
@JsonKey(name: 'zgtAnnotate')
String? zgtAnnotate; String? zgtAnnotate;
@JsonKey(name: 'lastAnswerTime') // ///
String lastAnswerTime; @JsonKey(name: 'lastAnswerTime')
final String lastAnswerTime;
// //
@JsonKey(name: 'annotateTime') @JsonKey(name: 'annotateTime')
@ -68,7 +77,6 @@ class DoPaperDetailsResult extends Object {
@JsonKey(name: 'continuePage', toJson: _continuePageToJson) @JsonKey(name: 'continuePage', toJson: _continuePageToJson)
ContinuePage? continuePage; ContinuePage? continuePage;
@JsonKey(name: 'nextPage', toJson: _nextPageToJson) @JsonKey(name: 'nextPage', toJson: _nextPageToJson)
NextPage? nextPage; NextPage? nextPage;
@ -102,35 +110,42 @@ class DoPaperDetailsResult extends Object {
this.annotateTime, this.annotateTime,
this.showZgtAnnotate, this.showZgtAnnotate,
{this.needAnnotate = false}) { {this.needAnnotate = false}) {
if (templateIds.keys.isNotEmpty) { // ID键列表和映射表
templateIdKeys = templateIds.keys.map((e) => int.parse(e)).toList(); if (templateIds.isNotEmpty) {
templateIdKeys = templateIds.keys.map(int.parse).toList();
templateIdKeyMap = <int, int>{}; templateIdKeyMap = <int, int>{};
for (var i = 1; i <= templateIdKeys!.length; i++) { for (int i = 0; i < templateIdKeys!.length; i++) {
var theVal = templateIdKeys![i - 1]; final templateId = templateIdKeys![i];
templateIdKeyMap![i] = theVal; templateIdKeyMap![i + 1] = templateId;
templateIdKeyMap![theVal] = i; templateIdKeyMap![templateId] = i + 1;
} }
} }
// //
if (students.isNotEmpty) { if (students.isNotEmpty) {
var currentStudent = students.firstWhereOrNull((e) => e.id == studentId); try {
if (currentStudent != null) priority = currentStudent.isPriority; final currentStudent = students.firstWhere((e) => e.id == studentId);
priority = currentStudent.isPriority;
} catch (e) {
//
}
} }
// URL
if (!zgtAnswer.contains(RequestConfig.imgUrl)) { if (!zgtAnswer.contains(RequestConfig.imgUrl)) {
zgtAnswer = '${RequestConfig.imgUrl}$zgtAnswer?$lastAnswerTime'; zgtAnswer = '${RequestConfig.imgUrl}$zgtAnswer?$lastAnswerTime';
} }
if (zgtAnnotate?.isNotEmpty ?? false) { // URL
showZgtAnnotate = RequestConfig.imgUrl + zgtAnnotate!; // if (zgtAnnotate?.isNotEmpty == true) {
if (annotateTime != null) showZgtAnnotate = '${showZgtAnnotate!}?$annotateTime'; showZgtAnnotate = RequestConfig.imgUrl + zgtAnnotate!;
if (annotateTime != null) {
showZgtAnnotate = '${showZgtAnnotate!}?$annotateTime';
}
} }
// //
if (annotateTime == null || studentQuestions.indexWhere((e) => e.studentScore == null) != -1) { needAnnotate = annotateTime == null || studentQuestions.any((e) => e.studentScore == null);
needAnnotate = true;
}
// print('学生作答图片:${annotatedCount}'); // print('学生作答图片:${annotatedCount}');
// print('老师批注图片提交数量:${submitCount}'); // print('老师批注图片提交数量:${submitCount}');
} }
@ -139,14 +154,15 @@ class DoPaperDetailsResult extends Object {
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>> _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 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>? _nextPageToJson(NextPage? example) => example?.toJson();
static Map<String, dynamic>? _lastPageToJson(LastPage? example) => example?.toJson(); static Map<String, dynamic>? _lastPageToJson(LastPage? example) => example?.toJson();
static Map<String, dynamic>? _continuePageToJson(ContinuePage? example) => example?.toJson(); static Map<String, dynamic>? _continuePageToJson(ContinuePage? example) => example?.toJson();
} }
@JsonSerializable() @JsonSerializable()
@ -260,22 +276,20 @@ class NextPage extends Object {
Map<String, dynamic> toJson() => _$NextPageToJson(this); Map<String, dynamic> toJson() => _$NextPageToJson(this);
} }
@JsonSerializable() @JsonSerializable()
class ContinuePage extends Object { class ContinuePage extends Object {
@JsonKey(name: 'templateId') @JsonKey(name: 'templateId')
int templateId; int templateId;
@JsonKey(name: 'studentId') @JsonKey(name: 'studentId')
int studentId; int studentId;
ContinuePage(this.templateId,this.studentId,); ContinuePage(
this.templateId,
this.studentId,
);
factory ContinuePage.fromJson(Map<String, dynamic> srcJson) => _$ContinuePageFromJson(srcJson); factory ContinuePage.fromJson(Map<String, dynamic> srcJson) => _$ContinuePageFromJson(srcJson);
Map<String, dynamic> toJson() => _$ContinuePageToJson(this); Map<String, dynamic> toJson() => _$ContinuePageToJson(this);
} }

View File

@ -0,0 +1,44 @@
import 'package:get/get.dart';
import '../mixins/request_tool_mixin.dart';
/// getx GetxController的拓展
///
///
/// 1. State
/// ``` dart
/// dart class MyState {
/// final String name;
/// MyState(this.name);
/// }
/// ```
/// 2. Controller
/// ``` dart
/// class MyController extends GetxControllerExpand<MyState> {
/// @override
/// MyState get state => MyState('hello');
/// }
/// ```
/// 3. View
/// ``` dart
/// class MyView extends GetViewExpand<MyController, MyState> {
/// const MyView({super.key});
///
/// @override
/// Widget build(BuildContext context) {
/// return Text(state.name);
/// }
/// }
/// ```
abstract class GetViewExpand<T extends GetxControllerExpand<U>, U> extends GetView<T> {
const GetViewExpand({super.key});
U get state => controller.state;
@override
T get controller => GetInstance().find<T>(tag: tag);
}
abstract class GetxControllerExpand<U> extends GetxController with RequestToolMixin {
U get state;
}

View File

@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
abstract class GetxKeepAliveWidget<T extends GetxController> extends StatefulWidget {
const GetxKeepAliveWidget({super.key});
@override
State<GetxKeepAliveWidget<T>> createState() => _GetxKeepAliveWidgetState<T>();
Widget buildContent(BuildContext context, T controller);
void initState() {}
void dispose() {}
}
class _GetxKeepAliveWidgetState<T extends GetxController> extends State<GetxKeepAliveWidget<T>>
with AutomaticKeepAliveClientMixin {
T get controller => GetInstance().find<T>();
@override
bool get wantKeepAlive => true;
@override
void initState() {
widget.initState();
super.initState();
}
@override
void dispose() {
widget.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
return GetBuilder<T>(
builder: (controller) => widget.buildContent(context, controller),
assignId: true,
);
}
}

View File

@ -1,15 +1,16 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
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:get/get.dart' as getx;
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';
import 'package:making_school_asignment_app/common/job/user_info.dart'; import 'package:making_school_asignment_app/common/job/user_info.dart';
import 'package:making_school_asignment_app/common/store/user_store.dart'; import 'package:making_school_asignment_app/common/store/user_store.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/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../job/user_info_detail.dart'; import '../job/user_info_detail.dart';
import '../utils/storage.dart'; import '../utils/storage.dart';
@ -40,7 +41,7 @@ class RequestTool {
RequestTool._internal(this._client); RequestTool._internal(this._client);
static get instance { static RequestTool get instance {
if (_instance == null) { if (_instance == null) {
RetrofitClient client = _init(); RetrofitClient client = _init();
_instance = RequestTool._internal(client); _instance = RequestTool._internal(client);
@ -166,7 +167,7 @@ class TheError extends Interceptor {
var errorMap = response.data; var errorMap = response.data;
if (errorMap is String && errorMap.length > 0) { if (errorMap is String && errorMap.isNotEmpty) {
try { try {
var errorModel = jsonDecode(errorMap); var errorModel = jsonDecode(errorMap);
var error = errorModel['error']; var error = errorModel['error'];

View File

@ -3,18 +3,26 @@ import 'package:making_school_asignment_app/common/store/user_store.dart';
class EnumUtils { class EnumUtils {
static String formatSubject(int id) { static String formatSubject(int id) {
if (UserStore.to.subjectList.isEmpty || id == null) { if (UserStore.to.subjectList.isEmpty) {
return ''; return '';
} }
try {
EnumSubject item = UserStore.to.subjectList.firstWhere((element) => element.id == id); EnumSubject item = UserStore.to.subjectList.firstWhere((element) => element.id == id);
return item.name ?? ''; return item.name;
} catch (e) {
return '';
}
} }
static String formatGrade(int id) { static String formatGrade(int id) {
if (UserStore.to.gradeList.isEmpty || id == null) { if (UserStore.to.gradeList.isEmpty) {
return ''; return '';
} }
try {
EnumSubject item = UserStore.to.gradeList.firstWhere((element) => element.id == id); EnumSubject item = UserStore.to.gradeList.firstWhere((element) => element.id == id);
return item.name ?? ''; return item.name;
} catch (e) {
return '';
}
} }
} }

View File

@ -0,0 +1,106 @@
// ramer_douglas_peucker.dart
import 'package:flutter/material.dart';
/// --Ramer-Douglas-Peucker, RDP
///
/// RDP算法用于将一条由大量点组成的曲线
///
class RamerDouglasPeucker {
/// 线
///
/// [point]:
/// [start]: 线
/// [end]: 线
/// 线线0
static double _getPerpendicularDistance(Offset point, Offset start, Offset end) {
final double dx = end.dx - start.dx;
final double dy = end.dy - start.dy;
final double lengthSquared = dx * dx + dy * dy;
// 线0
if (lengthSquared == 0.0) {
return (point - start).distance;
}
// 线
// t是投影点在线段方向向量上的比例因子
final double t = ((point.dx - start.dx) * dx + (point.dy - start.dy) * dy) / lengthSquared;
// t限制在[0, 1]线
// t<0startt>1end
final double tClamped = t.clamp(0.0, 1.0);
final Offset projection = Offset(start.dx + tClamped * dx, start.dy + tClamped * dy);
return (point - projection).distance;
}
/// 使RDP简化算法
///
/// [points]:
/// [epsilon]:
///
static List<Offset> simplify(List<Offset> points, double epsilon) {
final int totalPoints = points.length;
//
if (totalPoints < 3) {
return points;
}
// 使
final List<List<int>> stack = [];
// `keepPoint`
final List<bool> keepPoint = List.filled(totalPoints, false);
//
keepPoint[0] = true;
keepPoint[totalPoints - 1] = true;
// [0, totalPoints - 1]
stack.add([0, totalPoints - 1]);
while (stack.isNotEmpty) {
final List<int> currentSegment = stack.removeLast();
final int startIndex = currentSegment[0];
final int endIndex = currentSegment[1];
double maxDistance = 0.0;
int farthestIndex = 0;
// 线startIndex和endIndex构成
for (int i = startIndex + 1; i < endIndex; i++) {
final double distance = _getPerpendicularDistance(points[i], points[startIndex], points[endIndex]);
if (distance > maxDistance) {
maxDistance = distance;
farthestIndex = i;
}
}
// epsilon
if (maxDistance > epsilon) {
//
keepPoint[farthestIndex] = true;
// 线
//
if (farthestIndex - startIndex > 1) {
stack.add([startIndex, farthestIndex]);
}
//
if (endIndex - farthestIndex > 1) {
stack.add([farthestIndex, endIndex]);
}
}
}
//
final List<Offset> simplifiedPoints = [];
for (int i = 0; i < totalPoints; i++) {
if (keepPoint[i]) {
simplifiedPoints.add(points[i]);
}
}
return simplifiedPoints;
}
}

View File

@ -0,0 +1,414 @@
// shape_recognizer.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'ramer_douglas_peucker.dart';
///
enum GestureType {
none, //
check, // ()
cross, // (X)
halfCheck, // ( x)线
slash, // 线 (\ /)线线
}
///
/// Offset点
/// RDP算法线
class ShapeRecognizer {
// --- ---
/// Ramer-Douglas-Peucker epsilon
final double rdpEpsilon;
///
final double checkAngleMin;
///
final double checkAngleMax;
/// 线
final double crossAngleMin;
/// 线
final double crossAngleMax;
/// 线线
/// 0.15 线 15%
final double minIntersectionDistance;
///
///
final double checkLengthRatio;
/// 线线线
final double slashSlopeMin;
/// 线线线
final double slashSlopeMax;
/// 线线线
final double slashRadianMax;
///
ShapeRecognizer({
this.rdpEpsilon = 8.0,
this.checkAngleMin = 60.0,
this.checkAngleMax = 140.0,
this.crossAngleMin = 40.0,
this.crossAngleMax = 160.0,
this.minIntersectionDistance = 0.15,
this.checkLengthRatio = 1.1,
this.slashSlopeMin = 0.5,
this.slashSlopeMax = 2.0,
this.slashRadianMax = 140,
});
/// --- ---
///
/// 122
/// GestureType GestureType.none
GestureType recognize(List<List<Offset>> strokes) {
if (strokes.isEmpty) return GestureType.none; //
switch (strokes.length) {
case 1:
/// () 线 (\ /)
final points = strokes.first; //
if (points.length < 2) return GestureType.none; // 2线
var result = _recognizeCheck(points); //
if (result != GestureType.none) return result;
return _recognizeSlash(points); // 线
case 2:
// []
//
var halfCheckResult = _recognizeHalfCheck(strokes);
if (halfCheckResult != GestureType.none) return halfCheckResult;
//
return _recognizeCross(strokes);
// // (X) ( + 线)
// var crossResult = _recognizeCross(strokes); //
// if (crossResult != GestureType.none) return crossResult;
// return _recognizeHalfCheck(strokes); //
default:
return GestureType.none; //
}
}
/// --- ---
///
/// `_getCheckmarkDetails` `GestureType.check`
GestureType _recognizeCheck(List<Offset> rawPoints) {
return _getCheckmarkDetails(rawPoints) != null ? GestureType.check : GestureType.none;
}
/// 线
/// `_getSlashDetails` 线 `GestureType.slash`
GestureType _recognizeSlash(List<Offset> rawPoints) {
return _getSlashDetails(rawPoints) ? GestureType.slash : GestureType.none;
}
///
/// `_getCrossDetails` `GestureType.cross`
GestureType _recognizeCross(List<List<Offset>> rawStrokes) {
//
return _getCrossDetails(rawStrokes) != null ? GestureType.cross : GestureType.none;
}
///
/// 线
GestureType _recognizeHalfCheck(List<List<Offset>> strokes) {
if (strokes.length != 2) return GestureType.none;
/// 线
var firstStroke = strokes[0];
var lastStroke = strokes[1];
/// ,线
List<Offset>? checkDetails = _getCheckmarkDetails(firstStroke);
if (checkDetails == null) {
///
checkDetails = _getCheckmarkDetails(lastStroke);
if (checkDetails == null) {
return GestureType.none;
} else {
// checkDetails
final temp = firstStroke;
firstStroke = lastStroke;
lastStroke = temp;
}
}
// 1 stroke1 stroke2 线
if (_isHalfCheckCombination(
checkCandidate: firstStroke, crosserCandidate: lastStroke, checkDetails: checkDetails)) {
return GestureType.halfCheck;
}
return GestureType.none;
// if (twoStrokes.length != 2) return GestureType.none;
// final stroke1 = twoStrokes[0];
// final stroke2 = twoStrokes[1];
// // 1: stroke1是勾stroke2是交叉线
// final checkDetails1 = _getCheckmarkDetails(stroke1);
// if (checkDetails1 != null) {
// if (_checkHalfCheckLogic(checkDetails1, stroke2)) {
// return GestureType.halfCheck;
// }
// }
// // 2: stroke2是勾stroke1是交叉线
// final checkDetails2 = _getCheckmarkDetails(stroke2);
// if (checkDetails2 != null) {
// if (_checkHalfCheckLogic(checkDetails2, stroke1)) {
// return GestureType.halfCheck;
// }
// }
// return GestureType.none;
}
///
/// [checkCandidate]
/// [crosserCandidate] 线
/// [checkDetails]
bool _isHalfCheckCombination({
required List<Offset> checkCandidate,
required List<Offset> crosserCandidate,
required List<Offset> checkDetails,
}) {
//
final checkCorner = checkDetails[1];
final checkEndpoint = checkDetails[2];
final simplifiedCrosser = RamerDouglasPeucker.simplify(crosserCandidate, rdpEpsilon);
if (simplifiedCrosser.length < 2) return false;
// 2线
// 使
final intersection = _findIntersectionNearTip(
simplifiedCrosser.first, simplifiedCrosser.last, //
checkCorner, checkEndpoint, //
);
if (intersection != null) {
//
final vCross = simplifiedCrosser.last - simplifiedCrosser.first;
final vCheckLeg = checkEndpoint - checkCorner;
final angle = _calculateAngleBetweenVectors(vCross, vCheckLeg);
if (angle > crossAngleMin && angle < crossAngleMax) {
return true; //
}
}
return false; //
}
///
/// `checkLeg` `crossLine`
Offset? _findIntersectionNearTip(
Offset crossLineStart, Offset crossLineEnd, Offset checkLegStart, Offset checkLegEnd) {
final s1X = crossLineEnd.dx - crossLineStart.dx;
final s1Y = crossLineEnd.dy - crossLineStart.dy;
final s2X = checkLegEnd.dx - checkLegStart.dx;
final s2Y = checkLegEnd.dy - checkLegStart.dy;
final denominator = -s2X * s1Y + s1X * s2Y;
if (denominator.abs() < 1e-6) return null;
// t crossLine
final t =
(s2X * (crossLineStart.dy - checkLegStart.dy) - s2Y * (crossLineStart.dx - checkLegStart.dx)) / denominator;
// s checkLeg
final s =
(-s1Y * (crossLineStart.dx - checkLegStart.dx) + s1X * (crossLineStart.dy - checkLegStart.dy)) / denominator;
// 1: 线穿
final isCrosserIntersectedWell = t > minIntersectionDistance && t < (1 - minIntersectionDistance);
print("minIntersectionDistance: $minIntersectionDistance, t: $t");
// 2: (s > 0.1) (s <= 1.0)
final isCheckLegIntersectedNearTip = s >= 0.1 && s <= 1.0;
print(
"isCheckLegIntersectedNearTip: $isCheckLegIntersectedNearTip,isCrosserIntersectedWell: $isCrosserIntersectedWell, s: $s");
if (isCrosserIntersectedWell && isCheckLegIntersectedNearTip) {
return Offset(crossLineStart.dx + (t * s1X), crossLineStart.dy + (t * s1Y));
}
return null;
}
/// --- ---
///
/// `null`
List<Offset>? _getCheckmarkDetails(List<Offset> rawPoints) {
// 1. 使 RDP
final simplifiedPoints = RamerDouglasPeucker.simplify(rawPoints, rdpEpsilon);
// 3
if (simplifiedPoints.length < 3) return null;
// 2.
int cornerIndex = -1; //
double minAngle = 180.0; //
for (int i = 1; i < simplifiedPoints.length - 1; i++) {
//
final angle = _calculateAngle(simplifiedPoints[i - 1], simplifiedPoints[i], simplifiedPoints[i + 1]);
if (angle < minAngle) {
minAngle = angle;
cornerIndex = i;
}
}
// 3.
if (cornerIndex == -1 || minAngle < checkAngleMin || minAngle > checkAngleMax) return null;
// 4.
final p1 = simplifiedPoints.first, p2 = simplifiedPoints[cornerIndex], p3 = simplifiedPoints.last;
// 5. (p1-p2) (p2-p3)
// p2 p1 p3 p2 `p3.dy < p2.dy` V字
// p2.dy > p1.dy p2在p1下方p3.dy < p2.dy p3在p2上方
if (p2.dy > p1.dy && p3.dy < p2.dy) {
final length1 = (p2 - p1).distance, length2 = (p3 - p2).distance; // 线
// 6. `length2` `length1` `checkLengthRatio`
if (length1 > 0 && length2 > length1 * checkLengthRatio) return [p1, p2, p3]; //
}
return null; //
}
/// 线线
/// 线 RDP
bool _getSlashDetails(List<Offset> points) {
if (points.isEmpty) return false;
final simplified = RamerDouglasPeucker.simplify(points, rdpEpsilon);
// 1. 2线线
if (simplified.length == 2) {
final p1 = simplified.first, p2 = simplified.last;
final deltaX = (p2.dx - p1.dx).abs();
final deltaY = (p2.dy - p1.dy).abs();
if (deltaX < 1e-6) return true;
if (deltaY < 1e-6) return false;
final slope = deltaY / deltaX;
return slope > slashSlopeMin && slope < slashSlopeMax;
}
// 2. 3线
if (simplified.length == 3) {
final p1 = simplified[0];
final p2 = simplified[1]; //
final p3 = simplified[2];
//
final angle = _calculateAngle(p1, p2, p3);
// > 160线
// 线线
// 160
if (angle > slashRadianMax) {
// 线
final overallP1 = points.first;
final overallP3 = points.last;
final deltaX = (overallP3.dx - overallP1.dx).abs();
final deltaY = (overallP3.dy - overallP1.dy).abs();
if (deltaX < 1e-6) return true;
if (deltaY < 1e-6) return false;
final slope = deltaY / deltaX;
return slope > slashSlopeMin && slope < slashSlopeMax;
}
}
// 3. 14线
return false;
}
/// null
/// 线
Offset? _getCrossDetails(List<List<Offset>> twoStrokes) {
if (twoStrokes.length != 2) return null; //
//
final simplifiedStroke1 = RamerDouglasPeucker.simplify(twoStrokes[0], rdpEpsilon);
final simplifiedStroke2 = RamerDouglasPeucker.simplify(twoStrokes[1], rdpEpsilon);
if (simplifiedStroke1.length < 2 || simplifiedStroke2.length < 2) return null; // 线2
// 线使
final intersection = _findIntersection(
simplifiedStroke1.first, simplifiedStroke1.last, simplifiedStroke2.first, simplifiedStroke2.last);
if (intersection == null) return null; //
// 线
final angle = _calculateAngleBetweenVectors(
simplifiedStroke1.last - simplifiedStroke1.first, // 线
simplifiedStroke2.last - simplifiedStroke2.first); // 线
if (angle > crossAngleMin && angle < crossAngleMax) return intersection; //
return null;
}
/// --- ---
/// `p1``p2``p3` 线 `p2`
/// `p1-p2` `p3-p2`
double _calculateAngle(Offset p1, Offset p2, Offset p3) {
return _calculateAngleBetweenVectors(p1 - p2, p3 - p2);
}
/// `v1` `v2`
/// 使 `cosTheta`
double _calculateAngleBetweenVectors(Offset v1, Offset v2) {
final double dotProduct = v1.dx * v2.dx + v1.dy * v2.dy; //
final double magnitude1 = v1.distance; // v1
final double magnitude2 = v2.distance; // v2
if (magnitude1 == 0 || magnitude2 == 0) return 0; // 00
// cosTheta [-1, 1] acos
final cosTheta = (dotProduct / (magnitude1 * magnitude2)).clamp(-1.0, 1.0);
return acos(cosTheta) * 180 / pi; //
}
/// 线 (p1, p2) (p3, p4)
/// 使线
/// 线线线线 `null`
Offset? _findIntersection(Offset p1, Offset p2, Offset p3, Offset p4) {
final double s1X = p2.dx - p1.dx, s1Y = p2.dy - p1.dy; // 线
final double s2X = p4.dx - p3.dx, s2Y = p4.dy - p3.dy; // 线
final double denominator = -s2X * s1Y + s1X * s2Y; // 线
if (denominator == 0) return null; // 线线
// s t
// s 线(p3, p4)
final double s = (-s1Y * (p1.dx - p3.dx) + s1X * (p1.dy - p3.dy)) / denominator;
// t 线(p1, p2)
final double t = (s2X * (p1.dy - p3.dy) - s2Y * (p1.dx - p3.dx)) / denominator;
// 线线
// `s` `t` `minIntersectionDistance` `1 - minIntersectionDistance`
// 线
if (s > minIntersectionDistance &&
s < (1 - minIntersectionDistance) &&
t > minIntersectionDistance &&
t < (1 - minIntersectionDistance)) {
return Offset(p1.dx + (t * s1X), p1.dy + (t * s1Y)); //
}
return null; // 线
}
}

View File

@ -12,18 +12,13 @@ class PermissionDescribePage extends StatefulWidget {
final String describe; final String describe;
final List<Permission> permissions; final List<Permission> permissions;
const PermissionDescribePage( const PermissionDescribePage({required this.title, required this.describe, required this.permissions, super.key});
{required this.title,
required this.describe,
required this.permissions,
super.key});
@override @override
State<PermissionDescribePage> createState() => _PermissionDescribePageState(); State<PermissionDescribePage> createState() => _PermissionDescribePageState();
} }
class _PermissionDescribePageState extends State<PermissionDescribePage> class _PermissionDescribePageState extends State<PermissionDescribePage> with WidgetsBindingObserver {
with WidgetsBindingObserver {
Timer? _timerPermission; Timer? _timerPermission;
bool theOpenAppSettings = false; bool theOpenAppSettings = false;
int theExecutionFrequency = 0; int theExecutionFrequency = 0;
@ -40,7 +35,7 @@ class _PermissionDescribePageState extends State<PermissionDescribePage>
@override @override
void didChangeAppLifecycleState(AppLifecycleState state) { void didChangeAppLifecycleState(AppLifecycleState state) {
print("-didChangeAppLifecycleState-" + state.toString()); print("-didChangeAppLifecycleState-$state");
switch (state) { switch (state) {
case AppLifecycleState.inactive: // case AppLifecycleState.inactive: //
break; break;
@ -97,7 +92,7 @@ class _PermissionDescribePageState extends State<PermissionDescribePage>
); );
} }
permissions.add(p); permissions.add(p);
} else if (await status.isPermanentlyDenied) { } else if (status.isPermanentlyDenied) {
permanentRefusal.add(p); permanentRefusal.add(p);
} }
} }
@ -137,8 +132,7 @@ class _PermissionDescribePageState extends State<PermissionDescribePage>
/// ///
/// @param List<Permission> permissions /// @param List<Permission> permissions
Future<List<Permission>> getStoragePermission( Future<List<Permission>> getStoragePermission(BuildContext context, List<Permission> permissions,
BuildContext context, List<Permission> permissions,
[int executionFrequency = 0]) async { [int executionFrequency = 0]) async {
Map<Permission, PermissionStatus> statusRes = await permissions.request(); Map<Permission, PermissionStatus> statusRes = await permissions.request();
@ -180,8 +174,7 @@ class _PermissionDescribePageState extends State<PermissionDescribePage>
executionFrequency++; executionFrequency++;
} }
if (executionFrequency <= 1) { if (executionFrequency <= 1) {
var res = var res = await getStoragePermission(context, [key], executionFrequency);
await getStoragePermission(context, [key], executionFrequency);
permanentRefusal.addAll(res); permanentRefusal.addAll(res);
} else { } else {
permanentRefusal.add(key); permanentRefusal.add(key);
@ -215,7 +208,7 @@ class _PermissionDescribePageState extends State<PermissionDescribePage>
color: Colors.black.withOpacity(0.5), // color: Colors.black.withOpacity(0.5), //
spreadRadius: 2, // spreadRadius: 2, //
blurRadius: 4, // blurRadius: 4, //
offset: Offset(0, 3), // X Y offset: const Offset(0, 3), // X Y
), ),
], ],
), ),
@ -239,7 +232,7 @@ class PermissionDescribeUtil {
PermissionDescribeUtil._internal(); PermissionDescribeUtil._internal();
static get instance => _instance ??= PermissionDescribeUtil._internal(); static PermissionDescribeUtil get instance => _instance ??= PermissionDescribeUtil._internal();
/// ///
Future<void> toLaunchPermissionRequest( Future<void> toLaunchPermissionRequest(
@ -257,7 +250,7 @@ class PermissionDescribeUtil {
} }
} }
if (isGranted) { if (isGranted) {
Navigator.of(Get.context ?? context).push(PageRouteBuilder( await Navigator.of(Get.context ?? context).push(PageRouteBuilder(
opaque: false, opaque: false,
pageBuilder: (context, animation, secondaryAnimation) { pageBuilder: (context, animation, secondaryAnimation) {
return PermissionDescribePage( return PermissionDescribePage(

View File

@ -2,14 +2,16 @@ import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
class StorageService extends GetxService { class StorageService extends GetxService {
static StorageService get to => Get.find(); static StorageService get to => Get.find<StorageService>();
late final GetStorage _getStorage; late final GetStorage _getStorage;
get storage => _getStorage; get storage => _getStorage;
Future<StorageService> init() async { ///
StorageService._(this._getStorage);
static Future<StorageService> init() async {
await GetStorage.init(); await GetStorage.init();
_getStorage = GetStorage(); return StorageService._(GetStorage());
return this;
} }
T? read<T>(String key) { T? read<T>(String key) {

View File

@ -8,9 +8,9 @@
*/ */
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
class ToastUtils { class ToastUtils {
static void getFluttertoast({ static void getFluttertoast({
@ -60,8 +60,16 @@ class ToastUtils {
EasyLoading.showError(showMsg); EasyLoading.showError(showMsg);
} }
static showLoading() { static showLoading({
EasyLoading.show(status: 'loading...'); bool? dismissOnTap = false,
//
EasyLoadingMaskType? maskType = EasyLoadingMaskType.clear,
}) {
EasyLoading.show(
maskType: maskType, // 使
dismissOnTap: dismissOnTap,
status: 'loading...',
);
} }
static showInfo(String showMsg, {Duration? duration}) { static showInfo(String showMsg, {Duration? duration}) {

View File

@ -1,12 +1,10 @@
import 'dart:convert'; import 'dart:convert';
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';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/homework_details.dart'; 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 '../config/request_config.dart'; import '../config/request_config.dart';
@ -23,17 +21,13 @@ class Utils {
/// ///
static void hideKeyboard() { static void hideKeyboard() {
FocusScopeNode? currentFocus = Get.focusScope?.nearestScope; final currentFocus = Get.focusScope?.nearestScope;
if (currentFocus == null) { if (currentFocus != null && !currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
return;
}
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
} }
} }
void setTimeOut(int seconds, call) => void setTimeOut(int seconds, VoidCallback call) => Future.delayed(Duration(seconds: seconds), call);
Future.delayed(Duration(seconds: seconds), call);
// //
static bool isPad([double mobilePhoneScale = 1.2]) { static bool isPad([double mobilePhoneScale = 1.2]) {
@ -43,9 +37,9 @@ class Utils {
static String getDoubleRemoveZero(double? val, [String? defaultVal]) { static String getDoubleRemoveZero(double? val, [String? defaultVal]) {
try { try {
if (val == null) throw Exception('数据为空'); if (val == null) throw Exception('数据为空');
List<String> _valArr = val.toString().split('.'); List<String> valArr = val.toString().split('.');
if (_valArr.length >= 2) { if (valArr.length >= 2) {
if (int.parse(_valArr[1]) == 0) { if (int.parse(valArr[1]) == 0) {
return val.toInt().toString(); return val.toInt().toString();
} }
return val.toString(); return val.toString();
@ -97,43 +91,30 @@ class Utils {
dataCount.kgtAnswerCount = kgt.where((w) => w.state != 0).length; dataCount.kgtAnswerCount = kgt.where((w) => w.state != 0).length;
dataCount.kgtOkCount = kgt.where((w) => w.state == 3).length; dataCount.kgtOkCount = kgt.where((w) => w.state == 3).length;
dataCount.kgtDtlCount = kgt.length; dataCount.kgtDtlCount = kgt.length;
dataCount.kgtAnswerRate = dataCount.kgtAnswerRate = Utils.calcRate(dataCount.kgtAnswerCount!, dataCount.kgtDtlCount!);
Utils.calcRate(dataCount.kgtAnswerCount!, dataCount.kgtDtlCount!); dataCount.kgtOkRate = Utils.calcRate(dataCount.kgtOkCount!, dataCount.kgtDtlCount!);
dataCount.kgtOkRate =
Utils.calcRate(dataCount.kgtOkCount!, dataCount.kgtDtlCount!);
dataCount.kgtCorrectRate = dataCount.kgtCorrectRate = Utils.calcRate(dataCount.kgtOkCount!, dataCount.kgtAnswerCount!);
Utils.calcRate(dataCount.kgtOkCount!, dataCount.kgtAnswerCount!); dataCount.kgtCount = data.questions.where((w) => w.questionType == 1).length;
dataCount.kgtCount =
data.questions.where((w) => w.questionType == 1).length;
List<Dtls> zgt = data.dtls.where((w) => w.questionType == 2).toList(); List<Dtls> zgt = data.dtls.where((w) => w.questionType == 2).toList();
dataCount.zgtAnswerCount = zgt.where((w) => w.state != 0).length; dataCount.zgtAnswerCount = zgt.where((w) => w.state != 0).length;
dataCount.zgtOkCount = zgt.where((w) => w.state == 3).length; dataCount.zgtOkCount = zgt.where((w) => w.state == 3).length;
dataCount.zgtDtlCount = zgt.length; dataCount.zgtDtlCount = zgt.length;
dataCount.zgtAnswerRate = dataCount.zgtAnswerRate = Utils.calcRate(dataCount.zgtAnswerCount!, dataCount.zgtDtlCount!);
Utils.calcRate(dataCount.zgtAnswerCount!, dataCount.zgtDtlCount!); dataCount.zgtOkRate = Utils.calcRate(dataCount.zgtOkCount!, dataCount.zgtDtlCount!);
dataCount.zgtOkRate =
Utils.calcRate(dataCount.zgtOkCount!, dataCount.zgtDtlCount!);
dataCount.zgtCorrectRate = dataCount.zgtCorrectRate = Utils.calcRate(dataCount.zgtOkCount!, dataCount.zgtAnswerCount!);
Utils.calcRate(dataCount.zgtOkCount!, dataCount.zgtAnswerCount!); dataCount.zgtCount = data.questions.where((w) => w.questionType == 2).length;
dataCount.zgtCount =
data.questions.where((w) => w.questionType == 2).length;
dataCount.studentCount = data.students.length; dataCount.studentCount = data.students.length;
dataCount.priorityStudents = dataCount.priorityStudents = data.students.where((w) => w.priorityAnnotate!).toList();
data.students.where((w) => w.priorityAnnotate!).toList();
// //
dataCount.studentSubmitCount = dataCount.studentSubmitCount = data.students.where((s) => s.state != 0).length;
data.students.where((s) => s.state != 0).length; dataCount.studentSubmitStudents = data.students.where((s) => s.state != 0).toList();
dataCount.studentSubmitStudents =
data.students.where((s) => s.state != 0).toList();
// //
dataCount.noAnswerCount = dataCount.noAnswerCount = data.students.length - dataCount.studentSubmitCount!;
data.students.length - dataCount.studentSubmitCount!; dataCount.noAnswerStudents = data.students.where((s) => s.state == 0).toList();
dataCount.noAnswerStudents =
data.students.where((s) => s.state == 0).toList();
for (var stu in data.students) { for (var stu in data.students) {
stu.kgtStu = kgt.where((w) => w.studentId == stu.studentId).toList(); stu.kgtStu = kgt.where((w) => w.studentId == stu.studentId).toList();
@ -141,16 +122,13 @@ class Utils {
// stu.kgtStu!.sort((a, b) => num.parse(a.questionNo).compareTo(num.parse(b.questionNo))); // stu.kgtStu!.sort((a, b) => num.parse(a.questionNo).compareTo(num.parse(b.questionNo)));
stu.kgtStu!.sort((a, b) { stu.kgtStu!.sort((a, b) {
try { try {
if (RegExp(r'^\d*\.?\d+$').hasMatch(a.questionNo) && if (RegExp(r'^\d*\.?\d+$').hasMatch(a.questionNo) && RegExp(r'^\d*\.?\d+$').hasMatch(b.questionNo)) {
RegExp(r'^\d*\.?\d+$').hasMatch(b.questionNo)) {
return num.parse(a.questionNo).compareTo(num.parse(b.questionNo)); return num.parse(a.questionNo).compareTo(num.parse(b.questionNo));
} else { } else {
throw Exception(); throw Exception();
} }
} catch (e) { } catch (e) {
return a.questionNo return a.questionNo.toLowerCase().compareTo(b.questionNo.toLowerCase());
.toLowerCase()
.compareTo(b.questionNo.toLowerCase());
} }
}); });
stu.kgtOkCount = stu.kgtStu!.where((w) => w.state == 3).length; stu.kgtOkCount = stu.kgtStu!.where((w) => w.state == 3).length;
@ -161,16 +139,13 @@ class Utils {
// stu.zgtStu!.sort((a, b) => num.parse(a.questionNo).compareTo(num.parse(b.questionNo))); // stu.zgtStu!.sort((a, b) => num.parse(a.questionNo).compareTo(num.parse(b.questionNo)));
stu.zgtStu!.sort((a, b) { stu.zgtStu!.sort((a, b) {
try { try {
if (RegExp(r'^\d*\.?\d+$').hasMatch(a.questionNo) && if (RegExp(r'^\d*\.?\d+$').hasMatch(a.questionNo) && RegExp(r'^\d*\.?\d+$').hasMatch(b.questionNo)) {
RegExp(r'^\d*\.?\d+$').hasMatch(b.questionNo)) {
return num.parse(a.questionNo).compareTo(num.parse(b.questionNo)); return num.parse(a.questionNo).compareTo(num.parse(b.questionNo));
} else { } else {
throw Exception(); throw Exception();
} }
} catch (e) { } catch (e) {
return a.questionNo return a.questionNo.toLowerCase().compareTo(b.questionNo.toLowerCase());
.toLowerCase()
.compareTo(b.questionNo.toLowerCase());
} }
}); });
// //
@ -181,43 +156,32 @@ class Utils {
stu.zgtUnrated = stu.zgtStu!.where((w) => w.state == 1).length; stu.zgtUnrated = stu.zgtStu!.where((w) => w.state == 1).length;
// //
stu.zgtAnswerCount = stu.zgtStu!.where((w) => w.state != 0).length; stu.zgtAnswerCount = stu.zgtStu!.where((w) => w.state != 0).length;
stu.isAllCorrect = stu.isAllCorrect = stu.kgtOkCount! + stu.zgtOkCount! == kgt.length + zgt.length ? true : false;
stu.kgtOkCount! + stu.zgtOkCount! == kgt.length + zgt.length
? true
: false;
stu.allOk = data.dtls.where((w) { stu.allOk = data.dtls.where((w) {
if (stu.studentId == w.studentId) { if (stu.studentId == w.studentId) {
stu.useTime = w.useTime; stu.useTime = w.useTime;
} }
for (var que in data.questions) { for (var que in data.questions) {
if (w.templateId == que.templateId && if (w.templateId == que.templateId && w.questionNo == que.questionNo) {
w.questionNo == que.questionNo) {
w.answer = que.answer; w.answer = que.answer;
w.questionPicture = que.questionPicture; w.questionPicture = que.questionPicture;
} }
} }
return w.studentId == stu.studentId && w.state != 3; return w.studentId == stu.studentId && w.state != 3;
}).length ?? }).length;
0; if ((stu.kgtStu!.length - stu.kgtAnswerCount!) + (stu.zgtStu!.length - stu.zgtAnswerCount!) ==
if ((stu.kgtStu!.length - stu.kgtAnswerCount!) +
(stu.zgtStu!.length - stu.zgtAnswerCount!) ==
(stu.kgtStu!.length + stu.zgtStu!.length)) { (stu.kgtStu!.length + stu.zgtStu!.length)) {
stu.allNotDone = true; stu.allNotDone = true;
} else { } else {
stu.allNotDone = false; stu.allNotDone = false;
} }
stu.noAnswerCount = data.dtls stu.noAnswerCount = data.dtls.where((w) => w.state == 0 && stu.studentId == w.studentId).length;
.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) => .where((w) =>
w.studentId == stu.studentId && w.studentId == stu.studentId &&
ques.indexWhere((q) => ques.indexWhere((q) => w.templateId == q.templateId && w.questionNo == q.questionNo) > -1)
w.templateId == q.templateId &&
w.questionNo == q.questionNo) >
-1)
.toList(); .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;
@ -241,15 +205,7 @@ class Utils {
secList.sort(); secList.sort();
/* var maxSec = secList.isNotEmpty ? secList.last : 0; /* var maxSec = secList.isNotEmpty ? secList.last : 0;
var minSec = secList.isNotEmpty ? secList.first : 0;*/ var minSec = secList.isNotEmpty ? secList.first : 0;*/
var maxSec = secList.isNotEmpty final secTime = secList.isNotEmpty ? secList.last - secList.first : 0;
? secList
.reduce((value, element) => value > element ? value : element)
: 0;
var minSec = secList.isNotEmpty
? secList
.reduce((value, element) => value < element ? value : element)
: 0;
var secTime = secList.isNotEmpty ? maxSec - minSec : 0;
stu.allTime = secTime; stu.allTime = secTime;
stu.ttlSec = second2HMS(secTime); stu.ttlSec = second2HMS(secTime);
} }
@ -259,43 +215,32 @@ class Utils {
int num2 = b.state; int num2 = b.state;
return num2.compareTo(num1); return num2.compareTo(num1);
}); });
// //
dataCount.allCorrect = final allCorrectStudents = data.students.where((w) => w.isAllCorrect == true).toList();
data.students.where((w) => w.isAllCorrect == true).length; dataCount.allCorrect = allCorrectStudents.length;
dataCount.allCorrectStudents = dataCount.allCorrectStudents = allCorrectStudents;
data.students.where((w) => w.isAllCorrect == true).toList();
//
dataCount.levelOneCount =
data.students.where((s) => s.okRate! >= 85).length;
dataCount.levelOneStudents =
data.students.where((s) => s.okRate! >= 85).toList();
// final levelOneStudents = data.students.where((s) => s.okRate! >= 85).toList();
dataCount.levelTwoCount = dataCount.levelOneCount = levelOneStudents.length;
data.students.where((s) => s.okRate! < 85 && s.okRate! >= 55).length; dataCount.levelOneStudents = levelOneStudents;
dataCount.levelTwoStudents =
data.students.where((s) => s.okRate! < 85 && s.okRate! >= 55).toList();
// final levelTwoStudents = data.students.where((s) => s.okRate! < 85 && s.okRate! >= 55).toList();
dataCount.levelThreeCount = dataCount.levelTwoCount = levelTwoStudents.length;
data.students.where((s) => s.okRate! < 55 && s.okRate! >= 25).length; dataCount.levelTwoStudents = levelTwoStudents;
dataCount.levelThreeStudents =
data.students.where((s) => s.okRate! < 55 && s.okRate! >= 25).toList();
// final levelThreeStudents = data.students.where((s) => s.okRate! < 55 && s.okRate! >= 25).toList();
dataCount.levelFourCount = dataCount.levelThreeCount = levelThreeStudents.length;
data.students.where((s) => s.okRate! < 25).length; dataCount.levelThreeStudents = levelThreeStudents;
dataCount.levelFourStudents =
data.students.where((s) => s.okRate! < 25).toList(); final levelFourStudents = data.students.where((s) => s.okRate! < 25).toList();
dataCount.levelFourCount = levelFourStudents.length;
dataCount.levelFourStudents = levelFourStudents;
for (var que in data.questions) { for (var que in data.questions) {
List<Dtls> ques = data.dtls List<Dtls> ques =
.where((w) => data.dtls.where((w) => w.templateId == que.templateId && w.questionNo == que.questionNo).toList();
w.templateId == que.templateId && w.questionNo == que.questionNo)
.toList();
que.answerCount = ques.where((w) => w.state != 0).length; que.answerCount = ques.where((w) => w.state != 0).length;
que.answerRate = que.answerRate = Utils.calcRate(que.answerCount!, dataCount.studentCount!);
Utils.calcRate(que.answerCount!, dataCount.studentCount!);
int okCount = ques.where((w) => w.state == 3).length; int okCount = ques.where((w) => w.state == 3).length;
que.okRate = Utils.calcRate(okCount, dataCount.studentCount!); que.okRate = Utils.calcRate(okCount, dataCount.studentCount!);
que.priorityInfo = ques.where((w) { que.priorityInfo = ques.where((w) {
@ -308,9 +253,12 @@ class Utils {
}).toList(); }).toList();
que.correctRate = Utils.calcRate(okCount, que.answerCount!); que.correctRate = Utils.calcRate(okCount, que.answerCount!);
que.answerNgStudents = ques.where((w) { que.answerNgStudents = ques.where((w) {
w.studentName = data.students try {
.firstWhere((s) => s.studentId == w.studentId) var student = data.students.firstWhere((s) => s.studentId == w.studentId);
.studentName; w.studentName = student.studentName;
} catch (e) {
w.studentName = '';
}
return w.state == 2; return w.state == 2;
}).toList(); }).toList();
@ -325,21 +273,16 @@ class Utils {
int middleTime = 0; int middleTime = 0;
if (ques.length % 2 == 0) { if (ques.length % 2 == 0) {
int index = (ques.length / 2).ceil(); int index = (ques.length / 2).ceil();
middleTime = middleTime = ((ques[index].useTime + ques[index - 1].useTime) / 2).ceil();
((ques[index].useTime + ques[index - 1].useTime) / 2).ceil();
} else { } else {
int index = ((ques.length + 1) / 2).ceil(); int index = ((ques.length + 1) / 2).ceil();
middleTime = ques[index - 1].useTime; middleTime = ques[index - 1].useTime;
} }
var excellent = var excellent = ques.where((w) => w.state == 3 && w.useTime <= middleTime).length;
ques.where((w) => w.state == 3 && w.useTime <= middleTime).length; var good = ques.where((w) => w.state == 3 && w.useTime > middleTime).length;
var good = var middle = ques.where((w) => w.state != 3 && w.useTime <= middleTime).length;
ques.where((w) => w.state == 3 && w.useTime > middleTime).length; var differ = ques.where((w) => w.state != 3 && w.useTime > middleTime).length;
var middle =
ques.where((w) => w.state != 3 && w.useTime <= middleTime).length;
var differ =
ques.where((w) => w.state != 3 && w.useTime > middleTime).length;
que.overallTitles = [ que.overallTitles = [
OverallTitles('优秀', excellent), OverallTitles('优秀', excellent),
@ -349,8 +292,7 @@ class Utils {
]; ];
Map<String, int> optInfos = {}; Map<String, int> optInfos = {};
for (var d in ques for (var d in ques.where((w) => w.questionType == 1 && w.studentAnswer != null)) {
.where((w) => w.questionType == 1 && w.studentAnswer != null)) {
final key = d.studentAnswer!; final key = d.studentAnswer!;
if (optInfos[key] != null) { if (optInfos[key] != null) {
optInfos[key] = optInfos[key]! + 1; optInfos[key] = optInfos[key]! + 1;
@ -362,21 +304,14 @@ class Utils {
optInfos.forEach((key, value) { optInfos.forEach((key, value) {
// optInfos[key] = Utils.calcRate(value, ques.length).round(); // optInfos[key] = Utils.calcRate(value, ques.length).round();
List<Students> currentOptionStudents = data.students List<Students> currentOptionStudents = data.students
.where((s) => ques .where((s) => ques.where((w) => w.studentAnswer == key && w.studentId == s.studentId).isNotEmpty)
.where(
(w) => w.studentAnswer == key && w.studentId == s.studentId)
.isNotEmpty)
.toList(); .toList();
// //
String upStr = key.toUpperCase(); String upStr = key.toUpperCase();
List<String> chars = upStr.split('')..sort(); List<String> chars = upStr.split('')..sort();
String sortedStr = chars.join(); String sortedStr = chars.join();
student.add(SelectionRate( student.add(SelectionRate(sortedStr, Utils.calcRate(value, ques.length).round(), false, currentOptionStudents));
sortedStr,
Utils.calcRate(value, ques.length).round(),
false,
currentOptionStudents));
}); });
/* print('optitems=$optInfos'); /* print('optitems=$optInfos');
var ARate = Utils.calcRate( var ARate = Utils.calcRate(
@ -385,16 +320,10 @@ class Utils {
} }
for (var know in data.knows) { for (var know in data.knows) {
List<Questions> ques = data.questions List<Questions> ques =
.where((w) => data.questions.where((w) => w.knows.indexWhere((k) => k.knowledgeId == know.knowledgeId) > -1).toList();
w.knows.indexWhere((k) => k.knowledgeId == know.knowledgeId) > -1)
.toList();
List<Dtls> queDtls = data.dtls List<Dtls> queDtls = data.dtls
.where((w) => .where((w) => ques.indexWhere((q) => w.templateId == q.templateId && w.questionNo == q.questionNo) > -1)
ques.indexWhere((q) =>
w.templateId == q.templateId &&
w.questionNo == q.questionNo) >
-1)
.toList(); .toList();
know.okCount = queDtls.where((w) => w.state == 3).length; know.okCount = queDtls.where((w) => w.state == 3).length;
know.ttlCount = queDtls.length; know.ttlCount = queDtls.length;
@ -433,8 +362,7 @@ bool isPad([double mobilePhoneScale = 1.2]) {
return ScreenUtil().scaleWidth > mobilePhoneScale; return ScreenUtil().scaleWidth > mobilePhoneScale;
} }
void toUpState( void toUpState(Function(void Function()) setState, VoidCallback fn, bool mounted) {
Function(void Function()) setState, VoidCallback fn, bool mounted) {
if (mounted) setState(fn); if (mounted) setState(fn);
} }

View File

@ -1,8 +1,11 @@
import 'dart:io'; import 'dart:io';
import 'package:auto_updater/auto_updater.dart'; import 'package:auto_updater/auto_updater.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_native_splash/flutter_native_splash.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/config/app_config.dart'; import 'package:making_school_asignment_app/common/config/app_config.dart';
@ -11,25 +14,20 @@ 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/utils.dart'; import 'package:making_school_asignment_app/common/utils/utils.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'common/config/request_config.dart'; import 'common/config/request_config.dart';
import 'common/utils/app_upgrade/upgradeLogic.dart'; import 'common/utils/app_upgrade/upgradeLogic.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
void main() async { void main() async {
// Get // Get
Get.testMode = true; Get.testMode = true;
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); FlutterNativeSplash.preserve(widgetsBinding: WidgetsFlutterBinding.ensureInitialized());
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
/// ///
await Get.putAsync(() => StorageService().init()); await Get.putAsync<StorageService>(() => StorageService.init());
/// UserStore /// UserStore
Get.put<UserStore>(UserStore().init()); await Get.putAsync<UserStore>(() async => UserStore().init());
Get.put(UpgradeLogic());
WidgetsFlutterBinding.ensureInitialized();
// Windows // Windows
if (Platform.isWindows) { if (Platform.isWindows) {
@ -43,9 +41,11 @@ void main() async {
statusBarColor: Colors.transparent, // statusBarColor: Colors.transparent, //
statusBarIconBrightness: Brightness.light // dark: light statusBarIconBrightness: Brightness.light // dark: light
)); ));
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); // SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky, overlays: [SystemUiOverlay.top]); //
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); // await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); //
runApp(const MyApp()); runApp(const MyApp());
Future.delayed(const Duration(seconds: 3), () => FlutterNativeSplash.remove());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@ -121,12 +121,13 @@ class MyApp extends StatelessWidget {
initialRoute: Routes.startPage, initialRoute: Routes.startPage,
/// ///
initialBinding: MainBinding(),
getPages: AppPages.pages, getPages: AppPages.pages,
builder: EasyLoading.init( builder: EasyLoading.init(
builder: (context, child) { builder: (context, child) {
return MediaQuery( return MediaQuery(
//Setting font does not change with system font size //Setting font does not change with system font size
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), data: MediaQuery.of(context).copyWith(textScaler: const TextScaler.linear(1.0)),
child: Scaffold( child: Scaffold(
body: GestureDetector(onTap: () => Utils.hideKeyboard(), child: child), body: GestureDetector(onTap: () => Utils.hideKeyboard(), child: child),
), ),
@ -138,3 +139,12 @@ class MyApp extends StatelessWidget {
); );
} }
} }
/// MainBinding
class MainBinding implements Bindings {
@override
void dependencies() async {
/// APP
Get.put(UpgradeLogic(), permanent: true);
}
}

View File

@ -0,0 +1,300 @@
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:making_school_asignment_app/common/utils/gesture_recognition/ramer_douglas_peucker.dart';
import 'package:making_school_asignment_app/common/utils/gesture_recognition/shape_recognizer.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 812),
builder: () => MaterialApp(
title: '手势识别实验',
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
home: const GestureCanvasPage(),
),
);
}
}
class GestureCanvasPage extends StatefulWidget {
const GestureCanvasPage({super.key});
@override
State<GestureCanvasPage> createState() => _GestureCanvasPageState();
}
class _GestureCanvasPageState extends State<GestureCanvasPage> {
final List<List<Offset>> _allStrokes = [];
final _currentStrokeNotifier = ValueNotifier<List<Offset>>([]);
ui.Image? _cachedImage;
GestureType _recognizedGesture = GestureType.none;
late ShapeRecognizer _recognizer;
//
bool _showSimplifiedPoints = true;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
// 便
_recognizer = ShapeRecognizer(
rdpEpsilon: 8.r, //
///
checkAngleMin: 30.0,
///
checkAngleMax: 150.0,
crossAngleMin: 30.0,
crossAngleMax: 160.0,
minIntersectionDistance: 0.1,
checkLengthRatio: 1.1,
// [] 线
slashSlopeMin: 0.2, // 线11线
slashSlopeMax:
5.0, // 线79线线 _isSlash 线 (deltaX < 1e-6)线
);
}
});
}
Future<void> _updateCachedImage() async {
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
final size = context.size!;
_GestureCachePainter(allStrokes: _allStrokes).paint(canvas, size);
final picture = recorder.endRecording();
final newImage = await picture.toImage(size.width.toInt(), size.height.toInt());
if (mounted) {
setState(() {
_cachedImage = newImage;
});
}
}
void _onPanStart(DragStartDetails details) {
_currentStrokeNotifier.value = [details.localPosition];
}
void _onPanUpdate(DragUpdateDetails details) {
_currentStrokeNotifier.value = List.from(_currentStrokeNotifier.value)..add(details.localPosition);
}
void _onPanEnd(DragEndDetails details) {
if (_currentStrokeNotifier.value.isNotEmpty) {
_allStrokes.add(List.from(_currentStrokeNotifier.value));
_currentStrokeNotifier.value = [];
_updateCachedImage().then((_) {
if (mounted) {
setState(() {
_recognizedGesture = _recognizer.recognize(_allStrokes);
print('识别到的手势: $_recognizedGesture');
});
}
});
}
}
void _clearCanvas() {
setState(() {
_allStrokes.clear();
_recognizedGesture = GestureType.none;
_cachedImage = null;
});
_currentStrokeNotifier.value = [];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('手势识别 (可视化调试)'),
actions: [
// AppBar中添加一个信息提示
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(
Icons.science_outlined,
color: _showSimplifiedPoints ? Colors.green : Colors.grey,
),
)
],
),
body: Stack(
children: [
GestureDetector(
onPanStart: _onPanStart,
onPanUpdate: _onPanUpdate,
onPanEnd: _onPanEnd,
child: ValueListenableBuilder<List<Offset>>(
valueListenable: _currentStrokeNotifier,
builder: (context, currentStroke, child) {
return CustomPaint(
painter: _GestureCanvasPainter(
cachedImage: _cachedImage,
currentStroke: currentStroke,
// [] Painter
allStrokes: _allStrokes,
rdpEpsilon: _recognizer.rdpEpsilon,
showSimplifiedPoints: _showSimplifiedPoints,
),
size: Size.infinite,
);
},
),
),
Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'识别结果: ${_recognizedGesture.toString().split('.').last}\nEpsilon: ${_recognizer.rdpEpsilon.toStringAsFixed(2)}',
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blue),
),
),
),
],
),
// [] 使Column包裹多个FAB
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: _clearCanvas,
tooltip: '清空画布',
child: const Icon(Icons.clear),
),
const SizedBox(height: 10),
//
FloatingActionButton(
onPressed: () {
setState(() {
_showSimplifiedPoints = !_showSimplifiedPoints;
});
},
tooltip: '切换特征点显示',
backgroundColor: _showSimplifiedPoints ? Colors.green : Colors.grey[400],
child: const Icon(Icons.science_outlined),
),
],
),
);
}
}
class _GestureCanvasPainter extends CustomPainter {
final ui.Image? cachedImage;
final List<Offset> currentStroke;
final Paint _paint;
// []
final List<List<Offset>> allStrokes;
final double rdpEpsilon;
final bool showSimplifiedPoints;
final Paint _debugPaint; //
_GestureCanvasPainter({
required this.cachedImage,
required this.currentStroke,
required this.allStrokes,
required this.rdpEpsilon,
required this.showSimplifiedPoints,
}) : _paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0
..style = PaintingStyle.stroke,
//
_debugPaint = Paint()
..color = Colors.red
..strokeCap = StrokeCap.round
..strokeWidth = 8.0;
@override
void paint(Canvas canvas, Size size) {
// 1: ()
if (cachedImage != null) {
canvas.drawImage(cachedImage!, Offset.zero, Paint());
}
// 2:
if (currentStroke.length > 1) {
final path = Path();
path.moveTo(currentStroke.first.dx, currentStroke.first.dy);
for (int i = 1; i < currentStroke.length; i++) {
path.lineTo(currentStroke[i].dx, currentStroke[i].dy);
}
canvas.drawPath(path, _paint);
}
// 3:
if (showSimplifiedPoints) {
//
for (final stroke in allStrokes) {
if (stroke.length > 1) {
final simplifiedPoints = RamerDouglasPeucker.simplify(stroke, rdpEpsilon);
canvas.drawPoints(ui.PointMode.points, simplifiedPoints, _debugPaint);
}
}
//
if (currentStroke.length > 1) {
final simplifiedPoints = RamerDouglasPeucker.simplify(currentStroke, rdpEpsilon);
canvas.drawPoints(ui.PointMode.points, simplifiedPoints, _debugPaint);
}
}
}
@override
bool shouldRepaint(covariant _GestureCanvasPainter oldDelegate) {
// []
return oldDelegate.currentStroke != currentStroke ||
oldDelegate.showSimplifiedPoints != showSimplifiedPoints ||
oldDelegate.allStrokes.length != allStrokes.length;
}
}
//
class _GestureCachePainter extends CustomPainter {
final List<List<Offset>> allStrokes;
final Paint _paint;
_GestureCachePainter({required this.allStrokes})
: _paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0
..style = PaintingStyle.stroke;
@override
void paint(Canvas canvas, Size size) {
for (final stroke in allStrokes) {
if (stroke.length > 1) {
final path = Path();
path.moveTo(stroke.first.dx, stroke.first.dy);
for (int i = 1; i < stroke.length; i++) {
path.lineTo(stroke[i].dx, stroke[i].dy);
}
canvas.drawPath(path, _paint);
}
}
}
@override
bool shouldRepaint(covariant _GestureCachePainter oldDelegate) {
return oldDelegate.allStrokes != allStrokes;
}
}

View File

@ -18,7 +18,7 @@ class ReturnToHomepage extends StatelessWidget {
child: Container( child: Container(
padding: EdgeInsets.only(right: 4.5.w), padding: EdgeInsets.only(right: 4.5.w),
alignment: Alignment.center, alignment: Alignment.center,
child: Icon(Icons.home_rounded, size: 22.sp, color: bgColor), child: Icon(Icons.home_rounded, size: 30.r, color: bgColor),
), ),
); );
} }

View File

@ -18,6 +18,7 @@ Text quickText(text,
FontWeight? fontWeight, FontWeight? fontWeight,
TextOverflow overflow = TextOverflow.ellipsis, TextOverflow overflow = TextOverflow.ellipsis,
int maxLines = 1, int maxLines = 1,
double? height,
TextDecoration decoration = TextDecoration.none}) { TextDecoration decoration = TextDecoration.none}) {
return Text( return Text(
text.toString(), text.toString(),
@ -30,6 +31,7 @@ Text quickText(text,
color: color, color: color,
fontWeight: fontWeight, fontWeight: fontWeight,
wordSpacing: wordSpacing, wordSpacing: wordSpacing,
height: height,
), ),
); );
} }

View File

@ -1,21 +1,20 @@
import 'dart:async'; import 'dart:async';
import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/user_info.dart'; import 'package:making_school_asignment_app/common/job/user_info.dart';
import 'package:making_school_asignment_app/common/utils/storage.dart';
import 'package:making_school_asignment_app/common/store/user_store.dart';
import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
import 'package:making_school_asignment_app/page/home_page/home_view.dart';
import 'package:making_school_asignment_app/page/home_page/home_logic.dart';
import 'package:making_school_asignment_app/page/work_page/work_logic.dart';
import 'package:making_school_asignment_app/common/job/user_info_detail.dart'; import 'package:making_school_asignment_app/common/job/user_info_detail.dart';
import 'package:making_school_asignment_app/page/home_page/children/my_info.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/store/user_store.dart';
import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogic.dart'; import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogic.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/page/home_page/children/my_info.dart';
import 'package:making_school_asignment_app/page/home_page/home_view.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart';
class StartPage extends StatefulWidget { class StartPage extends StatefulWidget {
const StartPage({super.key}); const StartPage({super.key});
@ -37,14 +36,13 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
void initState() { void initState() {
super.initState(); super.initState();
Future.delayed(const Duration(seconds: 3), () => FlutterNativeSplash.remove());
// const WorkPage(), // const WorkPage(),
_bodyList = [const HomePage(), const MyInfo()]; _bodyList = [const HomePage(), const MyInfo()];
// APP // APP
WidgetsBinding.instance.addPostFrameCallback((e) { WidgetsBinding.instance.addPostFrameCallback((e) {
/// 40APP /// 40APP
if (!_upgradeLogic.showUpgrade.value && UserStore.to.userDetailInfo.value != null) _upgradeLogic.getAppUpgrade(context); if (!_upgradeLogic.showUpgrade.value && UserStore.to.userDetailInfo.value != null)
_upgradeLogic.getAppUpgrade(context);
}); });
_timer?.cancel(); _timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 40), (e) { _timer = Timer.periodic(const Duration(seconds: 40), (e) {
@ -91,7 +89,8 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WillPopScope( return PopScope(
canPop: false,
child: Scaffold( child: Scaffold(
body: PageView( body: PageView(
controller: _pageController._pageIndexState.pageController, controller: _pageController._pageIndexState.pageController,
@ -141,15 +140,19 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
); );
}), }),
), ),
onWillPop: () async { onPopInvokedWithResult: (bool didPop, dynamic result) async {
if (lastPopTime == null || DateTime.now().difference(lastPopTime!) > const Duration(seconds: 1)) { if (lastPopTime == null || DateTime.now().difference(lastPopTime!) > const Duration(seconds: 1)) {
lastPopTime = DateTime.now(); lastPopTime = DateTime.now();
ToastUtils.getFluttertoast(context: context, msg: '连续两次返回就退出'); ToastUtils.getFluttertoast(
return Future.value(false); context: context,
msg: '连续两次返回就退出',
gravity: ToastGravity.CENTER,
fontSize: 18,
);
} else { } else {
lastPopTime = DateTime.now(); lastPopTime = DateTime.now();
// 退app // 退app
return Future.value(true); SystemNavigator.pop();
} }
}, },
); );

View File

@ -1,3 +1,5 @@
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_easyrefresh/easy_refresh.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -35,8 +37,7 @@ class AnnotateClassLogic extends GetxController with RequestToolMixin {
} }
void getList() async { void getList() async {
List<AnnotatedClass> data = List<AnnotatedClass> data = await getClient().getAnnotatedClassList(state.homeworkId.value);
await getClient().getAnnotatedClassList(state.homeworkId.value);
state.classList.value = data; state.classList.value = data;
/* print('state.classList[0]=${state.classList[0].finishTime}'); /* print('state.classList[0]=${state.classList[0].finishTime}');
print('state.classList[1]=${state.classList[1].finishTime}');*/ print('state.classList[1]=${state.classList[1].finishTime}');*/
@ -61,9 +62,7 @@ class AnnotateClassLogic extends GetxController with RequestToolMixin {
void getAllCorrect(classId) async { void getAllCorrect(classId) async {
EasyLoading.show(status: 'loading...'); EasyLoading.show(status: 'loading...');
try { try {
await getClient() await getClient().getAllCorrect(state.homeworkId.value, classId).then((e) {
.getAllCorrect(state.homeworkId.value, classId)
.then((e) {
getList(); getList();
}); });
} catch (e) { } catch (e) {
@ -108,4 +107,25 @@ class AnnotateClassLogic extends GetxController with RequestToolMixin {
super.dispose(); super.dispose();
refreshController.dispose(); refreshController.dispose();
} }
///
/// [homeworkId]
/// [homeworkName]
/// [classId]
/// [subject]
void toGoReviewhomework({required homeworkId, required homeworkName, required classId, required subject}) async {
// UI
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky, overlays: <SystemUiOverlay>[]);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 300));
await Get.toNamed(Routes.reviewHomework, arguments: {
'homeworkId': homeworkId,
'homeworkName': homeworkName,
'classId': classId,
'subject': subject
})?.then((value) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge, overlays: [SystemUiOverlay.top]);
});
});
}
} }

View File

@ -1,18 +1,17 @@
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: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';
import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart';
import 'package:making_school_asignment_app/common/utils/enum_untils.dart'; import 'package:making_school_asignment_app/common/utils/enum_untils.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/global_widget/show_student_list.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/annotate_class/widget/item_btn.dart'; import 'package:making_school_asignment_app/page/home_page/children/annotate_class/widget/item_btn.dart';
import 'package:making_school_asignment_app/page/home_page/widget/progress_bar.dart'; import 'package:making_school_asignment_app/page/home_page/widget/progress_bar.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
import 'package:percent_indicator/percent_indicator.dart';
class AnnotateItem extends StatefulWidget { class AnnotateItem extends StatefulWidget {
final String homeworkId; final String homeworkId;
@ -20,7 +19,13 @@ class AnnotateItem extends StatefulWidget {
final double font; final double font;
final String name; final String name;
final AnnotateClassLogic logic; final AnnotateClassLogic logic;
const AnnotateItem({super.key, required this.homeworkId, required this.item, required this.font, required this.name, required this.logic}); const AnnotateItem(
{super.key,
required this.homeworkId,
required this.item,
required this.font,
required this.name,
required this.logic});
@override @override
State<AnnotateItem> createState() => _AnnotateItemState(); State<AnnotateItem> createState() => _AnnotateItemState();
@ -159,10 +164,16 @@ class _AnnotateItemState extends State<AnnotateItem> {
Expanded( Expanded(
flex: 4, flex: 4,
child: ItemBtn( child: ItemBtn(
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: {'homeworkName': widget.name, 'classId': widget.item.classId, 'homeworkId': widget.logic.state.homeworkId.value, 'grade': widget.item.grade}); Get.toNamed(Routes.favStudentPage, arguments: {
'homeworkName': widget.name,
'classId': widget.item.classId,
'homeworkId': widget.logic.state.homeworkId.value,
'grade': widget.item.grade
});
}, },
), ),
), ),
@ -207,10 +218,16 @@ class _AnnotateItemState extends State<AnnotateItem> {
Expanded( Expanded(
flex: 4, flex: 4,
child: ItemBtn( child: ItemBtn(
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: {'homeworkName': widget.name, 'classId': widget.item.classId, 'homeworkId': widget.logic.state.homeworkId.value, 'grade': widget.item.grade}); Get.toNamed(Routes.favStudentPage, arguments: {
'homeworkName': widget.name,
'classId': widget.item.classId,
'homeworkId': widget.logic.state.homeworkId.value,
'grade': widget.item.grade
});
}, },
), ),
), ),
@ -221,7 +238,10 @@ 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: [quickText('批阅进度', size: widget.font - 2.sp, color: const Color(0xFF8E8E8E)), quickText('${widget.item.annotateRate}%', size: widget.font + 10.sp, color: const Color(0xFF464646))], children: [
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(
@ -249,9 +269,27 @@ class _AnnotateItemState extends State<AnnotateItem> {
barRadius: Radius.circular(10.r), barRadius: Radius.circular(10.r),
), ),
), ),
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), ProgressBar(
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), title: '客观题正确率:',
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), 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),
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(
@ -277,7 +315,9 @@ class _AnnotateItemState extends State<AnnotateItem> {
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric(vertical: 10.r), padding: EdgeInsets.symmetric(vertical: 10.r),
decoration: BoxDecoration(border: Border(right: BorderSide(width: 1.r, color: const Color.fromRGBO(221, 221, 221, 1)))), decoration: BoxDecoration(
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),
), ),
@ -298,29 +338,20 @@ class _AnnotateItemState extends State<AnnotateItem> {
: [ : [
Expanded( Expanded(
child: InkWell( child: InkWell(
onTap: () => easyThrottle('TO_GO_REVIEWHOMEWORK', () async { onTap: () => easyThrottle('TO_GO_REVIEWHOMEWORK', () {
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); ///
WidgetsBinding.instance.addPostFrameCallback((_) { widget.logic.toGoReviewhomework(
Future.delayed(const Duration(milliseconds: 300), () { homeworkId: widget.homeworkId,
Get.toNamed(Routes.reviewHomework, arguments: { homeworkName: widget.name,
'homeworkId': widget.homeworkId, classId: itemData.classId,
'homeworkName': widget.name, subject: widget.logic.state.subject,
'classId': itemData.classId, );
'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(border: Border(right: BorderSide(width: 1.r, color: const Color.fromRGBO(221, 221, 221, 1)))), decoration: BoxDecoration(
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),
), ),

View File

@ -1,7 +1,6 @@
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:making_school_asignment_app/common/job/student_item.dart'; import 'package:making_school_asignment_app/common/job/student_item.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';
@ -16,7 +15,8 @@ class ClassStudentLogic extends GetxController with RequestToolMixin {
super.onInit(); super.onInit();
state.title.value = Get.arguments['title'] ?? ''; state.title.value = Get.arguments['title'] ?? '';
state.classId = Get.arguments['classId'] ?? ''; state.classId = Get.arguments['classId'] ?? '';
state.subject = Get.arguments['subject'] ?? -1; final subject = Get.arguments['subject'];
state.subject = subject == 0 ? null : subject;
state.page = Get.arguments['page'] ?? ''; state.page = Get.arguments['page'] ?? '';
EasyLoading.show(status: 'loading...'); EasyLoading.show(status: 'loading...');
refreshController = EasyRefreshController(); refreshController = EasyRefreshController();

View File

@ -8,7 +8,7 @@ class ClassStudentState {
late RxString title = ''.obs; late RxString title = ''.obs;
late final String classId; late final String classId;
late final int subject; late final int? subject;
late RxList<StudentItem> studentList = RxList(); late RxList<StudentItem> studentList = RxList();
late String page = ''; late String page = '';
late bool isClicking = true; late bool isClicking = true;

View File

@ -12,7 +12,7 @@ import 'package:making_school_asignment_app/page/home_page/children/job_report/w
import 'fav_student_logic.dart'; import 'fav_student_logic.dart';
class FavStudentPage extends StatefulWidget { class FavStudentPage extends StatefulWidget {
const FavStudentPage({Key? key}) : super(key: key); const FavStudentPage({super.key});
@override @override
State<FavStudentPage> createState() => _FavStudentPageState(); State<FavStudentPage> createState() => _FavStudentPageState();
@ -20,9 +20,7 @@ class FavStudentPage extends StatefulWidget {
class _FavStudentPageState extends State<FavStudentPage> { class _FavStudentPageState extends State<FavStudentPage> {
final logic = Get.find<FavStudentLogic>(); final logic = Get.find<FavStudentLogic>();
final state = Get final state = Get.find<FavStudentLogic>().state;
.find<FavStudentLogic>()
.state;
void showStudentDialog(BuildContext context, HomeworkFavs item, List groups) { void showStudentDialog(BuildContext context, HomeworkFavs item, List groups) {
showDialog( showDialog(
@ -31,16 +29,13 @@ class _FavStudentPageState extends State<FavStudentPage> {
return AlertDialog( return AlertDialog(
insetPadding: EdgeInsets.all(25.r), insetPadding: EdgeInsets.all(25.r),
content: FavoriteStudentDialog( content: FavoriteStudentDialog(
item: item, item: item, group: groups, deleteFav: logic.getDelete, confirmDialog: confirmDialog),
group: groups,
deleteFav: logic.getDelete,
confirmDialog: confirmDialog),
contentPadding: const EdgeInsets.all(0), contentPadding: const EdgeInsets.all(0),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.r))));
borderRadius: BorderRadius.all(Radius.circular(15.r))));
}, },
); );
} }
Future<bool> confirmDialog() async { Future<bool> confirmDialog() async {
return await showDialog( return await showDialog(
context: context, context: context,
@ -93,6 +88,7 @@ class _FavStudentPageState extends State<FavStudentPage> {
), ),
); );
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -113,8 +109,7 @@ class _FavStudentPageState extends State<FavStudentPage> {
ReturnToHomepage(), ReturnToHomepage(),
], ],
), ),
body:OrientationBuilder( body: OrientationBuilder(builder: (BuildContext context, Orientation orientation) {
builder: (BuildContext context, Orientation orientation){
return Column( return Column(
children: [ children: [
Padding( Padding(
@ -127,16 +122,17 @@ class _FavStudentPageState extends State<FavStudentPage> {
child: Obx(() { child: Obx(() {
return Text( return Text(
state.homeworkName.value, state.homeworkName.value,
style: style: TextStyle(
TextStyle(fontSize: 14.sp, color: const Color(0xFF3C3C3C),), fontSize: 14.sp,
color: const Color(0xFF3C3C3C),
),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
); );
}), }),
), ),
// //
Container( Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 10.w),
vertical: 10.h, horizontal: 10.w),
child: Row( child: Row(
children: [ children: [
Obx(() { Obx(() {
@ -144,7 +140,7 @@ class _FavStudentPageState extends State<FavStudentPage> {
bgColor: Colors.white, bgColor: Colors.white,
involveClasses: state.involveClasses.value, involveClasses: state.involveClasses.value,
classData: state.classData.value, classData: state.classData.value,
call: (AnnotatedClass item) { onClassSelected: (AnnotatedClass item) {
state.classData.value = item; state.classData.value = item;
if (item.grade == -1) state.classData.value = state.defaultClass; if (item.grade == -1) state.classData.value = state.defaultClass;
logic.getList(); logic.getList();
@ -157,105 +153,74 @@ class _FavStudentPageState extends State<FavStudentPage> {
), ),
), ),
Container( Container(
width: MediaQuery width: MediaQuery.of(context).size.width,
.of(context)
.size
.width,
height: 1.r, height: 1.r,
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Color(0xFFEEEEEE), color: Color(0xFFEEEEEE),
), ),
), ),
Obx((){ Obx(() {
return state.favList.isNotEmpty return state.favList.isNotEmpty
? Expanded( ? Expanded(
child: Utils.isPad() child: Utils.isPad()
? Padding( ? Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(top: 10.r, bottom: 8.r, left: 14.r, right: 14.r),
top: 10.r,
bottom: 8.r,
left: 14.r,
right: 14.r),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: children: List.generate(state.favList.length, (index) {
List.generate(state.favList.length, (index) {
var item = state.favList[index]; var item = state.favList[index];
return Padding( return Padding(
padding: EdgeInsets.only(bottom: 8.r), padding: EdgeInsets.only(bottom: 8.r),
child: Column( child: Column(
crossAxisAlignment: crossAxisAlignment: CrossAxisAlignment.start,
CrossAxisAlignment.start,
children: [ children: [
Padding( Padding(
padding: padding: EdgeInsets.only(bottom: 5.r),
EdgeInsets.only(bottom: 5.r),
child: Text( child: Text(
'${item['questionPage']}', '${item['questionPage']}',
style: TextStyle( style:
fontSize: 12.sp, TextStyle(fontSize: 12.sp, color: Theme.of(context).primaryColor),
color:Theme.of(context).primaryColor),
), ),
), ),
GridView( GridView(
gridDelegate: gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
mainAxisSpacing: 8.r, mainAxisSpacing: 8.r,
crossAxisSpacing: 10.r, crossAxisSpacing: 10.r,
childAspectRatio: 556 / 112, childAspectRatio: 556 / 112,
), ),
shrinkWrap: true, shrinkWrap: true,
children: List.generate( children: List.generate(item['list'].length, (i) {
item['list'].length, (i) {
HomeworkFavs student = item['list'][i]; HomeworkFavs student = item['list'][i];
return Container( return Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(vertical: 5.r, horizontal: 10.r),
vertical: 5.r,
horizontal: 10.r),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: borderRadius: BorderRadius.all(Radius.circular(6.r)),
BorderRadius.all(
Radius.circular(
6.r)),
), ),
child: InkWell( child: InkWell(
onTap: () { onTap: () {
showStudentDialog(context, showStudentDialog(context, student, state.favList);
student, state.favList);
}, },
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment: MainAxisAlignment.spaceBetween,
MainAxisAlignment
.spaceBetween,
children: [ children: [
quickText( quickText(student.studentName,
student.studentName, color: const Color(0xFF333333), size: 12.sp),
color: const Color( Expanded(child: Container()),
0xFF333333),
size: 12.sp),
Expanded(
child: Container()),
Padding( Padding(
padding: padding: EdgeInsets.only(right: 8.r),
EdgeInsets.only(
right: 8.r),
child: Text( child: Text(
student.className!, student.className!,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp, color: const Color(0xFF666666)),
color: const Color(
0xFF666666)),
), ),
), ),
InkWell( InkWell(
onTap: () async { onTap: () async {
var confim = var confim = await confirmDialog();
await confirmDialog();
if (confim) { if (confim) {
logic.getDelete( logic.getDelete(student);
student);
} }
}, },
child: Image.asset( child: Image.asset(
@ -279,22 +244,15 @@ class _FavStudentPageState extends State<FavStudentPage> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
var item = state.favList[index]; var item = state.favList[index];
return Padding( return Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(top: 10.r, bottom: 8.r, left: 14.r, right: 14.r),
top: 10.r,
bottom: 8.r,
left: 14.r,
right: 14.r),
child: Column( child: Column(
crossAxisAlignment: crossAxisAlignment: CrossAxisAlignment.start,
CrossAxisAlignment.start,
children: [ children: [
Padding( Padding(
padding: EdgeInsets.only(bottom: 5.r), padding: EdgeInsets.only(bottom: 5.r),
child: Text( child: Text(
'${item['questionPage']}', '${item['questionPage']}',
style: TextStyle( style: TextStyle(fontSize: 14.sp, color: Theme.of(context).primaryColor),
fontSize: 14.sp,
color:Theme.of(context).primaryColor),
), ),
), ),
ListView.builder( ListView.builder(
@ -302,43 +260,32 @@ class _FavStudentPageState extends State<FavStudentPage> {
HomeworkFavs student = item['list'][i]; HomeworkFavs student = item['list'][i];
return InkWell( return InkWell(
onTap: () { onTap: () {
showStudentDialog( showStudentDialog(context, student, state.favList);
context, student, state.favList);
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(vertical: 5.r, horizontal: 10.r),
vertical: 5.r,
horizontal: 10.r),
margin: EdgeInsets.only(top: 5.r), margin: EdgeInsets.only(top: 5.r),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(Radius.circular(6.r)),
Radius.circular(6.r)),
), ),
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment: MainAxisAlignment.spaceBetween,
MainAxisAlignment
.spaceBetween,
children: [ children: [
quickText(student.studentName, quickText(student.studentName,
color: const Color(0xFF333333), color: const Color(0xFF333333), size: 14.sp),
size: 14.sp),
Expanded(child: Container()), Expanded(child: Container()),
Padding( Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(right: 8.r),
right: 8.r),
child: Text( child: Text(
student.className!, student.className!,
style: TextStyle( style:
fontSize: 14.sp, TextStyle(fontSize: 14.sp, color: const Color(0xFF666666)),
color: const Color(
0xFF666666)),
), ),
), ),
InkWell( InkWell(
onTap: () async { onTap: () async {
var confim = var confim = await confirmDialog();
await confirmDialog();
if (confim) { if (confim) {
logic.getDelete(student); logic.getDelete(student);
} }
@ -365,20 +312,13 @@ class _FavStudentPageState extends State<FavStudentPage> {
), ),
) )
: Padding( : Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 2 - 200.r),
top: MediaQuery
.of(context)
.size
.height / 2 - 200.r),
child: const MyEmptyWidget(), child: const MyEmptyWidget(),
); );
}), }),
], ],
); );
} }));
)
);
} }
@override @override

View File

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ffi';
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:get/get.dart'; import 'package:get/get.dart';
@ -7,7 +7,6 @@ import 'package:making_school_asignment_app/common/job/marking_models/do_paper_b
import 'package:making_school_asignment_app/common/mixins/event_bus_mixin.dart'; import 'package:making_school_asignment_app/common/mixins/event_bus_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/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 '../configuration_files/index.dart'; import '../configuration_files/index.dart';
@ -19,7 +18,8 @@ class BottomAnnotationSwitch extends StatefulWidget {
State<BottomAnnotationSwitch> createState() => _BottomAnnotationSwitchJobState(); State<BottomAnnotationSwitch> createState() => _BottomAnnotationSwitchJobState();
} }
class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with SingleTickerProviderStateMixin, EventBusMixin<BottomOperationBar> { class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
with SingleTickerProviderStateMixin, EventBusMixin<BottomOperationBar> {
late AnimationController _animationController; // late AnimationController _animationController; //
final _homeworkLogic = Get.find<HomeworkReviewLogic>(); final _homeworkLogic = Get.find<HomeworkReviewLogic>();
final _logicControl = Get.find<HomeworkReviewLogic>().annotationState; final _logicControl = Get.find<HomeworkReviewLogic>().annotationState;
@ -61,15 +61,23 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double iconSize = 18.sp; return SafeArea(
Color actionColor = Colors.white; left: false,
Color defaultColor = const Color.fromRGBO(132, 146, 163, 1); right: false,
top: false,
return Container( child: Container(
width: double.infinity, width: double.infinity,
height: _animationController.value * 64.h, height: _animationController.value * 70.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(83, 83, 83, 1), color: const Color.fromRGBO(83, 83, 83, 1),
// padding: EdgeInsets.symmetric(vertical: 1.h), boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, -2),
),
],
),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
@ -82,105 +90,83 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
children: [ children: [
// //
Expanded( Expanded(
child: SizedBox( child: _buildActionButton(
width: double.infinity, '全对',
height: double.infinity, Icons.check_circle_outline,
child: TextButton( () => easyThrottle('homework_bottom_operation_bar_scoring_related',
child: quickText('全 对', color: Colors.white, size: 12.sp), () => _homeworkLogic.allPairs(context)),
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.allPairs(context)), isEnabled: !_homeworkLogic.state.submitLoading.value,
isPrimary: true,
), ),
), ),
), Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
// //
Expanded( Expanded(
child: SizedBox( child: Obx(() => _buildIconButton(
width: double.infinity,
height: double.infinity,
child: IconButton(
icon: Obx(() {
return Icon(
const IconData(0xe635, fontFamily: "AlibabaIcon"), const IconData(0xe635, fontFamily: "AlibabaIcon"),
size: iconSize, () => easyThrottle('homework_bottom_action_bar_annotations', () {
color: _logicControl.pen.value ? Theme.of(context).primaryColor : defaultColor, //
);
}),
onPressed: () => easyThrottle('homework_bottom_action_bar_annotations', () {
_logicControl.gestureMove.value = false; _logicControl.gestureMove.value = false;
_logicControl.pen.value = !_logicControl.pen.value; _logicControl.pen.value = true;
}), }),
isActive: _logicControl.pen.value,
tooltip: '注解笔',
)),
), ),
), Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
),
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
// //
Expanded( Expanded(
child: SizedBox( child: Obx(() => _buildIconButton(
width: double.infinity,
height: double.infinity,
child: IconButton(
icon: Obx(
() {
return Icon(
const IconData(0xe636, fontFamily: "AlibabaIcon"), const IconData(0xe636, fontFamily: "AlibabaIcon"),
size: iconSize, () => easyThrottle('homework_bottom_action_bar_annotations', () {
color: _logicControl.gestureMove.value ? actionColor : defaultColor, //
);
},
),
onPressed: () => easyThrottle('homework_bottom_action_bar_annotations', () {
_logicControl.pen.value = false; _logicControl.pen.value = false;
_logicControl.gestureMove.value = !_logicControl.gestureMove.value; _logicControl.gestureMove.value = true;
}), }),
), isActive: _logicControl.gestureMove.value,
), tooltip: '滑动试题',
)),
), ),
], ],
), ),
), ),
Container(width: double.infinity, color: Colors.white, height: 0.35.h), Container(width: double.infinity, color: Colors.white.withOpacity(0.3), height: 0.5.h),
Expanded( Expanded(
child: Row( child: Row(
children: [ children: [
// //
Expanded( Expanded(
child: SizedBox( child: _buildActionButton(
width: double.infinity, '全错',
height: double.infinity, Icons.cancel_outlined,
child: TextButton( () => easyThrottle('homework_bottom_operation_bar_scoring_related',
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.allWrongRating(context)), () => _homeworkLogic.allWrongRating(context)),
child: quickText('全 错', color: Colors.white, size: 12.sp), isEnabled: !_homeworkLogic.state.submitLoading.value,
isPrimary: false,
), ),
), ),
), Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
// //
Expanded( Expanded(
child: SizedBox( child: _buildIconButton(
width: double.infinity, const IconData(0xe638, fontFamily: "AlibabaIcon"),
height: double.infinity, () => easyThrottle(
child: IconButton(
onPressed: () => easyThrottle(
'homework_bottom_action_bar_annotations', 'homework_bottom_action_bar_annotations',
() => eventFire(model: BottomOperationBar(revokeAll: false, revokePreStep: true)), () => eventFire(model: BottomOperationBar(revokeAll: false, revokePreStep: true)),
), ),
icon: Icon(const IconData(0xe638, fontFamily: "AlibabaIcon"), size: iconSize, color: defaultColor), tooltip: '撤销上一步',
), ),
), ),
), Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
// //
Expanded( Expanded(
child: SizedBox( child: _buildIconButton(
width: double.infinity, const IconData(0xe637, fontFamily: "AlibabaIcon"),
height: double.infinity, () => easyThrottle(
child: IconButton(
icon: Icon(const IconData(0xe637, fontFamily: "AlibabaIcon"), size: iconSize, color: defaultColor),
onPressed: () => easyThrottle(
'homework_bottom_action_bar_annotations', 'homework_bottom_action_bar_annotations',
() => eventFire(model: BottomOperationBar(revokeAll: true, revokePreStep: false)), () => eventFire(model: BottomOperationBar(revokeAll: true, revokePreStep: false)),
), ),
), tooltip: '全部撤销',
), ),
), ),
], ],
@ -189,51 +175,161 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
], ],
), ),
), ),
Container(width: 0.3.w, height: double.infinity, color: Colors.white), Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
Expanded( Expanded(
flex: 3, flex: 3,
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: SizedBox( child: _buildActionButton(
width: double.infinity, '取消',
height: double.infinity, Icons.clear,
child: TextButton( () => easyThrottle(
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.cancelAllRatings()), 'homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.cancelAllRatings()),
child: quickText('取 消', size: 14.sp, color: Colors.white), isEnabled: true,
isPrimary: false,
isSecondary: true,
), ),
), ),
), Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
Expanded( Expanded(
child: SizedBox( child: Obx(() {
final submitLoading = _homeworkLogic.state.submitLoading.value;
return _buildActionButton(
'提交',
Icons.send,
() => easyThrottle(
'homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.submit(context)),
isEnabled: !submitLoading,
isPrimary: true,
isLoading: submitLoading,
);
}),
),
],
),
),
],
),
));
}
Widget _buildActionButton(
String text,
IconData icon,
VoidCallback? onPressed, {
required bool isEnabled,
required bool isPrimary,
bool isSecondary = false,
bool isLoading = false,
}) {
final primaryColor = Theme.of(context).primaryColor;
const defaultColor = Color.fromRGBO(132, 146, 163, 1);
return Material(
color: Colors.transparent,
child: InkWell(
onTap: isEnabled ? onPressed : null,
child: Semantics(
label: text,
hint: isLoading ? '正在处理中' : (isEnabled ? '点击执行$text操作' : '$text按钮已禁用'),
button: true,
enabled: isEnabled && !isLoading,
child: Container(
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
child: TextButton( decoration: BoxDecoration(
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.submit(context)), color: isPrimary
child: Row( ? (isEnabled ? primaryColor : primaryColor.withOpacity(0.5))
: isSecondary
? Colors.transparent
: Colors.transparent,
borderRadius: BorderRadius.circular(4.r),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Obx(() { if (isLoading)
if (_homeworkLogic.state.submitLoading.value) { SizedBox(
return Padding( width: 16.w,
padding: EdgeInsets.only(right: 4.w), height: 16.w,
child: const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)), child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
isPrimary ? Colors.white : primaryColor,
),
),
)
else
Icon(
icon,
size: 16.sp,
color: isEnabled
? (isPrimary ? Colors.white : (isSecondary ? Colors.white70 : defaultColor))
: Colors.white38,
),
SizedBox(height: 2.h),
Text(
text,
style: TextStyle(
fontSize: 10.sp,
color: isEnabled
? (isPrimary ? Colors.white : (isSecondary ? Colors.white70 : Colors.white))
: Colors.white38,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
),
); );
} }
return const SizedBox();
}), Widget _buildIconButton(
quickText('提 交', size: 14.sp, color: Theme.of(context).primaryColor), IconData icon,
], VoidCallback? onPressed, {
bool isActive = false,
String? tooltip,
}) {
final primaryColor = Theme.of(context).primaryColor;
const defaultColor = Color.fromRGBO(132, 146, 163, 1);
return Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
child: Tooltip(
message: tooltip ?? '',
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: isActive ? primaryColor.withOpacity(0.2) : Colors.transparent,
borderRadius: BorderRadius.circular(4.r),
), ),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 18.sp,
color: isActive ? primaryColor : defaultColor,
), ),
SizedBox(height: 2.h),
Container(
width: 4.w,
height: 4.w,
decoration: BoxDecoration(
color: isActive ? primaryColor : Colors.transparent,
shape: BoxShape.circle,
), ),
), ),
], ],
), ),
), ),
], ),
), ),
); );
} }

View File

@ -4,9 +4,9 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart'; 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/job/marking_models/do_paper_details_result.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/common/utils/toast_utils.dart';
import 'package:making_school_asignment_app/common/utils/utils.dart';
import 'package:making_school_asignment_app/page/global_widget/my_text.dart'; import 'package:making_school_asignment_app/page/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';
@ -61,7 +61,8 @@ class DropdownSwitchStudentsType extends StatelessWidget {
items: sateData.value?.templateIdKeys?.map((e) { items: sateData.value?.templateIdKeys?.map((e) {
return DropdownMenuItem( return DropdownMenuItem(
value: e, value: e,
child: quickText('${sateData.value!.templateIdKeyMap![e]}', color: const Color.fromRGBO(79, 79, 79, 1), size: 14.sp), child: quickText('${sateData.value!.templateIdKeyMap![e]}',
color: const Color.fromRGBO(79, 79, 79, 1), size: 14.sp),
); );
}).toList(), }).toList(),
onChanged: (value) { onChanged: (value) {
@ -77,7 +78,14 @@ class DropdownSwitchStudentsType extends StatelessWidget {
final students = sateData.value?.students ?? []; final students = sateData.value?.students ?? [];
// //
final currentStudent = currentStudentId != null ? students.firstWhereOrNull((e) => e.id == currentStudentId) : null; PaperStudents? currentStudent;
if (currentStudentId != null) {
try {
currentStudent = students.firstWhere((e) => e.id == currentStudentId);
} catch (e) {
currentStudent = null;
}
}
final studentName = currentStudent?.name ?? '当前学生'; final studentName = currentStudent?.name ?? '当前学生';
final questionNumber = templateIdKeyMap?[value] ?? '当前选择页'; final questionNumber = templateIdKeyMap?[value] ?? '当前选择页';
@ -95,7 +103,7 @@ class DropdownSwitchStudentsType extends StatelessWidget {
}), }),
), ),
), ),
const Expanded(flex: 1, child: SizedBox()), SizedBox(width: 8.w),
Expanded( Expanded(
flex: 3, flex: 3,
child: Stack( child: Stack(
@ -120,7 +128,9 @@ class DropdownSwitchStudentsType extends StatelessWidget {
alignment: const FractionalOffset(0, 0.62), alignment: const FractionalOffset(0, 0.62),
children: [ children: [
Container( Container(
padding: sateData.value?.studentId != e.id && e.isPriority ? EdgeInsets.only(left: 14.w) : null, padding: sateData.value?.studentId != e.id && e.isPriority
? EdgeInsets.only(left: 14.w)
: null,
child: quickText( child: quickText(
e.name, e.name,
size: 14.sp, size: 14.sp,
@ -134,7 +144,9 @@ class DropdownSwitchStudentsType extends StatelessWidget {
Icon( Icon(
const IconData(0xe63d, fontFamily: "AlibabaIcon"), const IconData(0xe63d, fontFamily: "AlibabaIcon"),
size: 12.sp, size: 12.sp,
color: e.isPriority ? Theme.of(context).primaryColor : const Color.fromRGBO(164, 164, 164, 1), color: e.isPriority
? Theme.of(context).primaryColor
: const Color.fromRGBO(164, 164, 164, 1),
), ),
quickText('优先', size: 4.sp, color: Colors.white), quickText('优先', size: 4.sp, color: Colors.white),
], ],
@ -161,7 +173,9 @@ class DropdownSwitchStudentsType extends StatelessWidget {
return Icon( return Icon(
const IconData(0xe63d, fontFamily: "AlibabaIcon"), const IconData(0xe63d, fontFamily: "AlibabaIcon"),
size: 12.sp, size: 12.sp,
color: sateData.value?.priority ?? false ? Theme.of(context).primaryColor : const Color.fromRGBO(164, 164, 164, 1), color: sateData.value?.priority ?? false
? Theme.of(context).primaryColor
: const Color.fromRGBO(164, 164, 164, 1),
); );
}), }),
quickText('优先', size: 4.sp, color: Colors.white), quickText('优先', size: 4.sp, color: Colors.white),
@ -173,9 +187,9 @@ class DropdownSwitchStudentsType extends StatelessWidget {
), ),
// const Expanded(flex: 1, child: SizedBox()), // const Expanded(flex: 1, child: SizedBox()),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Expanded( const Expanded(
flex: isPad() ? 4 : 5, flex: 5,
child: const Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@ -208,7 +222,8 @@ Widget $studentHandwriting(BuildContext context) {
if (templateIdKeyMap != null && templateId != null) { if (templateIdKeyMap != null && templateId != null) {
pageNum = templateIdKeyMap[templateId]; pageNum = templateIdKeyMap[templateId];
} }
await showAnswerHandwriting(context, homeworkId: homeworkId, studentId: studentId, templateId: templateId, pageNum: pageNum); await showAnswerHandwriting(context,
homeworkId: homeworkId, studentId: studentId, templateId: templateId, pageNum: pageNum);
ToastUtils.dismiss(); ToastUtils.dismiss();
}, },
), ),
@ -238,7 +253,10 @@ Widget $continueToReview(BuildContext context, {bool isFloatingAction = false})
int? annotatedCount = data?.annotatedCount; // int? annotatedCount = data?.annotatedCount; //
// || (submitCount == annotatedCount || (param.templateId == null && param.studentId == null)) // || (submitCount == annotatedCount || (param.templateId == null && param.studentId == null))
// if (data == null || (data.needAnnotate ? true : data.totalUnAnnotateCount <= 0) ) return const SizedBox(); // if (data == null || (data.needAnnotate ? true : data.totalUnAnnotateCount <= 0) ) return const SizedBox();
if (data == null || (data.needAnnotate ? (data.continuePage == null ? true : data.continuePage!.templateId == data.templateId) : data.totalUnAnnotateCount <= 0)) return const SizedBox(); if (data == null ||
(data.needAnnotate
? (data.continuePage == null ? true : data.continuePage!.templateId == data.templateId)
: data.totalUnAnnotateCount <= 0)) return const SizedBox();
callFun() => easyThrottle( callFun() => easyThrottle(
'DO_PAPERS_JOB_CONTINUE_TO_REVIEW', 'DO_PAPERS_JOB_CONTINUE_TO_REVIEW',
() { () {
@ -289,10 +307,18 @@ Widget $historyHomework(BuildContext context) {
int? studentId = sateData.value?.studentId; int? studentId = sateData.value?.studentId;
if (kDebugMode) print(studentId); if (kDebugMode) print(studentId);
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); PaperStudents? currentStudent;
if (currentStudent == null) return; try {
currentStudent = sateData.value!.students.firstWhere((e) => e.id == studentId);
} catch (e) {
return;
}
var theState = Get.find<HomeworkReviewLogic>().state; var theState = Get.find<HomeworkReviewLogic>().state;
Get.toNamed(Routes.studentWorkDetailPage, arguments: {'studentId': studentId, 'studentName': currentStudent.name, 'subject': theState.param.value.subject}); 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,

View File

@ -1,6 +1,6 @@
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:get/get.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/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';
@ -19,7 +19,8 @@ class FavoriteWidget extends StatelessWidget {
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', () => logic.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(
@ -28,8 +29,10 @@ class FavoriteWidget extends StatelessWidget {
Obx(() { Obx(() {
return Icon( return Icon(
const IconData(0xe63c, fontFamily: "AlibabaIcon"), const IconData(0xe63c, fontFamily: "AlibabaIcon"),
size: 18.sp, size: 24.r,
color: sateData.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),

View File

@ -3,13 +3,14 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart'; import 'package:functional_widget_annotation/functional_widget_annotation.dart';
import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/marking_models/do_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/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/common/utils/toast_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:percent_indicator/linear_percent_indicator.dart'; import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:vector_math/vector_math_64.dart' as vm64;
import '../configuration_files/index.dart'; import '../configuration_files/index.dart';
import '../configuration_files/zoom_logic.dart'; import '../configuration_files/zoom_logic.dart';
@ -37,7 +38,11 @@ class QuestionNumberView extends GetView<HomeworkReviewLogic> {
if (zoomFile == null || studentQuestions.isEmpty) return const SizedBox(); if (zoomFile == null || studentQuestions.isEmpty) return const SizedBox();
return $QuestionNumberScrollView(controller: controller, sateData: sateData, sateZoomData: sateZoomData, studentQuestions: studentQuestions); return $QuestionNumberScrollView(
controller: controller,
sateData: sateData,
sateZoomData: sateZoomData,
studentQuestions: studentQuestions);
}), }),
), ),
); );
@ -63,11 +68,10 @@ Widget $questionNumberScrollView({
// var studentQuestionsStream = sateData.studentQuestions.listen((e) { // var studentQuestionsStream = sateData.studentQuestions.listen((e) {
// studentQuestions.value = e ?? []; // studentQuestions.value = e ?? [];
// }); // });
var sateDataDataStream = sateData.data.listen((e){ var sateDataDataStream = sateData.data.listen((e) {
scrollControllerNum.jumpTo(0); scrollControllerNum.jumpTo(0);
}); });
var stream = sateZoomData.initScale.listen((e) { var stream = sateZoomData.initScale.listen((e) {
// print("initScale : $e"); // print("initScale : $e");
useZoom.value = e ?? 1; useZoom.value = e ?? 1;
@ -101,6 +105,18 @@ Widget $questionNumberScrollView({
scrollControllerNum.addListener(() { scrollControllerNum.addListener(() {
/// ///
// if (sateData.panQuestView == false) sateData.slide.value = scrollControllerNum.offset; // if (sateData.panQuestView == false) sateData.slide.value = scrollControllerNum.offset;
final currentMatrix = controller.zoomController.value;
//
final newMatrix = Matrix4.copy(currentMatrix)
..setTranslation(vm64.Vector3(
currentMatrix.getTranslation().x, //
-scrollControllerNum.offset, //
currentMatrix.getTranslation().z, // Z
));
controller.zoomController.value = newMatrix;
}); });
var listenVal = sateData.slide.listen((e) { var listenVal = sateData.slide.listen((e) {
if (sateData.panQuestView != null && sateData.panQuestView == true && e != scrollControllerNum.offset) { if (sateData.panQuestView != null && sateData.panQuestView == true && e != scrollControllerNum.offset) {
@ -116,9 +132,7 @@ Widget $questionNumberScrollView({
// var actualImgHeight = useImageInfo.value?.actualImgHeight ?? 0; // // var actualImgHeight = useImageInfo.value?.actualImgHeight ?? 0; //
// print("图片高度:$actualImgHeight"); print("图片高度:${usePiddingTop.value}");
print("FFFFFFFFF ${usePiddingTop.value}");
return SingleChildScrollView( return SingleChildScrollView(
controller: scrollControllerNum, controller: scrollControllerNum,
@ -133,11 +147,11 @@ Widget $questionNumberScrollView({
children: studentQuestions children: studentQuestions
.map((e) => $ScoringQuestionsView( .map((e) => $ScoringQuestionsView(
key: Key('${sateData.data.value?.templateId}_${sateData.data.value?.studentId}_${e.questionNo}'), key: Key('${sateData.data.value?.templateId}_${sateData.data.value?.studentId}_${e.questionNo}'),
sateData:sateData, sateData: sateData,
item:e, item: e,
logic:controller, logic: controller,
scaleRatio:sateZoomData.zoomFile.value!.scaleRatio, scaleRatio: sateZoomData.zoomFile.value!.scaleRatio,
initScale:useZoom.value, initScale: useZoom.value,
)) ))
.toList(), .toList(),
), ),
@ -170,8 +184,12 @@ Widget $scoringQuestionsView(
studentScoreListener() { studentScoreListener() {
print(item.toJson()); print(item.toJson());
item.studentScore = studentScore.value; item.studentScore = studentScore.value;
var theVal = sateData.studentQuestions.value?.firstWhereOrNull((e) => e.questionNo == item.questionNo); try {
if (theVal != null) theVal.studentScore = studentScore.value; var theVal = sateData.studentQuestions.value?.firstWhere((e) => e.questionNo == item.questionNo);
theVal?.studentScore = studentScore.value;
} catch (e) {
//
}
var studentQuestions = sateData.studentQuestions.value; var studentQuestions = sateData.studentQuestions.value;
if (item.studentScore == null) return; if (item.studentScore == null) return;
@ -179,18 +197,26 @@ Widget $scoringQuestionsView(
// //
var annotateTime = logic.state.data.value?.annotateTime; var annotateTime = logic.state.data.value?.annotateTime;
if (annotateTime == null) { if (annotateTime == null) {
var noRatingGiven = studentQuestions!.firstWhereOrNull((e) => e.useTime != 0 && e.studentScore == null); try {
if (noRatingGiven == null) logic.submit(context); studentQuestions!.firstWhere((e) => e.useTime != 0 && e.studentScore == null);
} catch (e) {
//
logic.submit(context);
}
} }
} }
studentScore.addListener(studentScoreListener); studentScore.addListener(studentScoreListener);
var studentQuestionsStream = sateData.studentQuestions.listen((e) { var studentQuestionsStream = sateData.studentQuestions.listen((e) {
var itemVal = (e ?? []).firstWhereOrNull((e1) => e1.questionNo == item.questionNo); try {
if (itemVal != null && studentScore.value != itemVal.studentScore) { var itemVal = (e ?? []).firstWhere((e1) => e1.questionNo == item.questionNo);
if (studentScore.value != itemVal.studentScore) {
studentScore.value = itemVal.studentScore; studentScore.value = itemVal.studentScore;
} }
} catch (e) {
//
}
}); });
return () { return () {

View File

@ -2,10 +2,10 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart' hide TransformationController;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart'; 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_bus.dart'; import 'package:making_school_asignment_app/common/job/marking_models/do_paper_bus.dart';
@ -30,8 +30,7 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
HomeworkReviewState get sateData => controller.state; HomeworkReviewState get sateData => controller.state;
ZoomState get zoomState => controller.zoomLogic.zoomState; ZoomState get zoomState => controller.zoomLogic.zoomState;
HomeworkReviewAnnotationsControlState get annotationState => HomeworkReviewAnnotationsControlState get annotationState => controller.annotationState;
controller.annotationState;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -47,10 +46,8 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
if (zoomFileModel == null) { if (zoomFileModel == null) {
/// ///
return LayoutBuilder(builder: return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
(BuildContext context, BoxConstraints constraints) { WidgetsBinding.instance.addPostFrameCallback((_) => zoomState.zoomFile.value = ZoomFileModel(
WidgetsBinding.instance.addPostFrameCallback(
(_) => zoomState.zoomFile.value = ZoomFileModel(
viewWidth: constraints.maxWidth, viewWidth: constraints.maxWidth,
viewHeight: constraints.maxHeight, viewHeight: constraints.maxHeight,
)); ));
@ -67,10 +64,14 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
children: [ children: [
// //
QuestionImageView( QuestionImageView(
maxWidth, maxHeight, annotationState, controller, maxWidth,
maxHeight,
annotationState,
controller,
zoomState: zoomState, zoomState: zoomState,
sateData: sateData, sateData: sateData,
actualHeight: zoomFileModel.actualHeight!), actualHeight: zoomFileModel.actualHeight!,
),
// //
// Positioned(right: 3.w, bottom: 4.h, child: const $ContinueToReview(isFloatingAction: true)), // Positioned(right: 3.w, bottom: 4.h, child: const $ContinueToReview(isFloatingAction: true)),
// //
@ -84,19 +85,15 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
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),
); );
}), }),
), ),
@ -112,18 +109,14 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
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),
); );
}), }),
), ),
@ -160,8 +153,7 @@ class DataErrorThenRequestAgainButton extends StatelessWidget {
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),
), ),
@ -172,8 +164,7 @@ class DataErrorThenRequestAgainButton extends StatelessWidget {
// //
@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();
@ -189,9 +180,7 @@ Widget $totalSubmitCountView(
elevation: 10, elevation: 10,
backgroundColor: Colors.white, backgroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(topLeft: Radius.circular(10.r), topRight: Radius.circular(10.r))),
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),
@ -209,8 +198,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, // ()
@ -221,39 +209,29 @@ 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 ? Theme.of(context).primaryColor
: const Color.fromRGBO( : const Color.fromRGBO(164, 164, 164, 1),
164, 164, 164, 1),
), ),
quickText('优先', quickText('优先', size: 4.sp, color: Colors.white),
size: 4.sp, color: Colors.white),
], ],
), ),
], ],
@ -275,15 +253,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, color: Theme.of(context).primaryColor, size: 12.sp, fontWeight: FontWeight.bold),
size: 12.sp, quickText('/', color: const Color.fromRGBO(117, 117, 117, 1), size: 12.sp),
fontWeight: FontWeight.bold),
quickText('/',
color: const Color.fromRGBO(117, 117, 117, 1), size: 12.sp),
quickText(data.submitCount - data.annotatedCount, quickText(data.submitCount - data.annotatedCount,
color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp), color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
], ],
@ -294,8 +268,7 @@ Widget $totalSubmitCountView(
} }
// //
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 double actualHeight;
@ -304,11 +277,15 @@ class QuestionImageView extends HookWidget
final HomeworkReviewState sateData; final HomeworkReviewState sateData;
final HomeworkReviewAnnotationsControlState annotationState; final HomeworkReviewAnnotationsControlState annotationState;
QuestionImageView( QuestionImageView(
this.maxWidth, this.maxHeight, this.annotationState, this.logic, this.maxWidth,
{required this.actualHeight, this.maxHeight,
this.annotationState,
this.logic, {
required this.actualHeight,
required this.zoomState, required this.zoomState,
required this.sateData, required this.sateData,
super.key}); super.key,
});
/// ///
int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) { int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) {
@ -334,7 +311,7 @@ class QuestionImageView extends HookWidget
timer = Timer(const Duration(milliseconds: timeoutDuration), () { timer = Timer(const Duration(milliseconds: timeoutDuration), () {
if (_activePointers > 2) { if (_activePointers > 2) {
_activePointers = 0; _activePointers = 0;
if (vnHandWritings.value.last != null) { if (vnHandWritings.value.isNotEmpty && vnHandWritings.value.last != null) {
vnHandWritings.value.add(null); // 线 vnHandWritings.value.add(null); // 线
sateData.handwritings = vnHandWritings.value; // sateData.handwritings = vnHandWritings.value; //
} }
@ -362,12 +339,10 @@ class QuestionImageView extends HookWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theMaxHeight = useState<double>(maxHeight); final theMaxHeight = useState<double>(maxHeight);
useValueChanged<double, void>( useValueChanged<double, void>(maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight);
maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight);
var zoomKey = useState<GlobalKey>(GlobalKey()); var zoomKey = useState<GlobalKey>(GlobalKey());
useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId, useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId, (old, __) {
(old, __) {
zoomKey.value = GlobalKey(); zoomKey.value = GlobalKey();
}); });
@ -388,11 +363,9 @@ class QuestionImageView extends HookWidget
var streamSubscriptionSlide = sateData.slide.listen((e) { var streamSubscriptionSlide = sateData.slide.listen((e) {
if (sateData.panQuestView != null && if (sateData.panQuestView != null &&
sateData.panQuestView == false && sateData.panQuestView == false &&
initPosition.value?.dy.abs().toInt().toDouble() != initPosition.value?.dy.abs().toInt().toDouble() != sateData.slide.value) {
sateData.slide.value) {
if (sateData.zoomOffset != null) { if (sateData.zoomOffset != null) {
sateData.zoomOffset = sateData.zoomOffset = Offset(sateData.zoomOffset!.dx, -sateData.slide.value);
Offset(sateData.zoomOffset!.dx, -sateData.slide.value);
} }
initPosition.value = sateData.zoomOffset; initPosition.value = sateData.zoomOffset;
} }
@ -413,15 +386,10 @@ class QuestionImageView extends HookWidget
bool? res = await showDialog<bool>( bool? res = await showDialog<bool>(
context: Get.context ?? context, context: Get.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(context1).primaryColor),
onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定",
color: Theme.of(context1).primaryColor),
onPressed: () => Navigator.pop(context1, true)) onPressed: () => Navigator.pop(context1, true))
]); ]);
}, },
@ -433,15 +401,10 @@ class QuestionImageView extends HookWidget
await showDialog<bool>( await showDialog<bool>(
context: Get.context ?? context, context: Get.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(context1).primaryColor),
onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定",
color: Theme.of(context1).primaryColor),
onPressed: () => easyThrottle( onPressed: () => easyThrottle(
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT', 'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
() { () {
@ -450,17 +413,14 @@ class QuestionImageView extends HookWidget
sateData.data.value?.showZgtAnnotate = null; sateData.data.value?.showZgtAnnotate = null;
if (sateData.data.value != null) { if (sateData.data.value != null) {
sateData.data.update((_) { sateData.data.update((_) {
var theStudentQuestions = var theStudentQuestions = sateData.studentQuestions.value;
sateData.studentQuestions.value; if (theStudentQuestions?.isNotEmpty ?? false) {
if (theStudentQuestions?.isNotEmpty ?? try {
false) { var noMarking = theStudentQuestions?.firstWhere((e) => e.studentScore == null);
var noMarking = ToastUtils.showInfo("未提交!请为第${noMarking?.questionNo}题打分,再手动提交");
theStudentQuestions?.firstWhereOrNull(
(e) => e.studentScore == null);
if (noMarking != null) {
ToastUtils.showInfo(
"未提交!请为第${noMarking.questionNo}题打分,再手动提交");
return; return;
} catch (e) {
//
} }
} }
logic.submit(Get.context ?? context); logic.submit(Get.context ?? context);
@ -484,15 +444,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: () => easyThrottle( onPressed: () => easyThrottle(
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT', 'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
() { () {
@ -500,8 +455,7 @@ class QuestionImageView extends HookWidget
sateData.data.value?.zgtAnnotate = null; sateData.data.value?.zgtAnnotate = null;
sateData.data.value?.showZgtAnnotate = null; sateData.data.value?.showZgtAnnotate = null;
if (sateData.data.value != null) { if (sateData.data.value != null) {
sateData.data.update( sateData.data.update((_) => logic.submit(Get.context ?? context));
(_) => logic.submit(Get.context ?? context));
} }
}, },
), ),
@ -566,17 +520,15 @@ class QuestionImageView extends HookWidget
Offset localPosition = event.localPosition; // Offset localPosition = event.localPosition; //
var zoomFile = zoomState.zoomFile.value!; var zoomFile = zoomState.zoomFile.value!;
// var imageHeightOffsetStart = zoomFile.imageHeightOffsetStart??0; // var imageHeightOffsetStart = zoomFile.imageHeightOffsetStart??0;
var imageHeightOffsetStart = zoomState.zoomFile.value! var imageHeightOffsetStart = zoomState.zoomFile.value!.getZoomFileOffsetStart(zoomState.initScale.value ?? 1);
.getZoomFileOffsetStart(zoomState.initScale.value ?? 1);
// print("位置:$localPosition; 图片所在位置:$imageHeightOffsetStart"); // print("位置:$localPosition; 图片所在位置:$imageHeightOffsetStart");
if (imageHeightOffsetStart == 0) return; if (imageHeightOffsetStart == 0) return;
var dy = localPosition.dy; var dy = localPosition.dy;
// print(zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1)); // print(zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1));
if (dy < imageHeightOffsetStart || if (dy < imageHeightOffsetStart || dy > zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1)) {
dy > return; //
zoomFile.getZoomFileHeightOffsetEnd( }
zoomState.initScale.value ?? 1)) return; //
var theScale = zoomState.initScale.value ?? 1; var theScale = zoomState.initScale.value ?? 1;
// if (theScale != 1) { // if (theScale != 1) {
@ -597,54 +549,59 @@ class QuestionImageView extends HookWidget
// } // }
// (dy / theScale) - (max(0, imageHeightOffsetStart) / theScale) + ((sateData.zoomOffset?.dy.abs() ?? 0) / theScale), // (dy / theScale) - (max(0, imageHeightOffsetStart) / theScale) + ((sateData.zoomOffset?.dy.abs() ?? 0) / theScale),
var zoomWtdthSpaceVal = zoomFile var leftOffset = zoomFile.getZoomFileOffsetStartWidth(theScale);
.getZoomFileOffsetStartWidth(zoomState.initScale.value ?? 1); var topOffset = max(0, imageHeightOffsetStart);
localPosition = Offset( // 使//
(localPosition.dx - final translation = logic.zoomController.value.getTranslation();
zoomFile.getZoomFileOffsetStartWidth( final panDx = translation.x;
zoomState.initScale.value ?? 1) + final panDy = translation.y;
((zoomWtdthSpaceVal <= 0.1)
? (sateData.zoomOffset?.dx.abs() ?? 0) //
: 0)) / final correctedDx = (localPosition.dx - leftOffset - panDx) / theScale;
theScale, final correctedDy = (dy - topOffset - panDy) / theScale;
(dy - localPosition = Offset(correctedDx, correctedDy);
max(0, imageHeightOffsetStart) +
((zoomFile.imageHeightOffsetStart == null ||
zoomFile.imageHeightOffsetStart! <= 0.1)
? (sateData.zoomOffset?.dy.abs() ?? 0)
: 0)) /
theScale,
);
/// ///
if (Platform.isAndroid) { if (Platform.isAndroid) {
var lastDrop = getLastDrop( var lastDrop = getLastDrop(vnHandWritings.value, vnHandWritings.value.length - 1);
vnHandWritings.value, vnHandWritings.value.length - 1);
if (lastDrop != null && if (lastDrop != null &&
((lastDrop.dx - localPosition.dx).abs() > 65 || ((lastDrop.dx - localPosition.dx).abs() > 65 || (lastDrop.dy - localPosition.dy).abs() > 65)) {
(lastDrop.dy - localPosition.dy).abs() > 65)) {
/// X点和上一个x点相差 10 /// X点和上一个x点相差 10
return; return;
} }
} }
// print("最终位置 $localPosition"); // print("最终位置 $localPosition");
vnHandWritings.value = List.from(vnHandWritings.value) vnHandWritings.value = List.from(vnHandWritings.value)..add(localPosition);
..add(localPosition);
sateData.handwritings = vnHandWritings.value; sateData.handwritings = vnHandWritings.value;
}, },
child: Obx(() { child: Obx(() {
var isPen = annotationState.pen.value; var isPen = annotationState.pen.value;
var showZgtAnnotate = sateData.data.value?.showZgtAnnotate; var showZgtAnnotate = sateData.data.value?.showZgtAnnotate;
print(sateData.data.value!.zgtAnswer);
return Container( return Container(
height: double.infinity, height: double.infinity,
width: double.infinity, width: double.infinity,
alignment: Alignment.center, alignment: Alignment.center,
child: IgnorePointer( child: IgnorePointer(
ignoring: isPen, // Zoom
child: Zoom( ignoring: !annotationState.gestureMove.value,
child:
// ZoomView(
// key: zoomKey.value,
// viewWidth: maxWidth,
// viewHeight: maxHeight,
// imageDisplayHeight: actualHeight,
// url: sateData.data.value!.zgtAnswer,
// onScale: logic.zoomLogic.onScaleUpdate,
// // onTrans: (offset) {
// // print("偏移位置:$offset");
// // },
// onContentOffset: logic.zoomLogic.onPanUpPosition,
// ),
Obx(
() => Zoom(
key: zoomKey.value, key: zoomKey.value,
// initTotalZoomOut: true, // // initTotalZoomOut: true, //
zoomSensibility: 0.05, zoomSensibility: 0.05,
@ -653,45 +610,43 @@ class QuestionImageView extends HookWidget
maxZoomWidth: maxWidth, maxZoomWidth: maxWidth,
maxZoomHeight: actualHeight, maxZoomHeight: actualHeight,
canvasColor: Colors.transparent, canvasColor: Colors.transparent,
doubleTapScaleChange: 1,
// initPosition: initPosition.value, // initPosition: initPosition.value,
// initScale: logic.zoomLogic.zoomState.initScale.value ?? 1, // initScale: logic.zoomLogic.zoomState.initScale.value ?? 1,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
onScaleUpdate: logic.zoomLogic.onScaleUpdate, onScaleUpdate: (double scale, double zoom) => logic.zoomLogic.onScaleUpdate(zoom),
onPositionUpdate: logic.zoomLogic.onPanUpPosition, onPositionUpdate: logic.zoomLogic.onPanUpPosition,
transformationController: logic.zoomController,
child: Stack( child: Stack(
children: [ children: [
$TheCachedNetworkImage( $TheCachedNetworkImage(
imgWidth: maxWidth, imgWidth: maxWidth,
imageUrl: sateData.data.value!.zgtAnswer, imageUrl: sateData.data.value!.zgtAnswer,
(_, imageProvider) => (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
Image(image: imageProvider, fit: BoxFit.fitWidth),
), ),
RepaintBoundary( RepaintBoundary(
key: logic.pictureOverviewKey, key: logic.pictureOverviewKey,
child: CustomPaint( child: CustomPaint(
// isComplex: true, // isComplex: true,
size: Size( size: Size(maxWidth, actualHeight),
maxWidth, zoomState.zoomFile.value!.actualHeight!),
foregroundPainter: DrawingPainter(ctrl: vnHandWritings), foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
// child: $TheCachedNetworkImage( // child: $TheCachedNetworkImage(
// imgWidth: maxWidth, // imgWidth: maxWidth,
// imageUrl: showZgtAnnotate ?? sateData.data.value!.zgtAnswer, // imageUrl: showZgtAnnotate ?? sateData.data.value!.zgtAnswer,
// (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth), // (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
// ), // ),
child: showZgtAnnotate != null child: showZgtAnnotate != null
? $TheCachedNetworkImage( ? $TheCachedNetworkImage(
imgWidth: maxWidth, imgWidth: maxWidth,
imageUrl: showZgtAnnotate, imageUrl: showZgtAnnotate,
(_, imageProvider) => Image( (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth))
image: imageProvider, fit: BoxFit.fitWidth))
: null, : null,
), ),
), ),
], ],
), ),
), ),
), )),
); );
}), }),
); );
@ -714,10 +669,11 @@ 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);
} }
} }
}
@override @override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false; bool shouldRepaint(covariant CustomPainter oldDelegate) => false;

View File

@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:making_school_asignment_app/common/utils/cached_network_img.dart';
/// [url]
/// [viewWidth]
/// [viewHeight]
/// [imageDisplayHeight]
/// [onTrans]
/// [onScale]
/// [onContentOffset]
/// [transformationController]
class ZoomView extends HookWidget {
final String url;
final double viewWidth;
final double viewHeight;
final double imageDisplayHeight;
final Function(Offset)? onTrans;
final Function(double)? onScale;
final Function(Offset)? onContentOffset;
final TransformationController? transformationController;
const ZoomView({
required this.url,
required this.viewWidth,
required this.viewHeight,
required this.imageDisplayHeight,
this.onTrans,
this.onScale,
this.onContentOffset,
this.transformationController,
super.key,
});
@override
Widget build(BuildContext context) {
final controller = transformationController ?? useTransformationController();
useEffect(() {
double? lastScale;
Offset? lastOffset;
Offset? lastContentOffset;
void notifyIfChanged() {
final matrix = controller.value;
final scale = matrix.getMaxScaleOnAxis();
final translation = matrix.getTranslation();
final offset = Offset(translation.x, translation.y);
// scale
if (onScale != null && (lastScale == null || (lastScale! - scale).abs() > 0.001)) {
onScale!(scale);
lastScale = scale;
}
// offset
if (onTrans != null && (lastOffset == null || (lastOffset! - offset).distance > 0.5)) {
onTrans!(offset);
lastOffset = offset;
}
//
if (onContentOffset != null) {
final scaledHeight = imageDisplayHeight * scale;
final dx = translation.x;
double dy = viewHeight > scaledHeight ? (viewHeight - scaledHeight) / 2.0 : 0;
dy += translation.y;
final contentOffset = Offset(dx, dy);
if (lastContentOffset == null || (lastContentOffset! - contentOffset).distance > 0.5) {
onContentOffset!(contentOffset);
lastContentOffset = contentOffset;
}
}
//
// final scaledHeight = imageDisplayHeight * scale;
// if (imageDisplayHeight < viewHeight && scaledHeight <= viewHeight) {
// final centerY = (viewHeight - scaledHeight) / 2.0;
// if ((translation.y - centerY).abs() > 0.5 || translation.x.abs() > 0.5) {
// final newMatrix = Matrix4.identity()
// ..translate(0.0, centerY)
// ..scale(scale);
// controller.value = newMatrix;
// }
// }
}
controller.addListener(notifyIfChanged);
return () {
controller.removeListener(notifyIfChanged);
};
}, [controller, imageDisplayHeight, viewHeight]);
return InteractiveViewer(
minScale: 1,
maxScale: 2.0,
panEnabled: true,
constrained: imageDisplayHeight < viewHeight,
transformationController: controller,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Center(
child: $TheCachedNetworkImage(
imageUrl: url,
imgWidth: viewWidth,
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
),
),
);
}
}

View File

@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
@ -7,8 +6,8 @@ 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';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get/get_rx/get_rx.dart';
import 'package:making_school_asignment_app/common/job/annotate_list_to_refresh.dart'; import 'package:making_school_asignment_app/common/job/annotate_list_to_refresh.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';
@ -20,8 +19,8 @@ import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dar
import 'package:making_school_asignment_app/common/utils/permission_describe_util.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:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:zoom_widget/zoom_widget.dart' as zoomWidget;
import 'zoom_logic.dart'; import 'zoom_logic.dart';
@ -35,7 +34,7 @@ class HomeworkReviewState {
late Rx<DoPaperDetailsResult?> data = Rx<DoPaperDetailsResult?>(null); late Rx<DoPaperDetailsResult?> data = Rx<DoPaperDetailsResult?>(null);
late Rx<List<StudentQuestions>?> studentQuestions = Rx<List<StudentQuestions>?>(null); late Rx<List<StudentQuestions>?> studentQuestions = Rx<List<StudentQuestions>?>(null);
late Rx<double> slide = 0.0.obs; // late Rx<double> slide = 0.0.obs; //
bool? panQuestView = null; bool? panQuestView;
// late Rx<TestQuestionsImageInfo?> imageScale; // late Rx<TestQuestionsImageInfo?> imageScale;
// late Rx<TestQuestionsImageInfo?> imageScaleZoom = Rx<TestQuestionsImageInfo?>(null); // late Rx<TestQuestionsImageInfo?> imageScaleZoom = Rx<TestQuestionsImageInfo?>(null);
@ -80,18 +79,19 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
late StreamSubscription<DoPaperDetailsResult?> _dataListen; late StreamSubscription<DoPaperDetailsResult?> _dataListen;
final HomeworkReviewState state = HomeworkReviewState(); final HomeworkReviewState state = HomeworkReviewState();
final HomeworkReviewAnnotationsControlState annotationState = HomeworkReviewAnnotationsControlState(); final HomeworkReviewAnnotationsControlState annotationState = HomeworkReviewAnnotationsControlState();
double appBarHeight = 56; double appBarHeight = kToolbarHeight;
StreamSubscription<TestQuestionsImageInfo?>? imageScaleZoomStream; StreamSubscription<TestQuestionsImageInfo?>? imageScaleZoomStream;
late final zoomWidget.TransformationController zoomController;
@override @override
void onInit() { void onInit() {
appBarHeight = MediaQuery.of(Get.context!).padding.top; appBarHeight = MediaQuery.of(Get.context!).padding.top;
print("appBarHeight $appBarHeight");
// WidgetsFlutterBinding.ensureInitialized(); // WidgetsFlutterBinding.ensureInitialized();
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); // // SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); //
zoomController = zoomWidget.TransformationController();
state.param = DoPaperDetailsParam( state.param = DoPaperDetailsParam(
homeworkId: Get.arguments['homeworkId'], homeworkId: Get.arguments['homeworkId'],
homeworkName: Get.arguments['homeworkName'], homeworkName: Get.arguments['homeworkName'],
@ -107,8 +107,6 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
if (e == null) return; if (e == null) return;
var zoomState = zoomLogic.zoomState; var zoomState = zoomLogic.zoomState;
final currentTemplateId = zoomState.zoomFile.value?.templateId; // ID final currentTemplateId = zoomState.zoomFile.value?.templateId; // ID
if (currentTemplateId != null && currentTemplateId != e.templateId) { if (currentTemplateId != null && currentTemplateId != e.templateId) {
// zoom zoom文件 // zoom zoom文件
@ -133,9 +131,10 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
void onReady() { void onReady() {
Future.delayed(Duration.zero, () { Future.delayed(Duration.zero, () {
DeviceInfoPlugin().androidInfo.then((androidInfo) { DeviceInfoPlugin().androidInfo.then((androidInfo) {
Permission storagePermission = androidInfo.version.sdkInt >= 33 ? Permission.manageExternalStorage : Permission.storage; Permission storagePermission =
androidInfo.version.sdkInt >= 33 ? Permission.manageExternalStorage : Permission.storage;
PermissionDescribeUtil.instance.toLaunchPermissionRequest( PermissionDescribeUtil.instance.toLaunchPermissionRequest(
Get.context, Get.context!,
title: '储存权限请求', title: '储存权限请求',
describe: "为了提供更好的服务,需要获取到存储权限用于保存批阅痕迹并上传", describe: "为了提供更好的服务,需要获取到存储权限用于保存批阅痕迹并上传",
permissions: [storagePermission], permissions: [storagePermission],
@ -148,7 +147,8 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
@override @override
void onClose() { void onClose() {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); // zoomController.dispose();
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); //
eventCancel(); eventCancel();
_dataListen.cancel(); _dataListen.cancel();
_paramListen.cancel(); _paramListen.cancel();
@ -157,17 +157,13 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
} }
void getData() async { void getData() async {
//
if (state.submitLoading.value) return;
var timerControl = Timer(const Duration(milliseconds: 300), () => ToastUtils.showLoading()); var timerControl = Timer(const Duration(milliseconds: 300), () => ToastUtils.showLoading());
try { try {
DoPaperDetailsResult data = await getClient().getDoPaperDetails(state.param.value); DoPaperDetailsResult data = await getClient().getDoPaperDetails(state.param.value);
// var studentQuestions = data.studentQuestions;
// // 0
// for (var i = 0; i < studentQuestions.length; i++) {
// var item = studentQuestions[i];
// item.topHeight = itemPre.height;
// }
state.getDataError.value = false; state.getDataError.value = false;
state.handwritings = []; state.handwritings = [];
@ -178,8 +174,8 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
state.data.value = data; state.data.value = data;
} catch (e) { } catch (e) {
print('获取数据报错了:$e'); print('获取数据报错了:$e');
// ToastUtils.showError('未获取到试题数据,请重试');
state.getDataError.value = true; state.getDataError.value = true;
ToastUtils.showError('获取试题数据失败,请检查网络连接后重试');
} finally { } finally {
if (timerControl.isActive) timerControl.cancel(); if (timerControl.isActive) timerControl.cancel();
ToastUtils.dismiss(); ToastUtils.dismiss();
@ -233,7 +229,8 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
if (data == null) return null; if (data == null) return null;
// OSS url // OSS url
String imgKey = UploadOssImgUtils.getInstance().setImgKey(param.homeworkId, data.studentId.toString(), data.templateId.toString()); String imgKey = UploadOssImgUtils.getInstance()
.setImgKey(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;
@ -276,17 +273,27 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
ToastUtils.showInfo("正在提交批阅数据,请勿重复操作"); ToastUtils.showInfo("正在提交批阅数据,请勿重复操作");
return; return;
} }
state.submitLoading.value = true; state.submitLoading.value = true;
var data = state.data.value; var data = state.data.value;
if (data == null) return; if (data == null) {
ToastUtils.showError('数据异常,请重新加载');
return;
}
if ((state.data.value?.studentQuestions.isEmpty ?? true) || (state.studentQuestions.value?.isEmpty ?? true)) {
ToastUtils.showError('没有可批阅的试题');
return;
}
if ((state.data.value?.studentQuestions.isEmpty ?? true) || (state.studentQuestions.value?.isEmpty ?? true)) return;
var studentQuestions = state.studentQuestions.value!.where((e) => e.useTime != null && e.useTime! > 0).toList(); var studentQuestions = state.studentQuestions.value!.where((e) => e.useTime != null && e.useTime! > 0).toList();
// //
var noRatingElement = studentQuestions.firstWhereOrNull((e) => e.studentScore == null); try {
if (noRatingElement != null) { var noRatingElement = studentQuestions.firstWhere((e) => e.studentScore == null);
ToastUtils.showInfo('${noRatingElement.questionNo}题请评分'); ToastUtils.showInfo('${noRatingElement.questionNo}题请评分');
return; return;
} catch (e) {
//
} }
// //
@ -316,35 +323,13 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
if (totalUnAnnotateCount <= 0) { if (totalUnAnnotateCount <= 0) {
// //
if (!state.lastQuestionPrompt) { if (!state.lastQuestionPrompt) {
await showDialog( await _showCompletionDialog(context);
context: Get.context ?? context,
builder: (BuildContext context1) {
return AlertDialog(
title: quickText('批阅已完成'),
content: const Text('暂无更多批阅项'),
actions: <Widget>[
TextButton(
child: const Text('继续'),
onPressed: () {
state.lastQuestionPrompt = true;
Navigator.of(context1).pop();
},
),
TextButton(
child: const Text('退出批阅'),
onPressed: () {
Navigator.of(context1).pop();
Get.back();
}),
],
);
},
);
} else { } else {
ToastUtils.showSuccess("提交", duration: const Duration(milliseconds: 800)); ToastUtils.showSuccess("批阅已提交", duration: const Duration(milliseconds: 800));
} }
return; return;
} }
var newParams = DoPaperDetailsParam.fromJson(state.param.value.toJson()); var newParams = DoPaperDetailsParam.fromJson(state.param.value.toJson());
if (totalUnAnnotateCount > 0) { if (totalUnAnnotateCount > 0) {
// //
@ -352,14 +337,52 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
newParams.studentId = null; newParams.studentId = null;
} }
state.param.value = newParams; state.param.value = newParams;
ToastUtils.showSuccess("批阅已提交");
}); });
} catch (_) { } catch (e) {
print("批阅提交报错 $_"); print("批阅提交报错 $e");
ToastUtils.showError('提交失败,请检查网络连接后重试');
} finally { } finally {
state.submitLoading.value = false; state.submitLoading.value = false;
} }
} }
Future<void> _showCompletionDialog(BuildContext context) async {
await showDialog(
context: Get.context ?? context,
barrierDismissible: false,
builder: (BuildContext context1) {
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.r)),
title: Row(
children: [
Icon(Icons.check_circle, color: Colors.green, size: 24.sp),
SizedBox(width: 8.w),
Text('批阅已完成', style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w600)),
],
),
content: Text('暂无更多批阅项,是否继续?', style: TextStyle(fontSize: 14.sp)),
actions: <Widget>[
TextButton(
child: Text('退出批阅', style: TextStyle(color: Colors.grey[600])),
onPressed: () {
Navigator.of(context1).pop();
Get.back();
},
),
TextButton(
child: Text('继续', style: TextStyle(color: Theme.of(context).primaryColor)),
onPressed: () {
state.lastQuestionPrompt = true;
Navigator.of(context1).pop();
},
),
],
);
},
);
}
Future<void> toFavorite() async { Future<void> toFavorite() async {
try { try {
var data = state.data.value!; var data = state.data.value!;

View File

@ -1,12 +1,9 @@
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 'dart:async';
import 'dart:ui' show ImmutableBuffer, ImageDescriptor;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'index.dart'; import 'index.dart';
@ -78,8 +75,8 @@ class ZoomLogic extends GetxController {
} }
// ==> // ==>
void onScaleUpdate(double scale, double zoom) async { void onScaleUpdate(double zoom) async {
print("$scale $zoom"); print("缩放比例:$zoom");
/// ///
zoomState.initScale.value = zoom; zoomState.initScale.value = zoom;
@ -90,14 +87,11 @@ class ZoomLogic extends GetxController {
void onPanUpPosition(Offset val) async { void onPanUpPosition(Offset val) async {
// //
var state = Get.find<HomeworkReviewLogic>().state; var state = Get.find<HomeworkReviewLogic>().state;
if (state.zoomOffset?.dy.toStringAsFixed(2) != val.dy.toStringAsFixed(2) || print('**************** 正在移动位置 YYY:${val.dy}');
state.zoomOffset?.dx.toStringAsFixed(2) != val.dx.toStringAsFixed(2)) { print('**************** 正在移动位置 XXX:${val.dx}');
// print('**************** 正在移动位置 YYY:${val.dy}');
// print('**************** 正在移动位置 XXX:${val.dx}');
state.zoomOffset = val; state.zoomOffset = val;
state.slide.value = val.dy.abs().toInt().toDouble(); state.slide.value = val.dy.abs().toInt().toDouble();
} }
}
} }
class HomeworkReviewZoomBinding extends Bindings { class HomeworkReviewZoomBinding extends Bindings {

View File

@ -3,12 +3,11 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/annotate_list_to_refresh.dart'; import 'package:making_school_asignment_app/common/job/annotate_list_to_refresh.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/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/bottom_operation_bar.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/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/dropdown_switch_students_type.dart'; import 'components/dropdown_switch_students_type.dart';
import 'components/favorite_widget.dart'; import 'components/favorite_widget.dart';
import 'configuration_files/index.dart'; import 'configuration_files/index.dart';
@ -18,35 +17,28 @@ class HomeworkReview extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final logic = Get.find<HomeworkReviewLogic>(); return GetBuilder<HomeworkReviewLogic>(builder: (logic) {
final sateData = logic.state; 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(); Get.find<AnnotateClassLogic>().getList();
homeLogicController.getList(); Get.find<HomeLogic>().getList();
logic.eventFire(model: AnnotateListToRefresh()); logic.eventFire(model: AnnotateListToRefresh());
} }
}, },
child: Scaffold( child: Scaffold(
appBar: AppBar( backgroundColor: Colors.transparent,
// titleSpacing: 0, body: Stack(
leading: IconButton(icon: const Icon(Icons.arrow_back_ios), onPressed: () => Get.back()), children: [
iconTheme: const IconThemeData(color: Colors.black), // body内容AppBar高度
title: quickText(sateData.param.value.homeworkName), const Padding(
backgroundColor: Colors.white, padding: EdgeInsets.only(top: kToolbarHeight),
elevation: 0, child: Column(
actions: [const FavoriteWidget(), SizedBox(width: 5.w), const ReturnToHomepage()],
),
body: const Column(
children: [ children: [
// //
DropdownSwitchStudentsType(), DropdownSwitchStudentsType(),
// SizedBox(height: 10),
// //
Expanded(child: QuestionPaperView()), Expanded(child: QuestionPaperView()),
// //
@ -54,6 +46,35 @@ class HomeworkReview extends StatelessWidget {
], ],
), ),
), ),
// AppBar
Container(
height: kToolbarHeight,
color: Colors.white,
child: Row(
children: [
SizedBox(width: 5.w),
IconButton(
icon: const Icon(Icons.arrow_back_ios, color: Colors.black),
onPressed: () => Get.back(),
tooltip: '返回'),
Expanded(
child: Text(
sateData.param.value.homeworkName,
style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
),
),
const FavoriteWidget(),
SizedBox(width: 10.w),
const ReturnToHomepage(),
SizedBox(width: 8.w),
],
),
),
],
),
),
); );
});
} }
} }

View File

@ -4,8 +4,8 @@ import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/annotated_class.dart'; import 'package:making_school_asignment_app/common/job/annotated_class.dart';
import 'package:making_school_asignment_app/page/global_widget/MyEmptyWidget.dart'; import 'package:making_school_asignment_app/page/global_widget/MyEmptyWidget.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/home_page/children/job_report/widget/dropdown_selection.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/job_report/widget/dropdown_selection.dart';
import 'package:making_school_asignment_app/page/home_page/children/job_report/widget/knowledge_point.dart'; import 'package:making_school_asignment_app/page/home_page/children/job_report/widget/knowledge_point.dart';
import 'package:making_school_asignment_app/page/home_page/children/job_report/widget/personnel_data_overview.dart'; import 'package:making_school_asignment_app/page/home_page/children/job_report/widget/personnel_data_overview.dart';
import 'package:making_school_asignment_app/page/home_page/children/job_report/widget/top_count.dart'; import 'package:making_school_asignment_app/page/home_page/children/job_report/widget/top_count.dart';
@ -64,7 +64,7 @@ class _JobReportPageState extends State<JobReportPage> {
return DropdownSelection( return DropdownSelection(
involveClasses: state.involveClasses.value, involveClasses: state.involveClasses.value,
classData: state.classData.value, classData: state.classData.value,
call: (AnnotatedClass item) { onClassSelected: (AnnotatedClass item) {
state.classData.value = item; state.classData.value = item;
if (item.grade == -1) state.classData.value = state.defaultClass; if (item.grade == -1) state.classData.value = state.defaultClass;
logic.getWorkData(); logic.getWorkData();
@ -75,7 +75,8 @@ class _JobReportPageState extends State<JobReportPage> {
), ),
), ),
// //
TopCount(state.dataCount, state.classData == null ? '' : state.classData.value.className, state.homeworkId.value, state.subject), TopCount(
state.dataCount, state.classData.value.className ?? '', state.homeworkId.value, state.subject),
// //
KgtZgtTable( KgtZgtTable(
studentCount: state.dataCount.studentCount!, studentCount: state.dataCount.studentCount!,
@ -94,7 +95,7 @@ class _JobReportPageState extends State<JobReportPage> {
child: KnowledgePoint( child: KnowledgePoint(
knowsList: state.knowsList, knowsList: state.knowsList,
data: state.homeData, data: state.homeData,
className: state.classData.value.className, className: state.classData.value.className ?? '',
homeworkId: state.homeworkId.value, homeworkId: state.homeworkId.value,
subject: state.subject, subject: state.subject,
)), )),
@ -107,7 +108,9 @@ class _JobReportPageState extends State<JobReportPage> {
margin: EdgeInsets.symmetric(horizontal: 10.r), margin: EdgeInsets.symmetric(horizontal: 10.r),
child: $UnitTimeAnsweringSituation(widget.id, data.questionAnswerInfos)),*/ child: $UnitTimeAnsweringSituation(widget.id, data.questionAnswerInfos)),*/
// //
Container(margin: EdgeInsets.symmetric(horizontal: 10.r), child: PersonnelDataOverview(studentList: state.studentList.value)), Container(
margin: EdgeInsets.symmetric(horizontal: 10.r),
child: PersonnelDataOverview(studentList: state.studentList.value)),
SizedBox( SizedBox(
height: 30.r, height: 30.r,
@ -116,7 +119,9 @@ class _JobReportPageState extends State<JobReportPage> {
), ),
); );
} else { } else {
return Padding(padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 2 - 200.r), child: const MyEmptyWidget()); return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 2 - 200.r),
child: const MyEmptyWidget());
} }
}), }),
); );

View File

@ -7,9 +7,11 @@ import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
class DropdownSelection extends StatefulWidget { class DropdownSelection extends StatefulWidget {
final List<AnnotatedClass>? involveClasses; final List<AnnotatedClass>? involveClasses;
final AnnotatedClass? classData; final AnnotatedClass? classData;
final Function(AnnotatedClass) call; final void Function(AnnotatedClass) onClassSelected;
final Color? bgColor; final Color? bgColor;
const DropdownSelection({Key? key, required this.involveClasses, required this.classData, required this.call,this.bgColor}) : super(key: key);
const DropdownSelection(
{super.key, required this.involveClasses, required this.classData, required this.onClassSelected, this.bgColor});
@override @override
State<DropdownSelection> createState() => _DropdownSelectionState(); State<DropdownSelection> createState() => _DropdownSelectionState();
@ -29,18 +31,27 @@ class _DropdownSelectionState extends State<DropdownSelection> {
)), )),
child: DropdownButton( child: DropdownButton(
value: widget.classData?.classId ?? '-1', value: widget.classData?.classId ?? '-1',
style: TextStyle(color: Color.fromRGBO(89, 89, 89, 1), fontSize: 12.sp), style: TextStyle(color: const Color.fromRGBO(89, 89, 89, 1), fontSize: 12.sp),
underline: Container(), underline: Container(),
// isExpanded:true, // isExpanded:true,
items: widget.involveClasses?.map((e) { items: widget.involveClasses?.map((classItem) {
return DropdownMenuItem( return DropdownMenuItem<String>(
value: e.classId!, value: classItem.classId,
child: quickText(e.classId == '-1' ? '全部' : ' ${EnumUtils.formatGrade(e.grade)}${e.className}', size: 12.sp, color: Colors.black), child: quickText(
classItem.classId == '-1' ? '全部' : ' ${EnumUtils.formatGrade(classItem.grade)}${classItem.className}',
size: 12.sp,
color: Colors.black),
); );
}).toList(), }).toList(),
onChanged: (value) { onChanged: (String? value) {
if (value == null) return; if (value == null || widget.involveClasses == null) return;
widget.call(widget.involveClasses!.firstWhere((element) => element.classId == value));
try {
final selectedClass = widget.involveClasses!.firstWhere((element) => element.classId == value);
widget.onClassSelected(selectedClass);
} catch (e) {
//
}
}, },
), ),
); );

View File

@ -5,7 +5,6 @@ import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/knowledge_points_grasp.dart'; import 'package:making_school_asignment_app/common/job/knowledge_points_grasp.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/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 'knowledge_points_grasp_state.dart'; import 'knowledge_points_grasp_state.dart';
@ -19,7 +18,9 @@ class KnowledgePointsGraspLogic extends GetxController with RequestToolMixin, Ge
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
state.subject = Get.arguments['subject'] ?? -1; final subject = Get.arguments['subject'];
state.subject = subject == 0 ? null : subject;
state.classId = Get.arguments['classId'] ?? ''; state.classId = Get.arguments['classId'] ?? '';
textController = TextEditingController(); textController = TextEditingController();
refreshController = EasyRefreshController(); refreshController = EasyRefreshController();
@ -31,8 +32,9 @@ class KnowledgePointsGraspLogic extends GetxController with RequestToolMixin, Ge
void getList() async { void getList() async {
try { try {
ToastUtils.showLoading(); ToastUtils.showLoading();
List<KnowledgePointsGrasp> data = List<KnowledgePointsGrasp> data = await getClient()
await getClient().getKnowledgeReport(state.dateStart, state.dateEnd, textController.text, state.classId, state.subject); .getKnowledgeReport(state.dateStart, state.dateEnd, textController.text, state.classId, state.subject);
print('data: ${data.length}');
state.dataList.value = data; state.dataList.value = data;
} catch (_) { } catch (_) {
} finally { } finally {

View File

@ -13,7 +13,6 @@ class KnowledgePointsGraspState {
late String dateStart = Utils.getWeekStartDate().toString().substring(0, 10); late String dateStart = Utils.getWeekStartDate().toString().substring(0, 10);
late String dateEnd = Utils.getWeekEndDate().toString().substring(0, 10); late String dateEnd = Utils.getWeekEndDate().toString().substring(0, 10);
late RxString customTimeStr = '自定义'.obs; late RxString customTimeStr = '自定义'.obs;
late final int subject; late final int? subject;
late final String classId; late final String classId;
} }

View File

@ -1,4 +1,3 @@
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart';
@ -87,7 +86,8 @@ class _KnowledgePointsGraspPageState extends State<KnowledgePointsGraspPage> {
child: Container( child: Container(
width: 50.r, width: 50.r,
height: 30.r, height: 30.r,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4.r), color: Theme.of(context).primaryColor), decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.r), color: Theme.of(context).primaryColor),
child: Center( child: Center(
child: Text( child: Text(
'查询', '查询',
@ -104,12 +104,11 @@ class _KnowledgePointsGraspPageState extends State<KnowledgePointsGraspPage> {
jobType: 1, jobType: 1,
controller: logic.tabController, controller: logic.tabController,
customTimeStr: state.customTimeStr.value, customTimeStr: state.customTimeStr.value,
customTime: logic.tabController.index != 2 || customTime: logic.tabController.index != 2 || ((state.dateEnd == '') && (state.dateStart == ''))
((state.dateEnd == null || state.dateEnd == '') && (state.dateStart == null || state.dateStart == ''))
? null ? null
: PickerDateRange( : PickerDateRange(
state.dateStart == null || state.dateStart == '' ? null : DateTime.parse(state.dateStart!), state.dateStart == '' ? null : DateTime.parse(state.dateStart),
state.dateEnd == null || state.dateEnd == '' ? null : DateTime.parse(state.dateEnd!), state.dateEnd == '' ? null : DateTime.parse(state.dateEnd),
), ),
onTimeFilter: (String? startTime, String? endTime) { onTimeFilter: (String? startTime, String? endTime) {
EasyLoading.show(status: 'loading...'); EasyLoading.show(status: 'loading...');
@ -133,9 +132,11 @@ class _KnowledgePointsGraspPageState extends State<KnowledgePointsGraspPage> {
state.customTimeStr.value = value.startDate?.toString().substring(0, 10) ?? ''; state.customTimeStr.value = value.startDate?.toString().substring(0, 10) ?? '';
if (value.endDate != null) { if (value.endDate != null) {
if (!Utils.isPad() && value.startDate!.year == value.endDate!.year) { if (!Utils.isPad() && value.startDate!.year == value.endDate!.year) {
state.customTimeStr.value = '${value.startDate.toString().substring(5, 10)}~${value.endDate.toString().substring(5, 10)}'; state.customTimeStr.value =
'${value.startDate.toString().substring(5, 10)}~${value.endDate.toString().substring(5, 10)}';
} else { } else {
state.customTimeStr.value = '${state.customTimeStr.value}~${value.endDate?.toString().substring(0, 10)}'; state.customTimeStr.value =
'${state.customTimeStr.value}~${value.endDate?.toString().substring(0, 10)}';
} }
} }
} }
@ -180,7 +181,8 @@ class _KnowledgePointsGraspPageState extends State<KnowledgePointsGraspPage> {
child: Container( child: Container(
margin: EdgeInsets.symmetric(vertical: 5.r, horizontal: 14.r), margin: EdgeInsets.symmetric(vertical: 5.r, horizontal: 14.r),
padding: EdgeInsets.symmetric(vertical: 14.r, horizontal: 10.r), padding: EdgeInsets.symmetric(vertical: 14.r, horizontal: 10.r),
decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(10.r)), color: Colors.white), decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.r)), color: Colors.white),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -207,12 +209,14 @@ class _KnowledgePointsGraspPageState extends State<KnowledgePointsGraspPage> {
children: [ children: [
Text( Text(
'${item.count}', '${item.count}',
style: TextStyle(fontSize: 10.sp, color: Theme.of(context).primaryColor), style:
TextStyle(fontSize: 10.sp, color: Theme.of(context).primaryColor),
), ),
SizedBox(width: 1.w), SizedBox(width: 1.w),
Padding( Padding(
padding: EdgeInsets.only(top: 2.h), padding: EdgeInsets.only(top: 2.h),
child: Icon(Icons.chevron_right, size: 12.r, color: Theme.of(context).primaryColor), child: Icon(Icons.chevron_right,
size: 12.r, color: Theme.of(context).primaryColor),
), ),
// Image.asset( // Image.asset(
// 'assets/images/job_data_right_icon.png', // 'assets/images/job_data_right_icon.png',

View File

@ -81,7 +81,6 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
fontSize: 13.sp, fontSize: 13.sp,
); );
return OrientationBuilder(builder: (BuildContext context, Orientation orientation) {
return Stack( return Stack(
children: [ children: [
SizedBox( SizedBox(
@ -134,7 +133,8 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
margin: EdgeInsets.only(top: 0.h), margin: EdgeInsets.only(top: 0.h),
child: Text( child: Text(
userInfo.value?.name ?? '请前往登录', userInfo.value?.name ?? '请前往登录',
style: TextStyle(fontSize: 16.sp, color: const Color(0xFF332A2A), fontWeight: FontWeight.w500), style:
TextStyle(fontSize: 16.sp, color: const Color(0xFF332A2A), fontWeight: FontWeight.w500),
), ),
), ),
), ),
@ -149,7 +149,7 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
color: const Color.fromRGBO(255, 255, 255, 1), color: const Color.fromRGBO(255, 255, 255, 1),
borderRadius: BorderRadius.circular(50.r), borderRadius: BorderRadius.circular(50.r),
), ),
child: Image.asset('assets/images/out_icon.png',fit: BoxFit.cover), child: Image.asset('assets/images/out_icon.png', fit: BoxFit.cover),
), ),
) )
], ],
@ -174,7 +174,10 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text('账号', style: personalInfoTitleStly), Text(userInfo.value?.name ?? '请前往登录', style: personalInfoValStly)], children: [
Text('账号', style: personalInfoTitleStly),
Text(userInfo.value?.name ?? '请前往登录', style: personalInfoValStly)
],
), ),
), ),
Container( Container(
@ -195,7 +198,10 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text('所在学校', style: personalInfoTitleStly), Text(userInfo.value?.schoolName ?? '', style: personalInfoValStly)], children: [
Text('所在学校', style: personalInfoTitleStly),
Text(userInfo.value?.schoolName ?? '', style: personalInfoValStly)
],
), ),
), ),
Container( Container(
@ -237,6 +243,5 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
), ),
], ],
); );
});
} }
} }

View File

@ -1,7 +1,7 @@
import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.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/read_over/widget/annotate_list.dart'; import 'package:making_school_asignment_app/page/home_page/children/read_over/widget/annotate_list.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
@ -37,7 +37,7 @@ class _ReadOverPageState extends State<ReadOverPage> {
statusBarBrightness: Brightness.light, statusBarBrightness: Brightness.light,
), ),
child: Scaffold( child: Scaffold(
backgroundColor: const Color.fromRGBO(244, 244, 244, 1), backgroundColor: Colors.white,
body: OrientationBuilder( body: OrientationBuilder(
builder: (BuildContext context, Orientation orientation) { builder: (BuildContext context, Orientation orientation) {
return Column( return Column(
@ -117,7 +117,9 @@ class _ReadOverPageState extends State<ReadOverPage> {
child: quickText( child: quickText(
'待批阅', '待批阅',
size: 14.sp, size: 14.sp,
color: state.tabIndex.value == 0 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), color: state.tabIndex.value == 0
? Theme.of(context).primaryColor
: const Color.fromRGBO(80, 94, 110, 1),
fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null, fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null,
), ),
); );
@ -137,7 +139,9 @@ class _ReadOverPageState extends State<ReadOverPage> {
child: quickText( child: quickText(
'已批阅', '已批阅',
size: 14.sp, size: 14.sp,
color: state.tabIndex.value == 1 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), color: state.tabIndex.value == 1
? Theme.of(context).primaryColor
: const Color.fromRGBO(80, 94, 110, 1),
fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null, fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null,
), ),
); );
@ -153,13 +157,16 @@ class _ReadOverPageState extends State<ReadOverPage> {
onTap: () { onTap: () {
Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'}); Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'});
}, },
child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"), color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp), child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"),
color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp),
), ),
), ),
], ],
), ),
), ),
Expanded( Expanded(
child: Container(
color: const Color.fromRGBO(245, 245, 245, 1),
child: Obx(() { child: Obx(() {
return AnnotateList( return AnnotateList(
tabIndex: state.tabIndex.value, tabIndex: state.tabIndex.value,
@ -167,6 +174,7 @@ class _ReadOverPageState extends State<ReadOverPage> {
); );
}), }),
), ),
),
], ],
); );
}, },

View File

@ -3,7 +3,6 @@ import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/work_student.dart'; import 'package:making_school_asignment_app/common/job/work_student.dart';
import 'package:making_school_asignment_app/common/job/work_student_params.dart'; import 'package:making_school_asignment_app/common/job/work_student_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/store/user_store.dart';
import 'home_state.dart'; import 'home_state.dart';
@ -24,16 +23,20 @@ class HomeLogic extends GetxController with RequestToolMixin, GetTickerProviderS
} }
void getList() async { void getList() async {
try {
params.pageNumber = state.pageNumber; params.pageNumber = state.pageNumber;
WorkStudent data = await getClient().getUnAnnotateList(params); WorkStudent data = await getClient().getUnAnnotateList(params);
state.totalCount.value = data.totalCount; state.totalCount.value = data.totalCount;
state.readOver.value = data.items.length; state.readOver.value = data.items.length;
/* if(params.pageNumber == 1){
state.workList.value = data.items; //
}else{ refreshController.finishRefresh();
state.workList.addAll(data.items); } catch (e) {
print('获取作业列表失败: $e');
//
refreshController.finishRefresh();
//
} }
refreshController.finishRefresh();*/
} }
@override @override

View File

@ -1,15 +1,12 @@
import 'package:badges/badges.dart' as badges;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart'; import 'package:functional_widget_annotation/functional_widget_annotation.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:badges/badges.dart' as badges; import 'package:making_school_asignment_app/common/mixins/getx_keepalive_widget.dart';
import 'package:percent_indicator/percent_indicator.dart';
import 'package:making_school_asignment_app/common/job/work_student.dart';
import 'package:making_school_asignment_app/common/utils/enum_untils.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/MyEmptyWidget.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/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
@ -17,165 +14,266 @@ import 'home_logic.dart';
part 'home_view.g.dart'; part 'home_view.g.dart';
class HomePage extends StatefulWidget { class HomePage extends GetxKeepAliveWidget<HomeLogic> {
const HomePage({super.key}); const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin {
final logic = Get.find<HomeLogic>();
final state = Get.find<HomeLogic>().state;
@override
bool get wantKeepAlive => true;
@override @override
void initState() { void initState() {
super.initState();
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent, // statusBarColor: Colors.transparent, //
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.light,
systemStatusBarContrastEnforced: false, systemStatusBarContrastEnforced: false,
)); ));
super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget buildContent(context, controller) {
super.build(context);
var spaceWidth = SizedBox(height: ScreenUtil().screenWidth / 30);
return Scaffold( return Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white,
body: OrientationBuilder( body: OrientationBuilder(
builder: (BuildContext context, Orientation orientation) { builder: (BuildContext context, Orientation orientation) {
final state = controller.state;
return EasyRefresh( return EasyRefresh(
firstRefresh: false, firstRefresh: false,
taskIndependence: true, taskIndependence: true,
enableControlFinishLoad: true, enableControlFinishLoad: true,
controller: logic.refreshController, controller: controller.refreshController,
header: MaterialHeader(), header: MaterialHeader(),
// footer: TaurusFooter(),
onRefresh: () async { onRefresh: () async {
state.pageNumber = 1; state.pageNumber = 1;
return logic.getList(); return controller.getList();
}, },
// onLoad: () async { child: CustomScrollView(
// if (state.workList.length < state.totalCount.value) { slivers: [
// state.pageNumber++; //
// return logic.getList(); SliverAppBar(
// } expandedHeight: 300.h,
// }, floating: false,
child: Stack( pinned: false,
children: [ backgroundColor: Colors.transparent,
Image.asset( flexibleSpace: FlexibleSpaceBar(
background: Image.asset(
'assets/images/home_banner.png', 'assets/images/home_banner.png',
width: Get.width, width: Get.width,
fit: BoxFit.fill, fit: BoxFit.cover,
), ),
Container( ),
margin: EdgeInsets.only(top: 300.h), ),
//
SliverToBoxAdapter(
child: Container(
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20.r), padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20.r),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.only(topLeft: Radius.circular(30.r), topRight: Radius.circular(30.r))), color: Colors.white,
child: Column( borderRadius: BorderRadius.only(topLeft: Radius.circular(30.r), topRight: Radius.circular(30.r)),
children: [
SizedBox(
height: 15.r,
), ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 20.r),
//
Row(
children: [
Icon(
Icons.assignment_outlined,
size: 24.sp,
color: const Color(0xFF4F4F4F),
),
SizedBox(width: 8.r),
Text( Text(
'我的作业管理', '我的作业管理',
style: TextStyle(fontSize: 20.sp, color: const Color(0xFF676666), fontWeight: FontWeight.w600), style: TextStyle(
fontSize: 22.sp, color: const Color(0xFF2C2C2C), fontWeight: FontWeight.w700),
), ),
SizedBox( const Spacer(),
height: 15.r, //
IconButton(
onPressed: () => controller.refreshController.callRefresh(),
icon: Icon(
Icons.refresh,
size: 20.sp,
color: const Color(0xFF4F4F4F),
), ),
tooltip: '刷新数据',
)
],
),
SizedBox(height: 24.r),
//
_buildMenuGrid(context, state),
SizedBox(height: 20.r),
],
),
),
),
],
),
);
},
),
);
}
Widget _buildStatCard(String title, String value, Color color, IconData icon) {
return Container(
padding: EdgeInsets.all(12.r),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: [
Icon(icon, color: color, size: 20.sp),
SizedBox(height: 4.r),
Text(
value,
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
color: color,
),
),
Text(
title,
style: TextStyle(
fontSize: 12.sp,
color: const Color(0xFF6C757D),
),
),
],
),
);
}
Widget _buildMenuGrid(BuildContext context, dynamic state) {
return Column(
children: [
//
Row( Row(
children: [ children: [
Expanded( Expanded(
child: Obx(() { child: Obx(() => $MenuItem(
return menuItem(
bgImg: 'assets/images/home_bg_01.png', bgImg: 'assets/images/home_bg_01.png',
name: '作业批阅', name: '作业批阅',
value: state.totalCount.value.toString(), value: state.totalCount.value > 0 ? state.totalCount.value.toString() : null,
url: Routes.readOverPage, url: Routes.readOverPage,
); isPrimary: true,
}), )),
), ),
SizedBox( SizedBox(width: 16.r),
width: 20.r, const Expanded(
child: $MenuItem(
bgImg: 'assets/images/home_bg_02.png',
name: '知识点掌握',
url: Routes.studentHistoryWorkPage,
page: 'points',
), ),
Expanded(
child: menuItem(bgImg: 'assets/images/home_bg_02.png', name: '知识点掌握', url: Routes.studentHistoryWorkPage, page: 'points'),
), ),
], ],
), ),
SizedBox( SizedBox(height: 16.r),
height: 20.r, //
),
Row( Row(
children: [ children: [
Expanded( const Expanded(
child: child: $MenuItem(
menuItem(bgImg: 'assets/images/home_bg_03.png', name: '学生历史作业', url: Routes.studentHistoryWorkPage, page: 'history'), bgImg: 'assets/images/home_bg_03.png',
name: '学生历史作业',
url: Routes.studentHistoryWorkPage,
page: 'history',
), ),
SizedBox(
width: 20.r,
), ),
Expanded( SizedBox(width: 16.r),
child: menuItem(bgImg: 'assets/images/home_bg_04.png', name: '答题轨迹', url: Routes.answerTrajectoryPage), const Expanded(
child: $MenuItem(bgImg: 'assets/images/home_bg_04.png', name: '答题轨迹', url: Routes.answerTrajectoryPage),
), ),
], ],
), ),
SizedBox( SizedBox(height: 16.r),
height: 20.r, //
),
Row( Row(
children: [ children: [
menuItem( const Expanded(
child: $MenuItem(
bgImg: 'assets/images/home_bg_05.png', bgImg: 'assets/images/home_bg_05.png',
name: '优先批阅设定', name: '优先批阅设定',
url: Routes.studentHistoryWorkPage, url: Routes.studentHistoryWorkPage,
page: 'set', page: 'set',
), ),
SizedBox(
width: 20.r,
), ),
SizedBox(width: 16.r),
//
const Expanded(child: SizedBox()),
], ],
), ),
SizedBox(height: 15.h),
], ],
),
)
],
),
); );
},
),
);
}
@override
void dispose() {
Get.delete<HomeLogic>();
super.dispose();
} }
} }
Widget menuItem({required String bgImg, required String name, String? value, required String url, String? page}) { @swidget
return InkWell( Widget $menuItem({
required String bgImg,
required String name,
String? value,
required String url,
String? page,
bool isPrimary = false,
}) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () { onTap: () {
Get.toNamed(url, arguments: {'page': page ?? ''}); Get.toNamed(url, arguments: {'page': page ?? ''});
}, },
borderRadius: BorderRadius.circular(12.r),
child: Semantics(
label: '$name${value != null ? ',待处理数量:$value' : ''}',
hint: '点击进入$name页面',
button: true,
child: Container( child: Container(
width: (Get.width - 60.r) / 2, width: (Get.width - 60.r) / 2,
height: (Get.width - 60.r) / 2 * 86 / 164, height: (Get.width - 60.r) / 2 * 86 / 164,
padding: EdgeInsets.symmetric(vertical: 15.r, horizontal: 15.r), padding: EdgeInsets.all(16.r),
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( borderRadius: BorderRadius.circular(12.r),
image: AssetImage(bgImg), gradient: isPrimary
fit: BoxFit.contain, ? const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFF667eea),
Color(0xFF764ba2),
],
)
: null,
color: isPrimary ? null : Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 8,
offset: const Offset(0, 2),
), ),
],
image: !isPrimary
? DecorationImage(
image: AssetImage(bgImg),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.white.withOpacity(0.8),
BlendMode.srcATop,
),
)
: null,
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -184,40 +282,64 @@ Widget menuItem({required String bgImg, required String name, String? value, req
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text( Expanded(
child: Text(
name, name,
style: TextStyle(fontSize: 14.sp, color: const Color(0xFF4F4F4F), fontWeight: FontWeight.w600), style: TextStyle(
fontSize: 14.sp,
color: isPrimary ? Colors.white : const Color(0xFF2C2C2C),
fontWeight: FontWeight.w600,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
), ),
if (value != null && value != '') if (value != null && value != '')
Container( Semantics(
width: 18.r, label: '待处理数量:$value',
height: 18.r, child: Container(
width: 20.r,
height: 20.r,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFFF6969), color: isPrimary ? Colors.white : const Color(0xFFFF6969),
borderRadius: BorderRadius.all(Radius.circular(9.r)), borderRadius: BorderRadius.all(Radius.circular(10.r)),
), ),
child: Text( child: Text(
value!, value,
style: TextStyle(fontSize: 10.sp, color: Colors.white, fontWeight: FontWeight.w600), style: TextStyle(
fontSize: 10.sp,
color: isPrimary ? const Color(0xFFFF6969) : Colors.white,
fontWeight: FontWeight.w600),
),
), ),
) )
], ],
), ),
const Spacer(), const Spacer(),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
if (isPrimary)
Icon(
Icons.arrow_forward_ios,
size: 14.sp,
color: Colors.white,
)
else
Image.asset( Image.asset(
'assets/images/home_right_icon.png', 'assets/images/home_right_icon.png',
width: 16.r, width: 16.r,
height: 16.r, height: 16.r,
semanticLabel: '进入图标',
), ),
], ],
), ),
], ],
), ),
), ),
),
),
); );
} }
@ -243,7 +365,7 @@ Widget $termRow(BuildContext context, List<EntranceModel> items, int? data) {
Expanded(flex: 9, child: $TermItem(items[0], data!)), Expanded(flex: 9, child: $TermItem(items[0], data!)),
// const Expanded(flex: 1, child: SizedBox()), // const Expanded(flex: 1, child: SizedBox()),
SizedBox(width: ScreenUtil().screenWidth / 30), SizedBox(width: ScreenUtil().screenWidth / 30),
Expanded(flex: 9, child: $TermItem(items[1], data!)), Expanded(flex: 9, child: $TermItem(items[1], data)),
]); ]);
break; break;
case 3: case 3:
@ -304,7 +426,8 @@ Widget $termItem(BuildContext context, EntranceModel e, int data, {double? theHe
badgeStyle: badges.BadgeStyle( badgeStyle: badges.BadgeStyle(
badgeColor: const Color.fromRGBO(255, 105, 105, 1), badgeColor: const Color.fromRGBO(255, 105, 105, 1),
shape: badges.BadgeShape.square, shape: badges.BadgeShape.square,
borderRadius: BorderRadius.only(topLeft: Radius.circular(10.r), topRight: Radius.circular(8.5.r), bottomRight: Radius.circular(8.5.r)), borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.r), topRight: Radius.circular(8.5.r), bottomRight: Radius.circular(8.5.r)),
// borderSide: BorderSide(color: Colors.white, width: 2), // borderSide: BorderSide(color: Colors.white, width: 2),
elevation: 1, elevation: 1,
padding: EdgeInsets.symmetric(horizontal: Utils.isPad() ? 11.w : 16.w, vertical: 2.h), padding: EdgeInsets.symmetric(horizontal: Utils.isPad() ? 11.w : 16.w, vertical: 2.h),
@ -332,7 +455,8 @@ Widget $termItem(BuildContext context, EntranceModel e, int data, {double? theHe
children: [ children: [
Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover), Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover),
SizedBox(height: 6.r), SizedBox(height: 6.r),
quickText(e.title, size: 12.sp, color: const Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500), quickText(e.title,
size: 12.sp, color: const Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500),
], ],
) )
: Row( : Row(
@ -340,7 +464,8 @@ Widget $termItem(BuildContext context, EntranceModel e, int data, {double? theHe
children: [ children: [
Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover), Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover),
SizedBox(width: 6.r), SizedBox(width: 6.r),
quickText(e.title, size: 12.sp, color: const Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500), quickText(e.title,
size: 12.sp, color: const Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500),
], ],
), ),
), ),

View File

@ -11,12 +11,12 @@ class StudentGroupList extends StatelessWidget {
final Function goNextPage; final Function goNextPage;
final Widget? rightBtn; final Widget? rightBtn;
const StudentGroupList(this.studentGroups, this.goNextPage, {Key? key, this.rightBtn}) : super(key: key); const StudentGroupList(this.studentGroups, this.goNextPage, {super.key, this.rightBtn});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Obx(() { return Obx(() {
return studentGroups != null && studentGroups.isNotEmpty return studentGroups.isNotEmpty
? Utils.isPad() ? Utils.isPad()
? GridView( ? GridView(
shrinkWrap: true, shrinkWrap: true,
@ -45,12 +45,12 @@ class StudentGroupList extends StatelessWidget {
padding: EdgeInsets.only(right: 8.r), padding: EdgeInsets.only(right: 8.r),
child: Text( child: Text(
'${EnumUtils.formatGrade(item.grade)}${item.className}', '${EnumUtils.formatGrade(item.grade)}${item.className}',
style: TextStyle(fontSize: 10.sp, color:Theme.of(context).primaryColor), style: TextStyle(fontSize: 10.sp, color: Theme.of(context).primaryColor),
), ),
), ),
Text( Text(
EnumUtils.formatSubject(item.subject!), EnumUtils.formatSubject(item.subject!),
style: TextStyle(fontSize: 10.sp, color: Color(0xFF8B8B8B)), style: TextStyle(fontSize: 10.sp, color: const Color(0xFF8B8B8B)),
), ),
const Spacer(), const Spacer(),
/*Expanded( /*Expanded(
@ -114,7 +114,7 @@ class StudentGroupList extends StatelessWidget {
), ),
Text( Text(
EnumUtils.formatSubject(item.subject!), EnumUtils.formatSubject(item.subject!),
style: TextStyle(fontSize: 10.sp, color: Color(0xFF8B8B8B)), style: TextStyle(fontSize: 10.sp, color: const Color(0xFF8B8B8B)),
), ),
const Spacer(), const Spacer(),
/* Expanded( /* Expanded(

View File

@ -1,47 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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/utils.dart'; import 'package:making_school_asignment_app/common/mixins/getx_keepalive_widget.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/read_over/read_over_logic.dart';
import 'package:making_school_asignment_app/page/home_page/children/read_over/read_over_view.dart';
import 'package:making_school_asignment_app/page/home_page/children/read_over/widget/annotate_list.dart'; import 'package:making_school_asignment_app/page/home_page/children/read_over/widget/annotate_list.dart';
import 'package:making_school_asignment_app/page/home_page/children/student_work_detail/widget/job_condition_filter.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
import 'package:syncfusion_flutter_datepicker/datepicker.dart';
import 'work_logic.dart'; import 'work_logic.dart';
class WorkPage extends StatefulWidget { class WorkPage extends GetxKeepAliveWidget<WorkLogic> {
const WorkPage({super.key}); const WorkPage({super.key});
@override @override
State<WorkPage> createState() => _WorkPageState(); Widget buildContent(BuildContext context, WorkLogic controller) {
} final state = controller.state;
class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin {
final logic = Get.find<WorkLogic>();
final state = Get.find<WorkLogic>().state;
@override
bool get wantKeepAlive => true;
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
super.build(context);
return Scaffold( return Scaffold(
backgroundColor: const Color.fromRGBO(244, 244, 244, 1), backgroundColor: const Color.fromRGBO(244, 244, 244, 1),
body: OrientationBuilder( body: Column(
builder: (BuildContext context, Orientation orientation) {
return Column(
children: <Widget>[ children: <Widget>[
Container( Container(
color: Colors.white, color: Colors.white,
@ -70,7 +45,7 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
indicatorPadding: EdgeInsets.zero, indicatorPadding: EdgeInsets.zero,
indicatorWeight: 0, indicatorWeight: 0,
labelPadding: EdgeInsets.symmetric(horizontal: 2.w), labelPadding: EdgeInsets.symmetric(horizontal: 2.w),
controller: logic.tabController, controller: controller.tabController,
unselectedLabelStyle: TextStyle( unselectedLabelStyle: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: const Color.fromRGBO(69, 83, 100, 1), color: const Color.fromRGBO(69, 83, 100, 1),
@ -104,7 +79,9 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
child: quickText( child: quickText(
'待批阅', '待批阅',
size: 14.sp, size: 14.sp,
color: state.tabIndex.value == 0 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), color: state.tabIndex.value == 0
? Theme.of(context).primaryColor
: const Color.fromRGBO(80, 94, 110, 1),
fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null, fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null,
), ),
); );
@ -124,7 +101,9 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
child: quickText( child: quickText(
'已批阅', '已批阅',
size: 14.sp, size: 14.sp,
color: state.tabIndex.value == 1 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), color: state.tabIndex.value == 1
? Theme.of(context).primaryColor
: const Color.fromRGBO(80, 94, 110, 1),
fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null, fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null,
), ),
); );
@ -140,7 +119,8 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
onTap: () { onTap: () {
Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'}); Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'});
}, },
child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"), color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp), child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"),
color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp),
), ),
), ),
], ],
@ -155,15 +135,7 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
}), }),
), ),
], ],
);
},
), ),
); );
} }
@override
void dispose() {
Get.delete<WorkLogic>();
super.dispose();
}
} }

View File

@ -12,8 +12,9 @@ import connectivity_plus
import device_info_plus import device_info_plus
import package_info_plus import package_info_plus
import path_provider_foundation import path_provider_foundation
import sqflite import sqflite_darwin
import url_launcher_macos import url_launcher_macos
import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppInstallerPlugin.register(with: registry.registrar(forPlugin: "AppInstallerPlugin")) AppInstallerPlugin.register(with: registry.registrar(forPlugin: "AppInstallerPlugin"))
@ -25,4 +26,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
} }

View File

@ -62,14 +62,18 @@ dependencies:
fluttertoast: ^8.0.9 fluttertoast: ^8.0.9
# #
functional_widget_annotation: ^0.10.0 functional_widget_annotation: ^0.10.0
flutter_widget_from_html_core: ^0.14.12 flutter_widget_from_html_core: ^0.17.0
# APP内存及本地存储插件 # APP内存及本地存储插件
get_storage: ^2.0.3 get_storage: ^2.0.3
zoom_widget: ^2.0.1 # zoom_widget: ^2.0.1
zoom_widget:
git:
url: https://gitea.23544.com/wangyang/zoom_widget.git
# url: https://github.com/semakers/zoom-widget.git
# ref: a35c9da6afe405c23b5897b449683d424016e9f1
# start retrofit请求封装 # start retrofit请求封装
retrofit: ^4.1.0 retrofit: ^4.1.0
json_annotation: ^4.9.0 json_annotation: 4.9.0
# end retrofit请求封装 # end retrofit请求封装
# 进度条 # 进度条
percent_indicator: ^4.2.3 percent_indicator: ^4.2.3
@ -79,7 +83,7 @@ dependencies:
data_table_2: 2.5.10 data_table_2: 2.5.10
flutter_staggered_grid_view: ^0.7.0 flutter_staggered_grid_view: ^0.7.0
dropdown_button2: ^2.3.9 dropdown_button2: ^2.3.9
syncfusion_flutter_datepicker: ^25.2.5 syncfusion_flutter_datepicker: ^30.2.7
easy_debounce: ^2.0.3 # 防抖节流 easy_debounce: ^2.0.3 # 防抖节流
flutter_hooks: ^0.20.5 flutter_hooks: ^0.20.5
flutter_spinkit: ^5.2.1 flutter_spinkit: ^5.2.1
@ -93,24 +97,26 @@ dependencies:
app_installer: ^1.1.0 app_installer: ^1.1.0
auto_updater: ^0.2.1 auto_updater: ^0.2.1
permission_handler: ^11.3.1 permission_handler: ^11.3.1
flutter_distributor: ^0.4.5 flutter_distributor: 0.6.5
flutter_native_splash: ^2.4.1 # fastforge: ^0.6.5
flutter_native_splash: ^2.4.6
icons_launcher: ^2.1.7 icons_launcher: ^2.1.7
app_settings: ^5.1.1 app_settings: ^5.1.1
device_info_plus: ^11.1.0 device_info_plus: ^11.1.0
auto_size_text: ^3.0.0 auto_size_text: ^3.0.0
# dependency_overrides: dependency_overrides:
archive: ^4.0.2
# meta: ^1.15.0 # meta: ^1.15.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
retrofit_generator: ^8.1.0 retrofit_generator: 9.6.0
build_runner: ^2.4.10 build_runner: 2.5.4
json_serializable: ^6.6.2 json_serializable: 6.9.5
# 分离样式 # 分离样式
functional_widget: ^0.10.2 functional_widget: ^0.10.3
# The "flutter_lints" package below contains a set of recommended lints to # The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is # encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your # activated in the `analysis_options.yaml` file located at the root of your

View File

@ -7,8 +7,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:making_school_asignment_app/main_offest.dart';
import 'package:making_school_asignment_app/main.dart';
void main() { void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async { testWidgets('Counter increments smoke test', (WidgetTester tester) async {

View File

@ -201,6 +201,20 @@
@ -224,7 +238,8 @@
body { body {
margin: 0; margin: 0;
min-height: 100%; min-height: 100%;
background-color: #8C68FF; background-color: #ffffff;
background-image: url("splash/img/light-background.png");
background-size: 100% 100%; background-size: 100% 100%;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB