Compare commits
7 Commits
5f4c9237c1
...
8fd6c57874
| Author | SHA1 | Date |
|---|---|---|
|
|
8fd6c57874 | |
|
|
db04323e36 | |
|
|
e34234da09 | |
|
|
8209b12459 | |
|
|
b7478fd785 | |
|
|
b6af1f57a4 | |
|
|
70f658f87d |
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"flutter": "3.32.0"
|
||||||
|
}
|
||||||
|
|
@ -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/
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"dart.flutterSdkPath": ".fvm/versions/3.32.0"
|
||||||
|
}
|
||||||
|
|
@ -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']
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 69 B After Width: | Height: | Size: 341 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 69 B After Width: | Height: | Size: 341 KiB |
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 69 B After Width: | Height: | Size: 341 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 69 B |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 69 B |
|
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 69 B |
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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!);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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'];
|
||||||
|
|
|
||||||
|
|
@ -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 '';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
EnumSubject item = UserStore.to.subjectList.firstWhere((element) => element.id == id);
|
||||||
|
return item.name;
|
||||||
|
} catch (e) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
EnumSubject item = UserStore.to.subjectList.firstWhere((element) => element.id == id);
|
|
||||||
return item.name ?? '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static String formatGrade(int id) {
|
static String formatGrade(int id) {
|
||||||
if (UserStore.to.gradeList.isEmpty || id == null) {
|
if (UserStore.to.gradeList.isEmpty) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
EnumSubject item = UserStore.to.gradeList.firstWhere((element) => element.id == id);
|
||||||
|
return item.name;
|
||||||
|
} catch (e) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
EnumSubject item = UserStore.to.gradeList.firstWhere((element) => element.id == id);
|
|
||||||
return item.name ?? '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<0,最近点是start;如果t>1,最近点是end。
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// --- 公共入口方法 ---
|
||||||
|
|
||||||
|
/// 识别传入的笔画集合属于哪种手势。
|
||||||
|
/// 根据笔画的数量(1笔、2笔或多于2笔)调用不同的内部识别逻辑。
|
||||||
|
/// 返回识别到的 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. 如果简化后是其他数量的点(如1个或4个以上),则不认为是斜线。
|
||||||
|
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; // 如果任何一个向量长度为0,夹角无意义,返回0。
|
||||||
|
// 限制 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; // 交点在线段外部或不满足最小交点距离条件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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}) {
|
||||||
|
|
|
||||||
|
|
@ -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 ??
|
if ((stu.kgtStu!.length - stu.kgtAnswerCount!) + (stu.zgtStu!.length - stu.zgtAnswerCount!) ==
|
||||||
0;
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
/// 首页用户未更新不进行更新判断,没有登陆的用户在登陆页面和40秒回调中检查弹出更新APP
|
/// 首页用户未更新不进行更新判断,没有登陆的用户在登陆页面和40秒回调中检查弹出更新APP
|
||||||
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();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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,10 +37,9 @@ 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}');*/
|
||||||
for (var element in state.classList.value) {
|
for (var element in state.classList.value) {
|
||||||
int commitStudentCount = 0;
|
int commitStudentCount = 0;
|
||||||
|
|
@ -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]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
@ -62,9 +57,9 @@ class _FavStudentPageState extends State<FavStudentPage> {
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'确定',
|
'确定',
|
||||||
style: TextStyle(color: Colors.white),
|
style: TextStyle(color: Colors.white),
|
||||||
))),
|
))),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// 执行操作
|
// 执行操作
|
||||||
// Navigator.of(context).pop(true);
|
// Navigator.of(context).pop(true);
|
||||||
|
|
@ -85,300 +80,245 @@ class _FavStudentPageState extends State<FavStudentPage> {
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
style: TextStyle(color: Color(0xFF666666)),
|
style: TextStyle(color: Color(0xFF666666)),
|
||||||
))),
|
))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color.fromRGBO(245, 245, 245, 1),
|
backgroundColor: const Color.fromRGBO(245, 245, 245, 1),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
// titleSpacing: 0,
|
// titleSpacing: 0,
|
||||||
elevation: 0.0,
|
elevation: 0.0,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back_ios),
|
icon: const Icon(Icons.arrow_back_ios),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Get.back();
|
Get.back();
|
||||||
}),
|
}),
|
||||||
iconTheme: const IconThemeData(color: Colors.black),
|
iconTheme: const IconThemeData(color: Colors.black),
|
||||||
title: quickText('收藏夹'),
|
title: quickText('收藏夹'),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
actions: const [
|
actions: const [
|
||||||
ReturnToHomepage(),
|
ReturnToHomepage(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body:OrientationBuilder(
|
body: OrientationBuilder(builder: (BuildContext context, Orientation orientation) {
|
||||||
builder: (BuildContext context, Orientation orientation){
|
return Column(
|
||||||
return Column(
|
children: [
|
||||||
children: [
|
Padding(
|
||||||
Padding(
|
padding: EdgeInsets.symmetric(vertical: 0.r, horizontal: 14.r),
|
||||||
padding: EdgeInsets.symmetric(vertical: 0.r, horizontal: 14.r),
|
child: Row(
|
||||||
child: Row(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
||||||
children: [
|
Expanded(
|
||||||
Expanded(
|
child: Obx(() {
|
||||||
child: Obx(() {
|
return Text(
|
||||||
return Text(
|
state.homeworkName.value,
|
||||||
state.homeworkName.value,
|
style: TextStyle(
|
||||||
style:
|
fontSize: 14.sp,
|
||||||
TextStyle(fontSize: 14.sp, color: const Color(0xFF3C3C3C),),
|
color: const Color(0xFF3C3C3C),
|
||||||
overflow: TextOverflow.ellipsis,
|
),
|
||||||
);
|
overflow: TextOverflow.ellipsis,
|
||||||
}),
|
);
|
||||||
),
|
|
||||||
// 下拉框
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
vertical: 10.h, horizontal: 10.w),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Obx(() {
|
|
||||||
return DropdownSelection(
|
|
||||||
bgColor: Colors.white,
|
|
||||||
involveClasses: state.involveClasses.value,
|
|
||||||
classData: state.classData.value,
|
|
||||||
call: (AnnotatedClass item) {
|
|
||||||
state.classData.value = item;
|
|
||||||
if (item.grade == -1) state.classData.value = state.defaultClass;
|
|
||||||
logic.getList();
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
],
|
),
|
||||||
),
|
// 下拉框
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 10.w),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Obx(() {
|
||||||
|
return DropdownSelection(
|
||||||
|
bgColor: Colors.white,
|
||||||
|
involveClasses: state.involveClasses.value,
|
||||||
|
classData: state.classData.value,
|
||||||
|
onClassSelected: (AnnotatedClass item) {
|
||||||
|
state.classData.value = item;
|
||||||
|
if (item.grade == -1) state.classData.value = state.defaultClass;
|
||||||
|
logic.getList();
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
Container(
|
||||||
),
|
width: MediaQuery.of(context).size.width,
|
||||||
Container(
|
height: 1.r,
|
||||||
width: MediaQuery
|
decoration: const BoxDecoration(
|
||||||
.of(context)
|
color: Color(0xFFEEEEEE),
|
||||||
.size
|
),
|
||||||
.width,
|
),
|
||||||
height: 1.r,
|
Obx(() {
|
||||||
decoration: const BoxDecoration(
|
return state.favList.isNotEmpty
|
||||||
color: Color(0xFFEEEEEE),
|
? Expanded(
|
||||||
),
|
child: Utils.isPad()
|
||||||
),
|
? Padding(
|
||||||
Obx((){
|
padding: EdgeInsets.only(top: 10.r, bottom: 8.r, left: 14.r, right: 14.r),
|
||||||
return state.favList.isNotEmpty
|
child: Column(
|
||||||
? Expanded(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Utils.isPad()
|
children: List.generate(state.favList.length, (index) {
|
||||||
? Padding(
|
var item = state.favList[index];
|
||||||
padding: EdgeInsets.only(
|
return Padding(
|
||||||
top: 10.r,
|
padding: EdgeInsets.only(bottom: 8.r),
|
||||||
bottom: 8.r,
|
child: Column(
|
||||||
left: 14.r,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
right: 14.r),
|
children: [
|
||||||
child: Column(
|
Padding(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
padding: EdgeInsets.only(bottom: 5.r),
|
||||||
children:
|
child: Text(
|
||||||
List.generate(state.favList.length, (index) {
|
'第${item['questionPage']}页',
|
||||||
var item = state.favList[index];
|
style:
|
||||||
return Padding(
|
TextStyle(fontSize: 12.sp, color: Theme.of(context).primaryColor),
|
||||||
padding: EdgeInsets.only(bottom: 8.r),
|
),
|
||||||
child: Column(
|
),
|
||||||
crossAxisAlignment:
|
GridView(
|
||||||
CrossAxisAlignment.start,
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
children: [
|
crossAxisCount: 2,
|
||||||
Padding(
|
mainAxisSpacing: 8.r,
|
||||||
padding:
|
crossAxisSpacing: 10.r,
|
||||||
EdgeInsets.only(bottom: 5.r),
|
childAspectRatio: 556 / 112,
|
||||||
child: Text(
|
),
|
||||||
'第${item['questionPage']}页',
|
shrinkWrap: true,
|
||||||
style: TextStyle(
|
children: List.generate(item['list'].length, (i) {
|
||||||
fontSize: 12.sp,
|
HomeworkFavs student = item['list'][i];
|
||||||
color:Theme.of(context).primaryColor),
|
return Container(
|
||||||
),
|
padding: EdgeInsets.symmetric(vertical: 5.r, horizontal: 10.r),
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
GridView(
|
color: Colors.white,
|
||||||
gridDelegate:
|
borderRadius: BorderRadius.all(Radius.circular(6.r)),
|
||||||
SliverGridDelegateWithFixedCrossAxisCount(
|
),
|
||||||
crossAxisCount: 2,
|
child: InkWell(
|
||||||
mainAxisSpacing: 8.r,
|
onTap: () {
|
||||||
crossAxisSpacing: 10.r,
|
showStudentDialog(context, student, state.favList);
|
||||||
childAspectRatio: 556 / 112,
|
},
|
||||||
),
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
quickText(student.studentName,
|
||||||
|
color: const Color(0xFF333333), size: 12.sp),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(right: 8.r),
|
||||||
|
child: Text(
|
||||||
|
student.className!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp, color: const Color(0xFF666666)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
var confim = await confirmDialog();
|
||||||
|
if (confim) {
|
||||||
|
logic.getDelete(student);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/favorite_delete_icon.png',
|
||||||
|
width: 24.r,
|
||||||
|
height: 24.r,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
: ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
children: List.generate(
|
itemBuilder: (context, index) {
|
||||||
item['list'].length, (i) {
|
var item = state.favList[index];
|
||||||
HomeworkFavs student = item['list'][i];
|
return Padding(
|
||||||
return Container(
|
padding: EdgeInsets.only(top: 10.r, bottom: 8.r, left: 14.r, right: 14.r),
|
||||||
padding: EdgeInsets.symmetric(
|
child: Column(
|
||||||
vertical: 5.r,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
horizontal: 10.r),
|
children: [
|
||||||
decoration: BoxDecoration(
|
Padding(
|
||||||
color: Colors.white,
|
padding: EdgeInsets.only(bottom: 5.r),
|
||||||
borderRadius:
|
child: Text(
|
||||||
BorderRadius.all(
|
'第${item['questionPage']}页',
|
||||||
Radius.circular(
|
style: TextStyle(fontSize: 14.sp, color: Theme.of(context).primaryColor),
|
||||||
6.r)),
|
|
||||||
),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
showStudentDialog(context,
|
|
||||||
student, state.favList);
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment
|
|
||||||
.spaceBetween,
|
|
||||||
children: [
|
|
||||||
quickText(
|
|
||||||
student.studentName,
|
|
||||||
color: const Color(
|
|
||||||
0xFF333333),
|
|
||||||
size: 12.sp),
|
|
||||||
Expanded(
|
|
||||||
child: Container()),
|
|
||||||
Padding(
|
|
||||||
padding:
|
|
||||||
EdgeInsets.only(
|
|
||||||
right: 8.r),
|
|
||||||
child: Text(
|
|
||||||
student.className!,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12.sp,
|
|
||||||
color: const Color(
|
|
||||||
0xFF666666)),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
InkWell(
|
),
|
||||||
onTap: () async {
|
ListView.builder(
|
||||||
var confim =
|
itemBuilder: (context, i) {
|
||||||
await confirmDialog();
|
HomeworkFavs student = item['list'][i];
|
||||||
if (confim) {
|
return InkWell(
|
||||||
logic.getDelete(
|
onTap: () {
|
||||||
student);
|
showStudentDialog(context, student, state.favList);
|
||||||
}
|
},
|
||||||
},
|
child: Container(
|
||||||
child: Image.asset(
|
padding: EdgeInsets.symmetric(vertical: 5.r, horizontal: 10.r),
|
||||||
'assets/images/favorite_delete_icon.png',
|
margin: EdgeInsets.only(top: 5.r),
|
||||||
width: 24.r,
|
decoration: BoxDecoration(
|
||||||
height: 24.r,
|
color: Colors.white,
|
||||||
),
|
borderRadius: BorderRadius.all(Radius.circular(6.r)),
|
||||||
),
|
),
|
||||||
],
|
child: Row(
|
||||||
),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
quickText(student.studentName,
|
||||||
|
color: const Color(0xFF333333), size: 14.sp),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(right: 8.r),
|
||||||
|
child: Text(
|
||||||
|
student.className!,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 14.sp, color: const Color(0xFF666666)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
var confim = await confirmDialog();
|
||||||
|
if (confim) {
|
||||||
|
logic.getDelete(student);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/favorite_delete_icon.png',
|
||||||
|
width: 32.r,
|
||||||
|
height: 32.r,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: item['list'].length,
|
||||||
|
shrinkWrap: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
})),
|
},
|
||||||
],
|
itemCount: state.favList.length,
|
||||||
),
|
|
||||||
);
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
: ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
var item = state.favList[index];
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: 10.r,
|
|
||||||
bottom: 8.r,
|
|
||||||
left: 14.r,
|
|
||||||
right: 14.r),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(bottom: 5.r),
|
|
||||||
child: Text(
|
|
||||||
'第${item['questionPage']}页',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14.sp,
|
|
||||||
color:Theme.of(context).primaryColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListView.builder(
|
|
||||||
itemBuilder: (context, i) {
|
|
||||||
HomeworkFavs student = item['list'][i];
|
|
||||||
return InkWell(
|
|
||||||
onTap: () {
|
|
||||||
showStudentDialog(
|
|
||||||
context, student, state.favList);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
vertical: 5.r,
|
|
||||||
horizontal: 10.r),
|
|
||||||
margin: EdgeInsets.only(top: 5.r),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(6.r)),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment
|
|
||||||
.spaceBetween,
|
|
||||||
children: [
|
|
||||||
quickText(student.studentName,
|
|
||||||
color: const Color(0xFF333333),
|
|
||||||
size: 14.sp),
|
|
||||||
Expanded(child: Container()),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
right: 8.r),
|
|
||||||
child: Text(
|
|
||||||
student.className!,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14.sp,
|
|
||||||
color: const Color(
|
|
||||||
0xFF666666)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
InkWell(
|
|
||||||
onTap: () async {
|
|
||||||
var confim =
|
|
||||||
await confirmDialog();
|
|
||||||
if (confim) {
|
|
||||||
logic.getDelete(student);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/images/favorite_delete_icon.png',
|
|
||||||
width: 32.r,
|
|
||||||
height: 32.r,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
},
|
: Padding(
|
||||||
itemCount: item['list'].length,
|
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 2 - 200.r),
|
||||||
shrinkWrap: true,
|
child: const MyEmptyWidget(),
|
||||||
),
|
);
|
||||||
],
|
}),
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
},
|
}));
|
||||||
itemCount: state.favList.length,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: MediaQuery
|
|
||||||
.of(context)
|
|
||||||
.size
|
|
||||||
.height / 2 - 200.r),
|
|
||||||
child: const MyEmptyWidget(),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -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,179 +61,275 @@ 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,
|
||||||
color: const Color.fromRGBO(83, 83, 83, 1),
|
decoration: BoxDecoration(
|
||||||
// padding: EdgeInsets.symmetric(vertical: 1.h),
|
color: const Color.fromRGBO(83, 83, 83, 1),
|
||||||
child: Row(
|
boxShadow: [
|
||||||
children: [
|
BoxShadow(
|
||||||
Expanded(
|
color: Colors.black.withOpacity(0.1),
|
||||||
flex: 7,
|
blurRadius: 8,
|
||||||
child: Column(
|
offset: const Offset(0, -2),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
),
|
||||||
children: [
|
],
|
||||||
Expanded(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
// 全对按钮
|
|
||||||
Expanded(
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
child: TextButton(
|
|
||||||
child: quickText('全 对', color: Colors.white, size: 12.sp),
|
|
||||||
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.allPairs(context)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
|
|
||||||
// 注解笔
|
|
||||||
Expanded(
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Obx(() {
|
|
||||||
return Icon(
|
|
||||||
const IconData(0xe635, fontFamily: "AlibabaIcon"),
|
|
||||||
size: iconSize,
|
|
||||||
color: _logicControl.pen.value ? Theme.of(context).primaryColor : defaultColor,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
onPressed: () => easyThrottle('homework_bottom_action_bar_annotations', () {
|
|
||||||
_logicControl.gestureMove.value = false;
|
|
||||||
_logicControl.pen.value = !_logicControl.pen.value;
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
|
|
||||||
// 滑动试题
|
|
||||||
Expanded(
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Obx(
|
|
||||||
() {
|
|
||||||
return Icon(
|
|
||||||
const IconData(0xe636, fontFamily: "AlibabaIcon"),
|
|
||||||
size: iconSize,
|
|
||||||
color: _logicControl.gestureMove.value ? actionColor : defaultColor,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
onPressed: () => easyThrottle('homework_bottom_action_bar_annotations', () {
|
|
||||||
_logicControl.pen.value = false;
|
|
||||||
_logicControl.gestureMove.value = !_logicControl.gestureMove.value;
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: double.infinity, color: Colors.white, height: 0.35.h),
|
|
||||||
Expanded(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
// 全错按钮
|
|
||||||
Expanded(
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.allWrongRating(context)),
|
|
||||||
child: quickText('全 错', color: Colors.white, size: 12.sp),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
|
|
||||||
// 撤销上一步
|
|
||||||
Expanded(
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
child: IconButton(
|
|
||||||
onPressed: () => easyThrottle(
|
|
||||||
'homework_bottom_action_bar_annotations',
|
|
||||||
() => eventFire(model: BottomOperationBar(revokeAll: false, revokePreStep: true)),
|
|
||||||
),
|
|
||||||
icon: Icon(const IconData(0xe638, fontFamily: "AlibabaIcon"), size: iconSize, color: defaultColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
|
|
||||||
// 全部撤销
|
|
||||||
Expanded(
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Icon(const IconData(0xe637, fontFamily: "AlibabaIcon"), size: iconSize, color: defaultColor),
|
|
||||||
onPressed: () => easyThrottle(
|
|
||||||
'homework_bottom_action_bar_annotations',
|
|
||||||
() => eventFire(model: BottomOperationBar(revokeAll: true, revokePreStep: false)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
|
child: Row(
|
||||||
Expanded(
|
children: [
|
||||||
flex: 3,
|
Expanded(
|
||||||
child: Row(
|
flex: 7,
|
||||||
children: [
|
child: Column(
|
||||||
Expanded(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: SizedBox(
|
children: [
|
||||||
width: double.infinity,
|
Expanded(
|
||||||
height: double.infinity,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.cancelAllRatings()),
|
|
||||||
child: quickText('取 消', size: 14.sp, color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: 0.3.w, height: double.infinity, color: Colors.white),
|
|
||||||
Expanded(
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.submit(context)),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
Obx(() {
|
// 全对按钮
|
||||||
if (_homeworkLogic.state.submitLoading.value) {
|
Expanded(
|
||||||
return Padding(
|
child: _buildActionButton(
|
||||||
padding: EdgeInsets.only(right: 4.w),
|
'全对',
|
||||||
child: const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)),
|
Icons.check_circle_outline,
|
||||||
);
|
() => easyThrottle('homework_bottom_operation_bar_scoring_related',
|
||||||
}
|
() => _homeworkLogic.allPairs(context)),
|
||||||
return const SizedBox();
|
isEnabled: !_homeworkLogic.state.submitLoading.value,
|
||||||
}),
|
isPrimary: true,
|
||||||
quickText('提 交', size: 14.sp, color: Theme.of(context).primaryColor),
|
),
|
||||||
|
),
|
||||||
|
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
||||||
|
// 注解笔
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => _buildIconButton(
|
||||||
|
const IconData(0xe635, fontFamily: "AlibabaIcon"),
|
||||||
|
() => easyThrottle('homework_bottom_action_bar_annotations', () {
|
||||||
|
// 互斥:启用注解笔,禁用滑动
|
||||||
|
_logicControl.gestureMove.value = false;
|
||||||
|
_logicControl.pen.value = true;
|
||||||
|
}),
|
||||||
|
isActive: _logicControl.pen.value,
|
||||||
|
tooltip: '注解笔',
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
||||||
|
// 滑动试题
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => _buildIconButton(
|
||||||
|
const IconData(0xe636, fontFamily: "AlibabaIcon"),
|
||||||
|
() => easyThrottle('homework_bottom_action_bar_annotations', () {
|
||||||
|
// 互斥:启用滑动,禁用注解笔
|
||||||
|
_logicControl.pen.value = false;
|
||||||
|
_logicControl.gestureMove.value = true;
|
||||||
|
}),
|
||||||
|
isActive: _logicControl.gestureMove.value,
|
||||||
|
tooltip: '滑动试题',
|
||||||
|
)),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Container(width: double.infinity, color: Colors.white.withOpacity(0.3), height: 0.5.h),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// 全错按钮
|
||||||
|
Expanded(
|
||||||
|
child: _buildActionButton(
|
||||||
|
'全错',
|
||||||
|
Icons.cancel_outlined,
|
||||||
|
() => easyThrottle('homework_bottom_operation_bar_scoring_related',
|
||||||
|
() => _homeworkLogic.allWrongRating(context)),
|
||||||
|
isEnabled: !_homeworkLogic.state.submitLoading.value,
|
||||||
|
isPrimary: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
||||||
|
// 撤销上一步
|
||||||
|
Expanded(
|
||||||
|
child: _buildIconButton(
|
||||||
|
const IconData(0xe638, fontFamily: "AlibabaIcon"),
|
||||||
|
() => easyThrottle(
|
||||||
|
'homework_bottom_action_bar_annotations',
|
||||||
|
() => eventFire(model: BottomOperationBar(revokeAll: false, revokePreStep: true)),
|
||||||
|
),
|
||||||
|
tooltip: '撤销上一步',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
||||||
|
// 全部撤销
|
||||||
|
Expanded(
|
||||||
|
child: _buildIconButton(
|
||||||
|
const IconData(0xe637, fontFamily: "AlibabaIcon"),
|
||||||
|
() => easyThrottle(
|
||||||
|
'homework_bottom_action_bar_annotations',
|
||||||
|
() => eventFire(model: BottomOperationBar(revokeAll: true, revokePreStep: false)),
|
||||||
|
),
|
||||||
|
tooltip: '全部撤销',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildActionButton(
|
||||||
|
'取消',
|
||||||
|
Icons.clear,
|
||||||
|
() => easyThrottle(
|
||||||
|
'homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.cancelAllRatings()),
|
||||||
|
isEnabled: true,
|
||||||
|
isPrimary: false,
|
||||||
|
isSecondary: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
||||||
|
Expanded(
|
||||||
|
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,
|
||||||
|
height: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isPrimary
|
||||||
|
? (isEnabled ? primaryColor : primaryColor.withOpacity(0.5))
|
||||||
|
: isSecondary
|
||||||
|
? Colors.transparent
|
||||||
|
: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(4.r),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (isLoading)
|
||||||
|
SizedBox(
|
||||||
|
width: 16.w,
|
||||||
|
height: 16.w,
|
||||||
|
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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildIconButton(
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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,17 +197,25 @@ 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);
|
||||||
studentScore.value = itemVal.studentScore;
|
if (studentScore.value != itemVal.studentScore) {
|
||||||
|
studentScore.value = itemVal.studentScore;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 未找到对应的题目
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,13 +46,11 @@ 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(
|
viewWidth: constraints.maxWidth,
|
||||||
(_) => zoomState.zoomFile.value = ZoomFileModel(
|
viewHeight: constraints.maxHeight,
|
||||||
viewWidth: constraints.maxWidth,
|
));
|
||||||
viewHeight: constraints.maxHeight,
|
|
||||||
));
|
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -67,10 +64,14 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
|
||||||
children: [
|
children: [
|
||||||
// 主图
|
// 主图
|
||||||
QuestionImageView(
|
QuestionImageView(
|
||||||
maxWidth, maxHeight, annotationState, controller,
|
maxWidth,
|
||||||
zoomState: zoomState,
|
maxHeight,
|
||||||
sateData: sateData,
|
annotationState,
|
||||||
actualHeight: zoomFileModel.actualHeight!),
|
controller,
|
||||||
|
zoomState: zoomState,
|
||||||
|
sateData: sateData,
|
||||||
|
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,
|
||||||
required this.zoomState,
|
this.annotationState,
|
||||||
required this.sateData,
|
this.logic, {
|
||||||
super.key});
|
required this.actualHeight,
|
||||||
|
required this.zoomState,
|
||||||
|
required this.sateData,
|
||||||
|
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,17 +386,12 @@ 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("确定", color: Theme.of(context1).primaryColor),
|
||||||
child: quickText("取消"),
|
onPressed: () => Navigator.pop(context1, true))
|
||||||
onPressed: () => Navigator.pop(context1, false)),
|
]);
|
||||||
TextButton(
|
|
||||||
child: quickText("确定",
|
|
||||||
color: Theme.of(context1).primaryColor),
|
|
||||||
onPressed: () => Navigator.pop(context1, true))
|
|
||||||
]);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (res == true) vnHandWritings.value = [];
|
if (res == true) vnHandWritings.value = [];
|
||||||
|
|
@ -433,43 +401,35 @@ 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("确定", color: Theme.of(context1).primaryColor),
|
||||||
child: quickText("取消"),
|
onPressed: () => easyThrottle(
|
||||||
onPressed: () => Navigator.pop(context1, false)),
|
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
|
||||||
TextButton(
|
() {
|
||||||
child: quickText("确定",
|
Navigator.pop(context1, true);
|
||||||
color: Theme.of(context1).primaryColor),
|
sateData.data.value?.zgtAnnotate = null;
|
||||||
onPressed: () => easyThrottle(
|
sateData.data.value?.showZgtAnnotate = null;
|
||||||
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
|
if (sateData.data.value != null) {
|
||||||
() {
|
sateData.data.update((_) {
|
||||||
Navigator.pop(context1, true);
|
var theStudentQuestions = sateData.studentQuestions.value;
|
||||||
sateData.data.value?.zgtAnnotate = null;
|
if (theStudentQuestions?.isNotEmpty ?? false) {
|
||||||
sateData.data.value?.showZgtAnnotate = null;
|
try {
|
||||||
if (sateData.data.value != null) {
|
var noMarking = theStudentQuestions?.firstWhere((e) => e.studentScore == null);
|
||||||
sateData.data.update((_) {
|
ToastUtils.showInfo("未提交!请为第${noMarking?.questionNo}题打分,再手动提交");
|
||||||
var theStudentQuestions =
|
return;
|
||||||
sateData.studentQuestions.value;
|
} catch (e) {
|
||||||
if (theStudentQuestions?.isNotEmpty ??
|
// 所有题目都已评分
|
||||||
false) {
|
}
|
||||||
var noMarking =
|
|
||||||
theStudentQuestions?.firstWhereOrNull(
|
|
||||||
(e) => e.studentScore == null);
|
|
||||||
if (noMarking != null) {
|
|
||||||
ToastUtils.showInfo(
|
|
||||||
"未提交!请为第${noMarking.questionNo}题打分,再手动提交");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logic.submit(Get.context ?? context);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
logic.submit(Get.context ?? context);
|
||||||
),
|
});
|
||||||
)
|
}
|
||||||
]);
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -484,29 +444,23 @@ 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("确定", color: Theme.of(context).primaryColor),
|
||||||
child: quickText("取消"),
|
onPressed: () => easyThrottle(
|
||||||
onPressed: () => Navigator.pop(context1, false)),
|
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
|
||||||
TextButton(
|
() {
|
||||||
child: quickText("确定",
|
Navigator.pop(context1, true);
|
||||||
color: Theme.of(context).primaryColor),
|
sateData.data.value?.zgtAnnotate = null;
|
||||||
onPressed: () => easyThrottle(
|
sateData.data.value?.showZgtAnnotate = null;
|
||||||
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
|
if (sateData.data.value != null) {
|
||||||
() {
|
sateData.data.update((_) => logic.submit(Get.context ?? context));
|
||||||
Navigator.pop(context1, true);
|
}
|
||||||
sateData.data.value?.zgtAnnotate = null;
|
},
|
||||||
sateData.data.value?.showZgtAnnotate = null;
|
),
|
||||||
if (sateData.data.value != null) {
|
)
|
||||||
sateData.data.update(
|
]);
|
||||||
(_) => 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,101 +549,104 @@ 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,
|
||||||
key: zoomKey.value,
|
child:
|
||||||
// initTotalZoomOut: true, // 展示全部内容 初始化不产生滚动条
|
// ZoomView(
|
||||||
zoomSensibility: 0.05,
|
// key: zoomKey.value,
|
||||||
scrollWeight: 4.r,
|
// viewWidth: maxWidth,
|
||||||
doubleTapAnimDuration: Duration.zero,
|
// viewHeight: maxHeight,
|
||||||
maxZoomWidth: maxWidth,
|
// imageDisplayHeight: actualHeight,
|
||||||
maxZoomHeight: actualHeight,
|
// url: sateData.data.value!.zgtAnswer,
|
||||||
canvasColor: Colors.transparent,
|
// onScale: logic.zoomLogic.onScaleUpdate,
|
||||||
// initPosition: initPosition.value,
|
// // onTrans: (offset) {
|
||||||
// initScale: logic.zoomLogic.zoomState.initScale.value ?? 1,
|
// // print("偏移位置:$offset");
|
||||||
backgroundColor: Colors.transparent,
|
// // },
|
||||||
onScaleUpdate: logic.zoomLogic.onScaleUpdate,
|
// onContentOffset: logic.zoomLogic.onPanUpPosition,
|
||||||
onPositionUpdate: logic.zoomLogic.onPanUpPosition,
|
// ),
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
$TheCachedNetworkImage(
|
|
||||||
imgWidth: maxWidth,
|
|
||||||
imageUrl: sateData.data.value!.zgtAnswer,
|
|
||||||
(_, imageProvider) =>
|
|
||||||
Image(image: imageProvider, fit: BoxFit.fitWidth),
|
|
||||||
),
|
|
||||||
RepaintBoundary(
|
|
||||||
key: logic.pictureOverviewKey,
|
|
||||||
child: CustomPaint(
|
|
||||||
// isComplex: true,
|
|
||||||
size: Size(
|
|
||||||
maxWidth, zoomState.zoomFile.value!.actualHeight!),
|
|
||||||
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
|
||||||
// child: $TheCachedNetworkImage(
|
|
||||||
// imgWidth: maxWidth,
|
|
||||||
// imageUrl: showZgtAnnotate ?? sateData.data.value!.zgtAnswer,
|
|
||||||
// (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
|
||||||
|
|
||||||
// ),
|
Obx(
|
||||||
child: showZgtAnnotate != null
|
() => Zoom(
|
||||||
? $TheCachedNetworkImage(
|
key: zoomKey.value,
|
||||||
imgWidth: maxWidth,
|
// initTotalZoomOut: true, // 展示全部内容 初始化不产生滚动条
|
||||||
imageUrl: showZgtAnnotate,
|
zoomSensibility: 0.05,
|
||||||
(_, imageProvider) => Image(
|
scrollWeight: 4.r,
|
||||||
image: imageProvider, fit: BoxFit.fitWidth))
|
doubleTapAnimDuration: Duration.zero,
|
||||||
: null,
|
maxZoomWidth: maxWidth,
|
||||||
),
|
maxZoomHeight: actualHeight,
|
||||||
|
canvasColor: Colors.transparent,
|
||||||
|
doubleTapScaleChange: 1,
|
||||||
|
// initPosition: initPosition.value,
|
||||||
|
// initScale: logic.zoomLogic.zoomState.initScale.value ?? 1,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
onScaleUpdate: (double scale, double zoom) => logic.zoomLogic.onScaleUpdate(zoom),
|
||||||
|
onPositionUpdate: logic.zoomLogic.onPanUpPosition,
|
||||||
|
transformationController: logic.zoomController,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
$TheCachedNetworkImage(
|
||||||
|
imgWidth: maxWidth,
|
||||||
|
imageUrl: sateData.data.value!.zgtAnswer,
|
||||||
|
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
||||||
|
),
|
||||||
|
RepaintBoundary(
|
||||||
|
key: logic.pictureOverviewKey,
|
||||||
|
child: CustomPaint(
|
||||||
|
// isComplex: true,
|
||||||
|
size: Size(maxWidth, actualHeight),
|
||||||
|
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
||||||
|
// child: $TheCachedNetworkImage(
|
||||||
|
// imgWidth: maxWidth,
|
||||||
|
// imageUrl: showZgtAnnotate ?? sateData.data.value!.zgtAnswer,
|
||||||
|
// (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
||||||
|
// ),
|
||||||
|
child: showZgtAnnotate != null
|
||||||
|
? $TheCachedNetworkImage(
|
||||||
|
imgWidth: maxWidth,
|
||||||
|
imageUrl: showZgtAnnotate,
|
||||||
|
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth))
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
)),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -714,8 +669,9 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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!;
|
||||||
|
|
|
||||||
|
|
@ -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,13 +87,10 @@ 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}');
|
state.zoomOffset = val;
|
||||||
// print('**************** 正在移动位置 XXX:${val.dx}');
|
state.slide.value = val.dy.abs().toInt().toDouble();
|
||||||
state.zoomOffset = val;
|
|
||||||
state.slide.value = val.dy.abs().toInt().toDouble();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,42 +17,64 @@ 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>();
|
return PopScope(
|
||||||
final HomeLogic homeLogicController = Get.find<HomeLogic>();
|
canPop: false,
|
||||||
|
onPopInvoked: (e) {
|
||||||
return PopScope(
|
if (e && sateData.needRefresh) {
|
||||||
canPop: false,
|
Get.find<AnnotateClassLogic>().getList();
|
||||||
onPopInvoked: (e) {
|
Get.find<HomeLogic>().getList();
|
||||||
if (e && sateData.needRefresh) {
|
logic.eventFire(model: AnnotateListToRefresh());
|
||||||
controller.getList();
|
}
|
||||||
homeLogicController.getList();
|
},
|
||||||
logic.eventFire(model: AnnotateListToRefresh());
|
child: Scaffold(
|
||||||
}
|
backgroundColor: Colors.transparent,
|
||||||
},
|
body: Stack(
|
||||||
child: Scaffold(
|
children: [
|
||||||
appBar: AppBar(
|
// body内容,顶部留出自定义AppBar高度
|
||||||
// titleSpacing: 0,
|
const Padding(
|
||||||
leading: IconButton(icon: const Icon(Icons.arrow_back_ios), onPressed: () => Get.back()),
|
padding: EdgeInsets.only(top: kToolbarHeight),
|
||||||
iconTheme: const IconThemeData(color: Colors.black),
|
child: Column(
|
||||||
title: quickText(sateData.param.value.homeworkName),
|
children: [
|
||||||
backgroundColor: Colors.white,
|
// 下拉切换
|
||||||
elevation: 0,
|
DropdownSwitchStudentsType(),
|
||||||
actions: [const FavoriteWidget(), SizedBox(width: 5.w), const ReturnToHomepage()],
|
// 试题展示区和打分区
|
||||||
|
Expanded(child: QuestionPaperView()),
|
||||||
|
// 底部功能区
|
||||||
|
BottomAnnotationSwitch()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 顶部自定义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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: const Column(
|
);
|
||||||
children: [
|
});
|
||||||
// 下拉切换
|
|
||||||
DropdownSwitchStudentsType(),
|
|
||||||
// SizedBox(height: 10),
|
|
||||||
// 试题展示区和打分区
|
|
||||||
Expanded(child: QuestionPaperView()),
|
|
||||||
// 底部功能区
|
|
||||||
BottomAnnotationSwitch()
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,18 +75,19 @@ 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!,
|
||||||
homeworkId: state.homeworkId.value,
|
homeworkId: state.homeworkId.value,
|
||||||
kgReport: state.kgReport,
|
kgReport: state.kgReport,
|
||||||
zgReport: state.zgReport,
|
zgReport: state.zgReport,
|
||||||
kgtOkRate: state.dataCount.kgtOkRate!.toStringAsFixed(0),
|
kgtOkRate: state.dataCount.kgtOkRate!.toStringAsFixed(0),
|
||||||
zgtOkRate: state.dataCount.zgtOkRate!.toStringAsFixed(0),
|
zgtOkRate: state.dataCount.zgtOkRate!.toStringAsFixed(0),
|
||||||
kgtCorrectRate: state.dataCount.kgtCorrectRate!.toStringAsFixed(0),
|
kgtCorrectRate: state.dataCount.kgtCorrectRate!.toStringAsFixed(0),
|
||||||
zgtCorrectRate: state.dataCount.zgtCorrectRate!.toStringAsFixed(0),
|
zgtCorrectRate: state.dataCount.zgtCorrectRate!.toStringAsFixed(0),
|
||||||
subject: state.subject,
|
subject: state.subject,
|
||||||
),
|
),
|
||||||
// 掌握知识点的情况
|
// 掌握知识点的情况
|
||||||
Container(
|
Container(
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
// 如果没有找到匹配的班级,不执行任何操作
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -81,162 +81,167 @@ 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(
|
height: double.infinity,
|
||||||
height: double.infinity,
|
child: Column(
|
||||||
child: Column(
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 220.h,
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage('assets/images/personal_bg.png'),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
color: const Color.fromRGBO(248, 248, 248, 1),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
body: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
height: 220.h,
|
height: 150.h,
|
||||||
width: double.infinity,
|
padding: EdgeInsets.only(left: 20.r, right: 20.r),
|
||||||
decoration: const BoxDecoration(
|
// alignment: Alignment.center,
|
||||||
image: DecorationImage(
|
child: Row(
|
||||||
image: AssetImage('assets/images/personal_bg.png'),
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
fit: BoxFit.cover,
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/images/default_user_dead.png',
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 5.w,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
/*if (tokenState == '' || userState.id == '') {
|
||||||
|
toLoginPage(context);
|
||||||
|
}*/
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 0.h),
|
||||||
|
child: Text(
|
||||||
|
userInfo.value?.name ?? '请前往登录',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 16.sp, color: const Color(0xFF332A2A), fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
_showAlertDialog(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(2.r),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color.fromRGBO(255, 255, 255, 1),
|
||||||
|
borderRadius: BorderRadius.circular(50.r),
|
||||||
|
),
|
||||||
|
child: Image.asset('assets/images/out_icon.png', fit: BoxFit.cover),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 14.h),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 5.h, horizontal: 16.w),
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 16.w),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(6.w)),
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Color.fromRGBO(46, 91, 255, 0.1),
|
||||||
|
offset: Offset.zero, //阴影y轴偏移量
|
||||||
|
blurRadius: 20, //阴影模糊程度
|
||||||
|
spreadRadius: 10, //阴影扩散程度
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('账号', style: personalInfoTitleStly),
|
||||||
|
Text(userInfo.value?.name ?? '请前往登录', style: personalInfoValStly)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 5.h, horizontal: 16.w),
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 16.w),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(6.w)),
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Color.fromRGBO(46, 91, 255, 0.1),
|
||||||
|
offset: Offset.zero, //阴影y轴偏移量
|
||||||
|
blurRadius: 20, //阴影模糊程度
|
||||||
|
spreadRadius: 10, //阴影扩散程度
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('所在学校', style: personalInfoTitleStly),
|
||||||
|
Text(userInfo.value?.schoolName ?? '', style: personalInfoValStly)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 5.h, horizontal: 16.w),
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 16.w),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(6.w)),
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Color.fromRGBO(46, 91, 255, 0.1),
|
||||||
|
offset: Offset.zero, //阴影y轴偏移量
|
||||||
|
blurRadius: 20, //阴影模糊程度
|
||||||
|
spreadRadius: 10, //阴影扩散程度
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(Routes.otherPage);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('其他', style: personalInfoTitleStly),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
color: const Color.fromRGBO(80, 87, 103, 1),
|
||||||
|
size: 13.sp,
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
color: const Color.fromRGBO(248, 248, 248, 1),
|
|
||||||
))
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SafeArea(
|
),
|
||||||
child: Scaffold(
|
],
|
||||||
backgroundColor: Colors.transparent,
|
);
|
||||||
body: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
height: 150.h,
|
|
||||||
padding: EdgeInsets.only(left: 20.r, right: 20.r),
|
|
||||||
// alignment: Alignment.center,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
'assets/images/default_user_dead.png',
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 5.w,
|
|
||||||
),
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
/*if (tokenState == '' || userState.id == '') {
|
|
||||||
toLoginPage(context);
|
|
||||||
}*/
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
margin: EdgeInsets.only(top: 0.h),
|
|
||||||
child: Text(
|
|
||||||
userInfo.value?.name ?? '请前往登录',
|
|
||||||
style: TextStyle(fontSize: 16.sp, color: const Color(0xFF332A2A), fontWeight: FontWeight.w500),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
_showAlertDialog(context);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.all(2.r),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color.fromRGBO(255, 255, 255, 1),
|
|
||||||
borderRadius: BorderRadius.circular(50.r),
|
|
||||||
),
|
|
||||||
child: Image.asset('assets/images/out_icon.png',fit: BoxFit.cover),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 14.h),
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(vertical: 5.h, horizontal: 16.w),
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 16.w),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(6.w)),
|
|
||||||
color: Colors.white,
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color.fromRGBO(46, 91, 255, 0.1),
|
|
||||||
offset: Offset.zero, //阴影y轴偏移量
|
|
||||||
blurRadius: 20, //阴影模糊程度
|
|
||||||
spreadRadius: 10, //阴影扩散程度
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [Text('账号', style: personalInfoTitleStly), Text(userInfo.value?.name ?? '请前往登录', style: personalInfoValStly)],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(vertical: 5.h, horizontal: 16.w),
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 16.w),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(6.w)),
|
|
||||||
color: Colors.white,
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color.fromRGBO(46, 91, 255, 0.1),
|
|
||||||
offset: Offset.zero, //阴影y轴偏移量
|
|
||||||
blurRadius: 20, //阴影模糊程度
|
|
||||||
spreadRadius: 10, //阴影扩散程度
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [Text('所在学校', style: personalInfoTitleStly), Text(userInfo.value?.schoolName ?? '', style: personalInfoValStly)],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(vertical: 5.h, horizontal: 16.w),
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 16.w),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(6.w)),
|
|
||||||
color: Colors.white,
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color.fromRGBO(46, 91, 255, 0.1),
|
|
||||||
offset: Offset.zero, //阴影y轴偏移量
|
|
||||||
blurRadius: 20, //阴影模糊程度
|
|
||||||
spreadRadius: 10, //阴影扩散程度
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Get.toNamed(Routes.otherPage);
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text('其他', style: personalInfoTitleStly),
|
|
||||||
Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
color: const Color.fromRGBO(80, 87, 103, 1),
|
|
||||||
size: 13.sp,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,19 +157,23 @@ 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: Obx(() {
|
child: Container(
|
||||||
return AnnotateList(
|
color: const Color.fromRGBO(245, 245, 245, 1),
|
||||||
tabIndex: state.tabIndex.value,
|
child: Obx(() {
|
||||||
assessType: 0,
|
return AnnotateList(
|
||||||
);
|
tabIndex: state.tabIndex.value,
|
||||||
}),
|
assessType: 0,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
params.pageNumber = state.pageNumber;
|
try {
|
||||||
WorkStudent data = await getClient().getUnAnnotateList(params);
|
params.pageNumber = state.pageNumber;
|
||||||
state.totalCount.value = data.totalCount;
|
WorkStudent data = await getClient().getUnAnnotateList(params);
|
||||||
state.readOver.value = data.items.length;
|
state.totalCount.value = data.totalCount;
|
||||||
/* if(params.pageNumber == 1){
|
state.readOver.value = data.items.length;
|
||||||
state.workList.value = data.items;
|
|
||||||
}else{
|
// 完成刷新
|
||||||
state.workList.addAll(data.items);
|
refreshController.finishRefresh();
|
||||||
|
} catch (e) {
|
||||||
|
print('获取作业列表失败: $e');
|
||||||
|
// 完成刷新并显示错误
|
||||||
|
refreshController.finishRefresh();
|
||||||
|
// 可以在这里添加错误提示
|
||||||
}
|
}
|
||||||
refreshController.finishRefresh();*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -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,136 +14,100 @@ 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(
|
||||||
'assets/images/home_banner.png',
|
background: Image.asset(
|
||||||
width: Get.width,
|
'assets/images/home_banner.png',
|
||||||
fit: BoxFit.fill,
|
width: Get.width,
|
||||||
),
|
fit: BoxFit.cover,
|
||||||
Container(
|
),
|
||||||
margin: EdgeInsets.only(top: 300.h),
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20.r),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white, borderRadius: BorderRadius.only(topLeft: Radius.circular(30.r), topRight: Radius.circular(30.r))),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
height: 15.r,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'我的作业管理',
|
|
||||||
style: TextStyle(fontSize: 20.sp, color: const Color(0xFF676666), fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 15.r,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Obx(() {
|
|
||||||
return menuItem(
|
|
||||||
bgImg: 'assets/images/home_bg_01.png',
|
|
||||||
name: '作业批阅',
|
|
||||||
value: state.totalCount.value.toString(),
|
|
||||||
url: Routes.readOverPage,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 20.r,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: menuItem(bgImg: 'assets/images/home_bg_02.png', name: '知识点掌握', url: Routes.studentHistoryWorkPage, page: 'points'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 20.r,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child:
|
|
||||||
menuItem(bgImg: 'assets/images/home_bg_03.png', name: '学生历史作业', url: Routes.studentHistoryWorkPage, page: 'history'),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 20.r,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: menuItem(bgImg: 'assets/images/home_bg_04.png', name: '答题轨迹', url: Routes.answerTrajectoryPage),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 20.r,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
menuItem(
|
|
||||||
bgImg: 'assets/images/home_bg_05.png',
|
|
||||||
name: '优先批阅设定',
|
|
||||||
url: Routes.studentHistoryWorkPage,
|
|
||||||
page: 'set',
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 20.r,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(height: 15.h),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
// 主要内容区域
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20.r),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.only(topLeft: Radius.circular(30.r), topRight: Radius.circular(30.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(
|
||||||
|
'我的作业管理',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.sp, color: const Color(0xFF2C2C2C), fontWeight: FontWeight.w700),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
// 刷新按钮
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -155,68 +116,229 @@ class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Widget _buildStatCard(String title, String value, Color color, IconData icon) {
|
||||||
void dispose() {
|
return Container(
|
||||||
Get.delete<HomeLogic>();
|
padding: EdgeInsets.all(12.r),
|
||||||
super.dispose();
|
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(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => $MenuItem(
|
||||||
|
bgImg: 'assets/images/home_bg_01.png',
|
||||||
|
name: '作业批阅',
|
||||||
|
value: state.totalCount.value > 0 ? state.totalCount.value.toString() : null,
|
||||||
|
url: Routes.readOverPage,
|
||||||
|
isPrimary: true,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
SizedBox(width: 16.r),
|
||||||
|
const Expanded(
|
||||||
|
child: $MenuItem(
|
||||||
|
bgImg: 'assets/images/home_bg_02.png',
|
||||||
|
name: '知识点掌握',
|
||||||
|
url: Routes.studentHistoryWorkPage,
|
||||||
|
page: 'points',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 16.r),
|
||||||
|
// 第二行
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Expanded(
|
||||||
|
child: $MenuItem(
|
||||||
|
bgImg: 'assets/images/home_bg_03.png',
|
||||||
|
name: '学生历史作业',
|
||||||
|
url: Routes.studentHistoryWorkPage,
|
||||||
|
page: 'history',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 16.r),
|
||||||
|
const Expanded(
|
||||||
|
child: $MenuItem(bgImg: 'assets/images/home_bg_04.png', name: '答题轨迹', url: Routes.answerTrajectoryPage),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 16.r),
|
||||||
|
// 第三行
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Expanded(
|
||||||
|
child: $MenuItem(
|
||||||
|
bgImg: 'assets/images/home_bg_05.png',
|
||||||
|
name: '优先批阅设定',
|
||||||
|
url: Routes.studentHistoryWorkPage,
|
||||||
|
page: 'set',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 16.r),
|
||||||
|
// 占位空间
|
||||||
|
const Expanded(child: SizedBox()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget menuItem({required String bgImg, required String name, String? value, required String url, String? page}) {
|
@swidget
|
||||||
return InkWell(
|
Widget $menuItem({
|
||||||
onTap: () {
|
required String bgImg,
|
||||||
Get.toNamed(url, arguments: {'page': page ?? ''});
|
required String name,
|
||||||
},
|
String? value,
|
||||||
child: Container(
|
required String url,
|
||||||
width: (Get.width - 60.r) / 2,
|
String? page,
|
||||||
height: (Get.width - 60.r) / 2 * 86 / 164,
|
bool isPrimary = false,
|
||||||
padding: EdgeInsets.symmetric(vertical: 15.r, horizontal: 15.r),
|
}) {
|
||||||
decoration: BoxDecoration(
|
return Material(
|
||||||
image: DecorationImage(
|
color: Colors.transparent,
|
||||||
image: AssetImage(bgImg),
|
child: InkWell(
|
||||||
fit: BoxFit.contain,
|
onTap: () {
|
||||||
|
Get.toNamed(url, arguments: {'page': page ?? ''});
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(12.r),
|
||||||
|
child: Semantics(
|
||||||
|
label: '$name${value != null ? ',待处理数量:$value' : ''}',
|
||||||
|
hint: '点击进入$name页面',
|
||||||
|
button: true,
|
||||||
|
child: Container(
|
||||||
|
width: (Get.width - 60.r) / 2,
|
||||||
|
height: (Get.width - 60.r) / 2 * 86 / 164,
|
||||||
|
padding: EdgeInsets.all(16.r),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12.r),
|
||||||
|
gradient: isPrimary
|
||||||
|
? 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(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: isPrimary ? Colors.white : const Color(0xFF2C2C2C),
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (value != null && value != '')
|
||||||
|
Semantics(
|
||||||
|
label: '待处理数量:$value',
|
||||||
|
child: Container(
|
||||||
|
width: 20.r,
|
||||||
|
height: 20.r,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isPrimary ? Colors.white : const Color(0xFFFF6969),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10.r)),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10.sp,
|
||||||
|
color: isPrimary ? const Color(0xFFFF6969) : Colors.white,
|
||||||
|
fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
if (isPrimary)
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 14.sp,
|
||||||
|
color: Colors.white,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Image.asset(
|
||||||
|
'assets/images/home_right_icon.png',
|
||||||
|
width: 16.r,
|
||||||
|
height: 16.r,
|
||||||
|
semanticLabel: '进入图标',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
name,
|
|
||||||
style: TextStyle(fontSize: 14.sp, color: const Color(0xFF4F4F4F), fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
if (value != null && value != '')
|
|
||||||
Container(
|
|
||||||
width: 18.r,
|
|
||||||
height: 18.r,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFFF6969),
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(9.r)),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
value!,
|
|
||||||
style: TextStyle(fontSize: 10.sp, color: Colors.white, fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
'assets/images/home_right_icon.png',
|
|
||||||
width: 16.r,
|
|
||||||
height: 16.r,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -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),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -1,169 +1,141 @@
|
||||||
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) {
|
children: <Widget>[
|
||||||
return Column(
|
Container(
|
||||||
children: <Widget>[
|
color: Colors.white,
|
||||||
Container(
|
margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
||||||
color: Colors.white,
|
padding: EdgeInsets.only(bottom: 9.h, top: 4.h),
|
||||||
margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
child: Row(
|
||||||
padding: EdgeInsets.only(bottom: 9.h, top: 4.h),
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
child: Row(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
Expanded(
|
||||||
children: [
|
flex: 1,
|
||||||
Expanded(
|
child: Container(
|
||||||
flex: 1,
|
alignment: Alignment.centerLeft,
|
||||||
child: Container(
|
padding: EdgeInsets.only(left: 10.w),
|
||||||
alignment: Alignment.centerLeft,
|
)),
|
||||||
padding: EdgeInsets.only(left: 10.w),
|
Expanded(
|
||||||
)),
|
flex: 4,
|
||||||
Expanded(
|
child: Container(
|
||||||
flex: 4,
|
padding: EdgeInsets.symmetric(vertical: 2.h),
|
||||||
child: Container(
|
alignment: Alignment.center,
|
||||||
padding: EdgeInsets.symmetric(vertical: 2.h),
|
decoration: BoxDecoration(
|
||||||
alignment: Alignment.center,
|
color: const Color.fromRGBO(243, 243, 243, 1),
|
||||||
decoration: BoxDecoration(
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
color: const Color.fromRGBO(243, 243, 243, 1),
|
|
||||||
borderRadius: BorderRadius.circular(8.r),
|
|
||||||
),
|
|
||||||
child: TabBar(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
indicatorPadding: EdgeInsets.zero,
|
|
||||||
indicatorWeight: 0,
|
|
||||||
labelPadding: EdgeInsets.symmetric(horizontal: 2.w),
|
|
||||||
controller: logic.tabController,
|
|
||||||
unselectedLabelStyle: TextStyle(
|
|
||||||
fontSize: 14.sp,
|
|
||||||
color: const Color.fromRGBO(69, 83, 100, 1),
|
|
||||||
),
|
|
||||||
labelStyle: TextStyle(
|
|
||||||
fontSize: 14.sp,
|
|
||||||
color: const Color(0xFF4CC793),
|
|
||||||
),
|
|
||||||
// labelColor: const Color.fromRGBO(45, 56, 76, 1),
|
|
||||||
indicator: const BoxDecoration(),
|
|
||||||
onTap: (index) {
|
|
||||||
state.tabIndex.value = index;
|
|
||||||
if (index == 1 && state.completedToRefresh) {
|
|
||||||
// 已阅卷
|
|
||||||
// _refreshController2.callRefresh();
|
|
||||||
state.completedToRefresh = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tabs: <Widget>[
|
|
||||||
Tab(
|
|
||||||
iconMargin: EdgeInsets.zero,
|
|
||||||
height: 34.h,
|
|
||||||
child: Obx(() {
|
|
||||||
return Container(
|
|
||||||
width: 140.w,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: state.tabIndex.value == 0 ? const Color.fromRGBO(255, 255, 255, 1) : null,
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8.r)),
|
|
||||||
),
|
|
||||||
child: quickText(
|
|
||||||
'待批阅',
|
|
||||||
size: 14.sp,
|
|
||||||
color: state.tabIndex.value == 0 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1),
|
|
||||||
fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
Tab(
|
|
||||||
iconMargin: EdgeInsets.zero,
|
|
||||||
height: 34.h,
|
|
||||||
child: Obx(() {
|
|
||||||
return Container(
|
|
||||||
width: 140.w,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: state.tabIndex.value == 1 ? const Color.fromRGBO(255, 255, 255, 1) : null,
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8.r)),
|
|
||||||
),
|
|
||||||
child: quickText(
|
|
||||||
'已批阅',
|
|
||||||
size: 14.sp,
|
|
||||||
color: state.tabIndex.value == 1 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1),
|
|
||||||
fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
child: TabBar(
|
||||||
flex: 1,
|
padding: EdgeInsets.zero,
|
||||||
child: InkWell(
|
indicatorPadding: EdgeInsets.zero,
|
||||||
onTap: () {
|
indicatorWeight: 0,
|
||||||
Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'});
|
labelPadding: EdgeInsets.symmetric(horizontal: 2.w),
|
||||||
},
|
controller: controller.tabController,
|
||||||
child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"), color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp),
|
unselectedLabelStyle: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: const Color.fromRGBO(69, 83, 100, 1),
|
||||||
),
|
),
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: const Color(0xFF4CC793),
|
||||||
|
),
|
||||||
|
// labelColor: const Color.fromRGBO(45, 56, 76, 1),
|
||||||
|
indicator: const BoxDecoration(),
|
||||||
|
onTap: (index) {
|
||||||
|
state.tabIndex.value = index;
|
||||||
|
if (index == 1 && state.completedToRefresh) {
|
||||||
|
// 已阅卷
|
||||||
|
// _refreshController2.callRefresh();
|
||||||
|
state.completedToRefresh = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tabs: <Widget>[
|
||||||
|
Tab(
|
||||||
|
iconMargin: EdgeInsets.zero,
|
||||||
|
height: 34.h,
|
||||||
|
child: Obx(() {
|
||||||
|
return Container(
|
||||||
|
width: 140.w,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: state.tabIndex.value == 0 ? const Color.fromRGBO(255, 255, 255, 1) : null,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8.r)),
|
||||||
|
),
|
||||||
|
child: quickText(
|
||||||
|
'待批阅',
|
||||||
|
size: 14.sp,
|
||||||
|
color: state.tabIndex.value == 0
|
||||||
|
? Theme.of(context).primaryColor
|
||||||
|
: const Color.fromRGBO(80, 94, 110, 1),
|
||||||
|
fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
iconMargin: EdgeInsets.zero,
|
||||||
|
height: 34.h,
|
||||||
|
child: Obx(() {
|
||||||
|
return Container(
|
||||||
|
width: 140.w,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: state.tabIndex.value == 1 ? const Color.fromRGBO(255, 255, 255, 1) : null,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8.r)),
|
||||||
|
),
|
||||||
|
child: quickText(
|
||||||
|
'已批阅',
|
||||||
|
size: 14.sp,
|
||||||
|
color: state.tabIndex.value == 1
|
||||||
|
? Theme.of(context).primaryColor
|
||||||
|
: const Color.fromRGBO(80, 94, 110, 1),
|
||||||
|
fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
Expanded(
|
flex: 1,
|
||||||
child: Obx(() {
|
child: InkWell(
|
||||||
return AnnotateList(
|
onTap: () {
|
||||||
tabIndex: state.tabIndex.value,
|
Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'});
|
||||||
assessType: 1,
|
},
|
||||||
);
|
child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"),
|
||||||
}),
|
color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
},
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() {
|
||||||
|
return AnnotateList(
|
||||||
|
tabIndex: state.tabIndex.value,
|
||||||
|
assessType: 1,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
Get.delete<WorkLogic>();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,20 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -224,8 +238,9 @@
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
background-color: #8C68FF;
|
background-color: #ffffff;
|
||||||
background-size: 100% 100%;
|
background-image: url("splash/img/light-background.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 341 KiB |