diff --git a/android/app/src/main/res/drawable-v21/background.png b/android/app/src/main/res/drawable-v21/background.png index 4ebb677..5c78b7d 100644 Binary files a/android/app/src/main/res/drawable-v21/background.png and b/android/app/src/main/res/drawable-v21/background.png differ diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png index 4ebb677..5c78b7d 100644 Binary files a/android/app/src/main/res/drawable/background.png and b/android/app/src/main/res/drawable/background.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 5337d5a..a684c13 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 8c85bf1..0b4f2d3 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index e37e85c..33380e7 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 342548c..c506f92 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 7e763e2..8fad73b 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/assets/icon-apps/home_add.png b/assets/icon-apps/home_add.png new file mode 100644 index 0000000..e529dcd Binary files /dev/null and b/assets/icon-apps/home_add.png differ diff --git a/assets/images/logo.png b/assets/images/logo.png index 64c5599..48d0c80 100644 Binary files a/assets/images/logo.png and b/assets/images/logo.png differ diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index b3c07f8..c7fc0b7 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -491,7 +491,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 62; DEVELOPMENT_TEAM = ""; @@ -503,10 +503,11 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.company.myapp2; + MARKETING_VERSION = 2.2.3; + PRODUCT_BUNDLE_IDENTIFIER = uni.UNI85F7A17; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq-des"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -685,7 +686,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 62; DEVELOPMENT_TEAM = ""; @@ -697,10 +698,11 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.company.myapp2; + MARKETING_VERSION = 2.2.3; + PRODUCT_BUNDLE_IDENTIFIER = uni.UNI85F7A17; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq-des"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -716,7 +718,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 62; DEVELOPMENT_TEAM = ""; @@ -728,10 +730,11 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.company.myapp2; + MARKETING_VERSION = 2.2.3; + PRODUCT_BUNDLE_IDENTIFIER = uni.UNI85F7A17; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq-des"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 5fa3a42..ba8b8ee 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 09a4baa..7e8a5a0 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index 13b9886..d64f318 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index f9dacfd..c425eaf 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 74c9145..3505ed8 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index 470a8e1..18e4fcd 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 772149b..646e77f 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 13b9886..d64f318 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index a30a68d..16711e2 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index 27d32eb..2c18a84 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png index feece6d..b30478a 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png index 4fa3790..a9b673f 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png index bf80268..76d9b8a 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png index 9e5fc9d..9fccbf0 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index 27d32eb..2c18a84 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index fe44b93..705316e 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png index 5337d5a..a684c13 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png index 342548c..c506f92 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index a2dd400..c1c0345 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index ae77c36..ff4a094 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index c429f45..9fbe461 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png index 4ebb677..5c78b7d 100644 Binary files a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png differ diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 13a000c..d62a787 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,91 +1,91 @@ - - CADisableMinimumFrameDurationOnPhone - - CFBundleDisplayName + + CADisableMinimumFrameDurationOnPhone + + CFBundleDisplayName ${PRODUCT_NAME} - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - 秦港双控 - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSApplicationQueriesSchemes - - baidumap - - LSRequiresIPhoneOS + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + 秦港双控 + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSApplicationQueriesSchemes + + baidumap + + LSRequiresIPhoneOS + + NFCReaderUsageDescription + 需要NFC权限来读取和写入标签 + NSAppTransportSecurity + + NSAllowsArbitraryLoads - NFCReaderUsageDescription - 需要NFC权限来读取和写入标签 - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - NSBluetoothAlwaysUsageDescription - app需要蓝牙权限连接设备 - NSCameraUsageDescription - app需要相机权限来扫描二维码 - NSContactsUsageDescription - app需要通讯录权限添加好友 - NSHealthShareUsageDescription - app需要读取健康数据 - NSHealthUpdateUsageDescription - app需要写入健康数据 - NSLocalNetworkUsageDescription - app需要发现本地网络设备 - NSLocationAlwaysAndWhenInUseUsageDescription - 我们需要访问您的位置来提供基于位置的服务,例如获取位置展示地图。您的位置数据不会用于其他目的。 - NSLocationAlwaysUsageDescription - 我们需要访问您的位置来提供基于位置的服务,例如获取位置展示地图。您的位置数据不会用于其他目的。 - NSLocationWhenInUseUsageDescription - 我们需要访问您的位置来提供基于位置的服务,例如获取位置展示地图。您的位置数据不会用于其他目的。 - NSMicrophoneUsageDescription - app需要麦克风权限进行语音通话 - NSMotionUsageDescription - app需要访问运动数据统计步数 - NSPhotoLibraryAddUsageDescription - app需要保存图片到相册 - NSPhotoLibraryUsageDescription - app需要访问相册以上传图片 - NSUserNotificationsUsageDescription - app需要发送通知提醒重要信息 - UIApplicationSupportsIndirectInputEvents - - UIBackgroundModes - - remote-notification - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIStatusBarHidden - - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - com.apple.developer.nfc.readersession.formats - - TAG - NDEF - - \ No newline at end of file + NSBluetoothAlwaysUsageDescription + app需要蓝牙权限连接设备 + NSCameraUsageDescription + app需要相机权限来扫描二维码 + NSContactsUsageDescription + app需要通讯录权限添加好友 + NSHealthShareUsageDescription + app需要读取健康数据 + NSHealthUpdateUsageDescription + app需要写入健康数据 + NSLocalNetworkUsageDescription + app需要发现本地网络设备 + NSLocationAlwaysAndWhenInUseUsageDescription + 我们需要访问您的位置来提供基于位置的服务,例如获取位置展示地图。您的位置数据不会用于其他目的。 + NSLocationAlwaysUsageDescription + 我们需要访问您的位置来提供基于位置的服务,例如获取位置展示地图。您的位置数据不会用于其他目的。 + NSLocationWhenInUseUsageDescription + 我们需要访问您的位置来提供基于位置的服务,例如获取位置展示地图。您的位置数据不会用于其他目的。 + NSMicrophoneUsageDescription + app需要麦克风权限进行语音通话 + NSMotionUsageDescription + app需要访问运动数据统计步数 + NSPhotoLibraryAddUsageDescription + app需要保存图片到相册 + NSPhotoLibraryUsageDescription + app需要访问相册以上传图片 + NSUserNotificationsUsageDescription + app需要发送通知提醒重要信息 + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + remote-notification + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + com.apple.developer.nfc.readersession.formats + + TAG + NDEF + + + diff --git a/lib/customWidget/custom_alert_dialog.dart b/lib/customWidget/custom_alert_dialog.dart index ad5c9ca..2e25d38 100644 --- a/lib/customWidget/custom_alert_dialog.dart +++ b/lib/customWidget/custom_alert_dialog.dart @@ -1,9 +1,12 @@ // custom_alert_dialog.dart +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/main.dart'; // 导入全局 navigatorKey /// 对话框模式 -enum DialogMode { text, input } +enum DialogMode { text, input, inputWithCode } class CustomAlertDialog extends StatefulWidget { final String title; @@ -17,6 +20,10 @@ class CustomAlertDialog extends StatefulWidget { final DialogMode mode; final bool force; + /// 新增:获取验证码回调,若提供则在用户点击获取验证码时调用。 + /// 回调返回 true 表示获取成功(开始倒计时),返回 false 表示获取失败(不开始倒计时)。 + final Future Function()? onGetCode; + const CustomAlertDialog({ Key? key, required this.title, @@ -29,85 +36,119 @@ class CustomAlertDialog extends StatefulWidget { this.onInputConfirm, this.mode = DialogMode.text, this.force = false, + this.onGetCode, }) : super(key: key); // ------------------ 静态快捷方法 ------------------ static Future showConfirm( - BuildContext context, { - required String title, - String content = '', - String cancelText = '取消', - String confirmText = '确定', - bool barrierDismissible = true, - VoidCallback? onConfirm, - bool force = false, - }) async { + BuildContext context, { + required String title, + String content = '', + String cancelText = '取消', + String confirmText = '确定', + bool barrierDismissible = true, + VoidCallback? onConfirm, + bool force = false, + }) async { final result = await showDialog( context: context, barrierDismissible: force ? false : barrierDismissible, - builder: (_) => PopScope( - canPop: false, - child: CustomAlertDialog( - title: title, - content: content, - cancelText: cancelText, - confirmText: confirmText, - onConfirm: onConfirm, - force: force, - ),) + builder: + (_) => PopScope( + canPop: false, + child: CustomAlertDialog( + title: title, + content: content, + cancelText: cancelText, + confirmText: confirmText, + onConfirm: onConfirm, + force: force, + ), + ), ); return result == true; } static Future showAlert( - BuildContext context, { - required String title, - String content = '', - String confirmText = '确定', - bool barrierDismissible = true, - VoidCallback? onConfirm, - bool force = false, - }) async { + BuildContext context, { + required String title, + String content = '', + String confirmText = '确定', + bool barrierDismissible = true, + VoidCallback? onConfirm, + bool force = false, + }) async { await showDialog( context: context, barrierDismissible: force ? false : barrierDismissible, - builder: (_) => CustomAlertDialog( - title: title, - content: content, - cancelText: '', - confirmText: confirmText, - onConfirm: onConfirm, - force: force, - ), + builder: + (_) => CustomAlertDialog( + title: title, + content: content, + cancelText: '', + confirmText: confirmText, + onConfirm: onConfirm, + force: force, + ), ); } static Future showInput( - BuildContext context, { - required String title, - String hintText = '', - String cancelText = '取消', - String confirmText = '确定', - bool barrierDismissible = true, - bool force = false, - }) async { + BuildContext context, { + required String title, + String hintText = '', + String cancelText = '取消', + String confirmText = '确定', + bool barrierDismissible = true, + bool force = false, + }) async { final result = await showDialog( context: context, barrierDismissible: force ? false : barrierDismissible, - builder: (_) => CustomAlertDialog( - title: title, - hintText: hintText, - cancelText: cancelText, - confirmText: confirmText, - mode: DialogMode.input, - force: force, - ), + builder: + (_) => CustomAlertDialog( + title: title, + hintText: hintText, + cancelText: cancelText, + confirmText: confirmText, + mode: DialogMode.input, + force: force, + ), ); // 取消/点遮罩会得到 null;确认会得到 String(可能为空串) return result; } + /// 新增:快捷方法——输入框 + 获取验证码 + static Future showInputWithCode( + BuildContext context, { + required String title, + String hintText = '', + String cancelText = '取消', + String confirmText = '确定', + bool barrierDismissible = true, + bool force = false, + Future Function()? onGetCode, + ValueChanged? onConfirm, // <-- 这里改为带参数的回调 + }) async { + final result = await showDialog( + context: context, + barrierDismissible: force ? false : barrierDismissible, + builder: + (_) => CustomAlertDialog( + title: title, + hintText: hintText, + cancelText: cancelText, + confirmText: confirmText, + mode: DialogMode.inputWithCode, + force: force, + onGetCode: onGetCode, + onInputConfirm: onConfirm, // 把回调传给内部使用的 onInputConfirm + ), + ); + return result; + } @override _CustomAlertDialogState createState() => _CustomAlertDialogState(); @@ -117,6 +158,11 @@ class _CustomAlertDialogState extends State { late TextEditingController _controller; bool _isClosing = false; + // 验证码计时器相关 + Timer? _timer; + int _seconds = 0; + static const int _defaultCountdown = 60; + @override void initState() { super.initState(); @@ -125,6 +171,7 @@ class _CustomAlertDialogState extends State { @override void dispose() { + _timer?.cancel(); _controller.dispose(); super.dispose(); } @@ -144,6 +191,41 @@ class _CustomAlertDialogState extends State { } } + void _startCountdown([int seconds = _defaultCountdown]) { + _timer?.cancel(); + setState(() { + _seconds = seconds; + }); + _timer = Timer.periodic(const Duration(seconds: 1), (t) { + if (!mounted) { + t.cancel(); + return; + } + if (_seconds <= 1) { + t.cancel(); + setState(() { + _seconds = 0; + }); + } else { + setState(() { + _seconds -= 1; + }); + } + }); + } + + Future _onGetCodePressed() async { + if (_seconds > 0) return; // 正在倒计时,忽略 + // 如果外部提供了回调,等待其执行并根据返回值决定是否开始倒计时 + if (widget.onGetCode != null) { + await widget.onGetCode!(); + _startCountdown(); + } else { + // 未提供回调:默认直接开始倒计时(方便调试) + _startCountdown(); + } + } + @override Widget build(BuildContext context) { return PopScope( @@ -177,14 +259,11 @@ class _CustomAlertDialogState extends State { padding: const EdgeInsets.symmetric(horizontal: 24), child: Text( widget.content, - style: const TextStyle( - fontSize: 16, - color: Colors.black54, - ), + style: const TextStyle(fontSize: 16, color: Colors.black54), textAlign: TextAlign.center, ), ) - else + else if (widget.mode == DialogMode.input) Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: TextField( @@ -204,6 +283,45 @@ class _CustomAlertDialogState extends State { ), ), ), + ) + else // DialogMode.inputWithCode + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + child: Row( + children: [ + Expanded( + child: TextField( + controller: _controller, + autofocus: true, + decoration: InputDecoration( + hintText: widget.hintText, + border: const OutlineInputBorder(), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.blue, + width: 1, + ), + borderRadius: BorderRadius.circular(4), + ), + isDense: true, + contentPadding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 10, + ), + ), + ), + ), + const SizedBox(width: 8), + SizedBox( + height: 44, + width: 100, + child: CustomButton( + text: _seconds > 0 ? '$_seconds s' : '发送验证码', + onPressed: _seconds > 0 ? null : _onGetCodePressed, + ), + ), + ], + ), ), const SizedBox(height: 20), const Divider(height: 1), @@ -298,4 +416,4 @@ class _CustomAlertDialogState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/http/ApiService.dart b/lib/http/ApiService.dart index 3aea3ff..53f714c 100644 --- a/lib/http/ApiService.dart +++ b/lib/http/ApiService.dart @@ -18,7 +18,7 @@ class ApiService { static final String baseImgPath = isProduct ? "https://jpfz.qhdsafety.com/gbsFileTest/" - : "http://192.168.20.100:9787/mnt/"; //内网图片地址 + : "http://192.168.20.240:9787/mnt/"; //内网图片地址 static const publicKey = '0402df2195296d4062ac85ad766994d73e871b887e18efb9a9a06b4cebc72372869b7da6c347c129dee2b46a0f279ff066b01c76208c2a052af75977c722a2ccee'; diff --git a/lib/http/HttpManager.dart b/lib/http/HttpManager.dart index d3bf7a8..fcb71ee 100644 --- a/lib/http/HttpManager.dart +++ b/lib/http/HttpManager.dart @@ -76,6 +76,7 @@ class HttpManager { CancelToken? cancelToken, String? contentType, // Content-Type,默认为 jsonContentType bool isHeartbeat = false, + bool isHaveToken = true, }) async { printLongString('参数:${jsonEncode(data)}'); Response resp; @@ -87,7 +88,7 @@ class HttpManager { }; final token = SessionService.instance.token ?? ''; // final token = 'jjb-saas-auth:oauth:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJjbGllbnRJZFwiOlwieGdmemRcIixcImFjY291bnRJZFwiOjE5OTE2NzQ0MzEzMzY4NDk0MDgsXCJ1c2VyVHlwZUVudW1cIjpcIlBMQVRGT1JNXCIsXCJ1c2VySWRcIjoxOTkxNjc0NDI4MjYxNTMxNjQ4LFwidGVuYW50SWRcIjoxOTkxNjc0NDI4MjYxNTMxNjQ4LFwidGVuYW50TmFtZVwiOlwi5Yas5rOz55u45YWz5pa5XCIsXCJ0ZW5hbnRUeXBlSWRcIjoxOTkwNjkzMzg4MDcyMTI0NDE2LFwidGVuYW50UGFyZW50SWRzXCI6XCIwLDE5ODM3NzMwMTMwODYwNDgyNTYsMTk5MTY3NDQyODI2MTUzMTY0OFwiLFwibmFtZVwiOlwi5Yas5rOz55u45YWz5pa5XCIsXCJhY2Nlc3NUaWNrZXRcIjpcIkg0YXBlMkFaRVcxZFR1OTIwOXNzSDREc3pPWjBoTkZ4eEVlZzRmYTJZaFRVUFA0QkZVZXZmSklhTVdoS1wiLFwicmVmcmVzaFRpY2tldFwiOlwiRlRlZUxIaXJVblhueTBMcXNMcUdyc2dFaGpqVlRRN0pncVptVTBLS0JHVkFCU1ExeENtT3RTWmxRbUdpXCIsXCJleHBpcmVJblwiOjYwNDgwMCxcInJlZnJlc2hFeHBpcmVzSW5cIjo2MDQ4MDAsXCJvcmdJZFwiOjE5OTE2NzQ0MjgyNjE1MzE2NDgsXCJvcmdOYW1lXCI6XCLlhqzms7Pnm7jlhbPmlrlcIixcIm9yZ0lkc1wiOlsxOTkxNjc0NDI4MjYxNTMxNjQ4XSxcInJvbGVzVHlwZXNcIjpbXCJHT1ZfQ0hJTERfQUNDT1VOVFwiXSxcInJvbGVJZHNcIjpbMTk5MDY5MjE3NTA2NjgyNDcwNV0sXCJzY29wZXNcIjpbXSxcInJwY1R5cGVFbnVtXCI6XCJIVFRQXCIsXCJiaW5kTW9iaWxlU2lnblwiOlwiRkFMU0VcIn0iLCJpc3MiOiJwcm8tc2VydmVyIiwiZXhwIjoxNzY1OTU4NDIzfQ.RphPGGnh18RdGZ2vB0-2gKHp6bQg3-rKR4xPvDgH1ek'; - if (token != null && token.isNotEmpty && !isHeartbeat) { + if (token != null && token.isNotEmpty && !isHeartbeat && isHaveToken) { headers['token'] = token; } diff --git a/lib/http/modules/auth_api.dart b/lib/http/modules/auth_api.dart index 748f466..9361e4a 100644 --- a/lib/http/modules/auth_api.dart +++ b/lib/http/modules/auth_api.dart @@ -38,6 +38,7 @@ class AuthApi { ApiService.basePath, '/login/captcha', method: Method.get, + isHaveToken: false, data: {}, ); } @@ -60,7 +61,8 @@ class AuthApi { static Future> getUserData() { return HttpManager().request( ApiService.basePath, - '/basicInfo/user/getInfo', + // '/basicInfo/user/getInfo', + '/basicInfo/user/${SessionService.instance.accountId}', method: Method.get, data: {}, ); diff --git a/lib/http/modules/basic_info_api.dart b/lib/http/modules/basic_info_api.dart index da7c3e3..3d1fb2e 100644 --- a/lib/http/modules/basic_info_api.dart +++ b/lib/http/modules/basic_info_api.dart @@ -1,6 +1,7 @@ import 'package:dio/dio.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/http/HttpManager.dart'; +import 'package:qhd_prevention/services/SessionService.dart'; class BasicInfoApi { /// 注册 @@ -12,6 +13,17 @@ class BasicInfoApi { data: {...data}, ); } + /// 注销 + static Future> logout(Map data) { + return HttpManager().request( + ApiService.basePath + (ApiService.isProduct ? '/basicInfo' : '/basicInfo') , + '/appuser/logOut', + method: Method.post, + data: { + ...data + }, + ); + } /// 发送验证码 static Future> sendRegisterSms(Map data) { @@ -42,15 +54,6 @@ class BasicInfoApi { data: {...data}, ); } - // 就职信息 - static Future> getEntryInfo(String id) { - return HttpManager().request( - ApiService.basePath + (ApiService.isProduct ? '/basicInfo' : '/basicInfo') , - '/app/userCorpRecord/getInfoById/$id', - method: Method.get, - data: {}, - ); - } /// 获取用户信息 static Future> getUserMessage(String value) { return HttpManager().request( @@ -79,6 +82,44 @@ class BasicInfoApi { data: {...data}, ); } + /// 就职单位列表 + static Future> getFirmListByUser(Map data) { + return HttpManager().request( + ApiService.basePath + (ApiService.isProduct ? '/basicInfo' : '/basicInfo') , + '/app/userCorpRecord/list', + method: Method.post, + data: {...data}, + ); + } + /// 企业入职详情 + static Future> getFirmInfo(String id) { + return HttpManager().request( + ApiService.basePath + (ApiService.isProduct ? '/basicInfo' : '/basicInfo') , + '/app/userCorpRecord/getInfoById/$id', + method: Method.get, + data: {}, + ); + } + /// 离职申请 + static Future> leaveApply(Map data) { + return HttpManager().request( + ApiService.basePath + (ApiService.isProduct ? '/basicInfo' : '/basicInfo') , + '/appuser/appUserResignation', + method: Method.post, + data: {...data}, + ); + } + /// 在职企业列表 + static Future> getJoinFirmList() { + return HttpManager().request( + ApiService.basePath + (ApiService.isProduct ? '/basicInfo' : '/basicInfo') , + '/appuser/getUserCorpList/${SessionService.instance.accountId}', + method: Method.post, + data: {}, + ); + } + + diff --git a/lib/http/modules/file_api.dart b/lib/http/modules/file_api.dart index f1482dd..03eb8ec 100644 --- a/lib/http/modules/file_api.dart +++ b/lib/http/modules/file_api.dart @@ -17,7 +17,6 @@ class FileApi { } final fileName = file.path.split(Platform.pathSeparator).last; - return HttpManager().uploadImages( baseUrl: ApiService.basePath, path: '/basicInfo/imgFiles/save', diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 4b97e6a..1447494 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -9,6 +9,7 @@ import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/pages/home/scan_page.dart'; import 'package:qhd_prevention/pages/home/unit/unit_tab_page.dart'; import 'package:qhd_prevention/pages/main_tab.dart'; +import 'package:qhd_prevention/pages/user/choose_userFirm_page.dart'; import 'package:qhd_prevention/pages/user/firm_list_page.dart'; import 'package:qhd_prevention/tools/h_colors.dart'; import 'package:qhd_prevention/tools/tools.dart'; @@ -422,6 +423,10 @@ class HomePageState extends RouteAwareState ); } + Future _joinFirm() async { + pushPage(FirmListPage(isBack: true,), context); + } + // 固定在屏幕右上角的图标(不会随页面滚动) Widget _buildFixedTopIcons(BuildContext context) { final double statusBar = MediaQuery.of(context).padding.top; @@ -446,6 +451,19 @@ class HomePageState extends RouteAwareState ), ), ), + GestureDetector( + onTap: _joinFirm, + child: Container( + width: 38, + height: 38, + alignment: Alignment.center, + child: Image.asset( + "assets/icon-apps/home_add.png", + width: 22, + height: 22, + ), + ), + ), ], ), ); @@ -594,7 +612,7 @@ class HomePageState extends RouteAwareState CustomButton( text: '点击入职企业', onPressed: () { - pushPage(FirmListPage(), context); + pushPage(FirmListPage(isBack: true,), context); }, ) ], diff --git a/lib/pages/home/unit/unit_join_detail_page.dart b/lib/pages/home/unit/unit_join_detail_page.dart new file mode 100644 index 0000000..770a26b --- /dev/null +++ b/lib/pages/home/unit/unit_join_detail_page.dart @@ -0,0 +1,475 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/constants/app_enums.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/bottom_picker.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/item_list_widget.dart'; +import 'package:qhd_prevention/customWidget/photo_picker_row.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/pages/main_tab.dart'; +import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/services/SessionService.dart'; +import 'package:qhd_prevention/tools/tools.dart'; + +class UnitJoinDetailPage extends StatefulWidget { + const UnitJoinDetailPage({super.key, required this.firmId}); + final String firmId; + @override + State createState() => _UnitJoinDetailPageState(); +} + +class _UnitJoinDetailPageState extends State { + Map pd = { + }; + late bool _isEdit; + + String _genderText = ''; + String _birthText = ''; + List _idCardImgList = []; + List _idCartImgIds = []; + List _idCardImgRemoveList = []; + + List _wenhuachengduList = []; + List _zhengzhimianmaoList = []; + List _hunyinList = [ + {"name": "已婚", "value": 1}, + {"name": "未婚", "value": 0}, + ]; + List idPhotos = []; + + @override + void initState() { + super.initState(); + _isEdit = false; + _getUserDetail(); + + _getKeyValues(); + } + + Future _getUserDetail() async { + final res = await BasicInfoApi.getFirmInfo( + widget.firmId, + ); + if (res['success']) { + final data = res['data']; + _genderText = data['sex'] ?? ''; + _birthText = data['birthday'] ?? ''; + final eqForeignKey = data['userId']; + await FileApi.getImagePathWithType( + eqForeignKey, + '', + UploadFileType.idCardPhoto, + ).then((result) { + if (result['success']) { + List files = result['data'] ?? []; + _idCardImgList = + files.map((item) => item['filePath'].toString()).toList(); + _idCartImgIds = files.map((item) => item['id'].toString()).toList(); + // final filePath = fileData.first['filePath'] ?? ''; + } + }); + + setState(() { + pd = data; + try{ + final idCardBase64 = utf8.decode(base64.decode(pd['userIdCard'])); + if (idCardBase64.isNotEmpty) { + pd['userIdCard'] =idCardBase64; + } + }catch(e){ + print(e); + } + + }); + } + } + + Future _getKeyValues() async { + await BasicInfoApi.getDictValues('wenhuachengdu').then((res) { + _wenhuachengduList = res['data']; + }); + await BasicInfoApi.getDictValues('zhengzhimianmao').then((res) { + _zhengzhimianmaoList = res['data']; + }); + } + + Future _saveSuccess() async { + + pd['userIdCard'] = base64.encode(utf8.encode(pd['userIdCard'])); + await BasicInfoApi.updateUserInfo(pd).then((res) { + LoadingDialogHelper.hide(); + if (res['success']) { + ToastUtil.showNormal(context, '保存成功'); + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (_) => const MainPage(isChooseFirm: false)), + ); + } else { + ToastUtil.showNormal(context, '保存失败'); + } + }); + + } + + @override + Widget build(BuildContext context) { + bool isShow = _isEdit; + if (!_isEdit && FormUtils.hasValue(pd, 'id')) { + isShow = true; + } + return Scaffold( + appBar: MyAppbar( + title: '查看信息', + ), + + body: SafeArea( + child: ItemListWidget.itemContainer( + horizontal: 5, + isShow ? ListView( + children: [ + RepairedPhotoSection( + title: '照片', + inlineSingle: true, + isRequired: _isEdit, + initialMediaPaths: + FormUtils.hasValue(pd, 'userAvatarUrl') + ? [ + '${ApiService.baseImgPath}${pd['userAvatarUrl'] ?? ''}', + ] + : [], + horizontalPadding: _isEdit ? 12 : 0, + inlineImageWidth: 60, + isFaceImage: true, + isEdit: _isEdit, + onChanged: (files) { + if (files.isEmpty) { + return; + } + pd['faceFiles'] = files.first.path; + }, + onAiIdentify: () {}, + // onMediaRemovedForIndex: (index) async { + // final deleFile = pd['userAvatarUrl'] ?? ''; + // if (deleFile.contains(UploadFileType.idCardPhoto.path)) { + // _idCardImgRemoveList.add(deleFile); + // } + // }, + ), + if (_isEdit) + ItemListWidget.itemContainer( + const Text( + '温馨提示:该照片为进入项目施工场所口门人脸识别使用', + style: TextStyle(color: Colors.red, fontSize: 10), + ), + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '姓名:', + isRequired: true, + hintText: '请输入姓名', + text: pd['name'] ?? '', + isEditable: _isEdit, + onChanged: (value) { + pd['name'] = value; + }, + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '手机号:', + isRequired: true, + text: pd['username'] ?? '', + isNumericInput: true, + hintText: '请输入手机号', + strongRequired: _isEdit, + isEditable: false, + ), + const Divider(), + // 身份证输入:只使用 onChanged 回调(value 是实时输入框的值) + ItemListWidget.singleLineTitleText( + label: '身份证:', + isRequired: true, + hintText: '请输入身份证号', + text: pd['userIdCard'] ?? '', + isEditable: _isEdit, + onChanged: (value) { + }, + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '民族:', + isEditable: _isEdit, + text: pd['nationName'] ?? '请选择', + isRequired: _isEdit, + onTap: () async { + final found = await BottomPicker.show( + context, + items: nationMapList, + itemBuilder: + (i) => + Text(i['name']!, textAlign: TextAlign.center), + initialIndex: 0, + ); + //FocusHelper.clearFocus(context); + + if (found != null) { + setState(() { + pd['nationName'] = found['name']; + pd['nation'] = found['code']; + }); + } + }, + ), + const Divider(), + // 性别:不可编辑,显示解析结果(也允许手动覆盖) + ItemListWidget.selectableLineTitleTextRightButton( + label: '性别:', + isEditable: false, + text: _genderText, + + strongRequired: _isEdit, + isRequired: true, + onTap: () { + // 允许手动选择覆盖解析结果 + showModalBottomSheet( + context: context, + builder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + title: const Text('男'), + onTap: () { + setState(() { + pd['gender'] = '男'; + _genderText = '男'; + }); + Navigator.pop(context); + }, + ), + ListTile( + title: const Text('女'), + onTap: () { + setState(() { + pd['gender'] = '女'; + _genderText = '女'; + }); + Navigator.pop(context); + }, + ), + ], + ); + }, + ); + }, + ), + const Divider(), + // 出生年月:显示解析结果,但仍然可以手动修改 + ItemListWidget.selectableLineTitleTextRightButton( + label: '出生年月:', + isEditable: false, + text: _birthText, + strongRequired: _isEdit, + isRequired: true, + onTap: () async { + }, + ), + const Divider(), + + ItemListWidget.singleLineTitleText( + label: '户口所在地:', + isRequired: _isEdit, + hintText: '请输入户口所在地', + text: pd['locationAddress'] ?? '', + isEditable: _isEdit, + onChanged: (value) { + pd['locationAddress'] = value; + }, + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '现住址:', + isRequired: true, + text: pd['currentAddress'] ?? '', + hintText: '请输入现住址', + isEditable: _isEdit, + onChanged: (value) { + pd['currentAddress'] = value; + }, + ), + const Divider(), + if (_isEdit || _idCardImgList.isNotEmpty) + RepairedPhotoSection( + title: '身份证照片', + isRequired: _isEdit, + maxCount: 2, + initialMediaPaths: + _idCardImgList + .map( + (item) => + ApiService.baseImgPath + item, + ) + .toList(), + isEdit: _isEdit, + horizontalPadding: _isEdit ? 12 : 0, + inlineImageWidth: 60, + onChanged: (files) { + idPhotos = files.map((file) => file.path).toList(); + }, + onMediaRemovedForIndex: (index) async { + final deleFile = _idCardImgList[index]; + final deleId = _idCartImgIds[index]; + if (deleFile.contains(UploadFileType.idCardPhoto.path)) { + _idCardImgList.removeAt(index); + _idCartImgIds.removeAt(index); + _idCardImgRemoveList.add(deleId); + } + }, + + onAiIdentify: () { + /* ... */ + }, + ), + if (_isEdit) + ItemListWidget.itemContainer( + const Text( + '温馨提示:用户要上传身份证正反面(身份证照片数量是2张才能进行人员培训)', + style: TextStyle(color: Colors.red, fontSize: 10), + ), + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '文化程度:', + isEditable: _isEdit, + text: pd['culturalLevelName'] ?? '请选择', + isRequired: _isEdit, + onTap: () async { + final found = await BottomPicker.show( + context, + items: _wenhuachengduList, + itemBuilder: + (i) => Text( + i['dictLabel']!, + textAlign: TextAlign.center, + ), + initialIndex: 0, + ); + //FocusHelper.clearFocus(context); + + if (found != null) { + setState(() { + pd['culturalLevelName'] = found['dictLabel']; + pd['culturalLevel'] = found['dictValue']; + }); + } + }, + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '政治面貌:', + isEditable: _isEdit, + text: pd['politicalAffiliationName'] ?? '请选择', + isRequired: _isEdit, + onTap: () async { + final found = await BottomPicker.show( + context, + items: _zhengzhimianmaoList, + itemBuilder: + (i) => Text( + i['dictLabel']!, + textAlign: TextAlign.center, + ), + initialIndex: 0, + ); + //FocusHelper.clearFocus(context); + + if (found != null) { + setState(() { + pd['politicalAffiliationName'] = found['dictLabel']; + pd['politicalAffiliation'] = found['dictValue']; + }); + } + }, + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '婚姻状态:', + isEditable: _isEdit, + text: pd['maritalStatusName'] ?? '请选择', + isRequired: _isEdit, + onTap: () async { + final found = await BottomPicker.show( + context, + items: _hunyinList, + itemBuilder: + (i) => + Text(i['name']!, textAlign: TextAlign.center), + initialIndex: 0, + ); + //FocusHelper.clearFocus(context); + + if (found != null) { + setState(() { + pd['maritalStatusName'] = found['name']; + pd['maritalStatus'] = found['value']; + }); + } + }, + ), + const Divider(), + ListItemFactory.createYesNoSection( + title: "是否流动人员:", + horizontalPadding: 2, + verticalPadding: 0, + yesLabel: "是", + noLabel: "否", + text: pd['flowFlag'] == 1 ? '是' : '否', + isRequired: true, + isEdit: _isEdit, + groupValue: (pd['flowFlag'] ?? 0) == 1, + onChanged: (val) { + setState(() { + pd['flowFlag'] = val ? 1 : 0; + }); + }, + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '电子邮箱:', + isRequired: false, + isNumericInput: false, + hintText: '请输入电子邮箱', + text: pd['email'] ?? '', + isEditable: _isEdit, + onChanged: (value) { + pd['email'] = value; + }, + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '部门:', + isRequired: false, + isNumericInput: false, + hintText: '', + text: pd['departmentName'] ?? '', + isEditable: _isEdit, + + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '岗位(工种):', + isRequired: false, + isNumericInput: false, + hintText: '', + text: pd['postName'] ?? '', + isEditable: _isEdit, + ), + const Divider(), + ], + ) : NoDataWidget.show(), + ), + ), + ); + } +} diff --git a/lib/pages/home/unit/unit_join_list_page.dart b/lib/pages/home/unit/unit_join_list_page.dart new file mode 100644 index 0000000..5de1910 --- /dev/null +++ b/lib/pages/home/unit/unit_join_list_page.dart @@ -0,0 +1,249 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/pages/home/unit/unit_join_detail_page.dart'; +import 'package:qhd_prevention/pages/home/unit/unit_quit_apply_page.dart'; +import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/services/SessionService.dart'; +import 'package:qhd_prevention/services/StorageService.dart'; + +import 'package:qhd_prevention/tools/tools.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; + +class UnitJoinListPage extends StatefulWidget { + const UnitJoinListPage({Key? key}) : super(key: key); + + @override + _UnitJoinListPageState createState() => _UnitJoinListPageState(); +} + +class _UnitJoinListPageState extends State { + // Data and state variables + List list = []; + int currentPage = 1; + int rows = 10; + int totalPage = 1; + bool isLoading = false; + + List> flowList = []; + final GlobalKey _scaffoldKey = GlobalKey(); + final ScrollController _scrollController = ScrollController(); + final employmentFlag = {'0': '离职', '1': '在职', '3': '未入职'}; + final statusInfo = { + '1': '待审批', + '2': '通过', + '3': '驳回', + }; + + @override + void initState() { + super.initState(); + _fetchData(); + _scrollController.addListener(_onScroll); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + void _onScroll() { + if (_scrollController.position.pixels >= + _scrollController.position.maxScrollExtent && + !isLoading) { + if (currentPage < totalPage) { + currentPage++; + _fetchData(); + } + } + } + String formatDate(String dateTimeStr) { + if (dateTimeStr == null || dateTimeStr.isEmpty) { + return ''; + } + // 解析字符串为DateTime对象 + DateTime dateTime = DateTime.parse(dateTimeStr); + final time = dateTime == null ? '' : '${dateTime.year}年${dateTime.month}月${dateTime.day}日'; + // 格式化为年月日 + return time; + } + Future _fetchData() async { + if (isLoading) return; + setState(() => isLoading = true); + + try { + + final data = { + 'pageIndex': currentPage, + 'pageSize': rows, + 'eqUserId': SessionService.instance.accountId + }; + final response = await BasicInfoApi.getFirmListByUser(data); + setState(() { + if (currentPage == 1) { + list = response['data']; + } else { + list.addAll(response['data']); + } + Map page = response['page']; + totalPage = page['totalPage'] ?? 1; + isLoading = false; + }); + } catch (e) { + print('Error fetching data: $e'); + setState(() => isLoading = false); + } + } + + //查看 + void _goToDetail(Map item) async { + await pushPage( + UnitJoinDetailPage(firmId: item['id'],), + context, + ); + _fetchData(); + } + + // 指派 + void _leaveFirm(Map item) async { + await pushPage( + UnitQuitApplyPage(firmInfo: item,), + context, + ); + _fetchData(); + } + + + Widget _buildListItem(Map item) { + final startTime = formatDate(item['startTime'] ?? ''); + final endTime = item['employmentFlag'] == 1 ? '至今' : formatDate(item['endTime'] ?? ''); + return Card( + color: Colors.white, + margin: const EdgeInsets.all(8.0), + child: InkWell( + onTap: () => _goToDetail(item), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '单位名称:${item['corpinfoName'] ?? ''}', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ], + ), + const SizedBox(height: 8), + if (startTime.isNotEmpty && endTime.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [Text("就职时间:$startTime - $endTime")], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [Text("就职状态: ${employmentFlag['${item['employmentFlag']}'] ?? ''}")], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "审核状态: ${statusInfo['${item['status']}'] ?? ''}", + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), + ], + ), + const SizedBox(height: 8), + _statusButtons(item), + ], + ), + ), + ), + ); + } + + Widget _statusButtons(Map item) { + final List buttons = []; + final List buttonRowChildren = []; + + buttons.add( + CustomButton( + text: '查看', + backgroundColor: Colors.blue, + onPressed: () { + _goToDetail(item); + }, + ), + ); + if (item['employmentFlag'] == 1) { + buttons.add( + CustomButton( + text: '离职', + backgroundColor: Colors.blue, + onPressed: () { + _leaveFirm(item); + }, + ), + ); + } + + for (int i = 0; i < buttons.length; i++) { + buttonRowChildren.add( + Expanded(child: SizedBox(height: 40, child: buttons[i])), + ); + if (i != buttons.length - 1) { + buttonRowChildren.add(const SizedBox(width: 10)); + } + } + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: buttonRowChildren, + ); + } + + Widget _buildListContent() { + if (isLoading && list.isEmpty) { + // 初始加载时显示居中的加载指示器 + return Center(child: CircularProgressIndicator()); + } else if (list.isEmpty) { + // 没有数据 + return NoDataWidget.show(); + } else { + // 有数据或加载更多 + return ListView.builder( + padding: EdgeInsets.zero, + + controller: _scrollController, + itemCount: list.length + (isLoading ? 1 : 0), + itemBuilder: (context, index) { + if (index >= list.length) { + // 加载更多时在列表底部显示加载指示器 + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Center(child: CircularProgressIndicator()), + ); + } + return _buildListItem(list[index]); + }, + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + appBar: MyAppbar(title: '就职单位', actions: []), + + body: SafeArea(child: _buildListContent()), + ); + } +} diff --git a/lib/pages/home/unit/unit_quit_apply_page.dart b/lib/pages/home/unit/unit_quit_apply_page.dart new file mode 100644 index 0000000..6bd5456 --- /dev/null +++ b/lib/pages/home/unit/unit_quit_apply_page.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/item_list_widget.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/services/SessionService.dart'; +import 'package:qhd_prevention/tools/tools.dart'; + +class UnitQuitApplyPage extends StatefulWidget { + const UnitQuitApplyPage({super.key, required this.firmInfo}); + + final Map firmInfo; + @override + State createState() => _UnitQuitApplyPageState(); +} + +class _UnitQuitApplyPageState extends State { + String _resignationReason = ''; + + @override + void initState() { + super.initState(); + + } + + Future _submit() async { + final Map data = { + 'corpinfoId' : widget.firmInfo['corpinfoId'], + 'id' : widget.firmInfo['userId'], + 'resignationReason' : _resignationReason, + }; + LoadingDialogHelper.show(); + try { + final result = await BasicInfoApi.leaveApply(data); + LoadingDialogHelper.hide(); + if (result['success'] == true) { + ToastUtil.showNormal(context, '申请已提交'); + Navigator.pop(context); + }else{ + ToastUtil.showNormal(context, result['errMessage']); + } + }catch(e){ + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '申请失败'); + } + + } + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '离职申请'), + body: SafeArea( + child: ItemListWidget.itemContainer( + Column( + children: [ + ItemListWidget.singleLineTitleText( + label: '离职申请单位', + text: widget.firmInfo['corpinfoName'] ?? '', + isEditable: false, + ), + const Divider(), + ItemListWidget.multiLineTitleTextField( + label: '离职原因', + isEditable: true, + isRequired: false, + hintText: '请输入原因', + onChanged: (value) { + _resignationReason = value; + }, + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '申请时间', + text: DateFormat('yyyy年MM月dd日').format(DateTime.now()), + isEditable: false, + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '申请人', + text: SessionService.instance.name, + isEditable: false, + ), + const Divider(), + const SizedBox(height: 30,), + CustomButton(text: '提交', onPressed: () { + _submit(); + },) + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/unit/unit_tab_page.dart b/lib/pages/home/unit/unit_tab_page.dart index 6e8c399..a06c7c2 100644 --- a/lib/pages/home/unit/unit_tab_page.dart +++ b/lib/pages/home/unit/unit_tab_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/work_tab_icon_grid.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/customWidget/IconBadgeButton.dart'; +import 'package:qhd_prevention/pages/home/unit/unit_join_list_page.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/common/route_aware_state.dart'; @@ -42,6 +43,11 @@ class _UnitTabPageState extends RouteAwareState { switch (index) { case 0: break; + case 1: + pushPage(UnitJoinListPage(), context); + break; + default: + break; } _getData(); } diff --git a/lib/pages/mine/forgot_pwd_page.dart b/lib/pages/mine/forgot_pwd_page.dart index 9c6fc5b..29d1219 100644 --- a/lib/pages/mine/forgot_pwd_page.dart +++ b/lib/pages/mine/forgot_pwd_page.dart @@ -131,6 +131,7 @@ class _ForgotPwdPageState extends State { } if (!_canSend) return; + setState(() => _isSending = true); LoadingDialogHelper.show(); try { @@ -140,7 +141,7 @@ class _ForgotPwdPageState extends State { ToastUtil.showNormal(context, '验证码已发送'); _startCountdown(60); } else { - ToastUtil.showNormal(context, res?['message'] ?? '发送验证码失败'); + ToastUtil.showNormal(context, res?['errMessage'] ?? '发送验证码失败'); } } catch (e) { LoadingDialogHelper.hide(); @@ -244,7 +245,6 @@ class _ForgotPwdPageState extends State { return null; }, ), - const SizedBox(height: 12), // 验证码行 diff --git a/lib/pages/mine/mine_change_firm_page.dart b/lib/pages/mine/mine_change_firm_page.dart new file mode 100644 index 0000000..7080e3a --- /dev/null +++ b/lib/pages/mine/mine_change_firm_page.dart @@ -0,0 +1,161 @@ +import 'dart:io'; +import 'dart:convert'; +import 'package:qhd_prevention/services/StorageService.dart'; +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/pages/main_tab.dart'; +import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/services/SessionService.dart'; +import 'package:qhd_prevention/services/auth_service.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class MineChangeFirmPage extends StatefulWidget { + const MineChangeFirmPage({super.key}); + + @override + State createState() => _MineChangeFirmPageState(); +} + +class _MineChangeFirmPageState extends State { + // 入职企业列表 + List list = []; + String joinedUnitId = ''; + + @override + void initState() { + super.initState(); + Map jsonData = json.decode( + StorageService.instance.getString('key.saveJoinFirmInfo') ?? '{}', + ); + joinedUnitId = jsonData['unitId'] ?? ''; + _getList(); + } + + void _getList() async { + LoadingDialogHelper.show(); + var res = await BasicInfoApi.getJoinFirmList(); + LoadingDialogHelper.dismiss(); + if (res['success'] == true) { + setState(() { + list = res['data']; + }); + } + } + + // 切换账号 + Future _changeAccount(dynamic data) async { + await CustomAlertDialog.showConfirm( + context, + title: '温馨提示', + content: '确认切换到企业"${data['corpName']}"?', + onConfirm: () async { + LoadingDialogHelper.show(); + final prefs = await SharedPreferences.getInstance(); + final phone = prefs.getString('savePhone') ?? ''; + final pwd = prefs.getString('savePass') ?? ''; + var params = {'unitId': data['id']}; + try { + var res = await AuthService.gbsLogin(phone, pwd, params); + LoadingDialogHelper.dismiss(); + if (res['success'] == true) { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (_) => const MainPage(isChooseFirm: true), + ), + ); + } else { + ToastUtil.showNormal(context, res['errMessage'] ?? '切换账户失败,请重试'); + } + } catch (e) { + LoadingDialogHelper.dismiss(); + ToastUtil.showNormal(context, '切换账户失败,请重试'); + } + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + // backgroundColor: Colors.white, + appBar: MyAppbar(title: '切换账号'), + body: Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + color: Colors.blue.shade50, + padding: const EdgeInsets.symmetric(vertical: 8), + // height: 40, + child: Center(child: Text('点击企业名称以切换账号')), + ), + const SizedBox(height: 10), + Container( + height: 40.0 * list.length, + width: MediaQuery.of(context).size.width - 24, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.grey.shade300), + ), + child: + list.isEmpty + ? NoDataWidget.show() + : ListView.builder( + padding: EdgeInsets.zero, + itemCount: list.length, + itemBuilder: (context, index) { + if (index >= list.length) { + // 加载更多时在列表底部显示加载指示器 + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Center(child: CircularProgressIndicator()), + ); + } + var data = list[index]; + return SizedBox( + height: 40.0, + child: Column( + children: [ + GestureDetector( + onTap: () async { + _changeAccount(data); + }, + child: SizedBox( + height: 39.0, + child: Center( + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Text('${data['corpName'] ?? 'aaaa'}'), + if (joinedUnitId == + data['id']) ...[ + const Icon( + Icons.check, + size: 20, + color: Colors.green, + ), + ] else ...[ + const SizedBox(width: 20), + ], + ], + ), + ), + ), + ), + if (index < list.length - 1) + const Divider(height: 1), + ], + ), + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/mine/mine_feedback_page.dart b/lib/pages/mine/mine_feedback_page.dart index ad6f2bf..980e736 100644 --- a/lib/pages/mine/mine_feedback_page.dart +++ b/lib/pages/mine/mine_feedback_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'dart:io'; import '../../../../../customWidget/photo_picker_row.dart'; @@ -274,20 +275,7 @@ class _FeedbackPageState extends State { // 提交按钮 SizedBox( width: double.infinity, - child: ElevatedButton( - onPressed: _submitFeedback, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: const Text( - '提交', - style: TextStyle(fontSize: 18, color: Colors.white), - ), - ), + child: CustomButton(text: '提交', onPressed: _submitFeedback,) ), ], ), diff --git a/lib/pages/mine/mine_page.dart b/lib/pages/mine/mine_page.dart index 77ddada..38f77a1 100644 --- a/lib/pages/mine/mine_page.dart +++ b/lib/pages/mine/mine_page.dart @@ -4,9 +4,11 @@ import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/home/scan_page.dart'; import 'package:qhd_prevention/pages/home/userinfo_page.dart'; import 'package:qhd_prevention/pages/mine/face_ecognition_page.dart'; +import 'package:qhd_prevention/pages/mine/mine_change_firm_page.dart'; import 'package:qhd_prevention/pages/mine/mine_feedback_page.dart'; import 'package:qhd_prevention/pages/mine/mine_set_pwd_page.dart'; import 'package:qhd_prevention/pages/mine/onboarding_full_page.dart'; @@ -107,7 +109,7 @@ class MinePageState extends State { MaterialPageRoute( builder: (context) => const LoginPage(), ), - (Route route) => false, + (Route route) => false, ); }, ); @@ -154,6 +156,69 @@ class MinePageState extends State { ); } + Future _logout() async { + + LoadingDialogHelper.show(); + + /// 获取用户在职列表 + final firmData = await BasicInfoApi.getJoinFirmList(); + if (firmData['success'] == true) { + final firmList = firmData['data']; + LoadingDialogHelper.dismiss(); + if (firmList.isNotEmpty) { + CustomAlertDialog.showAlert( + context, + title: '温馨提示', + content: '您目前还有入职信息无法直接注销。\n请先在“就职单位”页面中离职。', + ); + } else { + CustomAlertDialog.showConfirm( + context, + title: '温馨提示', + content: '注销后您的所有信息将会被删除\n请确认是否注销。 ', + onConfirm: () async { + CustomAlertDialog.showInputWithCode( + context, + title: '手机号:${SessionService.instance.phone}', + onGetCode: () async { + LoadingDialogHelper.show(); + final res = await BasicInfoApi.sendRegisterSms({ + 'phone': phone, + }); + LoadingDialogHelper.dismiss(); + return true; + }, + onConfirm: (code) async { + LoadingDialogHelper.show(); + Map data = { + 'id' : SessionService.instance.accountId, + 'phoneCode' : code, + }; + await BasicInfoApi.logout(data).then((res) async { + LoadingDialogHelper.dismiss(); + if (res['success'] == true) { + ToastUtil.showNormal(context, '账号已注销'); + await SessionService.instance.clear(clearPrefs: true); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (_) => const LoginPage()), + ); + } else { + ToastUtil.showNormal(context, res['errMessage'] ?? ''); + } + }); + } + ); + }, + ); + } + } else { + LoadingDialogHelper.dismiss(); + ToastUtil.showNormal(context, firmData['errMessage'] ?? ''); + } + } + Widget _buildSloganSection() { return Container( margin: EdgeInsets.fromLTRB(0, 100, 0, 0), @@ -180,11 +245,11 @@ class MinePageState extends State { ], ), const SizedBox(width: 16), - ], ), ); } + Widget _buildSettingsList() { return Container( margin: const EdgeInsets.fromLTRB(20, 0, 20, 0), @@ -206,14 +271,18 @@ class MinePageState extends State { title: "我的信息", icon: "assets/images/ico9.png", value: notificationsEnabled, - num: 0, - onChanged: (value) => setState(() => notificationsEnabled = value!), + onChanged: (value) { + pushPage( + FullUserinfoPage(isEidt: false, isChooseFirm: true), + context, + ); + }, ), + _buildSettingItem( title: "修改密码", icon: "assets/images/ico16.png", value: notificationsEnabled, - num: 1, onChanged: (value) async { await pushPage(MineSetPwdPage('0'), context); }, @@ -222,23 +291,38 @@ class MinePageState extends State { title: "扫码入职", icon: "assets/images/ico10.png", value: scanAuthentication, - num: 2, - onChanged: (value) async {}, + onChanged: (value) async { + final result = await pushPage( + ScanPage(type: ScanType.Onboarding), + context, + ); + if (result == null) { + return; + } + pushPage(OnboardingFullPage(scanData: result), context); + }, ), _buildSettingItem( title: "人脸认证", icon: "assets/images/ico11.png", value: faceAuthentication, - num: 3, - onChanged: (value) => setState(() => faceAuthentication = value!), + onChanged: (value) { + pushPage( + const FaceRecognitionPage( + studentId: '', + data: {}, + mode: FaceMode.setUpdata, + ), + context, + ); + }, ), _buildSettingItem( title: "证书信息", icon: "assets/images/ico12.png", value: passwordChanged, - num: 4, onChanged: (value) => setState(() => passwordChanged = value!), ), @@ -246,7 +330,6 @@ class MinePageState extends State { title: "问题反馈", icon: "assets/images/ico13.png", value: passwordChanged, - num: 5, onChanged: (value) => setState(() => passwordChanged = value!), ), @@ -255,7 +338,6 @@ class MinePageState extends State { title: "版本更新", icon: "assets/images/ico14.png", value: updateAvailable, - num: 6, onChanged: (value) => setState(() => updateAvailable = value!), ), @@ -263,21 +345,24 @@ class MinePageState extends State { title: "关于我们", icon: "assets/images/ico15.png", value: logoutSelected, - num: 7, onChanged: (value) { setState(() => logoutSelected = value!); - // if (value == true) { - // _showLogoutConfirmation(); - // } + }, + ), + _buildSettingItem( + title: "切换账户", + icon: "assets/images/ico15.png", + value: logoutSelected, + onChanged: (value) { + pushPage(MineChangeFirmPage(), context); }, ), _buildSettingItem( title: "账户注销", icon: "assets/images/ico15.png", value: logoutSelected, - num: 8, onChanged: (value) { - + _logout(); }, ), ], @@ -289,53 +374,11 @@ class MinePageState extends State { required String title, required String icon, required bool value, - required int num, required ValueChanged onChanged, }) { return GestureDetector( onTap: () async { - switch (num) { - case 0: - pushPage(FullUserinfoPage(isEidt: false, isChooseFirm: true,), context); - break; - case 1: - await pushPage(MineSetPwdPage('0'), context); - - break; - case 2: - final result = await pushPage( - ScanPage(type: ScanType.Onboarding), - context, - ); - if (result == null) { - return; - } - pushPage(OnboardingFullPage(scanData: result), context); - - break; - - case 3: - pushPage( - const FaceRecognitionPage( - studentId: '', - data: {}, - mode: FaceMode.setUpdata, - ), - context, - ); - // pushPage(ChangePassPage(), context); - break; - case 4: - // _showLogoutConfirmation(); - break; - case 5: - pushPage(FeedbackPage(), context); - break; - case 6: - break; - case 7: - break; - } + onChanged(value); }, child: ListTile( leading: Container( @@ -366,84 +409,8 @@ class MinePageState extends State { ); } - Widget _buildActionButton() { - return SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: () { - // 保存设置逻辑 - _saveSettings(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF1A237E), - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 3, // 注意:elevation应该放在styleFrom内部 - ), - child: const Text( - "保存设置", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), - ), - ), - ); - } - - void _saveSettings() { - // 这里可以添加保存设置到服务器的逻辑 - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: const Text("设置已保存成功"), - backgroundColor: Colors.green[700], - duration: const Duration(seconds: 2), - behavior: SnackBarBehavior.floating, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - ), - ); - } - Future _clearUserSession() async { final prefs = await SharedPreferences.getInstance(); await prefs.remove('isLoggedIn'); // 清除登录状态 } } - -class _StatItem extends StatelessWidget { - final String number; - final String label; - - const _StatItem({required this.number, required this.label}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - // 数字圆圈 - Center( - child: Text( - number, - style: const TextStyle( - color: Colors.black, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), - - const SizedBox(height: 8), - - // 标签 - Text( - label, - style: TextStyle( - fontSize: 12, - color: Colors.grey[600], - fontWeight: FontWeight.w500, - ), - ), - ], - ); - } -} diff --git a/lib/pages/mine/onboarding_full_page.dart b/lib/pages/mine/onboarding_full_page.dart index 359adb7..5f7cffa 100644 --- a/lib/pages/mine/onboarding_full_page.dart +++ b/lib/pages/mine/onboarding_full_page.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; +import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/item_list_widget.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/modules/basic_info_api.dart'; import 'package:qhd_prevention/pages/main_tab.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/pages/user/login_page.dart'; import 'package:qhd_prevention/services/SessionService.dart'; import 'package:qhd_prevention/services/auth_service.dart'; import 'package:qhd_prevention/tools/tools.dart'; @@ -13,20 +15,25 @@ import 'package:shared_preferences/shared_preferences.dart'; class OnboardingFullPage extends StatefulWidget { const OnboardingFullPage({super.key, required this.scanData}); + final Map scanData; + @override State createState() => _OnboardingFullPageState(); } class _OnboardingFullPageState extends State { Map pd = {}; - // 部门列表 + + // 部门列表 List _deptList = []; + @override void initState() { super.initState(); _getDept(); } + // 获取部门 Future _getDept() async { try { @@ -36,57 +43,57 @@ class _OnboardingFullPageState extends State { }; final result = await BasicInfoApi.getDeptTree(data); if (result['success'] == true) { - _deptList = result['data']; + final list = result['data'] ?? []; + if (list.length > 0) { + setState(() { + _deptList = list[0]['childrenList'] ?? []; + }); + } } - } catch (e) { - } + } catch (e) {} } + // 提交 Future _saveSuccess() async { - if (!FormUtils.hasValue(pd, 'corpinfoId')) { - ToastUtil.showNormal(context, '请选择部门'); - return; - } - if (!FormUtils.hasValue(pd, 'postName')) { - ToastUtil.showNormal(context, '请输入岗位'); - return; - } - LoadingDialogHelper.show(); - pd['id'] = widget.scanData['id']; - try { - final result = await BasicInfoApi.userFirmEntry(pd); - LoadingDialogHelper.hide(); - if (result['success'] == true) { - ToastUtil.showNormal(context, '操作成功'); - _relogin(); - } - } catch (e) { - LoadingDialogHelper.hide(); - ToastUtil.showNormal(context, '操作成功'); - } - } - /// 重新登录 - Future _relogin() async { - final prefs = await SharedPreferences.getInstance(); - final username = prefs.getString('savePhone') ?? ''; - final password = prefs.getString('savePass') ?? ''; - - try { - Map data = { - 'id': widget.scanData['id'] ?? '', - 'corpinfoId':widget.scanData['corpinfoId'] ?? '', - }; - final result = await AuthService.gbsLogin(username, password, data); - if (result['success'] == true) { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (_) => const MainPage(isChooseFirm: true,)), - ); - } - } catch (e) { - ToastUtil.showNormal(context, '重新登录失败'); + if (!FormUtils.hasValue(pd, 'corpinfoId')) { + ToastUtil.showNormal(context, '请选择部门'); + return; } + if (!FormUtils.hasValue(pd, 'postName')) { + ToastUtil.showNormal(context, '请输入岗位'); + return; + } + await CustomAlertDialog.showConfirm( + context, + title: '温馨提示', + content: '确定加入${widget.scanData['corpName'] ?? ''}?', + onConfirm: () async { + LoadingDialogHelper.show(); + pd['id'] = SessionService.instance.accountId; + try { + final result = await BasicInfoApi.userFirmEntry(pd); + LoadingDialogHelper.hide(); + if (result['success'] == true) { + ToastUtil.showNormal(context, '申请成功'); + _relogin(); + } else { + ToastUtil.showNormal(context, result['errMessage']); + } + } catch (e) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '操作失败,请重试'); + } + }, + ); + } + + /// 回到首页 + Future _relogin() async { + bool isChooseFirm = (SessionService.instance.token ?? '').isNotEmpty; + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (_) => MainPage(isChooseFirm: isChooseFirm)), + ); } @override @@ -98,6 +105,11 @@ class _OnboardingFullPageState extends State { horizontal: 5, ListView( children: [ + ItemListWidget.singleLineTitleText( + label: '企业名称', + isEditable: false, + text: widget.scanData['corpName'] ?? '', + ), ItemListWidget.selectableLineTitleTextRightButton( verticalInset: 15, label: '选择入职部门:', @@ -113,8 +125,7 @@ class _OnboardingFullPageState extends State { context, items: _deptList, itemBuilder: - (i) => - Text(i['name']!, textAlign: TextAlign.center), + (i) => Text(i['name']!, textAlign: TextAlign.center), initialIndex: 0, ); //FocusHelper.clearFocus(context); @@ -125,7 +136,6 @@ class _OnboardingFullPageState extends State { pd['departmentName'] = found['name']; pd['corpinfoId'] = found['corpinfoId']; pd['corpinfoName'] = found['corpinfoName']; - }); } }, diff --git a/lib/pages/user/choose_userFirm_page.dart b/lib/pages/user/choose_userFirm_page.dart index fe41e03..db61a65 100644 --- a/lib/pages/user/choose_userFirm_page.dart +++ b/lib/pages/user/choose_userFirm_page.dart @@ -53,8 +53,7 @@ class _ChooseUserfirmPageState extends State { return; } final params = { - 'corpId': widget.firms[_selectedIndex]['id'], - 'corpName': widget.firms[_selectedIndex]['corpName'], + 'unitId': widget.firms[_selectedIndex]['id'], }; LoadingDialogHelper.show(); final result = await AuthService.gbsLogin(widget.userName, widget.password, params); diff --git a/lib/pages/user/firm_list_page.dart b/lib/pages/user/firm_list_page.dart index d164f93..f45a935 100644 --- a/lib/pages/user/firm_list_page.dart +++ b/lib/pages/user/firm_list_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/search_bar_widget.dart'; import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/pages/main_tab.dart'; import 'package:qhd_prevention/pages/mine/onboarding_full_page.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; @@ -9,7 +10,9 @@ import 'package:lpinyin/lpinyin.dart'; import 'package:qhd_prevention/tools/tools.dart'; class FirmListPage extends StatefulWidget { - const FirmListPage({super.key}); + const FirmListPage({super.key, required this.isBack}); + + final bool isBack; @override State createState() => _FirmListPageState(); @@ -274,11 +277,29 @@ class _FirmListPageState extends State { // 右侧字母索引(灰色圆角背景),使用固定高度以忽略键盘导致的可用高度变化 final mq = MediaQuery.of(context); final fixedIndexHeight = - mq.size.height - kToolbarHeight - mq.padding.top - 24-100; + mq.size.height - kToolbarHeight - mq.padding.top - 24 - 100; return Scaffold( backgroundColor: Colors.white, - appBar: MyAppbar(title: '选择企业'), + appBar: MyAppbar( + title: '选择企业', + isBack: widget.isBack, + actions: [ + if (!widget.isBack) + TextButton( + onPressed: () { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (_) => MainPage(isChooseFirm: false)), + ); + }, + child: const Text( + '跳过', + style: TextStyle(color: Colors.white, fontSize: 17), + ), + ), + ], + ), body: SafeArea( child: Column( children: [ diff --git a/lib/pages/user/full_userinfo_page.dart b/lib/pages/user/full_userinfo_page.dart index 66709cc..9f464ee 100644 --- a/lib/pages/user/full_userinfo_page.dart +++ b/lib/pages/user/full_userinfo_page.dart @@ -15,9 +15,11 @@ import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/main_tab.dart'; import 'package:qhd_prevention/pages/mine/face_ecognition_page.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/pages/user/firm_list_page.dart'; import 'package:qhd_prevention/services/SessionService.dart'; import 'package:qhd_prevention/tools/id_cart_util.dart'; import 'package:qhd_prevention/tools/tools.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class FullUserinfoPage extends StatefulWidget { const FullUserinfoPage({super.key, required this.isEidt, required this.isChooseFirm}); @@ -61,6 +63,7 @@ class _FullUserinfoPageState extends State { String _genderText = ''; String _birthText = ''; String _idValue = ''; + String? _userId = SessionService.instance.userId; List _idCardImgList = []; List _idCartImgIds = []; /// 是否修改了身份证 @@ -83,7 +86,6 @@ class _FullUserinfoPageState extends State { if (!_isEdit) { _getUserDetail(); } else { - pd['username'] = SessionService.instance.userName; pd['id'] = SessionService.instance.accountId; pd['flowFlag'] = 0; } @@ -129,12 +131,18 @@ class _FullUserinfoPageState extends State { } Future _getKeyValues() async { + final prefs = await SharedPreferences.getInstance(); + final phone = await prefs.getString("savePhone"); + pd['username'] = phone; await BasicInfoApi.getDictValues('wenhuachengdu').then((res) { _wenhuachengduList = res['data']; }); await BasicInfoApi.getDictValues('zhengzhimianmao').then((res) { _zhengzhimianmaoList = res['data']; }); + setState(() { + + }); } Future _saveSuccess() async { @@ -160,7 +168,7 @@ class _FullUserinfoPageState extends State { return; } LoadingDialogHelper.show(); - // 签字上传 + // 人脸上传 final signResult = await _checkFaceImage(); // 证件上传图片 final situationResult = await _checkIDCartImages(); @@ -173,12 +181,13 @@ class _FullUserinfoPageState extends State { LoadingDialogHelper.hide(); if (res['success']) { ToastUtil.showNormal(context, '保存成功'); + bool ischange = _isChange ? true: false; Navigator.pushReplacement( context, - MaterialPageRoute(builder: (_) => const MainPage(isChooseFirm: false)), + MaterialPageRoute(builder: (_) => FirmListPage(isBack: false,)), ); } else { - ToastUtil.showNormal(context, '保存失败'); + ToastUtil.showNormal(context, res['errMessage']); } }); }else{ @@ -211,7 +220,7 @@ class _FullUserinfoPageState extends State { late bool isSuccess = true; if (faceImgPath.isNotEmpty) { try { - await FileApi.uploadFile(faceImgPath, fileType, '').then((result) { + await FileApi.uploadFile(faceImgPath, fileType, _userId ?? '').then((result) { if (result['success']) { pd['userAvatarUrl'] = result['data']['filePath'] ?? ''; isSuccess = true; @@ -223,7 +232,7 @@ class _FullUserinfoPageState extends State { }); } catch (e) { LoadingDialogHelper.hide(); - ToastUtil.showNormal(context, '签名上传失败'); + ToastUtil.showNormal(context, '人脸照片上传失败'); isSuccess = false; } } @@ -241,10 +250,9 @@ class _FullUserinfoPageState extends State { await FileApi.uploadFiles( idPhotos, UploadFileType.idCardPhoto, - pd['userId'] ?? '', + _userId ?? '', ).then((result) { if (result['success']) { - pd['userId'] = result['data']['foreignKey'] ?? ''; } else { LoadingDialogHelper.hide(); ToastUtil.showNormal(context, '图片上传失败'); @@ -282,8 +290,6 @@ class _FullUserinfoPageState extends State { }); } else { // 解析失败或校验位不正确 — 清除解析字段但保留输入 - ToastUtil.showNormal(context, '请输入正确格式身份证号'); - setState(() { pd.remove('birthday'); pd.remove('age'); diff --git a/lib/pages/user/login_page.dart b/lib/pages/user/login_page.dart index e40792e..4d4d199 100644 --- a/lib/pages/user/login_page.dart +++ b/lib/pages/user/login_page.dart @@ -67,13 +67,18 @@ class _LoginPageState extends State { } Future _getCaptcha() async { - final response = await AuthApi.getUserCaptcha(); - if (response['success']) { - setState(() { - _captchaImageBase64 = response['data']['img']; - _captchaIdentifier = response['data']['captchaKey']; - }); + try{ + final response = await AuthApi.getUserCaptcha(); + if (response['success']) { + setState(() { + _captchaImageBase64 = response['data']['img']; + _captchaIdentifier = response['data']['captchaKey']; + }); + } + }catch(e){ + print(e); } + } Future _getData() async { @@ -122,7 +127,7 @@ class _LoginPageState extends State { children: [ const SizedBox(height: 70), // 顶部间距 Image.asset( - "assets/images/logo.png", + "assets/images/g_logo.png", width: 40, height: 40, ), @@ -282,17 +287,14 @@ class _LoginPageState extends State { Padding( padding: const EdgeInsets.symmetric( - horizontal: 25, + horizontal: 10, ), child: _buildCaptchaImage(), ), ], ), ), - const Divider( - height: 0.5, - color: Colors.black26, - ), + const Divider(), const SizedBox(height: 10), diff --git a/lib/pages/user/register_page.dart b/lib/pages/user/register_page.dart index 606a855..beba42c 100644 --- a/lib/pages/user/register_page.dart +++ b/lib/pages/user/register_page.dart @@ -167,7 +167,9 @@ class _RegisterPageState extends State { 'newPassword': pwd, 'confirmPassword': pwd, }; + LoadingDialogHelper.show(); final resp = await BasicInfoApi.register(data); + LoadingDialogHelper.hide(); if (resp != null && resp['success'] == true) { ToastUtil.showNormal(context, '注册成功,请登录'); @@ -181,6 +183,7 @@ class _RegisterPageState extends State { ToastUtil.showNormal(context, resp?['message'] ?? '注册失败,请重试'); } } catch (e) { + LoadingDialogHelper.hide(); ToastUtil.showNormal(context, '注册失败,请稍后重试'); } } diff --git a/lib/services/SessionService.dart b/lib/services/SessionService.dart index 2a866d9..ea20513 100644 --- a/lib/services/SessionService.dart +++ b/lib/services/SessionService.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:qhd_prevention/tools/tools.dart'; import 'package:shared_preferences/shared_preferences.dart'; class BizAttr { @@ -342,8 +343,8 @@ class SessionService { /// 从完整的API响应更新会话数据 void updateFromApiResponse(Map responseJson) { - if (responseJson['data'] != null) { - userData = UserData.fromJson(responseJson['data'] as Map); + if (responseJson != null) { + userData = UserData.fromJson(responseJson); } // 注意:token可能需要从响应头或其他地方单独设置 } @@ -401,6 +402,7 @@ class SessionService { print('姓名: ${userData!.name}'); print('部门: ${userData!.departmentName}'); print('租户ID: ${userData!.tenantId}'); + // printLongString(text) } } } diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart index 4d42eaa..871264c 100644 --- a/lib/services/auth_service.dart +++ b/lib/services/auth_service.dart @@ -29,14 +29,20 @@ class AuthService { return {}; } final resData = res['data']; + if (resData == null) { + Fluttertoast.showToast(msg: '登录失败'); + return {}; + } bool isInfoComplete = resData['isInfoComplete'] ?? false; List firmList = resData['corpInfoCOList'] ?? []; + /// 保存用户信息 + SessionService.instance.updateFromApiResponse(resData['userCO']); + await SessionService.instance.saveToPrefs(); if (firmList.length == 1 && isInfoComplete) { // 入职一个企业直接进行底座登录传入企业 Map data = { - 'id': firmList.first['id'] ?? '', - 'corpinfoId': firmList.first['corpinfoId'] ?? '', + 'unitId': firmList.first['id'] ?? '', }; return AuthService.gbsLogin(username, password, data); } else if (firmList.length > 1) { @@ -94,6 +100,7 @@ class AuthService { _data['password'] = encrypted; // printLongString(jsonEncode(_data)); final result = await AuthApi.loginCheck(_data); + final success = result['success'] as bool; if (!success) { Fluttertoast.showToast(msg: result['errMessage'] ?? ''); @@ -116,11 +123,25 @@ class AuthService { json.encode(params), ); - await AuthApi.getUserData().then((result) async { - SessionService.instance.updateFromApiResponse(result); - await SessionService.instance.saveToPrefs(); - SessionService.instance.setToken(token); - }); + try { + await AuthApi.getUserData().then((result) async { + if (!result['success']) { + LoadingDialogHelper.hide(); + Fluttertoast.showToast(msg: result['errMessage'] ?? ''); + return {}; + } + SessionService.instance.updateFromApiResponse(result['data']); + await SessionService.instance.saveToPrefs(); + SessionService.instance.setToken(token); + SessionService.instance.printUserInfo(); + + }); + } catch (e) { + LoadingDialogHelper.hide(); + Fluttertoast.showToast(msg: '用户信息获取失败,请重试'); + return {}; + } + return result; } diff --git a/lib/tools/tools.dart b/lib/tools/tools.dart index 513df9b..3bbefe5 100644 --- a/lib/tools/tools.dart +++ b/lib/tools/tools.dart @@ -316,6 +316,17 @@ class LoadingDialogHelper { _timer?.cancel(); _timer = null; + try { + EasyLoading.dismiss(); + } catch (e) { + debugPrint('EasyLoading.dismiss error: $e'); + } + } + static void dismiss() { + // 清理计时器 + _timer?.cancel(); + _timer = null; + try { EasyLoading.dismiss(); } catch (e) { diff --git a/pubspec.yaml b/pubspec.yaml index 6c2b8be..3b01db7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,9 +32,13 @@ environment: # ios: true # image_path: "assets/images/app-logo.png" # min_sdk_android: 21 # (可选) Android 最低支持 +flutter_launcher_icons: + android: true + ios: true + image_path: "assets/images/logo.png" flutter_native_splash: - background_image: assets/images/bg-login.png + background_image: assets/images/lun.jpg android: true ios: true web: false