From 87b1f80d3d5b7a847c338b6fffbeadc48c577477 Mon Sep 17 00:00:00 2001 From: hs <873121290@qq.com> Date: Thu, 21 Aug 2025 16:44:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AB=E9=A1=B9=E4=BD=9C=E4=B8=9A=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E9=80=BB=E8=BE=91=E6=94=B9=E5=8A=A8=E5=89=8D=E4=BF=9D?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle.kts | 2 +- android/app/src/main/AndroidManifest.xml | 72 ++-- .../qhd_prevention/MainActivity.kt | 59 ++- android/app/src/main/res/xml/file_paths.xml | 9 + android/gradle.properties | 2 +- assets/map/test_baidu_map.html | 380 +++++++++++++++++ ios/Podfile.lock | 25 ++ ios/Runner.xcodeproj/project.pbxproj | 18 + ios/Runner/AppDelegate.swift | 67 ++- ios/Runner/Info.plist | 11 +- .../BaiDuMap/BaiduMapWebView.dart | 391 +++--------------- lib/customWidget/BaiDuMap/Map_page.dart | 203 +++++++++ lib/customWidget/ItemWidgetFactory.dart | 2 +- lib/customWidget/custom_alert_dialog.dart | 150 ++++--- lib/customWidget/department_picker.dart | 1 + .../picker/CupertinoDatePicker.dart | 164 ++++++-- lib/customWidget/promise/promise_page.dart | 348 ++++++++++++++++ lib/http/ApiService.dart | 94 ++++- lib/http/HttpManager.dart | 18 +- lib/main.dart | 4 +- .../check_record_detail_page.dart | 104 +++-- .../app/checking_information_one_page.dart | 3 + .../safecheck_sign_detail.dart | 5 +- .../CheckPersonSure/check_person_detail.dart | 5 +- .../Start/safeCheck_defend_set_page.dart | 5 +- .../Start/safeCheck_start_detail.dart | 5 +- .../company_safety_commitment_apply.dart | 1 + .../safety_commitment_page.dart | 26 +- .../team_safety_commitment_apply.dart | 1 + .../work_shop_safety_commitment_apply.dart | 1 + .../safety_meeting_detail_page.dart | 1 + lib/pages/home/home_page.dart | 28 +- lib/pages/home/study/study_my_task_page.dart | 3 + lib/pages/home/tap/item_list_widget.dart | 46 ++- .../special_wrok/dangerous_options_page.dart | 4 +- .../dh_work/HotWorkDetailFormWidget.dart | 170 ++++++-- .../hotwork_safe_func_sure.dart | 201 ++++----- .../aqgl_work_detail/hotwork_aqgl_detail.dart | 7 +- .../aqjd_work_detail/hotwork_aqjd_detail.dart | 7 +- .../dbbz_work_detail/hotwork_dbbz_detail.dart | 7 +- .../dh_work_detai/hotwork_apply_detail.dart | 358 ++++++++++++---- .../dhsp_work_detail/hotwork_dhsp_detail.dart | 7 +- .../dh_work/hotwork_list_page.dart | 30 +- .../jhr_work_detail/hotwork_jhr_detail.dart | 7 +- .../jsjd_work_detail/hotwork_jsjd_detail.dart | 7 +- .../jszy_work_detail/hotwork_jszy_detail.dart | 7 +- .../kszy_work_detail/hotwork_kszy_detail.dart | 7 +- .../hotwork_set_safe_detail.dart | 105 +++-- .../szdw_work_detail/hotwork_szdw_detail.dart | 7 +- .../ysgd_work_detail/hotwork_ysgd_detail.dart | 7 +- .../zyfz_work_detail/hotwork_zyfz_detail.dart | 7 +- .../dl_work/CutroadDetailFormWidget.dart | 98 ++++- .../cutroad_safe_func_sure.dart | 19 +- .../aqjd_work_detail/cutroad_aqjd_detail.dart | 7 +- .../dl_work/cutroad_list_page.dart | 11 +- .../dl_work_detai/cutroad_apply_detail.dart | 194 ++++++++- .../jhr_work_detail/cutroad_jhr_detail.dart | 7 +- .../jsjd_work_detail/cutroad_jsjd_detail.dart | 7 +- .../jszy_work_detail/cutroad_jszy_detail.dart | 26 +- .../kszy_work_detail/cutroad_kszy_detail.dart | 7 +- .../shbm_work_detail/cutroad_shbm_detail.dart | 7 +- .../spbm_work_detail/cutroad_spbm_detail.dart | 7 +- .../cutroad_set_safe_detail.dart | 7 +- .../szdw_work_detail/cutroad_szdw_detail.dart | 7 +- .../ysgd_work_detail/cutroad_ysgd_detail.dart | 7 +- .../zyfz_work_detail/cutroad_zyfz_detail.dart | 7 +- .../zyr_work_detail/cutroad_zyr_detail.dart | 7 +- .../dt_work/BreakgroundDetailFormWidget.dart | 98 ++++- .../breakground_safe_func_sure.dart | 10 +- .../breakground_aqjd_detail.dart | 7 +- .../dt_work/breakground_list_page.dart | 7 + .../breakground_apply_detail.dart | 188 ++++++++- .../breakground_dzzh_detail.dart | 7 +- .../breakground_jhr_detail.dart | 5 +- .../breakground_jsjd_detail.dart | 7 +- .../breakground_jszy_detail.dart | 26 +- .../breakground_kszy_detail.dart | 7 +- .../breakground_shbm_detail.dart | 7 +- .../breakground_spbm_detail.dart | 7 +- .../breakground_ssr_detail.dart | 7 +- .../breakground_set_safe_detail.dart | 5 +- .../breakground_szdw_detail.dart | 7 +- .../breakground_ysgd_detail.dart | 7 +- .../breakground_zyfz_detail.dart | 7 +- .../breakground_zyr_detail.dart | 7 +- .../dz_work/HoistworkDetailFormWidget.dart | 97 ++++- .../hoistwork_safe_func_sure.dart | 8 +- .../hoistwork_aqjd_detail.dart | 7 +- .../dz_work_detai/hoistwork_apply_detail.dart | 189 ++++++++- .../hoistwork_dzzh_detail.dart | 7 +- .../dz_work/hoistwork_list_page.dart | 8 +- .../jhr_work_detail/hoistwork_jhr_detail.dart | 7 +- .../hoistwork_jsjd_detail.dart | 7 +- .../hoistwork_jszy_detail.dart | 26 +- .../hoistwork_kszy_detail.dart | 7 +- .../hoistwork_shbm_detail.dart | 7 +- .../hoistwork_spbm_detail.dart | 7 +- .../ssr_work_detail/hoistwork_ssr_detail.dart | 7 +- .../hoistwork_set_safe_detail.dart | 7 +- .../hoistwork_szdw_detail.dart | 7 +- .../hoistwork_ysgd_detail.dart | 7 +- .../hoistwork_zyfz_detail.dart | 7 +- .../zyr_work_detail/hoistwork_zyr_detail.dart | 7 +- .../gc_work/HighWorkDetailFormWidget.dart | 100 ++++- .../highwork_safe_func_sure.dart | 19 +- .../highwork_aqjd_detail.dart | 7 +- .../gc_work_detai/highwork_apply_detail.dart | 199 ++++++++- .../gc_work/highwork_list_page.dart | 7 + .../jhr_work_detail/highwork_jhr_detail.dart | 7 +- .../highwork_jsjd_detail.dart | 7 +- .../highwork_jszy_detail.dart | 26 +- .../highwork_kszy_detail.dart | 7 +- .../highwork_shbm_detail.dart | 7 +- .../highwork_spbm_detail.dart | 7 +- .../highwork_set_safe_detail.dart | 7 +- .../highwork_szdw_detail.dart | 7 +- .../highwork_ysgd_detail.dart | 5 +- .../highwork_zyfz_detail.dart | 7 +- .../zyr_work_detail/highwork_zyr_detail.dart | 7 +- .../special_wrok/home_gas_test_page.dart | 5 +- .../ElectricityDetailFormWidget.dart | 99 ++++- .../electricity_safe_func_sure.dart | 19 +- .../electricity_aqjd_detail.dart | 7 +- .../electricity_dbbz_detail.dart | 7 +- .../lsyd_work/electricity_list_page.dart | 7 + .../electricity_jhr_detail.dart | 7 +- .../electricity_jsjd_detail.dart | 7 +- .../electricity_jszy_detail.dart | 26 +- .../electricity_kszy_detail.dart | 7 +- .../electricity_apply_detail.dart | 187 ++++++++- .../electricity_psdw_detail.dart | 7 +- .../electricity_gas_test_page.dart | 5 +- .../electricity_set_safe_detail.dart | 7 +- .../electricity_yddw_detail.dart | 7 +- .../electricity_ydr_detail.dart | 7 +- .../electricity_ysgd_detail.dart | 7 +- .../electricity_zyfz_detail.dart | 7 +- .../electricity_zyr_detail.dart | 7 +- .../mbcd_work/BlindboardDetailFormWidget.dart | 98 ++++- .../blindboard_safe_func_sure.dart | 19 +- .../blindboard_aqjd_detail.dart | 7 +- .../mbcd_work/blindboard_list_page.dart | 12 +- .../blindboard_cjry_detail.dart | 7 +- .../blindboard_jhr_detail.dart | 7 +- .../blindboard_jsjd_detail.dart | 7 +- .../blindboard_jszy_detail.dart | 26 +- .../blindboard_kszy_detail.dart | 7 +- .../blindboard_apply_detail.dart | 199 ++++++++- .../blindboard_shbm_detail.dart | 7 +- .../blindboard_spbm_detail.dart | 7 +- .../blindboard_set_safe_detail.dart | 7 +- .../blindboard_szdw_detail.dart | 7 +- .../blindboard_ysgd_detail.dart | 7 +- .../blindboard_zyfz_detail.dart | 7 +- .../blindboard_zyr_detail.dart | 7 +- .../sxkj_work/SpaceWorkDetailFormWidget.dart | 99 ++++- .../spacework_safe_func_sure.dart | 7 +- .../spacework_aqgl_detail.dart | 7 +- .../spacework_aqjd_detail.dart | 7 +- .../spacework_dbbz_detail.dart | 7 +- .../spacework_dhsp_detail.dart | 7 +- .../jhr_work_detail/spacework_jhr_detail.dart | 7 +- .../spacework_jsjd_detail.dart | 7 +- .../spacework_jszy_detail.dart | 26 +- .../spacework_kszy_detail.dart | 7 +- .../spacework_gas_test_page.dart | 5 +- .../spacework_apply_detail.dart | 191 ++++++++- .../sxkj_work/spacework_list_page.dart | 7 + .../spacework_set_safe_detail.dart | 5 +- .../spacework_szdw_detail.dart | 7 +- .../spacework_ysgd_detail.dart | 7 +- .../spacework_zyfz_detail.dart | 7 +- .../zyr_work_detail/spacework_zyr_detail.dart | 6 +- .../special_wrok/work_area_helper.dart | 50 +++ .../home/tap/tabList/work_tab_mbcd_list.dart | 5 + lib/pages/home/tap/workArea_picker.dart | 287 +++++++++++++ lib/pages/login_page.dart | 16 +- lib/pages/mine/mine_first_sign_page.dart | 12 +- lib/pages/mine/mine_set_page.dart | 2 +- lib/{tools => services}/StorageService.dart | 0 lib/{tools => services}/auth_service.dart | 1 - lib/services/location_service.dart | 71 ++++ lib/services/update_service.dart | 168 ++++++++ lib/tools/tools.dart | 49 +++ lib/tools/update/update_dialogs.dart | 188 +++++++++ pubspec.lock | 4 +- pubspec.yaml | 2 +- 187 files changed, 5598 insertions(+), 1428 deletions(-) create mode 100644 android/app/src/main/res/xml/file_paths.xml create mode 100644 assets/map/test_baidu_map.html create mode 100644 lib/customWidget/BaiDuMap/Map_page.dart create mode 100644 lib/customWidget/promise/promise_page.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/work_area_helper.dart create mode 100644 lib/pages/home/tap/workArea_picker.dart rename lib/{tools => services}/StorageService.dart (100%) rename lib/{tools => services}/auth_service.dart (99%) create mode 100644 lib/services/location_service.dart create mode 100644 lib/services/update_service.dart create mode 100644 lib/tools/update/update_dialogs.dart diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 29d1b9a..3f71f7d 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -30,7 +30,7 @@ android { defaultConfig { applicationId = "com.zhuoyun.qhdprevention.qhd_prevention" - minSdk = flutter.minSdkVersion + minSdk = 22 targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ff7cb19..d3fe771 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,11 +1,12 @@ - - + + + - + - - + @@ -19,57 +20,56 @@ - - - - + - - - + + + - + + + + - - - - - + + android:enableOnBackInvokedCallback="true"> + - + + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" /> + - + + + + + - + + + + diff --git a/android/app/src/main/kotlin/com/zhuoyun/qhdprevention/qhd_prevention/MainActivity.kt b/android/app/src/main/kotlin/com/zhuoyun/qhdprevention/qhd_prevention/MainActivity.kt index 4a716d1..73d2ef5 100644 --- a/android/app/src/main/kotlin/com/zhuoyun/qhdprevention/qhd_prevention/MainActivity.kt +++ b/android/app/src/main/kotlin/com/zhuoyun/qhdprevention/qhd_prevention/MainActivity.kt @@ -1,5 +1,60 @@ package com.zhuoyun.qhdprevention.qhd_prevention - +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import androidx.core.content.FileProvider import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel +import java.io.File -class MainActivity : FlutterActivity() +class MainActivity: FlutterActivity() { + private val CHANNEL = "app.install" + private val REQ_INSTALL_UNKNOWN = 9999 + + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> + when (call.method) { + "installApk" -> { + val path = call.argument("path") + if (path == null) { + result.error("NO_PATH", "no path provided", null) + return@setMethodCallHandler + } + installApk(path, result) + } + else -> result.notImplemented() + } + } + } + + private fun installApk(path: String, result: MethodChannel.Result) { + val file = File(path) + if (!file.exists()) { + result.error("NO_FILE", "file not exist", null) + return + } + + // Android 8.0+ 需要允许安装未知来源 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (!packageManager.canRequestPackageInstalls()) { + // 引导用户去设置允许安装未知来源(你可以在 Flutter 侧提示用户) + val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:$packageName")) + startActivity(intent) + result.error("NEED_INSTALL_PERMISSION", "need install permission", null) + return + } + } + + val apkUri: Uri = FileProvider.getUriForFile(this, "$packageName.fileprovider", file) + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(apkUri, "application/vnd.android.package-archive") + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + startActivity(intent) + result.success(true) + } +} diff --git a/android/app/src/main/res/xml/file_paths.xml b/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..80def29 --- /dev/null +++ b/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties index f018a61..24863d2 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,3 @@ org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true -android.enableJetifier=true +android.enableJetifier=true \ No newline at end of file diff --git a/assets/map/test_baidu_map.html b/assets/map/test_baidu_map.html new file mode 100644 index 0000000..bd5fd19 --- /dev/null +++ b/assets/map/test_baidu_map.html @@ -0,0 +1,380 @@ + + + + + 特殊作业扎点 + + + + + + + + + + +
+ + + + + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b225011..d15ed44 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,9 +1,21 @@ PODS: + - BaiduMapKit/Base (6.6.4) + - BaiduMapKit/Map (6.6.4): + - BaiduMapKit/Base + - BaiduMapKit/Utils (6.6.4): + - BaiduMapKit/Base - camera_avfoundation (0.0.1): - Flutter - connectivity_plus (0.0.1): - Flutter - Flutter (1.0.0) + - flutter_baidu_mapapi_base (3.9.0): + - BaiduMapKit/Utils (= 6.6.4) + - Flutter + - flutter_baidu_mapapi_map (3.9.0): + - BaiduMapKit/Map (= 6.6.4) + - Flutter + - flutter_baidu_mapapi_base - flutter_new_badger (0.0.1): - Flutter - fluttertoast (0.0.2): @@ -46,6 +58,8 @@ DEPENDENCIES: - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - Flutter (from `Flutter`) + - flutter_baidu_mapapi_base (from `.symlinks/plugins/flutter_baidu_mapapi_base/ios`) + - flutter_baidu_mapapi_map (from `.symlinks/plugins/flutter_baidu_mapapi_map/ios`) - flutter_new_badger (from `.symlinks/plugins/flutter_new_badger/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`) @@ -62,6 +76,10 @@ DEPENDENCIES: - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`) +SPEC REPOS: + trunk: + - BaiduMapKit + EXTERNAL SOURCES: camera_avfoundation: :path: ".symlinks/plugins/camera_avfoundation/ios" @@ -69,6 +87,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/connectivity_plus/ios" Flutter: :path: Flutter + flutter_baidu_mapapi_base: + :path: ".symlinks/plugins/flutter_baidu_mapapi_base/ios" + flutter_baidu_mapapi_map: + :path: ".symlinks/plugins/flutter_baidu_mapapi_map/ios" flutter_new_badger: :path: ".symlinks/plugins/flutter_new_badger/ios" fluttertoast: @@ -101,9 +123,12 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" SPEC CHECKSUMS: + BaiduMapKit: 84991811cb07b24c6ead7d59022c13245427782c camera_avfoundation: be3be85408cd4126f250386828e9b1dfa40ab436 connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_baidu_mapapi_base: 24dd82034374c6f52a73e90316834c63ff8d4f64 + flutter_baidu_mapapi_map: f799cc1bb3d39196b8d3d59399ca8635e690bd44 flutter_new_badger: 133aaf93e9a5542bf905c8483d8b83c5ef4946ea fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 85c1dbd..a6ba6fd 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -200,6 +200,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 9D4AB6A98949D5BF69896B41 /* [CP] Embed Pods Frameworks */, + 278BCE4BA5CD53A70DAEC00A /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -271,6 +272,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 278BCE4BA5CD53A70DAEC00A /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 59bf2ac..764ad24 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,14 +1,77 @@ -import Flutter import UIKit +import Flutter @main @objc class AppDelegate: FlutterAppDelegate { + // 动态方向掩码(默认竖屏) + static var orientationMask: UIInterfaceOrientationMask = .portrait + override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - + + let controller = window?.rootViewController as! FlutterViewController + let channel = FlutterMethodChannel(name: "app.orientation", + binaryMessenger: controller.binaryMessenger) + + channel.setMethodCallHandler { [weak self] call, result in + guard let self = self else { return } + + if call.method == "setOrientation" { + guard let arg = call.arguments as? String else { + result(FlutterError(code: "BAD_ARGS", message: "need 'landscape' | 'portrait'", details: nil)) + return + } + + // 先更新允许的方向掩码 + if arg == "landscape" { + AppDelegate.orientationMask = .landscape + } else if arg == "portrait" { + AppDelegate.orientationMask = .portrait + } else { + result(FlutterError(code: "BAD_ARGS", message: "unknown arg", details: nil)) + return + } + + + // 再请求实际旋转 + if #available(iOS 16.0, *) { + // 通知顶层 VC:其 supportedInterfaceOrientations 需要刷新 + self.window?.rootViewController?.setNeedsUpdateOfSupportedInterfaceOrientations() + + if let scene = self.window?.windowScene { + let orientations: UIInterfaceOrientationMask = + (arg == "landscape") ? .landscape : .portrait + do { + try scene.requestGeometryUpdate(.iOS(interfaceOrientations: orientations)) + } catch { + result(FlutterError(code: "GEOMETRY_UPDATE_FAILED", + message: error.localizedDescription, details: nil)) + return + } + } + } else { + let target: UIInterfaceOrientation = + (arg == "landscape") ? .landscapeLeft : .portrait + UIDevice.current.setValue(target.rawValue, forKey: "orientation") + UIViewController.attemptRotationToDeviceOrientation() + } + + result(true) + } else { + result(FlutterMethodNotImplemented) + } + } + GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + // 关键:把当前的掩码提供给系统 + override func application(_ application: UIApplication, + supportedInterfaceOrientationsFor window: UIWindow?) + -> UIInterfaceOrientationMask { + return AppDelegate.orientationMask + } } diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index b55c16f..cc11b8b 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -71,12 +71,17 @@ Main UISupportedInterfaceOrientations - UIInterfaceOrientationPortrait - + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight diff --git a/lib/customWidget/BaiDuMap/BaiduMapWebView.dart b/lib/customWidget/BaiDuMap/BaiduMapWebView.dart index e17039a..1287f44 100644 --- a/lib/customWidget/BaiDuMap/BaiduMapWebView.dart +++ b/lib/customWidget/BaiDuMap/BaiduMapWebView.dart @@ -1,342 +1,71 @@ -// baidu_map_webview_debug.dart -import 'dart:async'; -import 'dart:convert'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:webview_flutter/webview_flutter.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; -/// 更健壮的 BaiduMapWebView(带 JS 错误回传、ready 检测、超时/重试) -class BaiduMapWebView extends StatefulWidget { - final String ak; // 请确保这是 web (JS API) 可用的 AK - final double latitude; - final double longitude; - final int zoom; // 对应 uniapp 的 scale - final List> covers; - final ValueChanged>? onMarkerTap; - final Duration readyTimeout; +/// 可复用的地图 WebView 组件 +/// BaiduMapWebView( +/// controller: _controller, +/// isLoading: _isLoading, +/// errorMessage: _errorMessage, +/// onRetry: _initLocation, +/// ) +/// ``` +class BaiduMapWebView extends StatelessWidget { + final WebViewController? controller; + final bool isLoading; + final String? errorMessage; + final VoidCallback onRetry; const BaiduMapWebView({ - Key? key, - required this.ak, - required this.latitude, - required this.longitude, - this.zoom = 13, - this.covers = const [], - this.onMarkerTap, - this.readyTimeout = const Duration(seconds: 8), - }) : super(key: key); - - @override - State createState() => _BaiduMapWebViewState(); -} - -class _BaiduMapWebViewState extends State { - late final WebViewController _controller; - bool _pageLoaded = false; - bool _mapReady = false; - String? _lastJsMessage; - Timer? _readyTimer; - bool _showError = false; - String _errorText = ''; - - String _html(String ak) { - // HTML 模板:创建 map,定义 setCenter / setMarkersFromFlutter,转发 console 与 onerror - return ''' - - - - - - - - -
- - - - -'''; - } - - String _escapeForJs(String s) => s.replaceAll(r'\', r'\\').replaceAll("'", r"\\'").replaceAll('\n', r' '); - - @override - void initState() { - super.initState(); - - _controller = WebViewController() - ..setJavaScriptMode(JavaScriptMode.unrestricted) - ..addJavaScriptChannel('Bridge', onMessageReceived: _onJsMessage) - ..setNavigationDelegate(NavigationDelegate( - onPageFinished: (url) { - _pageLoaded = true; - _startReadyTimer(); - // inject initial center & markers AFTER slight delay to allow JS to define functions - Future.delayed(const Duration(milliseconds: 300), () async { - await _controller.runJavaScript('setCenter(${widget.latitude}, ${widget.longitude}, ${widget.zoom});'); - final coversJson = jsonEncode(widget.covers); - await _controller.runJavaScript("setMarkersFromFlutter('${_escapeForJs(coversJson)}');"); - }); - }, - onWebResourceError: (err) { - _reportError('WebResourceError: ${err.description}'); - }, - )) - ..loadHtmlString(_html(widget.ak)); - } - - void _startReadyTimer() { - _readyTimer?.cancel(); - _readyTimer = Timer(widget.readyTimeout, () { - if (!mounted) return; - if (!_mapReady) { - setState(() { - _showError = true; - _errorText = '地图初始化超时,可能 AK 无效或网络受限。请检查 AK(需为百度 JS API Key)与网络。'; - }); - } - }); - } - - void _onJsMessage(JavaScriptMessage msg) { - _lastJsMessage = msg.message; - // 解析 message - try { - final m = jsonDecode(msg.message); - if (m is Map && m.containsKey('__mapReady') && m['__mapReady'] == true) { - // JS 报告地图已 ready - _readyTimer?.cancel(); - if (mounted) { - setState(() { - _mapReady = true; - _showError = false; - _errorText = ''; - }); - } - return; - } - } catch (_) { - // not json meta, maybe marker data or console log - } - - // handle console / error wrapper shape: {__bridge:true, kind:..., msg:...} - try { - final decoded = jsonDecode(msg.message); - if (decoded is Map && decoded['__bridge'] == true) { - final kind = decoded['kind']; - final mm = decoded['msg']; - if (kind == 'error' || kind == 'onerror') { - _reportError('JS error: $mm'); - } else { - // console log: keep last message (debug only) - debugPrint('JS log: $mm'); - } - return; - } - } catch (_) {} - - // otherwise treat as marker click payload (likely non-meta JSON) - try { - final payload = jsonDecode(msg.message) as Map; - if (widget.onMarkerTap != null) widget.onMarkerTap!(payload); - } catch (e) { - // not JSON? ignore - debugPrint('Unrecognized JS message: ${msg.message}'); - } - } - - void _reportError(String text) { - debugPrint(text); - if (!mounted) return; - setState(() { - _showError = true; - _errorText = text; - }); - } - - @override - void didUpdateWidget(covariant BaiduMapWebView oldWidget) { - super.didUpdateWidget(oldWidget); - if (_pageLoaded) { - if (oldWidget.latitude != widget.latitude || - oldWidget.longitude != widget.longitude || - oldWidget.zoom != widget.zoom) { - _controller.runJavaScript('setCenter(${widget.latitude}, ${widget.longitude}, ${widget.zoom});'); - } - if (!listEquals(oldWidget.covers, widget.covers)) { - final coversJson = jsonEncode(widget.covers); - _controller.runJavaScript("setMarkersFromFlutter('${_escapeForJs(coversJson)}');"); - } - } - } - - @override - void dispose() { - _readyTimer?.cancel(); - super.dispose(); - } + super.key, + required this.controller, + required this.isLoading, + required this.errorMessage, + required this.onRetry, + }); @override Widget build(BuildContext context) { - return Stack( - children: [ - WebViewWidget(controller: _controller), - if (!_mapReady && !_showError) - const Center(child: CircularProgressIndicator()), - if (_showError) - Positioned.fill( - child: Container( - color: Colors.white70, - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('地图加载失败', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), - const SizedBox(height: 8), - Text(_errorText, textAlign: TextAlign.center), - const SizedBox(height: 12), - ElevatedButton( - onPressed: () { - setState(() { - _showError = false; - _mapReady = false; - }); - // 重新加载页面 - _controller.loadHtmlString(_html(widget.ak)); - }, - child: const Text('重试'), - ), - const SizedBox(height: 6), - ElevatedButton( - onPressed: () { - // 把最后一条 js message 展示出来,便于你贴错误给我 - showDialog( - context: context, - builder: (_) => AlertDialog( - title: const Text('JS 消息'), - content: SingleChildScrollView(child: Text(_lastJsMessage ?? '无')), - actions: [ - TextButton(onPressed: () => Navigator.pop(context), child: const Text('关闭')), - ], - ), - ); - }, - child: const Text('查看 JS 日志'), - ), - ], - ), - ), - ), + if (errorMessage != null) { + return Center( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + const Icon(Icons.error_outline, color: Colors.red, size: 50), + const SizedBox(height: 16), + Text( + errorMessage!, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16), ), - ), - ], - ); + const SizedBox(height: 24), + CustomButton(text: '重试', backgroundColor: Colors.blue, onPressed: onRetry) + ]), + ), + ); + } + + if (isLoading) { + return const Center(child: CircularProgressIndicator()); + } + + // 非加载且无错误时,controller 应该存在;若为空则显示提示 + if (controller == null) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.map_outlined, size: 48), + const SizedBox(height: 8), + const Text('地图未初始化'), + const SizedBox(height: 12), + ElevatedButton(onPressed: onRetry, child: const Text('重试初始化')), + ], + ), + ); + } + + return WebViewWidget(controller: controller!); } } diff --git a/lib/customWidget/BaiDuMap/Map_page.dart b/lib/customWidget/BaiDuMap/Map_page.dart new file mode 100644 index 0000000..dea67bf --- /dev/null +++ b/lib/customWidget/BaiDuMap/Map_page.dart @@ -0,0 +1,203 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/BaiDuMap/BaiduMapWebView.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/services/location_service.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:geolocator/geolocator.dart'; + +class MapPage extends StatefulWidget { + final String gson; + + const MapPage({super.key, required this.gson}); + + @override + State createState() => _MapPageState(); +} + +class _MapPageState extends State { + late final WebViewController _controller; + bool _isLoading = true; + String? _errorMessage; + double? _longitude; + double? _latitude; + List _gsonList = []; + late Map mapData = {}; + @override + void initState() { + super.initState(); + _initLocation(); + } + + /// 获取定位(并初始化 WebView 控制器) + Future _initLocation() async { + setState(() { + _isLoading = true; + _errorMessage = null; + }); + + try { + final LocationResult loc = await LocationService.getCurrentLocation( + timeout: const Duration(seconds: 10), + ); + + // 解析 gson + try { + final parsed = jsonDecode(widget.gson); + if (parsed is List) { + _gsonList = parsed; + } else { + _gsonList = []; + } + } catch (e) { + debugPrint('解析 gson 失败: $e'); + _gsonList = []; + } + + if (!mounted) return; + + setState(() { + _longitude = loc.longitudeAsDouble; + _latitude = loc.latitudeAsDouble; + }); + + + // 初始化 WebViewController 并加载本地页面 + _controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + // 注册 JS 通道 'JS' —— 对应 HTML 中的 window.JS.postMessage(...) + ..addJavaScriptChannel('JS', onMessageReceived: (dynamic message) { + // message 是动态对象,不在签名中引用具体类型以避免 "Undefined class" 问题 + String payload; + try { + payload = message.message ?? message.toString(); + } catch (e) { + payload = message.toString(); + } + _onJsMessage(payload); + }) + // 也保留一个备用通道 'Flutter' + ..addJavaScriptChannel('Flutter', onMessageReceived: (dynamic message) { + String payload; + try { + payload = message.message ?? message.toString(); + } catch (e) { + payload = message.toString(); + } + _onJsMessage(payload); + }) + ..setNavigationDelegate( + NavigationDelegate( + onPageFinished: (String url) async { + debugPrint('网页加载完成: $url'); + await _injectLocationParams(); + }, + onWebResourceError: (err) { + debugPrint('Web resource error: ${err.description}'); + }, + ), + ); + + // 加载本地 assets 中的 HTML + // await _controller.loadFlutterAsset('assets/map/test_baidu_map.html'); + await _controller.loadRequest(Uri.parse('http://47.92.102.56:7811/file/fluteightmap/index.html')); + + setState(() { + _isLoading = false; + }); + } catch (e, st) { + debugPrint('获取位置或初始化失败: $e\n$st'); + if (!mounted) return; + setState(() { + _errorMessage = '获取位置失败: ${e.toString()}'; + _isLoading = false; + }); + } + } + + /// 注入参数并调用页面初始化函数 window.initWithData(...) + Future _injectLocationParams() async { + if (_longitude == null || _latitude == null) { + debugPrint('位置尚未准备好,跳过注入'); + return; + } + + final params = { + 'longitude': _longitude, + 'latitude': _latitude, + 'GSON': _gsonList, + 't': DateTime.now().millisecondsSinceEpoch, + }; + + final jsonParams = jsonEncode(params); + + try { + await _controller.runJavaScript(''' + (function(){ + try { + if (typeof window.initWithData === 'function') { + window.initWithData($jsonParams); + } else if (typeof window.initMap === 'function') { + window.initMap($jsonParams); + } else { + console.error('initWithData / initMap function not found'); + } + } catch(e) { + console.error('call initWithData error', e); + } + })(); + '''); + debugPrint('已注入地图初始化参数'); + } catch (e) { + debugPrint('注入位置参数失败: $e'); + } + } + + /// 处理来自 Web 的消息(字符串或 JSON) + void _onJsMessage(String message) { + debugPrint('收到来自 Web 的消息: $message'); + if (message.isEmpty) return; + try { + Map data = jsonDecode(message); + if (FormUtils.hasValue(data, 'ok') && data['ok'] == true) { + }else{ + if (FormUtils.hasValue(data, 'type') && data['type'] == 'converted') { + setState(() { + mapData = data; + }); + }else{ + ToastUtil.showNormal(context, '当前选择点位不在区域中'); + setState(() { + mapData = {}; + }); + } + } + } catch (_) { + setState(() { + mapData = {}; + }); + ToastUtil.showNormal(context, '当前选择点位不在区域中'); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '地图选择', actions: [ + if (mapData.isNotEmpty) + TextButton(onPressed: (){ + Navigator.of(context).pop((mapData)); + }, child: Text('确定', style: TextStyle(color: Colors.white, fontSize: 17),)) + ],), + body: SafeArea( + child: BaiduMapWebView( + controller: _isLoading || _errorMessage != null ? null : _controller, + isLoading: _isLoading, + errorMessage: _errorMessage, + onRetry: _initLocation, + ),), + ); + } +} diff --git a/lib/customWidget/ItemWidgetFactory.dart b/lib/customWidget/ItemWidgetFactory.dart index 7cf4881..09b1028 100644 --- a/lib/customWidget/ItemWidgetFactory.dart +++ b/lib/customWidget/ItemWidgetFactory.dart @@ -319,7 +319,7 @@ class ListItemFactory { Text(yesLabel), ], ), - const SizedBox(width: 16), + const SizedBox(width: 5), Row( children: [ Radio( diff --git a/lib/customWidget/custom_alert_dialog.dart b/lib/customWidget/custom_alert_dialog.dart index 8b08150..0cf204d 100644 --- a/lib/customWidget/custom_alert_dialog.dart +++ b/lib/customWidget/custom_alert_dialog.dart @@ -14,6 +14,9 @@ class CustomAlertDialog extends StatefulWidget { final ValueChanged? onInputConfirm; // 输入模式回调 final DialogMode mode; // 对话框模式 + /// 如果 force 为 true:弹窗为强制模式(不可点背景关闭,不响应返回键,并只显示确定按钮) + final bool force; + const CustomAlertDialog({ Key? key, required this.title, @@ -25,6 +28,7 @@ class CustomAlertDialog extends StatefulWidget { this.onConfirm, this.onInputConfirm, this.mode = DialogMode.text, + this.force = false, }) : super(key: key); @override @@ -42,11 +46,12 @@ class CustomAlertDialog extends StatefulWidget { String confirmText = '确定', bool barrierDismissible = true, final VoidCallback? onConfirm, - + bool force = false, // 新参数:强制模式 }) async { final result = await showDialog( context: context, - barrierDismissible: barrierDismissible, + // force 优先控制是否可通过点击蒙层关闭 + barrierDismissible: force ? false : barrierDismissible, builder: (_) { return CustomAlertDialog( title: title, @@ -55,6 +60,7 @@ class CustomAlertDialog extends StatefulWidget { confirmText: confirmText, mode: DialogMode.text, onConfirm: onConfirm, + force: force, ); }, ); @@ -69,11 +75,11 @@ class CustomAlertDialog extends StatefulWidget { String confirmText = '确定', bool barrierDismissible = true, final VoidCallback? onConfirm, - + bool force = false, // 新参数:强制模式 }) async { await showDialog( context: context, - barrierDismissible: barrierDismissible, + barrierDismissible: force ? false : barrierDismissible, builder: (_) { return CustomAlertDialog( title: title, @@ -82,7 +88,7 @@ class CustomAlertDialog extends StatefulWidget { confirmText: confirmText, mode: DialogMode.text, onConfirm: onConfirm, - + force: force, ); }, ); @@ -96,10 +102,11 @@ class CustomAlertDialog extends StatefulWidget { String cancelText = '取消', String confirmText = '确定', bool barrierDismissible = true, + bool force = false, // 新参数:强制模式(会隐藏取消并禁止关闭) }) async { final result = await showDialog( context: context, - barrierDismissible: barrierDismissible, + barrierDismissible: force ? false : barrierDismissible, builder: (_) { return CustomAlertDialog( title: title, @@ -107,6 +114,7 @@ class CustomAlertDialog extends StatefulWidget { cancelText: cancelText, confirmText: confirmText, mode: DialogMode.input, + force: force, ); }, ); @@ -130,69 +138,83 @@ class _CustomAlertDialogState extends State { super.dispose(); } - bool get hasCancel => widget.cancelText.trim().isNotEmpty; + /// 当 force 为 true 时,始终隐藏取消按钮 + bool get hasCancel => !widget.force && widget.cancelText.trim().isNotEmpty; + + /// 统一关闭 dialog 的方法(带安全 mount 检查) + void _closeDialog([dynamic result]) { + if (!mounted) return; + Navigator.of(context).pop(result); + } @override Widget build(BuildContext context) { - return Dialog( - backgroundColor: Colors.transparent, - child: Container( - constraints: const BoxConstraints(minWidth: 280), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Text( - widget.title, - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - textAlign: TextAlign.center, - ), - ), - const SizedBox(height: 16), - - // ★ 根据 mode 决定展示文字还是输入框 ★ - if (widget.mode == DialogMode.text) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Text( - widget.content, - style: const TextStyle(fontSize: 16, color: Colors.black54), - textAlign: TextAlign.center, - ), - ) - else + // 使用 WillPopScope 禁止返回键关闭(当 force 为 true) + return WillPopScope( + onWillPop: () async { + // 如果是强制模式,禁止返回(返回 false);否则允许(true) + return !widget.force; + }, + child: Dialog( + backgroundColor: Colors.transparent, + child: Container( + constraints: const BoxConstraints(minWidth: 280), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 20), - 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, + child: Text( + widget.title, + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 16), + + // ★ 根据 mode 决定展示文字还是输入框 ★ + if (widget.mode == DialogMode.text) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Text( + widget.content, + style: const TextStyle(fontSize: 16, color: Colors.black54), + textAlign: TextAlign.center, + ), + ) + else + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + 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(height: 20), - const Divider(height: 1), + const SizedBox(height: 20), + const Divider(height: 1), - hasCancel ? _buildDoubleButtons(context) : _buildSingleButton(context), - ], + hasCancel ? _buildDoubleButtons(context) : _buildSingleButton(context), + ], + ), ), ), ); @@ -209,7 +231,7 @@ class _CustomAlertDialogState extends State { final ret = widget.mode == DialogMode.text ? false : null; // 先触发回调(如果开发者传了),再关闭并把结果返回给调用者 widget.onCancel?.call(); - Navigator.of(context).pop(ret); + _closeDialog(ret); }, child: Container( padding: const EdgeInsets.symmetric(vertical: 12), @@ -234,11 +256,11 @@ class _CustomAlertDialogState extends State { onTap: () { if (widget.mode == DialogMode.text) { widget.onConfirm?.call(); - Navigator.of(context).pop(true); + _closeDialog(true); } else { final value = _controller.text.trim(); widget.onInputConfirm?.call(value); - Navigator.of(context).pop(value); + _closeDialog(value); } }, child: Container( @@ -264,11 +286,11 @@ class _CustomAlertDialogState extends State { onTap: () { if (widget.mode == DialogMode.text) { widget.onConfirm?.call(); - Navigator.of(context).pop(true); + _closeDialog(true); } else { final value = _controller.text.trim(); widget.onInputConfirm?.call(value); - Navigator.of(context).pop(value); + _closeDialog(value); } }, child: Container( diff --git a/lib/customWidget/department_picker.dart b/lib/customWidget/department_picker.dart index 35b637c..1cfcb87 100644 --- a/lib/customWidget/department_picker.dart +++ b/lib/customWidget/department_picker.dart @@ -79,6 +79,7 @@ class _DepartmentPickerState extends State { final String nodes = result['zTreeNodes'] as String; SessionService.instance.departmentJsonStr = nodes; raw = json.decode(nodes) as List; + } setState(() { original = raw.map((e) => Category.fromJson(e as Map)).toList(); diff --git a/lib/customWidget/picker/CupertinoDatePicker.dart b/lib/customWidget/picker/CupertinoDatePicker.dart index e90c067..4d1b2d3 100644 --- a/lib/customWidget/picker/CupertinoDatePicker.dart +++ b/lib/customWidget/picker/CupertinoDatePicker.dart @@ -5,12 +5,17 @@ import 'package:flutter/material.dart'; /// DateTime? picked = await BottomDateTimePicker.showDate( /// context, /// allowFuture: true, // 添加此参数允许选择未来时间 +/// minTimeStr: '2025-08-20 08:30', // 可选:不允许选择早于此时间 /// ); /// if (picked != null) { /// print('用户选择的时间:$picked'); /// } class BottomDateTimePicker { - static Future showDate(BuildContext context, {bool allowFuture = false}) { + static Future showDate( + BuildContext context, { + bool allowFuture = false, + String? minTimeStr, // 新增:可选起始时间格式 'yyyy-MM-dd HH:mm' + }) { return showModalBottomSheet( context: context, backgroundColor: Colors.white, @@ -18,21 +23,31 @@ class BottomDateTimePicker { shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(12)), ), - builder: (_) => _InlineDateTimePickerContent(allowFuture: allowFuture), + builder: (_) => _InlineDateTimePickerContent( + allowFuture: allowFuture, + minTimeStr: minTimeStr, + ), ); } } class _InlineDateTimePickerContent extends StatefulWidget { final bool allowFuture; // 允许未来 + final String? minTimeStr; // 新增:最小允许时间字符串 'yyyy-MM-dd HH:mm' - const _InlineDateTimePickerContent({Key? key, this.allowFuture = false}) : super(key: key); + const _InlineDateTimePickerContent({ + Key? key, + this.allowFuture = false, + this.minTimeStr, + }) : super(key: key); @override - State<_InlineDateTimePickerContent> createState() => _InlineDateTimePickerContentState(); + State<_InlineDateTimePickerContent> createState() => + _InlineDateTimePickerContentState(); } -class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerContent> { +class _InlineDateTimePickerContentState + extends State<_InlineDateTimePickerContent> { // 数据源 final List years = List.generate(101, (i) => 1970 + i); final List months = List.generate(12, (i) => i + 1); @@ -56,24 +71,43 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte late int selectedHour; late int selectedMinute; + DateTime? _minTime; // 解析后的最小允许时间(如果有) + @override void initState() { super.initState(); + + // 解析 minTimeStr(若提供) + _minTime = _parseMinTime(widget.minTimeStr); + + // 选择初始时间:取 DateTime.now() 与 _minTime 的较大者(确保初始选中合法) final now = DateTime.now(); - selectedYear = now.year; - selectedMonth = now.month; - selectedDay = now.day; - selectedHour = now.hour; - selectedMinute = now.minute; + DateTime initial = now; + if (_minTime != null && _minTime!.isAfter(initial)) { + initial = _minTime!; + } + + selectedYear = initial.year; + selectedMonth = initial.month; + selectedDay = initial.day; + selectedHour = initial.hour; + selectedMinute = initial.minute; // 初始化天数列表 days = _getDaysInMonth(selectedYear, selectedMonth); - yearCtrl = FixedExtentScrollController(initialItem: years.indexOf(selectedYear)); - monthCtrl = FixedExtentScrollController(initialItem: selectedMonth - 1); - dayCtrl = FixedExtentScrollController(initialItem: selectedDay - 1); - hourCtrl = FixedExtentScrollController(initialItem: selectedHour); - minuteCtrl = FixedExtentScrollController(initialItem: selectedMinute); + // controllers 初始项索引需在范围内 + yearCtrl = FixedExtentScrollController( + initialItem: years.indexOf(selectedYear).clamp(0, years.length - 1)); + monthCtrl = FixedExtentScrollController(initialItem: (selectedMonth - 1).clamp(0, months.length - 1)); + dayCtrl = FixedExtentScrollController(initialItem: (selectedDay - 1).clamp(0, days.length - 1)); + hourCtrl = FixedExtentScrollController(initialItem: selectedHour.clamp(0, hours.length - 1)); + minuteCtrl = FixedExtentScrollController(initialItem: selectedMinute.clamp(0, minutes.length - 1)); + + // 如果初始时间小于 minTime(理论上不会,因为我们取了较大者),再修正一次 + WidgetsBinding.instance.addPostFrameCallback((_) { + _enforceConstraintsAndUpdateControllers(); + }); } // 根据年月获取当月天数 @@ -82,8 +116,31 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte return List.generate(lastDay, (i) => i + 1); } + // 解析 'yyyy-MM-dd HH:mm' 返回 DateTime 或 null + DateTime? _parseMinTime(String? s) { + if (s == null || s.trim().isEmpty) return null; + try { + final trimmed = s.trim(); + final parts = trimmed.split(' '); + final dateParts = parts[0].split('-').map((e) => int.parse(e)).toList(); + final timeParts = (parts.length > 1) + ? parts[1].split(':').map((e) => int.parse(e)).toList() + : [0, 0]; + final year = dateParts[0]; + final month = dateParts[1]; + final day = dateParts[2]; + final hour = (timeParts.isNotEmpty) ? timeParts[0] : 0; + final minute = (timeParts.length > 1) ? timeParts[1] : 0; + return DateTime(year, month, day, hour, minute); + } catch (e) { + // 解析失败则忽略 + debugPrint('parseMinTime failed for "$s": $e'); + return null; + } + } + // 更新天数列表并调整选中日期 - void _updateDays() { + void _updateDays({bool jumpDay = true}) { final newDays = _getDaysInMonth(selectedYear, selectedMonth); final isDayValid = selectedDay <= newDays.length; @@ -91,35 +148,56 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte days = newDays; if (!isDayValid) { selectedDay = newDays.last; - dayCtrl.jumpToItem(selectedDay - 1); + if (jumpDay) dayCtrl.jumpToItem(selectedDay - 1); } }); } - // 检查并限制时间(当不允许未来时间时) - void _checkAndClampToNow() { - if (widget.allowFuture) return; // 允许未来时间时跳过检查 - + // 检查并限制时间: + // 1) 优先检查 _minTime(如果存在),不允许早于 _minTime + // 2) 如果没有 _minTime,则根据 allowFuture 决定是否限制到 now + // 注意:_minTime 优先级高于 allowFuture(如果 _minTime 在未来,会选择 _minTime) + void _enforceConstraintsAndUpdateControllers() { final picked = DateTime(selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute); - final now = DateTime.now(); - if (picked.isAfter(now)) { - // 回滚到当前时间 - selectedYear = now.year; - selectedMonth = now.month; - selectedDay = now.day; - selectedHour = now.hour; - selectedMinute = now.minute; + // 1) 最小时间约束(优先) + if (_minTime != null && picked.isBefore(_minTime!)) { + final m = _minTime!; + selectedYear = m.year; + selectedMonth = m.month; + selectedDay = m.day; + selectedHour = m.hour; + selectedMinute = m.minute; - // 更新各滚轮位置 + // 更新天数列表与控制器索引 + _updateDays(jumpDay: false); yearCtrl.jumpToItem(years.indexOf(selectedYear)); monthCtrl.jumpToItem(selectedMonth - 1); dayCtrl.jumpToItem(selectedDay - 1); hourCtrl.jumpToItem(selectedHour); minuteCtrl.jumpToItem(selectedMinute); + return; + } - // 更新天数列表 - _updateDays(); + // 2) 禁止选择未来(当 allowFuture == false 且没有 minTime 或 minTime <= now) + if (!widget.allowFuture) { + final now = DateTime.now(); + // 如果 minTime 存在并大于 now,我们已在上面处理(minTime 优先),所以这里处理的是普通情况 + if (picked.isAfter(now)) { + selectedYear = now.year; + selectedMonth = now.month; + selectedDay = now.day; + selectedHour = now.hour; + selectedMinute = now.minute; + + _updateDays(jumpDay: false); + yearCtrl.jumpToItem(years.indexOf(selectedYear)); + monthCtrl.jumpToItem(selectedMonth - 1); + dayCtrl.jumpToItem(selectedDay - 1); + hourCtrl.jumpToItem(selectedHour); + minuteCtrl.jumpToItem(selectedMinute); + return; + } } } @@ -136,7 +214,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte @override Widget build(BuildContext context) { return SizedBox( - height: 300, + height: 330, child: Column( children: [ // 顶部按钮 @@ -178,8 +256,8 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte onSelected: (idx) { setState(() { selectedYear = years[idx]; - _updateDays(); // 年份变化时更新天数 - _checkAndClampToNow(); + _updateDays(); + _enforceConstraintsAndUpdateControllers(); }); }, ), @@ -191,8 +269,8 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte onSelected: (idx) { setState(() { selectedMonth = months[idx]; - _updateDays(); // 月份变化时更新天数 - _checkAndClampToNow(); + _updateDays(); + _enforceConstraintsAndUpdateControllers(); }); }, ), @@ -203,8 +281,10 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte items: days.map((e) => e.toString().padLeft(2, '0')).toList(), onSelected: (idx) { setState(() { - selectedDay = days[idx]; - _checkAndClampToNow(); + // 防护:idx 可能超出当前 days 长度(极小概率) + final safeIdx = idx.clamp(0, days.length - 1); + selectedDay = days[safeIdx]; + _enforceConstraintsAndUpdateControllers(); }); }, ), @@ -216,7 +296,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte onSelected: (idx) { setState(() { selectedHour = hours[idx]; - _checkAndClampToNow(); + _enforceConstraintsAndUpdateControllers(); }); }, ), @@ -228,7 +308,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte onSelected: (idx) { setState(() { selectedMinute = minutes[idx]; - _checkAndClampToNow(); + _enforceConstraintsAndUpdateControllers(); }); }, ), @@ -257,4 +337,4 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte ), ); } -} \ No newline at end of file +} diff --git a/lib/customWidget/promise/promise_page.dart b/lib/customWidget/promise/promise_page.dart new file mode 100644 index 0000000..42e49d3 --- /dev/null +++ b/lib/customWidget/promise/promise_page.dart @@ -0,0 +1,348 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; +import 'package:qhd_prevention/pages/main_tab.dart'; +import 'package:qhd_prevention/pages/mine/mine_sign_page.dart'; +import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/tools/tools.dart'; + +class PromisePage extends StatefulWidget { + const PromisePage({super.key}); + + @override + State createState() => _PromisePageState(); +} + +class _PromisePageState extends State { + bool _loading = true; + + Map info = {}; + String? baseImgPath; + + @override + void initState() { + super.initState(); + _loadData(); + } + + Future _loadData() async { + setState(() { + _loading = true; + }); + + try { + final Map payload = + await ApiService.safeCorppromiseDetail(); + + final coll = (payload['COLLATERAL'] as List?) ?? []; + final DETAIL = + coll.map((e) { + return { + 'value': e?['COLLATERAL']?.toString() ?? '', + 'id': e?['PROMISEDETAIL_ID']?.toString() ?? '', + }; + }).toList(); + + final Map resolved = {}; + if (payload['TEXT'] is Map) { + resolved.addAll(payload['TEXT'] as Map); + } else { + resolved['TEXT'] = payload['TEXT']?.toString() ?? ''; + } + resolved['DETAIL'] = DETAIL; + resolved['SIGNTIME'] = + DateTime.now().millisecondsSinceEpoch.toString(); + resolved['COVERPEOPLE'] = + (payload['COVERPEOPLE'] is List && payload['COVERPEOPLE'].isNotEmpty) + ? payload['COVERPEOPLE'][0]['USERNAME']?.toString() ?? '' + : ''; + // 合并其他顶层字段(保留现有字段) + if (payload is Map) { + payload.forEach((k, v) { + if (!resolved.containsKey(k)) resolved[k] = v; + }); + } + resolved['PROMISEPEOPLE_ID'] = payload['PROMISEPEOPLE_ID'] ?? ''; + + setState(() { + info = resolved; + _loading = false; + }); + } catch (e, st) { + debugPrint('safeCorppromiseDetail error: $e\n$st'); + setState(() { + _loading = false; + }); + ToastUtil.showNormal(context, '加载失败,请稍后重试'); + } + } + + String _formatDateShort(String? s) { + if (s == null) return ''; + if (s.length >= 10) return s.substring(0, 10); + return s; + } + + // 点击签字:导航到你自己实现的签字页面,等待返回签字路径(String) + Future _sign() async { + await NativeOrientation.setLandscape(); + final String path = await Navigator.push( + context, + MaterialPageRoute(builder: (c) => MineSignPage()), + ); + if (path != null) { + setState(() { + info['FILEPATH'] = path; + }); + FocusHelper.clearFocus(context); + } + } + + // 提交:占位实现 —— 请把下面的逻辑替换为你项目的上传/提交 API(例如 ApiService.editPeopleII 或类似) + Future submitSignedPromise() async { + LoadingDialogHelper.show(); + final filePath = (info['FILEPATH'] ?? '').toString(); + if (filePath.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + final result = await ApiService.submitCorppromiseSign(filePath,info); + LoadingDialogHelper.hide(); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '提交成功'); + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (_) => const MainPage()), + ); + } + } + + Widget _buildTitle() { + final type = (info['TYPE'] ?? '').toString(); + final title = type == '0' ? '安全生产承诺书' : '安全生产责任状'; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Text( + title, + style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + ); + } + + Widget _buildHeaderName() { + final type = (info['TYPE'] ?? '').toString(); + if (type == '0') { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + '${info['COVERPEOPLE'] ?? ''}:', + style: const TextStyle(fontSize: 16), + ), + ); + } + return const SizedBox.shrink(); + } + + Widget _buildParagraph(String text) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0), + child: RichText( + textAlign: TextAlign.justify, + text: TextSpan( + style: const TextStyle( + height: 1.6, + letterSpacing: 0.5, + color: Colors.black, + ), + children: [ + const WidgetSpan( + child: SizedBox(width: 28), // 缩进2个汉字宽度 + ), + TextSpan(text: text), + ], + ), + ), + ); + } + + Widget _buildCollateralList() { + final detail = (info['DETAIL'] as List?) ?? []; + if (detail.isEmpty) return const SizedBox.shrink(); + return Padding( + padding: const EdgeInsets.only(left: 0, right: 8.0, top: 6.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: + detail.map((e) { + return _buildParagraph(e['value']?.toString() ?? ''); + }).toList(), + ), + ); + } + + Widget _buildFooter() { + final type = (info['TYPE'] ?? '').toString(); + final signImagePath = (info['FILEPATH'] ?? '').toString(); + final signTime = (info['SIGNTIME'] ?? '').toString(); + final creatTime = (info['CREATTIME'] ?? '').toString(); + + Widget signPreview; + if (signImagePath.isNotEmpty && signImagePath.startsWith('http')) { + signPreview = Image.network( + (baseImgPath ?? '') + signImagePath, + width: 100, + height: 50, + fit: BoxFit.cover, + ); + } else if (signImagePath.isNotEmpty && File(signImagePath).existsSync()) { + signPreview = Image.file( + File(signImagePath), + width: 100, + height: 50, + fit: BoxFit.cover, + ); + } else { + signPreview = const SizedBox.shrink(); + } + + return Padding( + padding: const EdgeInsets.only( + top: 12.0, + left: 8.0, + right: 8.0, + bottom: 20.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (type == '0') + const Align( + alignment: Alignment.centerRight, + child: Text('承诺单位(盖章):'), + ), + if (type == '1') + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text('发状人:${info['COVERPEOPLE'] ?? ''}'), + const SizedBox(height: 6), + Text(creatTime.isNotEmpty ? _formatDateShort(creatTime) : ''), + ], + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text(type == '0' ? '主要负责人签字:' : '受状人:'), + const SizedBox(height: 8), + GestureDetector( + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: info['FILEPATH'] ?? ''), + context, + ); + }, + child: signPreview, + ), + const SizedBox(height: 8), + CustomButton( + text: + (info['FILEPATH'] ?? '').toString().isNotEmpty + ? '重签' + : '手写签字', + backgroundColor: Colors.blue, + padding: EdgeInsets.symmetric( + vertical: 0, + horizontal: 20, + ), + height: 35, + onPressed: _sign, + ), + ], + ), + ), + ], + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [Text(signTime)], + ), + ], + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const MyAppbar(title: '安全承诺', isBack: false), + body: + _loading + ? const Center(child: CircularProgressIndicator()) + : SingleChildScrollView( + child: Container( + color: Colors.white, + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 5, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [_buildTitle()], + ), + _buildHeaderName(), + _buildParagraph(info['TEXT']?.toString() ?? ''), + _buildCollateralList(), + if ((info['TYPE'] ?? '').toString() == '0') ...[ + _buildParagraph( + '若违反上述承诺和未履行安全生产职责,或发生责任事故的,接受政府或公司事故调查组做出的处罚决定。', + ), + _buildParagraph( + '承诺期限自${_formatDateShort(info['PROMISE_TERM_START']?.toString())}至${_formatDateShort(info['PROMISE_TERM_END']?.toString())}。', + ), + ] else ...[ + _buildParagraph( + '若未履行安全生产职责,或发生生产安全事故的,接受公司或政府事故调查组做出的处罚。', + ), + _buildParagraph( + '责任期限自${_formatDateShort(info['PROMISE_TERM_START']?.toString())}至${_formatDateShort(info['PROMISE_TERM_END']?.toString())}。', + ), + ], + const SizedBox(height: 8), + _buildFooter(), + const SizedBox(height: 12), + SizedBox( + width: double.infinity, + child: CustomButton( + text: '提 交', + backgroundColor: Colors.blue, + onPressed: submitSignedPromise, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/http/ApiService.dart b/lib/http/ApiService.dart index 0fea833..c8ae6b5 100644 --- a/lib/http/ApiService.dart +++ b/lib/http/ApiService.dart @@ -194,6 +194,51 @@ U6Hzm1ninpWeE+awIDAQAB data: {}, ); } + /// 校验安全承诺 + static Future> checkSafeCorppromise() async { + return HttpManager().request( + basePath, + '/app/corppromise/ISSIGN_II', + method: Method.post, + data: { + 'USER_ID': SessionService.instance.loginUserId, + }, + ); + } + /// 安全承诺详情 + static Future> safeCorppromiseDetail() async { + return HttpManager().request( + basePath, + '/app/corppromise/promise_II', + method: Method.post, + data: { + 'USER_ID': SessionService.instance.loginUserId, + }, + ); + } + // /// 安全承诺提交 + static Future> submitCorppromiseSign(String imagePath, Map data) async { + final file = File(imagePath); + if (!await file.exists()) { + throw ApiException('file_not_found', '图片不存在:$imagePath'); + } + final fileName = file.path.split(Platform.pathSeparator).last; + return HttpManager().uploadFaceImage( + baseUrl: basePath, + path: '/app/corppromise/editpeople_II', + fromData: { + ...data, + 'CORPINFO_ID': SessionService.instance.corpinfoId, + 'FFILE': await MultipartFile.fromFile( + file.path, + filename: fileName + ), + } + ); + + } + + /// 检查记录详情地图信息 static Future> getRecordMapInfo(String CHECKRECORD_ID) { return HttpManager().request( @@ -558,6 +603,21 @@ U6Hzm1ninpWeE+awIDAQAB } ///TODO -------------–-------------------- 首页特殊作业 -------------–-------------------- + /// 作业区域校验 + static Future> checkSpecialWorkIsInPls(String ELECTRONIC_FENCE_AREA_ID, String LONGITUDE, String LATITUDE) { + return HttpManager().request( + basePath, + '/app/eightwork/isInPls', + method: Method.post, + data: { + "CORPINFO_ID":SessionService.instance.corpinfoId, + "USER_ID":SessionService.instance.loginUserId, + "ELECTRONIC_FENCE_AREA_ID":ELECTRONIC_FENCE_AREA_ID, + "LONGITUDE" :LONGITUDE, + "LATITUDE" : LATITUDE, + }, + ); + } /// 特殊作业 static Future> specialCheckWork() { return HttpManager().request( @@ -783,7 +843,39 @@ U6Hzm1ninpWeE+awIDAQAB }, ); } - + /// 获取是摄像头列表 + static Future> getVideomanagerList() { + return HttpManager().request( + basePath, + '/app/videomanager/listAll', + method: Method.post, + data: { + "CORPINFO_ID":SessionService.instance.corpinfoId, + }, + ); + } + /// 获取承包商列表 + static Future> getUnitListAll() { + return HttpManager().request( + basePath, + '/app/units/listAll', + method: Method.post, + data: { + "CORPINFO_ID":SessionService.instance.corpinfoId, + }, + ); + } + /// 获取作业区域列表 + static Future> getWorkAreaList() { + return HttpManager().request( + basePath, + '/app/electronicFence/listTree', + method: Method.post, + data: { + "CORPINFO_ID":SessionService.instance.corpinfoId, + }, + ); + } /// 保存或作废气体检测 static Future> saveGasTest(String workType, diff --git a/lib/http/HttpManager.dart b/lib/http/HttpManager.dart index ae27331..9857a1c 100644 --- a/lib/http/HttpManager.dart +++ b/lib/http/HttpManager.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'dart:ui'; import 'package:dio/dio.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; /// 全局接口异常 class ApiException implements Exception { @@ -82,9 +83,12 @@ class HttpManager { try { switch (method) { case Method.get: + final queryParameters = {}; + if (params != null) queryParameters.addAll(params); + if (data != null) queryParameters.addAll(data); resp = await _dio.get( url, - queryParameters: params, + queryParameters: queryParameters, cancelToken: cancelToken, options: options, ); @@ -128,12 +132,12 @@ class HttpManager { final json = resp.data is Map ? resp.data as Map : {}; - final result = json['result'] as String?; - final msg = json['msg'] as String? ?? json['message'] as String? ?? ''; - if (result != 'success') { - // 非 success 都抛异常 - throw ApiException(result ?? 'unknown', msg); - } + // final result = json['result'] as String?; + // final msg = json['msg'] as String? ?? json['message'] as String? ?? ''; + // if (result != 'success') { + // // 非 success 都抛异常 + // throw ApiException(result ?? 'unknown', msg); + // } return json; } } diff --git a/lib/main.dart b/lib/main.dart index 725c363..763de12 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:qhd_prevention/pages/badge_manager.dart'; -import 'package:qhd_prevention/tools/auth_service.dart'; +import 'package:qhd_prevention/services/auth_service.dart'; +import 'package:qhd_prevention/tools/tools.dart'; import 'package:shared_preferences/shared_preferences.dart'; import './pages/login_page.dart'; import './pages/main_tab.dart'; @@ -60,7 +61,6 @@ void main() async { GlobalMessage.showError('会话已过期,请重新登录'); }); }; - // 自动登录逻辑 final prefs = await SharedPreferences.getInstance(); final savedLogin = prefs.getBool('isLoggedIn') ?? false; diff --git a/lib/pages/app/Danger_paicha/check_record_detail_page.dart b/lib/pages/app/Danger_paicha/check_record_detail_page.dart index 58b140d..4f8b3a2 100644 --- a/lib/pages/app/Danger_paicha/check_record_detail_page.dart +++ b/lib/pages/app/Danger_paicha/check_record_detail_page.dart @@ -14,7 +14,9 @@ import 'package:qhd_prevention/pages/app/danger_wait_list_page.dart'; import 'package:qhd_prevention/pages/app/detail_images_page.dart'; import 'package:qhd_prevention/pages/app/hidden_record_detail_page.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/services/location_service.dart'; import 'package:qhd_prevention/tools/tools.dart'; +import 'package:webview_flutter/webview_flutter.dart'; class CheckRecordDetailPage extends StatefulWidget { const CheckRecordDetailPage(this.id, this.type, {super.key}); @@ -31,6 +33,7 @@ class _CheckRecordDetailPageState extends State { List> covers = []; double centerLat = 39.8883; double centerLng = 119.519; + WebViewController? _controller; Map pd = { "LIST_NAME": "", @@ -58,8 +61,13 @@ class _CheckRecordDetailPageState extends State { super.initState(); // 启动时并行请求,UI 使用 setState 更新 _fetchAll(); + _initLocation(); + } + @override + void dispose() { + _controller = null; + super.dispose(); } - Future _fetchAll() async { // 并行发请求,界面加载更快 await Future.wait([ @@ -73,12 +81,6 @@ class _CheckRecordDetailPageState extends State { setState(() => _loadingMap = true); try { final resData = await ApiService.getRecordMapInfo(widget.id); - if (resData == null) { - // 接口返回空的处理 - setState(() => _loadingMap = false); - return; - } - final built = buildCoversFromRes(resData); double lat = centerLat; double lng = centerLng; @@ -367,6 +369,71 @@ class _CheckRecordDetailPageState extends State { ), ); } + /// 获取定位(并初始化 WebView 控制器) + Future _initLocation() async { + + // 允许多次赋值 + _controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..addJavaScriptChannel('JS', onMessageReceived: (JavaScriptMessage message) { + debugPrint('JS LOG: ${message.message}'); + }) + ..setNavigationDelegate( + NavigationDelegate( + onPageFinished: (String url) async { + debugPrint('网页加载完成: $url'); + await _injectLocationParams(); + }, + onWebResourceError: (err) { + debugPrint('Web resource error: ${err.description}'); + }, + ), + ); + + // 加载页面(注意:如果 loadRequest 失败你可以捕获异常) + try { + await _controller!.loadRequest(Uri.parse('http://47.92.102.56:7811/file/fluteightmap/index.html')); + } catch (e, st) { + debugPrint('loadRequest 错误: $e\n$st'); + } + } + + Future _injectLocationParams() async { + if (_controller == null) { + debugPrint('_injectLocationParams: controller 为空,跳过注入'); + return; + } + + final params = { + 'longitude': centerLng, + 'latitude': centerLat, + 'GSON': [], + 't': DateTime.now().millisecondsSinceEpoch, + }; + + final jsonParams = jsonEncode(params); + + try { + await _controller!.runJavaScript(''' + (function(){ + try { + if (typeof window.initWithData === 'function') { + window.initWithData($jsonParams); + } else if (typeof window.initMap === 'function') { + window.initMap($jsonParams); + } else { + console.error('initWithData / initMap function not found'); + } + } catch(e) { + console.error('call initWithData error', e); + } + })(); + '''); + debugPrint('已注入地图初始化参数'); + } catch (e) { + debugPrint('注入位置参数失败: $e'); + } + } Widget _buildTableHeaderCell(String text) { return Padding(padding: EdgeInsets.all(8), child: Center(child: Text(text, style: TextStyle(fontWeight: FontWeight.bold)))); @@ -382,24 +449,11 @@ class _CheckRecordDetailPageState extends State { child: _loadingMap ? Center(child: CircularProgressIndicator()) : BaiduMapWebView( - ak: Platform.isIOS ? 'g3lZyqt0KkFnZGUsjIVO7U6lTCfpjSCt' : '43G1sKuHV6oRTrdR9VTIGPF9soej7V5a', - latitude: centerLat, - longitude: centerLng, - zoom: 13, - covers: covers, - onMarkerTap: (data) { - debugPrint('marker click: $data'); - // 如果 data 中包含 item 或 id,可用于跳转 - final type = data['type'] ?? ''; - if (type == 'varList' && data['item'] != null) { - // 示例:打开对应检查项详情 - final item = data['item']; - if (item is Map && item['HIDDEN_ID'] != null) { - _goToDetail(item['HIDDEN_ID'].toString()); - } - } - }, - ), + controller: _controller, + isLoading: _loadingMap, + errorMessage: null, + onRetry: _initLocation, + ) ), ); } diff --git a/lib/pages/app/checking_information_one_page.dart b/lib/pages/app/checking_information_one_page.dart index 032afaf..5f0d75e 100644 --- a/lib/pages/app/checking_information_one_page.dart +++ b/lib/pages/app/checking_information_one_page.dart @@ -476,10 +476,13 @@ class _CheckingInformationOnePageState extends State height: 30, child: ElevatedButton( onPressed: () async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); + // 更新状态(当子页面关闭时) setState(() { imagePath = path ?? ''; diff --git a/lib/pages/home/SafeCheck/CheckPersonSign/safecheck_sign_detail.dart b/lib/pages/home/SafeCheck/CheckPersonSign/safecheck_sign_detail.dart index f342b44..816e50e 100644 --- a/lib/pages/home/SafeCheck/CheckPersonSign/safecheck_sign_detail.dart +++ b/lib/pages/home/SafeCheck/CheckPersonSign/safecheck_sign_detail.dart @@ -272,14 +272,15 @@ class _SafecheckSignDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages = []; signTimes = []; signImages.add(path); diff --git a/lib/pages/home/SafeCheck/CheckPersonSure/check_person_detail.dart b/lib/pages/home/SafeCheck/CheckPersonSure/check_person_detail.dart index 66fc4c2..a20fe11 100644 --- a/lib/pages/home/SafeCheck/CheckPersonSure/check_person_detail.dart +++ b/lib/pages/home/SafeCheck/CheckPersonSure/check_person_detail.dart @@ -258,14 +258,15 @@ class _CheckPersonDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages = []; signTimes = []; signImages.add(path); diff --git a/lib/pages/home/SafeCheck/Start/safeCheck_defend_set_page.dart b/lib/pages/home/SafeCheck/Start/safeCheck_defend_set_page.dart index 8ec9f68..cb181bb 100644 --- a/lib/pages/home/SafeCheck/Start/safeCheck_defend_set_page.dart +++ b/lib/pages/home/SafeCheck/Start/safeCheck_defend_set_page.dart @@ -59,14 +59,15 @@ class _SafecheckDefendSetPageState extends State { } Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages = []; signTimes = []; signImages.add(path); diff --git a/lib/pages/home/SafeCheck/Start/safeCheck_start_detail.dart b/lib/pages/home/SafeCheck/Start/safeCheck_start_detail.dart index 39869d6..b89051f 100644 --- a/lib/pages/home/SafeCheck/Start/safeCheck_start_detail.dart +++ b/lib/pages/home/SafeCheck/Start/safeCheck_start_detail.dart @@ -560,14 +560,15 @@ class _SafecheckStartDetailState extends State { } } Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); diff --git a/lib/pages/home/SafetyCommitment/company_safety_commitment_apply.dart b/lib/pages/home/SafetyCommitment/company_safety_commitment_apply.dart index 4898a78..10164c9 100644 --- a/lib/pages/home/SafetyCommitment/company_safety_commitment_apply.dart +++ b/lib/pages/home/SafetyCommitment/company_safety_commitment_apply.dart @@ -447,6 +447,7 @@ class _CompanySafetyCommitmentApplyState extends State _sign() async { + await NativeOrientation.setLandscape(); final String path = await Navigator.push( context, MaterialPageRoute(builder: (c) => MineSignPage()), diff --git a/lib/pages/home/SafetyCommitment/safety_commitment_page.dart b/lib/pages/home/SafetyCommitment/safety_commitment_page.dart index 83ed122..db068d0 100644 --- a/lib/pages/home/SafetyCommitment/safety_commitment_page.dart +++ b/lib/pages/home/SafetyCommitment/safety_commitment_page.dart @@ -36,19 +36,19 @@ class _SafetyCommitmentPageState extends State { ), const Divider(), - // Padding(padding: EdgeInsets.only(top: 10), - // child:ItemListWidget.selectableLineTitleTextRightButton( - // label: '其他行业安全承诺:', - // isEditable: true, - // isRequired:false, - // onTap: () { - // pushPage(SafetyCommitmentOtherListPage(), context); - // - // }, - // text: ' ', - // ), - // ), - // const Divider(), + Padding(padding: EdgeInsets.only(top: 10), + child:ItemListWidget.selectableLineTitleTextRightButton( + label: '其他行业安全承诺:', + isEditable: true, + isRequired:false, + onTap: () { + pushPage(SafetyCommitmentOtherListPage(), context); + + }, + text: ' ', + ), + ), + const Divider(), ], ), ); diff --git a/lib/pages/home/SafetyCommitment/team_safety_commitment_apply.dart b/lib/pages/home/SafetyCommitment/team_safety_commitment_apply.dart index 8143dcf..d46eb7b 100644 --- a/lib/pages/home/SafetyCommitment/team_safety_commitment_apply.dart +++ b/lib/pages/home/SafetyCommitment/team_safety_commitment_apply.dart @@ -817,6 +817,7 @@ class _TeamSafetyCommitmentApplyState extends State { Future _sign() async { + await NativeOrientation.setLandscape(); final String path = await Navigator.push( context, MaterialPageRoute(builder: (c) => MineSignPage()), diff --git a/lib/pages/home/SafetyCommitment/work_shop_safety_commitment_apply.dart b/lib/pages/home/SafetyCommitment/work_shop_safety_commitment_apply.dart index d5c99b5..6e1a524 100644 --- a/lib/pages/home/SafetyCommitment/work_shop_safety_commitment_apply.dart +++ b/lib/pages/home/SafetyCommitment/work_shop_safety_commitment_apply.dart @@ -780,6 +780,7 @@ class _WorkShopSafetyCommitmentApplyState extends State _sign() async { + await NativeOrientation.setLandscape(); final String path = await Navigator.push( context, MaterialPageRoute(builder: (c) => MineSignPage()), diff --git a/lib/pages/home/Safetymeeting/safety_meeting_detail_page.dart b/lib/pages/home/Safetymeeting/safety_meeting_detail_page.dart index 9fb2b34..4a71da6 100644 --- a/lib/pages/home/Safetymeeting/safety_meeting_detail_page.dart +++ b/lib/pages/home/Safetymeeting/safety_meeting_detail_page.dart @@ -296,6 +296,7 @@ class _SafetyMeetingDetailPageState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 965f0f8..0e9120c 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -3,8 +3,10 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; +import 'package:qhd_prevention/customWidget/promise/promise_page.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; -import 'package:qhd_prevention/tools/auth_service.dart'; +import 'package:qhd_prevention/services/auth_service.dart'; +import 'package:qhd_prevention/tools/update/update_dialogs.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; import 'package:qhd_prevention/pages/KeyProjects/keyProjects_tab_list.dart'; @@ -188,12 +190,32 @@ class _HomePageState extends State { context, title: '更新通知', content: pd['UPLOAD_CONTENT'] ?? '', - onConfirm: () { - ToastUtil.showNormal(context, '更新去吧!'); + onConfirm: () async{ + final apkUrl = 'http://192.168.1.191:8888/app-release.apk'; + await showUpdateConfirmDialog(context, apkUrl: apkUrl); }, ); return; } + + final corppromiseData = await ApiService.checkSafeCorppromise(); + if (corppromiseData['ISSIGN'] == 1) { + // 承诺 + CustomAlertDialog.showConfirm( + context, + title: '温馨提示', + content: '有未签署的安全承诺,点击确认前往签署', + force: true, + onConfirm: () { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (_) => const PromisePage()), + ); + }, + ); + return; + } + await _loadHiddenCache(); // 拉取其他数据 + 隐患列表(当 hiddenList 为空时显示 loading,否则不显示) await _fetchData(); diff --git a/lib/pages/home/study/study_my_task_page.dart b/lib/pages/home/study/study_my_task_page.dart index 3528e85..bf8ccbe 100644 --- a/lib/pages/home/study/study_my_task_page.dart +++ b/lib/pages/home/study/study_my_task_page.dart @@ -142,10 +142,13 @@ class _StudyMyTaskPageState extends State { } void _onTapSign(Map item) async { + await NativeOrientation.setLandscape(); + final String? imagePath = await Navigator.push( context, MaterialPageRoute(builder: (_) => MineSignPage()), ); + await NativeOrientation.setPortrait(); // 用户有签名并取到了 imagePath if (imagePath != null && imagePath.isNotEmpty) { diff --git a/lib/pages/home/tap/item_list_widget.dart b/lib/pages/home/tap/item_list_widget.dart index 6ad47eb..57e1a49 100644 --- a/lib/pages/home/tap/item_list_widget.dart +++ b/lib/pages/home/tap/item_list_widget.dart @@ -8,6 +8,9 @@ import 'package:qhd_prevention/tools/tools.dart'; class ItemListWidget { static const Color detailtextColor = Colors.black54; + static const double requiredInset = 0; + static const double horizontal_inset = 12; + static const double vertical_inset = 5; /// 单行水平排列: /// - 可编辑时:标题 + TextField @@ -28,7 +31,7 @@ class ItemListWidget { TextInputType keyboardType = TextInputType.text, }) { return Container( - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset), child: Row( mainAxisAlignment: isEditable @@ -164,12 +167,12 @@ class ItemListWidget { VoidCallback? onTapClean, // 清除回调 bool isRequired = true, String cleanText = '清除', - double horizontalnum=12, + double horizontalnum= horizontal_inset, }) { return InkWell( onTap: isEditable ? onTap : null, child: Container( - padding: EdgeInsets.symmetric(vertical: 5, horizontal: horizontalnum), + padding: EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontalnum), child: Row( children: [ // 1. 标题 @@ -214,7 +217,7 @@ class ItemListWidget { overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: fontSize, - color: isEditable ? Colors.black : detailtextColor, + color: isEditable ? (text == '请选择' ? Colors.black87 :Colors.black) : detailtextColor, ), ), ), @@ -250,7 +253,7 @@ class ItemListWidget { return InkWell( onTap: isEditable ? onTap : null, child: Container( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -334,7 +337,7 @@ class ItemListWidget { bool isRequired = true, }) { return Container( - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), + padding: EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -415,16 +418,18 @@ class ItemListWidget { static Widget twoRowButtonTitleText({ required String label, // 第一行标题 required bool isEditable, // 是否可编辑 + bool isInput = true, // 是否可输入 required String text, // 显示内容或提示 TextEditingController? controller, // 第二行编辑控制器 required VoidCallback? onTap, // 第一行点击回调 + String buttonText = '选择其他', required String hintText, double fontSize = 15, // 字体大小 double row2Height = 80, // 第二行高度 bool isRequired = true, }) { return Container( - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -455,12 +460,13 @@ class ItemListWidget { const SizedBox(width: 8), if (isEditable) CustomButton( - text: "选择其他", + text: buttonText, height: 30, padding: const EdgeInsets.symmetric( vertical: 2, - horizontal: 5, + horizontal: 10, ), + textStyle: TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold), backgroundColor: Colors.green, onPressed: onTap, ), @@ -473,7 +479,7 @@ class ItemListWidget { height: row2Height, padding: const EdgeInsets.symmetric(vertical: 8), child: - isEditable + (isEditable && isInput) ? TextField( autofocus: false, controller: controller, @@ -513,7 +519,7 @@ class ItemListWidget { String buttonText = '分析详情' }) { return Container( - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -545,7 +551,11 @@ class ItemListWidget { CustomButton( text: buttonText, height: 30, - padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5), + padding: const EdgeInsets.symmetric( + vertical: 2, + horizontal: 10, + ), + textStyle: TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold), backgroundColor: Colors.green, onPressed: onTap, ), @@ -565,7 +575,7 @@ class ItemListWidget { bool isRequired = false, }) { return Container( - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -605,7 +615,7 @@ class ItemListWidget { void Function(String)? onTapCallBack, }) { return Container( - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -661,7 +671,7 @@ class ItemListWidget { children: [ // 标题部分 Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset), child: Text( title, style: TextStyle( @@ -735,7 +745,7 @@ class ItemListWidget { bool isRequired = true, }) { return Container( - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -814,14 +824,14 @@ class ItemListWidget { ); } - static Widget itemContainer(Widget child, {double horizontal = 12}) { + static Widget itemContainer(Widget child, {double horizontal = horizontal_inset}) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), - padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: 8), + padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical_inset), child: child, ); } diff --git a/lib/pages/home/tap/tabList/special_wrok/dangerous_options_page.dart b/lib/pages/home/tap/tabList/special_wrok/dangerous_options_page.dart index 2e87fd5..a1331e0 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dangerous_options_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dangerous_options_page.dart @@ -155,11 +155,13 @@ class _DangerousOptionsPageState extends State { } Future _sign() async { + await NativeOrientation.setLandscape(); final String path = await Navigator.push( context, MaterialPageRoute(builder: (c) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); setState(() { final imageData = SignImageData( diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/HotWorkDetailFormWidget.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/HotWorkDetailFormWidget.dart index 370ff18..621b67f 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/HotWorkDetailFormWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/HotWorkDetailFormWidget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; import '../../../../../../tools/tools.dart'; import '../../../item_list_widget.dart'; @@ -12,6 +13,19 @@ class HotWorkDetailFormWidget extends StatefulWidget { final VoidCallback onChooseHotworkUser; final VoidCallback onAnalyzeTap; + // 新增 + final VoidCallback? onChooseVideoManager; + /// 选择摄像头 + final VoidCallback? onWorkStartTimeHandle; + final VoidCallback? onWorkEndTimeHandle; + /// 承包商 + final VoidCallback? onContractorHandle; + /// 作业区域 + final VoidCallback? onWorkAreaHandle; + /// 作业地点经纬度 + final VoidCallback? onWorkAreaLocationHandle; + + /// 编辑模式下需提供以下控制器,非编辑可不传 final TextEditingController? contentController; final TextEditingController? locationController; @@ -27,6 +41,14 @@ class HotWorkDetailFormWidget extends StatefulWidget { required this.onChooseLevel, required this.onChooseHotworkUser, required this.onAnalyzeTap, + /// 新增 + this.onChooseVideoManager, + this.onWorkStartTimeHandle, + this.onWorkEndTimeHandle, + this.onContractorHandle, + this.onWorkAreaHandle, + this.onWorkAreaLocationHandle, + this.contentController, this.locationController, this.methodController, @@ -34,25 +56,31 @@ class HotWorkDetailFormWidget extends StatefulWidget { this.relatedController, this.riskController, }) : assert( - !isEditable || - (contentController != null && - locationController != null && - methodController != null && - hotworkPersonController != null && - relatedController != null && - riskController != null), - 'Editable mode requires all TextEditingController parameters', - ), - super(key: key); + !isEditable || + (contentController != null && + locationController != null && + methodController != null && + hotworkPersonController != null && + relatedController != null && + riskController != null), + 'Editable mode requires all TextEditingController parameters', + ), + super(key: key); @override - State createState() => _HotWorkDetailFormWidgetState(); + State createState() => + _HotWorkDetailFormWidgetState(); } class _HotWorkDetailFormWidgetState extends State { + bool IS_CONTRACTOR_WORK = false; @override Widget build(BuildContext context) { + if (FormUtils.hasValue(widget.pd, 'LATITUDE')) { + widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map + } final pd = widget.pd; + return Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: const BoxDecoration( @@ -102,6 +130,7 @@ class _HotWorkDetailFormWidgetState extends State { onTap: widget.onChooseLevel, text: pd['WORK_LEVEL'] ?? '', ), + const Divider(), ItemListWidget.singleLineTitleText( label: '动火方式:', @@ -127,21 +156,22 @@ class _HotWorkDetailFormWidgetState extends State { ), const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '关联其他特殊作业及安全作业票编号:', + label: '关联其他特殊作业及安全作业票编号', isEditable: widget.isEditable, onTap: () async { - final val = await showDialog( + await showDialog( context: context, - builder: (_) => SelectionPopup( - type: 'assignments', - initialValue: pd['SPECIAL_WORK'] ?? '', - onConfirm: (val) { - setState(() { - pd['SPECIAL_WORK'] = val; - widget.relatedController?.text = val; - }); - }, - ), + builder: + (_) => SelectionPopup( + type: 'assignments', + initialValue: pd['SPECIAL_WORK'] ?? '', + onConfirm: (val) { + setState(() { + pd['SPECIAL_WORK'] = val; + widget.relatedController?.text = val; + }); + }, + ), ); FocusHelper.clearFocus(context); }, @@ -151,21 +181,22 @@ class _HotWorkDetailFormWidgetState extends State { ), const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '风险辨识结果:', + label: '风险辨识结果', isEditable: widget.isEditable, onTap: () async { await showDialog( context: context, - builder: (_) => SelectionPopup( - type: 'identification', - initialValue: pd['RISK_IDENTIFICATION'] ?? '', - onConfirm: (val) { - setState(() { - pd['RISK_IDENTIFICATION'] = val; - widget.riskController?.text = val; - }); - }, - ), + builder: + (_) => SelectionPopup( + type: 'identification', + initialValue: pd['RISK_IDENTIFICATION'] ?? '', + onConfirm: (val) { + setState(() { + pd['RISK_IDENTIFICATION'] = val; + widget.riskController?.text = val; + }); + }, + ), ); FocusHelper.clearFocus(context); }, @@ -173,6 +204,77 @@ class _HotWorkDetailFormWidgetState extends State { controller: widget.riskController, text: pd['RISK_IDENTIFICATION'] ?? '', ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '视频监控:', + isClean: pd['WORK_LEVEL'] == '一级' || pd['WORK_LEVEL'] == '二级', + cleanText: '清除监控', + isRequired: pd['WORK_LEVEL'] == '特级', + isEditable: widget.isEditable, + onTap: widget.onChooseVideoManager ?? () {}, + text: pd['VIDEONAME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业开始时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkStartTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_START_TIME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业结束时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkEndTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_END_TIME'] ?? '', + ), + const Divider(), + ListItemFactory.createYesNoSection( + verticalPadding: 0, + horizontalPadding: 0, + title: '是否承包商作业', + isEdit: widget.isEditable, + text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否', + isRequired: true, + groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1', + onChanged: (bool value) { + setState(() { + widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0"; + }); + }, + ), + if (pd['IS_CONTRACTOR_WORK'] == '1') + Column( + children: [ + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '承包商:', + isEditable: widget.isEditable, + onTap: widget.onContractorHandle ?? () {}, + text: pd['UNITS_NAME'] ?? '', + ), + ], + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '作业区域:', + isEditable: widget.isEditable, + onTap: widget.onWorkAreaHandle ?? () {}, + text: pd['PLS_NAME'] ?? '', + ), + const Divider(), + + ItemListWidget.twoRowButtonTitleText( + label: '作业地点经纬度', + isInput: false, + isEditable: widget.isEditable, + buttonText: '定位', + onTap: widget.onWorkAreaLocationHandle ?? (){}, + hintText: '', + text: pd['LATITUDE_LONGITUDE'] ?? (widget.isEditable ? '' : '无'), + ), + if (FormUtils.hasValue(pd, 'ANALYZE_TIME')) ...[ const Divider(), ItemListWidget.OneRowButtonTitleText( diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/aqcs_work_detail/hotwork_safe_func_sure.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/aqcs_work_detail/hotwork_safe_func_sure.dart index 30fbd22..a026007 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/aqcs_work_detail/hotwork_safe_func_sure.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/aqcs_work_detail/hotwork_safe_func_sure.dart @@ -41,12 +41,10 @@ class _HotworkSafeFuncSureState extends State { /// 详情 late Map pd = {}; late List> measuresList = []; + /// 其他安全措施 final TextEditingController _otherController = TextEditingController(); - /// 动火人及证书编号 - late List workUserList = []; - /// 安全防护措施列表 List imagePaths = []; List signTimes = []; // 签字时间列表 @@ -54,7 +52,6 @@ class _HotworkSafeFuncSureState extends State { void initState() { super.initState(); _getData(); - _getHotWorkNameList(); } /// 弹出单位选择 @@ -65,8 +62,7 @@ class _HotworkSafeFuncSureState extends State { barrierColor: Colors.black54, backgroundColor: Colors.transparent, builder: - (_) => - DepartmentPicker( + (_) => DepartmentPicker( onSelected: (id, name) async { setState(() { item.DEPARTMENT_ID = id; @@ -88,15 +84,15 @@ class _HotworkSafeFuncSureState extends State { }); } - - /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); @@ -111,60 +107,61 @@ class _HotworkSafeFuncSureState extends State { Widget _signListWidget() { return Column( children: - imagePaths.map((path) { - return Column( - children: [ - const SizedBox(height: 10), - const Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + imagePaths.map((path) { + return Column( children: [ - GestureDetector( - child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain - ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 200, - maxHeight: 150, - ), - child: Image.file( - File(path), - // 改为完整显示 - fit: BoxFit.contain, - ), - ), - onTap: () { - presentOpaque( - SingleImageViewer(imageUrl: path), - context, - ); - }, - ), - Column( + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Container( - padding: const EdgeInsets.only(right: 5), - child: CustomButton( - text: 'X', - height: 30, - padding: const EdgeInsets.symmetric(horizontal: 10), - backgroundColor: Colors.red, - onPressed: () { - setState(() { - imagePaths.remove(path); - }); - }, + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + imagePaths.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], ), - const SizedBox(height: 80), ], ), ], - ), - ], - ); - }).toList(), + ); + }).toList(), ); } + bool _validateAndProceed(BuildContext context) { for (var i = 0; i < measuresList.length; i++) { final m = measuresList[i]; @@ -178,10 +175,9 @@ class _HotworkSafeFuncSureState extends State { // 只有 STATUS == '1' 时才检查后续 ANSWER if (m['STATUS'] == '1') { for (var j = 1; j <= 4; j++) { - if (FormUtils.hasValue(m, 'QUESTION${j}') - ) { - if (!FormUtils.hasValue(m, 'ANSWER${j}') - || (m['ANSWER${j}'] as String?)!.length == 0) { + if (FormUtils.hasValue(m, 'QUESTION${j}')) { + if (!FormUtils.hasValue(m, 'ANSWER${j}') || + (m['ANSWER${j}'] as String?)!.length == 0) { ToastUtil.showNormal(context, '第${i + 1}项未填写第${j}项'); return false; } @@ -191,7 +187,8 @@ class _HotworkSafeFuncSureState extends State { } return true; } - /// 提交 1 提交 0暂存 + + /// 提交 1 提交 0暂存 Future _submit(String status) async { if (_otherController.text.trim().isEmpty) { ToastUtil.showNormal(context, '请输入其他安全措施,没有请填“无”'); @@ -205,14 +202,13 @@ class _HotworkSafeFuncSureState extends State { if (status == '1') { if (!_validateAndProceed(context)) { - return; + return; } } else { await showDialog( context: context, builder: - (_) => - CustomAlertDialog( + (_) => CustomAlertDialog( title: '作废原因', mode: DialogMode.input, hintText: '请输入作废原因', @@ -241,8 +237,7 @@ class _HotworkSafeFuncSureState extends State { await showDialog( context: context, builder: - (_) => - CustomAlertDialog( + (_) => CustomAlertDialog( title: '提示', content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', cancelText: '取消', @@ -250,7 +245,8 @@ class _HotworkSafeFuncSureState extends State { onConfirm: () async { LoadingDialogHelper.show(); try { - final result = await ApiService.saveSafeFunctionSure('hotwork', + final result = await ApiService.saveSafeFunctionSure( + 'hotwork', formData, imagePaths, ); @@ -260,7 +256,7 @@ class _HotworkSafeFuncSureState extends State { context, status == '1' ? '提交成功' : '已暂存', ); - Navigator.pop(context); + Navigator.of(context).pop(true); // 传 true 表示成功(可选) } } catch (e) { LoadingDialogHelper.hide(); @@ -278,16 +274,12 @@ class _HotworkSafeFuncSureState extends State { } } - Future _getHotWorkNameList() async { - final result = await ApiService.getHotWorkNameList(); - setState(() { - workUserList = result['varList'] ?? ''; - }); - } - /// 初始化拉取数据 Future _getData() async { - final data = await ApiService.getHomeworkFindById('hotwork', widget.HOTWORK_ID); + final data = await ApiService.getHomeworkFindById( + 'hotwork', + widget.HOTWORK_ID, + ); setState(() { pd = data['pd']; _getMeasures(); @@ -295,7 +287,10 @@ class _HotworkSafeFuncSureState extends State { } Future _getMeasures() async { - final data = await ApiService.listSignSureAllMeasures('hotwork',widget.HOTWORK_ID); + final data = await ApiService.listSignSureAllMeasures( + 'hotwork', + widget.HOTWORK_ID, + ); setState(() { measuresList = List>.from( data['measuresForSignList'] ?? >[], @@ -303,7 +298,6 @@ class _HotworkSafeFuncSureState extends State { }); } - Future _itemToSign(Map measures, int index) async { // 签名图片列表 final List> signImgList = measures['SIGN_ITEM'] ?? []; @@ -312,44 +306,54 @@ class _HotworkSafeFuncSureState extends State { final List imgList = []; if ((measures['IMG_PATH'] as String?)?.isNotEmpty ?? false) { for (var path in (measures['IMG_PATH'] as String).split(',')) { - imgList.add(ImageData(localPath:path, serverPath: path)); + imgList.add(ImageData(localPath: path, serverPath: path)); } } //measures['STATUS'] - final result = await pushPage(DangerousOptionsPage(index: index,status: int.parse(FormUtils.hasValue(measures, 'STATUS') ? measures['STATUS'] : '1'), - measures: measures['PROTECTIVE_MEASURES'] ?? '', - imgList: imgList, - signImgList: signImgList), context); + final result = await pushPage( + DangerousOptionsPage( + index: index, + status: int.parse( + FormUtils.hasValue(measures, 'STATUS') ? measures['STATUS'] : '1', + ), + measures: measures['PROTECTIVE_MEASURES'] ?? '', + imgList: imgList, + signImgList: signImgList, + ), + context, + ); if (result != null) { setState(() { // 更新返回的图片列表(local + remote) final returned = result['imgList'] as List; // 先映射成 ImageData 列表 - final imgDataList = returned.map((m) { - return ImageData( - localPath: m['local'] as String, - serverPath: m['remote'] as String, - ); - }).toList(); + final imgDataList = + returned.map((m) { + return ImageData( + localPath: m['local'] as String, + serverPath: m['remote'] as String, + ); + }).toList(); // 抽取所有非空的 serverPath 并拼成逗号分隔的字符串 final serverPathString = imgDataList .map((e) => e.serverPath) .where((s) => s.isNotEmpty) - .map((s) => s) // 将 String? 转回 String + .map((s) => s) // 将 String? 转回 String .join(','); // 存回 measures measures['IMG_PATH'] = serverPathString; // 更新签字列表 - measures['SIGN_ITEM'] = List>.from(result['signImgList'] as List); + measures['SIGN_ITEM'] = List>.from( + result['signImgList'] as List, + ); // 更新状态、序号等 measures['STATUS'] = result['status'].toString(); index = result['index'] as int; }); } - } /// 安全防护措施 @@ -370,8 +374,7 @@ class _HotworkSafeFuncSureState extends State { Container( color: Colors.white, child: MeasuresListWidget( - measuresList: - measuresList, // List> + measuresList: measuresList, // List> baseImgPath: ApiService.baseImgPath, isAllowEdit: true, onSign: (item) { @@ -381,11 +384,12 @@ class _HotworkSafeFuncSureState extends State { ), ], ), - ItemListWidget.singleLineTitleText(label: '其他安全措施:', - isEditable: true, - hintText: '请输入其他安全措施', - controller - :_otherController), + ItemListWidget.singleLineTitleText( + label: '其他安全措施:', + isEditable: true, + hintText: '请输入其他安全措施', + controller: _otherController, + ), SizedBox(height: 20), Row( @@ -492,9 +496,8 @@ class MeasureItem { List>? userList, this.userIndex = -1, List>? selectMeasures, - }) - : userList = userList ?? [], - selectMeasures = selectMeasures ?? []; + }) : userList = userList ?? [], + selectMeasures = selectMeasures ?? []; Map toJson() { return { diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/aqgl_work_detail/hotwork_aqgl_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/aqgl_work_detail/hotwork_aqgl_detail.dart index 31f479e..c2e028e 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/aqgl_work_detail/hotwork_aqgl_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/aqgl_work_detail/hotwork_aqgl_detail.dart @@ -51,13 +51,14 @@ class _HotworkAqglDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -187,7 +188,7 @@ class _HotworkAqglDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/aqjd_work_detail/hotwork_aqjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/aqjd_work_detail/hotwork_aqjd_detail.dart index 970f598..eb5d405 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/aqjd_work_detail/hotwork_aqjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/aqjd_work_detail/hotwork_aqjd_detail.dart @@ -79,13 +79,14 @@ class _HotworkAqjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -217,7 +218,7 @@ class _HotworkAqjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/dbbz_work_detail/hotwork_dbbz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/dbbz_work_detail/hotwork_dbbz_detail.dart index 3a278be..56cc116 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/dbbz_work_detail/hotwork_dbbz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/dbbz_work_detail/hotwork_dbbz_detail.dart @@ -53,13 +53,14 @@ class _HotworkDbbzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -190,7 +191,7 @@ class _HotworkDbbzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart index 2e03f17..3374927 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart @@ -1,13 +1,17 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart'; import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/department_person_picker.dart'; import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/HotWorkDetailFormWidget.dart'; +import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart'; import 'package:qhd_prevention/tools/h_colors.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/http/ApiService.dart'; @@ -16,12 +20,12 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/qtfx_ import 'package:qhd_prevention/customWidget/bottom_picker.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart'; - enum EditUserType { ANALYZE('分析单位', '分析单位负责人', true), GUARDIAN('监护人单位', '监护人', true), CONFESS('安全交底人单位', '安全交底人', true), ACCEPT_CONFESS('接受交底人单位', '接受交底人', true), + WORK_USER('作业人单位', '作业人', true), CONFIRM('作业负责人单位', '作业负责人', true), LEADER('所在单位', '所在单位负责人', true), AUDIT('安全管理部门', '安全管理部门负责人', true), @@ -39,7 +43,6 @@ enum EditUserType { const EditUserType(this.displayName, this.personName, this.isRequired); } - class HotworkApplyDetail extends StatefulWidget { const HotworkApplyDetail({ super.key, @@ -77,6 +80,15 @@ class _HotworkApplyDetailState extends State { /// 动火人及证书编号 late List workUserList = []; + /// ------------------- 新增 ------------------- + /// 视频监控摄像 + late List videoMonitoringList = []; + /// 承包商列表 + late List unitAllList = []; + /// 作业区域列表 + late List> workAreaList = []; + /// -------------------------------------- + // 存储各单位的人员列表 final Map>> _personCache = {}; @@ -96,6 +108,9 @@ class _HotworkApplyDetailState extends State { pd['APPLY_USER_NAME'] = SessionService.instance.username; } _getHotWorkNameList(); + _getVideoList(); + _getUnitListAll(); + _getPlsList(); _contentController.addListener(() { setState(() { @@ -128,7 +143,11 @@ class _HotworkApplyDetailState extends State { } void set_pd_USER_ID(EditUserType type, String id) { - pd['${type.name}_USER_ID'] = id; + if (type == EditUserType.WORK_USER) { + pd['${type.name}_ID'] = id; + }else{ + pd['${type.name}_USER_ID'] = id; + } } void set_pd_USER_Name(EditUserType type, String name) { @@ -144,7 +163,11 @@ class _HotworkApplyDetailState extends State { } String get_pd_USER_ID(EditUserType type) { - return pd['${type.name}_USER_ID'] ?? ''; + if (type == EditUserType.WORK_USER) { + return pd['${type.name}_ID'] ?? ''; + } else { + return pd['${type.name}_USER_ID'] ?? ''; + } } String get_pd_USER_Name(EditUserType type) { @@ -190,6 +213,153 @@ class _HotworkApplyDetailState extends State { } } + /// ---------------------------- 新增 -------------------------------- + /// 视频监控摄像头 + Future _chooseVideoManager() async { + final choice = await BottomPicker.show( + context, + items: + videoMonitoringList + .map((item) => item['VIDEONAME'] as String) + .toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['VIDEONAME'] = choice; + + Map result = videoMonitoringList.firstWhere( + (item) => item['VIDEONAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) { + pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 选择承包商 + Future _chooseUnitManager() async { + final choice = await BottomPicker.show( + context, + items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['UNITS_NAME'] = choice; + + Map result = unitAllList.firstWhere( + (item) => item['UNITS_NAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'UNITS_ID')) { + pd['UNITS_ID'] = result['UNITS_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 预计作业开始时间 + Future _chooseWorkStartTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_START_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + + /// 开始时间必须早于结束时间 + if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') && + !isBeforeStr( + pd['WORK_EXPECTED_START_TIME'], + pd['WORK_EXPECTED_END_TIME'], + )) { + pd['WORK_EXPECTED_END_TIME'] = ''; + } + }); + } + } + /// 预计作业结束时间 + Future _chooseWorkEndTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '', + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_END_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + } + } + /// 选择经纬度 + Future _showLocationHandle() async{ + if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) { + ToastUtil.showNormal(context, '请选择作业区域'); + return; + } + Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context); + setState(() { + pd['LONGITUDE'] = mapData['longitue']; + pd['LATITUDE'] = mapData['latitude']; + pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}'; + }); + + } + /// 作业区域 + Future _getWorkArea() async { + FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => WorkAreaPicker( + onSelected: (String id, String POSITIONS, String name) { + pd['ELECTRONIC_FENCE_AREA_ID'] = id; + pd['POSITIONS'] = POSITIONS; + pd['PLS_NAME'] = name; + }, + ), + ).then((_) { + FocusHelper.clearFocus(context); + }); + } + /// 获取摄像头列表 + Future _getVideoList() async { + final result = await ApiService.getVideomanagerList(); + setState(() { + videoMonitoringList = result['varList'] ?? []; + }); + } + /// 获承包商列表 + Future _getUnitListAll() async { + final result = await ApiService.getUnitListAll(); + setState(() { + unitAllList = result['varList'] ?? []; + }); + } + /// 作业区域列表 + Future _getPlsList() async { + final result = await ApiService.getWorkAreaList(); + setState(() { + final String zTreeNodes = result['zTreeNodes'] ?? ''; + workAreaList = jsonDecode(zTreeNodes); + }); + } + /// ------------------------------------------------------------ + + Widget _card(Widget child) { return Container( padding: EdgeInsets.symmetric(vertical: 5), @@ -211,14 +381,13 @@ class _HotworkApplyDetailState extends State { isRequired = false; } - // 如果既不是二级也不是一级,就加上 APPROVE - if (type == EditUserType.APPROVE && pd['WORK_LEVEL'] != '特级') { - isRequired = false; - isClean = true; - - } - // EditUserType.APPROVE, - if (type == EditUserType.APPROVE && + // 如果既不是二级也不是一级,就加上 APPROVE + if (type == EditUserType.APPROVE && pd['WORK_LEVEL'] != '特级') { + isRequired = false; + isClean = true; + } + // EditUserType.APPROVE, + if (type == EditUserType.APPROVE && ((pd['WORK_LEVEL'] ?? '') == '二级' || (pd['WORK_LEVEL'] ?? '') == '一级')) { isClean = true; @@ -301,13 +470,15 @@ class _HotworkApplyDetailState extends State { return; } - if (personList.isEmpty) { // 一般这种情况是因为重新编辑没有缓存对应部门的负责人,所以先拉取一下接口 + if (personList.isEmpty) { + // 一般这种情况是因为重新编辑没有缓存对应部门的负责人,所以先拉取一下接口 await _getPersonListForUnitId(unitId, type); final list = _personCache[type] ?? []; - if (list.isEmpty) { // 如果还是没数据,说明该部门没有可选的人 + if (list.isEmpty) { + // 如果还是没数据,说明该部门没有可选的人 ToastUtil.showNormal(context, '暂无数据,请选择其他单位'); - }else{ + } else { choosePersonHandle(type); } return; @@ -355,14 +526,15 @@ class _HotworkApplyDetailState extends State { EditUserType.GUARDIAN, EditUserType.CONFESS, EditUserType.ACCEPT_CONFESS, + EditUserType.WORK_USER, EditUserType.CONFIRM, EditUserType.LEADER, // 如果不是二级,或者SAFETY_USER_ID有值就加上 AUDIT - if (pd['WORK_LEVEL'] != '二级' || FormUtils.hasValue(pd, 'SAFETY_USER_ID')) EditUserType.AUDIT, + if (pd['WORK_LEVEL'] != '二级' || FormUtils.hasValue(pd, 'SAFETY_USER_ID')) EditUserType.AUDIT, + EditUserType.AUDIT, // 如果既不是二级也不是一级,就加上 APPROVE - if (pd['WORK_LEVEL'] == '特级') - EditUserType.APPROVE, + if (pd['WORK_LEVEL'] == '特级') EditUserType.APPROVE, EditUserType.MONITOR, EditUserType.WORK_START, @@ -383,6 +555,30 @@ class _HotworkApplyDetailState extends State { ToastUtil.showNormal(context, '请选择动火级别'); return; } + if (level == '特级') { + if (!FormUtils.hasValue(pd, 'VIDEOMANAGER_ID')) { + ToastUtil.showNormal(context, '请选择视频监控'); + return; + } + } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业开始时间'); + return; + } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业开始时间'); + return; + } + + if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) { + ToastUtil.showNormal(context, '请选择承包商'); + return; + } + + if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) { + ToastUtil.showNormal(context, '请定位作业地点'); + return; + } for (var type in unitRules) { if (get_pd_DEPARTMENT_ID(type).length == 0) { @@ -433,10 +629,9 @@ class _HotworkApplyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存'); - Navigator.pop(context); - }else{ + Navigator.of(context).pop(true); + } else { ToastUtil.showSuccess(context, '提交失败'); - } } catch (e) { LoadingDialogHelper.hide(); @@ -447,15 +642,20 @@ class _HotworkApplyDetailState extends State { Future _getHotWorkNameList() async { final result = await ApiService.getHotWorkNameList(); setState(() { - workUserList = result['varList'] ?? ''; - List names = - workUserList.map((item) => item['NAME'] as String).toList(); + workUserList = result['varList'] ?? []; }); } + + +/// ---------------------------- 接口请求 -------------------------------- + /// 初始化拉取数据 Future _getData() async { - final data = await ApiService.getHomeworkFindById('hotwork', widget.HOTWORK_ID); + final data = await ApiService.getHomeworkFindById( + 'hotwork', + widget.HOTWORK_ID, + ); setState(() { pd = data['pd']; if (pd['STEP_ID'] == 0) { @@ -478,18 +678,18 @@ class _HotworkApplyDetailState extends State { // }); // LoadingDialogHelper.hide(); } - Future _getSigns(String homework_id) async { - final data = await ApiService.listSignFinished('hotwork', + final data = await ApiService.listSignFinished( + 'hotwork', homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, ); setState(() { signs = data['signs'] ?? {}; }); } - Future _getMeasures(String homework_id) async { - final data = await ApiService.listSignFinishMeasures('hotwork', + final data = await ApiService.listSignFinishMeasures( + 'hotwork', homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, ); setState(() { @@ -521,6 +721,14 @@ class _HotworkApplyDetailState extends State { riskController: _riskController, onChooseLevel: _chooseLevel, onChooseHotworkUser: _chooseHorkUser, + // 新增 + onChooseVideoManager: _chooseVideoManager, + onWorkStartTimeHandle: _chooseWorkStartTime, + onWorkEndTimeHandle: _chooseWorkEndTime, + onContractorHandle: _chooseUnitManager, + onWorkAreaHandle: _getWorkArea, + onWorkAreaLocationHandle: _showLocationHandle, + onAnalyzeTap: () { pushPage( HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), @@ -541,6 +749,8 @@ class _HotworkApplyDetailState extends State { SizedBox(height: 15), _card(_chooseItem(EditUserType.ACCEPT_CONFESS)), SizedBox(height: 15), + _card(_chooseItem(EditUserType.WORK_USER)), + SizedBox(height: 15), _card(_chooseItem(EditUserType.CONFIRM)), SizedBox(height: 15), _card(_chooseItem(EditUserType.LEADER)), @@ -604,7 +814,7 @@ class _HotworkApplyDetailState extends State { color: Colors.white, child: MeasuresListWidget( measuresList: - measuresList, // List> + measuresList, // List> baseImgPath: ApiService.baseImgPath, isAllowEdit: false, ), @@ -632,45 +842,9 @@ class _HotworkApplyDetailState extends State { ), isEditable ? Row( - spacing: 10, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: CustomButton( - height: 45, - textStyle: TextStyle( - fontSize: 16, - color: Colors.white, - ), - text: '提交', - backgroundColor: Colors.blue, - onPressed: () { - _submit('1'); - }, - ), - ), - - Expanded( - child: CustomButton( - textStyle: TextStyle( - fontSize: 16, - color: Colors.white, - ), - text: '暂存', - backgroundColor: Colors.green, - onPressed: () { - _submit('0'); - }, - ), - ), - ], - ) - : Column( - children: [ - SizedBox(height: 20), - Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox(width: 50), Expanded( child: CustomButton( height: 45, @@ -678,18 +852,54 @@ class _HotworkApplyDetailState extends State { fontSize: 16, color: Colors.white, ), - text: '返回', - backgroundColor: Colors.green, + text: '提交', + backgroundColor: Colors.blue, onPressed: () { - Navigator.pop(context); + _submit('1'); }, ), ), - SizedBox(width: 50), + + Expanded( + child: CustomButton( + textStyle: TextStyle( + fontSize: 16, + color: Colors.white, + ), + text: '暂存', + backgroundColor: Colors.green, + onPressed: () { + _submit('0'); + }, + ), + ), + ], + ) + : Column( + children: [ + SizedBox(height: 20), + Row( + children: [ + SizedBox(width: 50), + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle( + fontSize: 16, + color: Colors.white, + ), + text: '返回', + backgroundColor: Colors.green, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + SizedBox(width: 50), + ], + ), ], ), - ], - ), ], ), ), diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/dhsp_work_detail/hotwork_dhsp_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/dhsp_work_detail/hotwork_dhsp_detail.dart index fd8cdb7..4e6950b 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/dhsp_work_detail/hotwork_dhsp_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/dhsp_work_detail/hotwork_dhsp_detail.dart @@ -50,13 +50,14 @@ class _HotworkDhspDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -187,7 +188,7 @@ class _HotworkDhspDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/hotwork_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/hotwork_list_page.dart index f7d62f3..c3ffeb3 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/hotwork_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/hotwork_list_page.dart @@ -13,7 +13,9 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/szaq_ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/szdw_work_detail/hotwork_szdw_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/ysgd_work_detail/hotwork_ysgd_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/zyfz_work_detail/hotwork_zyfz_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/services/location_service.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; @@ -159,9 +161,15 @@ class _HotWorkListPageState extends State { } } + void _goToDetail(Map item) async { - final Map data = {'HOTWORK_ID': item['HOTWORK_ID'], 'flow': widget.flow}; - String routeName = ''; + final allowed = await WorkAreaHelper.checkInSpecialWorkArea( + context: context, + flow: widget.flow, + areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '', + ); + if (!allowed) return; + switch (widget.flow) { case '提交申请': await pushPage(HotworkApplyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); @@ -177,71 +185,55 @@ class _HotWorkListPageState extends State { await pushPage(HotworkSafeFuncSure(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '监护人签字': - routeName = '/hotwork-guardian-detail'; await pushPage(HotworkJhrDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '安全交底人签字': - routeName = '/hotwork-confess-detail'; await pushPage(HotworkAqjdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '接受交底人签字': - routeName = '/hotwork-acceptconfess-detail'; await pushPage(HotworkJsjdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '作业负责人签字': - routeName = '/hotwork-confirm-detail'; await pushPage(HotworkZyfzDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '所在单位签字': - routeName = '/hotwork-leader-detail'; await pushPage(HotworkSzdwDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '安全管理部门签字': - routeName = '/hotwork-audit-detail'; await pushPage(HotworkAqglDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '审批人签字': - routeName = '/hotwork-approve-detail'; await pushPage(HotworkDhspDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '当班班长验票': - routeName = '/hotwork-monitor-detail'; await pushPage(HotworkDbbzDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '开始作业': - routeName = '/hotwork-startwork-detail'; await pushPage(HotworkKszyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '结束作业': - routeName = '/hotwork-endwork-detail'; await pushPage(HotworkJszyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '验收签字': - routeName = '/hotwork-accept-detail'; await pushPage(HotworkYsgdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; default: - routeName = '/hotwork-detail'; + break; } setState(() { _fetchSteps(); _fetchData(); }); - // Navigator.pushNamed( - // context, - // routeName, - // arguments: {'HOTWORK_ID': item['HOTWORK_ID'], 'flow': widget.flow}, - // ); } Widget _buildFlowStepItem({ diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/jhr_work_detail/hotwork_jhr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/jhr_work_detail/hotwork_jhr_detail.dart index 0cc27b7..21526eb 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/jhr_work_detail/hotwork_jhr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/jhr_work_detail/hotwork_jhr_detail.dart @@ -47,13 +47,14 @@ class _HotworkJhrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -180,7 +181,7 @@ class _HotworkJhrDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/jsjd_work_detail/hotwork_jsjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/jsjd_work_detail/hotwork_jsjd_detail.dart index 4862cdf..cbd8861 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/jsjd_work_detail/hotwork_jsjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/jsjd_work_detail/hotwork_jsjd_detail.dart @@ -47,13 +47,14 @@ class _HotworkJsjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -178,7 +179,7 @@ class _HotworkJsjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/jszy_work_detail/hotwork_jszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/jszy_work_detail/hotwork_jszy_detail.dart index dd2c4ad..017b79d 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/jszy_work_detail/hotwork_jszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/jszy_work_detail/hotwork_jszy_detail.dart @@ -52,13 +52,14 @@ class _HotworkJszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -223,7 +224,7 @@ class _HotworkJszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/kszy_work_detail/hotwork_kszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/kszy_work_detail/hotwork_kszy_detail.dart index 7e9ba71..06678d1 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/kszy_work_detail/hotwork_kszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/kszy_work_detail/hotwork_kszy_detail.dart @@ -52,13 +52,14 @@ class _HotworkKszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -195,7 +196,7 @@ class _HotworkKszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/szaq_work_detail/hotwork_set_safe_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/szaq_work_detail/hotwork_set_safe_detail.dart index 5233e37..d313f19 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/szaq_work_detail/hotwork_set_safe_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/szaq_work_detail/hotwork_set_safe_detail.dart @@ -18,6 +18,7 @@ import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/pages/mine/mine_sign_page.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/dh_work/szaq_work_detail/SafeFunctionDialog.dart'; + /// 设置安全措施确认人 class HotworkSetSafeDetail extends StatefulWidget { const HotworkSetSafeDetail({ @@ -57,8 +58,6 @@ class _HotworkSetSafeDetailState extends State { return jsonEncode(jsonList); } - - Widget _chooseItem(MeasureItem item) { return Column( children: [ @@ -83,9 +82,9 @@ class _HotworkSetSafeDetailState extends State { onTap: () { showSafeFunctionDialog( context, - _getAvailableMeasures(item), // 只传可用的项 - initialSelected: item.selectMeasures, // 保留当前已选的 - (selected) { + _getAvailableMeasures(item), // 只传可用的项 + initialSelected: item.selectMeasures, // 保留当前已选的 + (selected) { setState(() { item.selectMeasures = selected; }); @@ -163,10 +162,13 @@ class _HotworkSetSafeDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); + FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); @@ -249,17 +251,11 @@ class _HotworkSetSafeDetailState extends State { int index = 0; for (var item in measuresListCopy) { if (item.USER_ID.isEmpty) { - ToastUtil.showNormal( - context, - '第${index + 1}项未设置确认人', - ); + ToastUtil.showNormal(context, '第${index + 1}项未设置确认人'); return; } if (item.selectMeasures.isEmpty) { - ToastUtil.showNormal( - context, - '第${index + 1}项未选择安全措施', - ); + ToastUtil.showNormal(context, '第${index + 1}项未选择安全措施'); return; } final userId = item.USER_ID; @@ -310,37 +306,35 @@ class _HotworkSetSafeDetailState extends State { formData['STEP_REASON'] = reasonText; formData['PREPARERS'] = json.encode(signers); printLongString('submit---:${json.encode(formData)}'); - await showDialog( - context: context, - builder: - (_) => CustomAlertDialog( - title: '提示', - content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', - cancelText: '取消', - confirmText: '确定', - onConfirm: () async { - LoadingDialogHelper.show(); - try { - final result = await ApiService.saveSafeFunctionSure( -'hotwork', - formData, - imagePaths, - ); - LoadingDialogHelper.hide(); - if (result['result'] == 'success') { - ToastUtil.showSuccess( - context, - '保存成功', - ); - Navigator.pop(context); - } - } catch (e) { - LoadingDialogHelper.hide(); - ToastUtil.showNormal(context, '操作失败:$e'); - } - }, - ), + final confirmed = await CustomAlertDialog.showConfirm( + context, + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + barrierDismissible: false, ); + if (confirmed) { + LoadingDialogHelper.show(); + try { + final result = await ApiService.saveSafeFunctionSure( + 'hotwork', + formData, + imagePaths, + ); + LoadingDialogHelper.hide(); + + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.of(context).pop(true); + } else { + ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}'); + } + } catch (e) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '操作失败:$e'); + } + } } void printLongString(String text, {int chunkSize = 800}) { @@ -352,7 +346,10 @@ class _HotworkSetSafeDetailState extends State { /// 初始化拉取数据 Future _getData() async { - final data = await ApiService.getHomeworkFindById('hotwork', widget.HOTWORK_ID); + final data = await ApiService.getHomeworkFindById( + 'hotwork', + widget.HOTWORK_ID, + ); setState(() { pd = data['pd']; _getMeasures(); @@ -390,6 +387,7 @@ class _HotworkSetSafeDetailState extends State { ); }); } + /// 计算当前 item 可选的安全措施 List> _getAvailableMeasures(MeasureItem current) { // 把除了 current 之外,已经被选走的措施 ID 全部搜集起来 @@ -401,17 +399,18 @@ class _HotworkSetSafeDetailState extends State { } } // 返回:所有 measuresList 中,ID 不在 usedIds 或者正好属于 current 的那几条 - List> list = measuresList.where((m) { - final id = m['BUS_HOTWORK_MEASURES_ID']; - final isCurrentSelected = current.selectMeasures - .any((sm) => sm['BUS_HOTWORK_MEASURES_ID'] == id); - return !usedIds.contains(id) || isCurrentSelected; - }).toList(); + List> list = + measuresList.where((m) { + final id = m['BUS_HOTWORK_MEASURES_ID']; + final isCurrentSelected = current.selectMeasures.any( + (sm) => sm['BUS_HOTWORK_MEASURES_ID'] == id, + ); + return !usedIds.contains(id) || isCurrentSelected; + }).toList(); return list; } - /// 安全防护措施 Widget _setSafeDetailWidget() { return Container( @@ -570,8 +569,8 @@ class _HotworkSetSafeDetailState extends State { HotWorkDetailFormWidget( pd: pd, isEditable: false, - onChooseLevel: (){}, - onChooseHotworkUser: (){}, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, onAnalyzeTap: () { pushPage( HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/szdw_work_detail/hotwork_szdw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/szdw_work_detail/hotwork_szdw_detail.dart index d03a83a..6f9e67a 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/szdw_work_detail/hotwork_szdw_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/szdw_work_detail/hotwork_szdw_detail.dart @@ -50,13 +50,14 @@ class _HotworkSzdwDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -187,7 +188,7 @@ class _HotworkSzdwDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/ysgd_work_detail/hotwork_ysgd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/ysgd_work_detail/hotwork_ysgd_detail.dart index b1e051c..6c82b29 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/ysgd_work_detail/hotwork_ysgd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/ysgd_work_detail/hotwork_ysgd_detail.dart @@ -85,13 +85,14 @@ class _HotworkYsgdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -248,7 +249,7 @@ class _HotworkYsgdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work/zyfz_work_detail/hotwork_zyfz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work/zyfz_work_detail/hotwork_zyfz_detail.dart index 8868db5..86af19a 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work/zyfz_work_detail/hotwork_zyfz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work/zyfz_work_detail/hotwork_zyfz_detail.dart @@ -50,13 +50,14 @@ class _HotworkZyfzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -187,7 +188,7 @@ class _HotworkZyfzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/CutroadDetailFormWidget.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/CutroadDetailFormWidget.dart index a79f7c9..c60be43 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/CutroadDetailFormWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/CutroadDetailFormWidget.dart @@ -20,6 +20,18 @@ class CutroadDetailFormWidget extends StatefulWidget { final TextEditingController? contentController; // 原因 final TextEditingController? relatedController; final TextEditingController? riskController; + + // 新增 + final VoidCallback? onChooseVideoManager; + /// 选择摄像头 + final VoidCallback? onWorkStartTimeHandle; + final VoidCallback? onWorkEndTimeHandle; + /// 承包商 + final VoidCallback? onContractorHandle; + /// 作业区域 + final VoidCallback? onWorkAreaHandle; + /// 作业地点经纬度 + final VoidCallback? onWorkAreaLocationHandle; /// 其他签字数据 final signs; @@ -28,6 +40,15 @@ class CutroadDetailFormWidget extends StatefulWidget { required this.pd, required this.isEditable, required this.onChooseLevel, + + /// 新增 + this.onChooseVideoManager, + this.onWorkStartTimeHandle, + this.onWorkEndTimeHandle, + this.onContractorHandle, + this.onWorkAreaHandle, + this.onWorkAreaLocationHandle, + this.unitController, this.contentController, this.relatedController, @@ -225,7 +246,11 @@ class _CutroadDetailFormWidgetState extends State { } @override Widget build(BuildContext context) { + if (FormUtils.hasValue(widget.pd, 'LATITUDE')) { + widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map + } final pd = widget.pd; + return Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: const BoxDecoration( @@ -283,9 +308,78 @@ class _CutroadDetailFormWidgetState extends State { ), const Divider(), ], + ItemListWidget.selectableLineTitleTextRightButton( + label: '视频监控:', + isClean: true, + cleanText: '清除监控', + isRequired: false, + isEditable: widget.isEditable, + onTap: widget.onChooseVideoManager ?? () {}, + text: pd['VIDEONAME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业开始时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkStartTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_START_TIME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业结束时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkEndTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_END_TIME'] ?? '', + ), + const Divider(), + ListItemFactory.createYesNoSection( + verticalPadding: 0, + horizontalPadding: 0, + title: '是否承包商作业', + isEdit: widget.isEditable, + text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否', + isRequired: true, + groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1', + onChanged: (bool value) { + setState(() { + widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0"; + }); + }, + ), + if (pd['IS_CONTRACTOR_WORK'] == '1') + Column( + children: [ + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '承包商:', + isEditable: widget.isEditable, + onTap: widget.onContractorHandle ?? () {}, + text: pd['UNITS_NAME'] ?? '', + ), + ], + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '作业区域:', + isEditable: widget.isEditable, + onTap: widget.onWorkAreaHandle ?? () {}, + text: pd['PLS_NAME'] ?? '', + ), + const Divider(), + + ItemListWidget.twoRowButtonTitleText( + label: '作业地点经纬度', + isInput: false, + isEditable: widget.isEditable, + buttonText: '定位', + onTap: widget.onWorkAreaLocationHandle ?? (){}, + hintText: '', + text: pd['LATITUDE_LONGITUDE'] ?? '无', + ), const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '关联其他特殊作业及安全作业票编号:', + label: '关联其他特殊作业及安全作业票编号', isEditable: widget.isEditable, onTap: () async { final val = await showDialog( @@ -309,7 +403,7 @@ class _CutroadDetailFormWidgetState extends State { ), const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '风险辨识结果:', + label: '风险辨识结果', isEditable: widget.isEditable, onTap: () async { await showDialog( diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/aqcs_work_detail/cutroad_safe_func_sure.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/aqcs_work_detail/cutroad_safe_func_sure.dart index 91c1ab6..44b7e60 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/aqcs_work_detail/cutroad_safe_func_sure.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/aqcs_work_detail/cutroad_safe_func_sure.dart @@ -42,8 +42,7 @@ class _CutroadSafeFuncSureState extends State { /// 其他安全措施 final TextEditingController _otherController = TextEditingController(); - /// 动火人及证书编号 - late List workUserList = []; + /// 安全防护措施列表 List imagePaths = []; @@ -52,7 +51,7 @@ class _CutroadSafeFuncSureState extends State { void initState() { super.initState(); _getData(); - _getHotWorkNameList(); + } /// 弹出单位选择 @@ -90,14 +89,15 @@ class _CutroadSafeFuncSureState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -259,7 +259,7 @@ class _CutroadSafeFuncSureState extends State { context, status == '1' ? '提交成功' : '已暂存', ); - Navigator.pop(context); + Navigator.of(context).pop(true); // 传 true 表示成功(可选) } } catch (e) { LoadingDialogHelper.hide(); @@ -277,12 +277,7 @@ class _CutroadSafeFuncSureState extends State { } } - Future _getHotWorkNameList() async { - final result = await ApiService.getHotWorkNameList(); - setState(() { - workUserList = result['varList'] ?? ''; - }); - } + /// 初始化拉取数据 Future _getData() async { diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/aqjd_work_detail/cutroad_aqjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/aqjd_work_detail/cutroad_aqjd_detail.dart index 6c0eb4a..5e60542 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/aqjd_work_detail/cutroad_aqjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/aqjd_work_detail/cutroad_aqjd_detail.dart @@ -78,13 +78,14 @@ class _CutroadAqjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -217,7 +218,7 @@ class _CutroadAqjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/cutroad_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/cutroad_list_page.dart index 40743f4..ed3768b 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/cutroad_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/cutroad_list_page.dart @@ -14,6 +14,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/szdw_ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/ysgd_work_detail/cutroad_ysgd_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/zyfz_work_detail/cutroad_zyfz_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/zyr_work_detail/cutroad_zyr_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; @@ -167,10 +168,12 @@ class _CutroadListPageState extends State { } void _goToDetail(Map item) async { - final Map data = { - 'CUTROAD_ID': item['CUTROAD_ID'], - 'flow': widget.flow, - }; + final allowed = await WorkAreaHelper.checkInSpecialWorkArea( + context: context, + flow: widget.flow, + areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '', + ); + if (!allowed) return; switch (widget.flow) { case '提交申请': await pushPage(CutroadApplyDetail(CUTROAD_ID: item['CUTROAD_ID'], flow: widget.flow), context); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/dl_work_detai/cutroad_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/dl_work_detai/cutroad_apply_detail.dart index 5681b19..5d1f429 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/dl_work_detai/cutroad_apply_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/dl_work_detai/cutroad_apply_detail.dart @@ -1,10 +1,12 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/department_person_picker.dart'; import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/CutroadDetailFormWidget.dart'; @@ -14,6 +16,8 @@ import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart'; +import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart'; +import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart'; enum EditUserType { @@ -65,9 +69,15 @@ class _CutroadApplyDetailState extends State { final TextEditingController _contentController = TextEditingController(); final TextEditingController _relatedController = TextEditingController(); final TextEditingController _riskController = TextEditingController(); + /// ------------------- 新增 ------------------- + /// 视频监控摄像 + late List videoMonitoringList = []; + /// 承包商列表 + late List unitAllList = []; + /// 作业区域列表 + late List> workAreaList = []; + /// -------------------------------------- - /// 动火人及证书编号 - late List workUserList = []; // 存储各单位的人员列表 final Map>> _personCache = {}; @@ -86,8 +96,10 @@ class _CutroadApplyDetailState extends State { pd['APPLY_USER_ID'] = SessionService.instance.loginUserId; pd['APPLY_USER_NAME'] = SessionService.instance.username; } - _getHotWorkNameList(); + _getVideoList(); + _getUnitListAll(); + _getPlsList(); _contentController.addListener(() { setState(() { pd['WORK_REASON'] = _contentController.text.trim(); @@ -296,6 +308,24 @@ class _CutroadApplyDetailState extends State { } } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业开始时间'); + return; + } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业结束时间'); + return; + } + + if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) { + ToastUtil.showNormal(context, '请选择承包商'); + return; + } + + if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) { + ToastUtil.showNormal(context, '请定位作业地点'); + return; + } for (var type in unitRules) { if (get_pd_DEPARTMENT_ID(type).length == 0) { @@ -307,6 +337,7 @@ class _CutroadApplyDetailState extends State { return; } } + } // LoadingDialogHelper.show(); @@ -342,7 +373,7 @@ class _CutroadApplyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); @@ -357,14 +388,152 @@ class _CutroadApplyDetailState extends State { } } - Future _getHotWorkNameList() async { - final result = await ApiService.getHotWorkNameList(); + + /// ---------------------------- 新增 -------------------------------- + /// 视频监控摄像头 + Future _chooseVideoManager() async { + final choice = await BottomPicker.show( + context, + items: + videoMonitoringList + .map((item) => item['VIDEONAME'] as String) + .toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['VIDEONAME'] = choice; + + Map result = videoMonitoringList.firstWhere( + (item) => item['VIDEONAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) { + pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 选择承包商 + Future _chooseUnitManager() async { + final choice = await BottomPicker.show( + context, + items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['UNITS_NAME'] = choice; + + Map result = unitAllList.firstWhere( + (item) => item['UNITS_NAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'UNITS_ID')) { + pd['UNITS_ID'] = result['UNITS_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 预计作业开始时间 + Future _chooseWorkStartTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_START_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + + /// 开始时间必须早于结束时间 + if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') && + !isBeforeStr( + pd['WORK_EXPECTED_START_TIME'], + pd['WORK_EXPECTED_END_TIME'], + )) { + pd['WORK_EXPECTED_END_TIME'] = ''; + } + }); + } + } + /// 预计作业结束时间 + Future _chooseWorkEndTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '', + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_END_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + } + } + /// 选择经纬度 + Future _showLocationHandle() async{ + if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) { + ToastUtil.showNormal(context, '请选择作业区域'); + return; + } + Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context); setState(() { - workUserList = result['varList'] ?? ''; - List names = - workUserList.map((item) => item['NAME'] as String).toList(); + pd['LONGITUDE'] = mapData['longitue']; + pd['LATITUDE'] = mapData['latitude']; + pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}'; + }); + + } + /// 作业区域 + Future _getWorkArea() async { + FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => WorkAreaPicker( + onSelected: (String id, String POSITIONS, String name) { + pd['ELECTRONIC_FENCE_AREA_ID'] = id; + pd['POSITIONS'] = POSITIONS; + pd['PLS_NAME'] = name; + }, + ), + ).then((_) { + FocusHelper.clearFocus(context); }); } + /// 获取摄像头列表 + Future _getVideoList() async { + final result = await ApiService.getVideomanagerList(); + setState(() { + videoMonitoringList = result['varList'] ?? []; + }); + } + /// 获承包商列表 + Future _getUnitListAll() async { + final result = await ApiService.getUnitListAll(); + setState(() { + unitAllList = result['varList'] ?? []; + }); + } + /// 作业区域列表 + Future _getPlsList() async { + final result = await ApiService.getWorkAreaList(); + setState(() { + final String zTreeNodes = result['zTreeNodes'] ?? ''; + workAreaList = jsonDecode(zTreeNodes); + }); + } + /// ------------------------------------------------------------ /// 初始化拉取数据 Future _getData() async { @@ -431,6 +600,13 @@ class _CutroadApplyDetailState extends State { relatedController: _relatedController, riskController: _riskController, onChooseLevel: (){}, + // 新增 + onChooseVideoManager: _chooseVideoManager, + onWorkStartTimeHandle: _chooseWorkStartTime, + onWorkEndTimeHandle: _chooseWorkEndTime, + onContractorHandle: _chooseUnitManager, + onWorkAreaHandle: _getWorkArea, + onWorkAreaLocationHandle: _showLocationHandle, ), ), if (isEditable) diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/jhr_work_detail/cutroad_jhr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/jhr_work_detail/cutroad_jhr_detail.dart index 93e110b..062baa5 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/jhr_work_detail/cutroad_jhr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/jhr_work_detail/cutroad_jhr_detail.dart @@ -49,13 +49,14 @@ class _CutroadJhrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -182,7 +183,7 @@ class _CutroadJhrDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/jsjd_work_detail/cutroad_jsjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/jsjd_work_detail/cutroad_jsjd_detail.dart index e46fa0e..9223523 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/jsjd_work_detail/cutroad_jsjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/jsjd_work_detail/cutroad_jsjd_detail.dart @@ -50,14 +50,15 @@ class _CutroadJsjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -181,7 +182,7 @@ class _CutroadJsjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/jszy_work_detail/cutroad_jszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/jszy_work_detail/cutroad_jszy_detail.dart index ba79832..bc5c813 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/jszy_work_detail/cutroad_jszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/jszy_work_detail/cutroad_jszy_detail.dart @@ -52,13 +52,14 @@ class _CutroadJszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -136,26 +137,7 @@ class _CutroadJszyDetailState extends State { return false; } - // 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') { - final diffMs = end.difference(start).inMilliseconds; - const max8h = 8 * 60 * 60 * 1000; - if (diffMs >= max8h) { - ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择'); - return false; - } - } - - // 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '二级') { - final diffMs = end.difference(start).inMilliseconds; - const max72h = 72 * 60 * 60 * 1000; - if (diffMs >= max72h) { - ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择'); - return false; - } - } return true; } /// 作废 -1 通过 1 @@ -223,7 +205,7 @@ class _CutroadJszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/kszy_work_detail/cutroad_kszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/kszy_work_detail/cutroad_kszy_detail.dart index d5b7081..1803a48 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/kszy_work_detail/cutroad_kszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/kszy_work_detail/cutroad_kszy_detail.dart @@ -54,14 +54,15 @@ class _CutroadKszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -190,7 +191,7 @@ class _CutroadKszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/shbm_work_detail/cutroad_shbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/shbm_work_detail/cutroad_shbm_detail.dart index f314d36..84c3604 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/shbm_work_detail/cutroad_shbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/shbm_work_detail/cutroad_shbm_detail.dart @@ -50,14 +50,15 @@ class _CutroadShbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _CutroadShbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/spbm_work_detail/cutroad_spbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/spbm_work_detail/cutroad_spbm_detail.dart index c46d6c6..dfcd0dd 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/spbm_work_detail/cutroad_spbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/spbm_work_detail/cutroad_spbm_detail.dart @@ -49,14 +49,15 @@ class _CutroadSpbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -186,7 +187,7 @@ class _CutroadSpbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/szaq_work_detail/cutroad_set_safe_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/szaq_work_detail/cutroad_set_safe_detail.dart index c6c474a..136281b 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/szaq_work_detail/cutroad_set_safe_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/szaq_work_detail/cutroad_set_safe_detail.dart @@ -163,13 +163,14 @@ class _CutroadSetSafeDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -332,7 +333,7 @@ class _CutroadSetSafeDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/szdw_work_detail/cutroad_szdw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/szdw_work_detail/cutroad_szdw_detail.dart index f1594c5..80e0727 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/szdw_work_detail/cutroad_szdw_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/szdw_work_detail/cutroad_szdw_detail.dart @@ -50,14 +50,15 @@ class _CutroadSzdwDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _CutroadSzdwDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/ysgd_work_detail/cutroad_ysgd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/ysgd_work_detail/cutroad_ysgd_detail.dart index fca5541..887762f 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/ysgd_work_detail/cutroad_ysgd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/ysgd_work_detail/cutroad_ysgd_detail.dart @@ -81,14 +81,15 @@ class _CutroadYsgdDetailState extends State { } /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -244,7 +245,7 @@ class _CutroadYsgdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/zyfz_work_detail/cutroad_zyfz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/zyfz_work_detail/cutroad_zyfz_detail.dart index 9be6c45..5969484 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/zyfz_work_detail/cutroad_zyfz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/zyfz_work_detail/cutroad_zyfz_detail.dart @@ -50,14 +50,15 @@ class _CutroadZyfzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _CutroadZyfzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dl_work/zyr_work_detail/cutroad_zyr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dl_work/zyr_work_detail/cutroad_zyr_detail.dart index dd5a511..29de46a 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dl_work/zyr_work_detail/cutroad_zyr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dl_work/zyr_work_detail/cutroad_zyr_detail.dart @@ -59,14 +59,15 @@ class _CutroadZyrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -206,7 +207,7 @@ class _CutroadZyrDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/BreakgroundDetailFormWidget.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/BreakgroundDetailFormWidget.dart index 724a8d0..bcbaf7d 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/BreakgroundDetailFormWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/BreakgroundDetailFormWidget.dart @@ -22,6 +22,17 @@ class BreakgroundDetailFormWidget extends StatefulWidget { final TextEditingController? contentController; // 内容 final TextEditingController? relatedController; final TextEditingController? riskController; +// 新增 + final VoidCallback? onChooseVideoManager; + /// 选择摄像头 + final VoidCallback? onWorkStartTimeHandle; + final VoidCallback? onWorkEndTimeHandle; + /// 承包商 + final VoidCallback? onContractorHandle; + /// 作业区域 + final VoidCallback? onWorkAreaHandle; + /// 作业地点经纬度 + final VoidCallback? onWorkAreaLocationHandle; const BreakgroundDetailFormWidget({ Key? key, @@ -29,6 +40,14 @@ class BreakgroundDetailFormWidget extends StatefulWidget { required this.isEditable, required this.onChooseLevel, required this.signs, + /// 新增 + this.onChooseVideoManager, + this.onWorkStartTimeHandle, + this.onWorkEndTimeHandle, + this.onContractorHandle, + this.onWorkAreaHandle, + this.onWorkAreaLocationHandle, + this.locationController, this.contentController, this.relatedController, @@ -254,7 +273,11 @@ class _BreakgroundDetailFormWidgetState @override Widget build(BuildContext context) { + if (FormUtils.hasValue(widget.pd, 'LATITUDE')) { + widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map + } final pd = widget.pd; + return Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: const BoxDecoration( @@ -333,9 +356,79 @@ class _BreakgroundDetailFormWidgetState ),), const Divider(), ], + ItemListWidget.selectableLineTitleTextRightButton( + label: '视频监控:', + isClean: true, + cleanText: '清除监控', + isRequired: false, + isEditable: widget.isEditable, + onTap: widget.onChooseVideoManager ?? () {}, + text: pd['VIDEONAME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业开始时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkStartTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_START_TIME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业结束时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkEndTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_END_TIME'] ?? '', + ), + const Divider(), + ListItemFactory.createYesNoSection( + verticalPadding: 0, + horizontalPadding: 0, + title: '是否承包商作业', + isEdit: widget.isEditable, + text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否', + isRequired: true, + groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1', + onChanged: (bool value) { + setState(() { + widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0"; + }); + }, + ), + if (pd['IS_CONTRACTOR_WORK'] == '1') + Column( + children: [ + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '承包商:', + isEditable: widget.isEditable, + onTap: widget.onContractorHandle ?? () {}, + text: pd['UNITS_NAME'] ?? '', + ), + ], + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '作业区域:', + isEditable: widget.isEditable, + onTap: widget.onWorkAreaHandle ?? () {}, + text: pd['PLS_NAME'] ?? '', + ), + const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '关联其他特殊作业及安全作业票编号:', + label: '作业地点经纬度', + isInput: false, + isEditable: widget.isEditable, + buttonText: '定位', + onTap: widget.onWorkAreaLocationHandle ?? (){}, + hintText: '', + text: pd['LATITUDE_LONGITUDE'] ?? '无', + ), + const Divider(), + + ItemListWidget.twoRowButtonTitleText( + label: '关联其他特殊作业及安全作业票编号', isEditable: widget.isEditable, onTap: () async { final val = await showDialog( @@ -360,7 +453,7 @@ class _BreakgroundDetailFormWidgetState ), const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '风险辨识结果:', + label: '风险辨识结果', isEditable: widget.isEditable, onTap: () async { await showDialog( @@ -383,6 +476,7 @@ class _BreakgroundDetailFormWidgetState controller: widget.riskController, text: pd['RISK_IDENTIFICATION'] ?? '', ), + // 作业人签字 if (FormUtils.hasValue(widget.signs, 'WORK_USER')) Column( diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/aqcs_work_detail/breakground_safe_func_sure.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/aqcs_work_detail/breakground_safe_func_sure.dart index 8f23c14..efad578 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/aqcs_work_detail/breakground_safe_func_sure.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/aqcs_work_detail/breakground_safe_func_sure.dart @@ -43,8 +43,7 @@ class _BreakgroundSafeFuncSureState extends State { final TextEditingController _otherController = TextEditingController(); late Map signs = {}; - /// 动火人及证书编号 - late List workUserList = []; + /// 安全防护措施列表 List imagePaths = []; @@ -90,14 +89,15 @@ class _BreakgroundSafeFuncSureState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -259,7 +259,7 @@ class _BreakgroundSafeFuncSureState extends State { context, status == '1' ? '提交成功' : '已暂存', ); - Navigator.pop(context); + Navigator.of(context).pop(true); // 传 true 表示成功(可选) } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/aqjd_work_detail/breakground_aqjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/aqjd_work_detail/breakground_aqjd_detail.dart index 5658f10..dba60ed 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/aqjd_work_detail/breakground_aqjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/aqjd_work_detail/breakground_aqjd_detail.dart @@ -78,13 +78,14 @@ class _BreakgroundAqjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -217,7 +218,7 @@ class _BreakgroundAqjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/breakground_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/breakground_list_page.dart index d20e2aa..f0e66ae 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/breakground_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/breakground_list_page.dart @@ -15,6 +15,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dt_work/szdw_ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dt_work/ysgd_work_detail/breakground_ysgd_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dt_work/zyfz_work_detail/breakground_zyfz_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dt_work/zyr_work_detail/breakground_zyr_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; @@ -169,6 +170,12 @@ class _BreakgroundListPageState extends State { } void _goToDetail(Map item) async { + final allowed = await WorkAreaHelper.checkInSpecialWorkArea( + context: context, + flow: widget.flow, + areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '', + ); + if (!allowed) return; switch (widget.flow) { case '提交申请': diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/dt_work_detai/breakground_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/dt_work_detai/breakground_apply_detail.dart index f834b2d..1db3883 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/dt_work_detai/breakground_apply_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/dt_work_detai/breakground_apply_detail.dart @@ -14,6 +14,10 @@ import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart'; +import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart'; +import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart'; enum EditUserType { @@ -68,9 +72,15 @@ class _BreakgroundApplyDetailState extends State { final TextEditingController _contentController = TextEditingController(); final TextEditingController _relatedController = TextEditingController(); final TextEditingController _riskController = TextEditingController(); + /// ------------------- 新增 ------------------- + /// 视频监控摄像 + late List videoMonitoringList = []; + /// 承包商列表 + late List unitAllList = []; + /// 作业区域列表 + late List> workAreaList = []; + /// -------------------------------------- - /// 动火人及证书编号 - late List workUserList = []; // 存储各单位的人员列表 final Map>> _personCache = {}; @@ -81,6 +91,7 @@ class _BreakgroundApplyDetailState extends State { if (widget.BREAKGROUND_ID.length > 0) { msg = 'edit'; _getData(); + } else { isEditable = true; pd['APPLY_DEPARTMENT_ID'] = SessionService.instance.deptId; @@ -94,6 +105,9 @@ class _BreakgroundApplyDetailState extends State { }); }); } + _getVideoList(); + _getUnitListAll(); + _getPlsList(); _contentController.addListener(() { setState(() { pd['JOB_CONTENT'] = _contentController.text.trim(); @@ -152,7 +166,151 @@ class _BreakgroundApplyDetailState extends State { String get_pd_USER_Name(EditUserType type) { return pd['${type.name}_USER_NAME'] ?? ''; } + /// ---------------------------- 新增 -------------------------------- + /// 视频监控摄像头 + Future _chooseVideoManager() async { + final choice = await BottomPicker.show( + context, + items: + videoMonitoringList + .map((item) => item['VIDEONAME'] as String) + .toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['VIDEONAME'] = choice; + Map result = videoMonitoringList.firstWhere( + (item) => item['VIDEONAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) { + pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 选择承包商 + Future _chooseUnitManager() async { + final choice = await BottomPicker.show( + context, + items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['UNITS_NAME'] = choice; + + Map result = unitAllList.firstWhere( + (item) => item['UNITS_NAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'UNITS_ID')) { + pd['UNITS_ID'] = result['UNITS_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 预计作业开始时间 + Future _chooseWorkStartTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_START_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + + /// 开始时间必须早于结束时间 + if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') && + !isBeforeStr( + pd['WORK_EXPECTED_START_TIME'], + pd['WORK_EXPECTED_END_TIME'], + )) { + pd['WORK_EXPECTED_END_TIME'] = ''; + } + }); + } + } + /// 预计作业结束时间 + Future _chooseWorkEndTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '', + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_END_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + } + } + /// 选择经纬度 + Future _showLocationHandle() async{ + if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) { + ToastUtil.showNormal(context, '请选择作业区域'); + return; + } + Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context); + setState(() { + pd['LONGITUDE'] = mapData['longitue']; + pd['LATITUDE'] = mapData['latitude']; + pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}'; + }); + + } + /// 作业区域 + Future _getWorkArea() async { + FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => WorkAreaPicker( + onSelected: (String id, String POSITIONS, String name) { + pd['ELECTRONIC_FENCE_AREA_ID'] = id; + pd['POSITIONS'] = POSITIONS; + pd['PLS_NAME'] = name; + }, + ), + ).then((_) { + FocusHelper.clearFocus(context); + }); + } + /// 获取摄像头列表 + Future _getVideoList() async { + final result = await ApiService.getVideomanagerList(); + setState(() { + videoMonitoringList = result['varList'] ?? []; + }); + } + /// 获承包商列表 + Future _getUnitListAll() async { + final result = await ApiService.getUnitListAll(); + setState(() { + unitAllList = result['varList'] ?? []; + }); + } + /// 作业区域列表 + Future _getPlsList() async { + final result = await ApiService.getWorkAreaList(); + setState(() { + final String zTreeNodes = result['zTreeNodes'] ?? ''; + workAreaList = jsonDecode(zTreeNodes); + }); + } + /// ------------------------------------------------------------ Future _chooseLevel() async { final choice = await BottomPicker.show( context, @@ -327,7 +485,24 @@ class _BreakgroundApplyDetailState extends State { return; } } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业开始时间'); + return; + } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业结束时间'); + return; + } + if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) { + ToastUtil.showNormal(context, '请选择承包商'); + return; + } + + if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) { + ToastUtil.showNormal(context, '请定位作业地点'); + return; + } for (var type in unitRules) { if (get_pd_DEPARTMENT_ID(type).length == 0) { ToastUtil.showNormal(context, '请选择${type.displayName}'); @@ -369,7 +544,7 @@ class _BreakgroundApplyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); @@ -451,6 +626,13 @@ class _BreakgroundApplyDetailState extends State { relatedController: _relatedController, riskController: _riskController, onChooseLevel: _chooseLevel, + // 新增 + onChooseVideoManager: _chooseVideoManager, + onWorkStartTimeHandle: _chooseWorkStartTime, + onWorkEndTimeHandle: _chooseWorkEndTime, + onContractorHandle: _chooseUnitManager, + onWorkAreaHandle: _getWorkArea, + onWorkAreaLocationHandle: _showLocationHandle, ), ), if (isEditable) diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/dzzh_work_detail/breakground_dzzh_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/dzzh_work_detail/breakground_dzzh_detail.dart index b9c4af1..65ed621 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/dzzh_work_detail/breakground_dzzh_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/dzzh_work_detail/breakground_dzzh_detail.dart @@ -50,13 +50,14 @@ class _BreakgroundDzzhDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -187,7 +188,7 @@ class _BreakgroundDzzhDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/jhr_work_detail/breakground_jhr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/jhr_work_detail/breakground_jhr_detail.dart index 5a86997..70cbe84 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/jhr_work_detail/breakground_jhr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/jhr_work_detail/breakground_jhr_detail.dart @@ -49,13 +49,14 @@ class _BreakgroundJhrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -182,7 +183,7 @@ class _BreakgroundJhrDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/jsjd_work_detail/breakground_jsjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/jsjd_work_detail/breakground_jsjd_detail.dart index 30efad7..11300b0 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/jsjd_work_detail/breakground_jsjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/jsjd_work_detail/breakground_jsjd_detail.dart @@ -47,14 +47,15 @@ class _BreakgroundJsjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -178,7 +179,7 @@ class _BreakgroundJsjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/jszy_work_detail/breakground_jszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/jszy_work_detail/breakground_jszy_detail.dart index 385dabb..b686d3b 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/jszy_work_detail/breakground_jszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/jszy_work_detail/breakground_jszy_detail.dart @@ -52,14 +52,15 @@ class _BreakgroundJszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -136,26 +137,7 @@ class _BreakgroundJszyDetailState extends State { return false; } - // 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') { - final diffMs = end.difference(start).inMilliseconds; - const max8h = 8 * 60 * 60 * 1000; - if (diffMs >= max8h) { - ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择'); - return false; - } - } - - // 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '二级') { - final diffMs = end.difference(start).inMilliseconds; - const max72h = 72 * 60 * 60 * 1000; - if (diffMs >= max72h) { - ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择'); - return false; - } - } return true; } /// 作废 -1 通过 1 @@ -223,7 +205,7 @@ class _BreakgroundJszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/kszy_work_detail/breakground_kszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/kszy_work_detail/breakground_kszy_detail.dart index b2d07eb..847c990 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/kszy_work_detail/breakground_kszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/kszy_work_detail/breakground_kszy_detail.dart @@ -54,14 +54,15 @@ class _BreakgroundKszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -190,7 +191,7 @@ class _BreakgroundKszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/shbm_work_detail/breakground_shbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/shbm_work_detail/breakground_shbm_detail.dart index 372b123..1ab4102 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/shbm_work_detail/breakground_shbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/shbm_work_detail/breakground_shbm_detail.dart @@ -50,14 +50,15 @@ class _BreakgroundShbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _BreakgroundShbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/spbm_work_detail/breakground_spbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/spbm_work_detail/breakground_spbm_detail.dart index 301e6d0..8e80b84 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/spbm_work_detail/breakground_spbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/spbm_work_detail/breakground_spbm_detail.dart @@ -49,14 +49,15 @@ class _BreakgroundSpbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -186,7 +187,7 @@ class _BreakgroundSpbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/ssr_work_detail/breakground_ssr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/ssr_work_detail/breakground_ssr_detail.dart index 47d9364..fc4d0a4 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/ssr_work_detail/breakground_ssr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/ssr_work_detail/breakground_ssr_detail.dart @@ -49,14 +49,15 @@ class _BreakgroundSsrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -180,7 +181,7 @@ class _BreakgroundSsrDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/szaq_work_detail/breakground_set_safe_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/szaq_work_detail/breakground_set_safe_detail.dart index d33d32e..77e134a 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/szaq_work_detail/breakground_set_safe_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/szaq_work_detail/breakground_set_safe_detail.dart @@ -164,13 +164,14 @@ class _BreakgroundSetSafeDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -333,7 +334,7 @@ class _BreakgroundSetSafeDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/szdw_work_detail/breakground_szdw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/szdw_work_detail/breakground_szdw_detail.dart index 8e9c35a..4ae6199 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/szdw_work_detail/breakground_szdw_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/szdw_work_detail/breakground_szdw_detail.dart @@ -50,14 +50,15 @@ class _BreakgroundSzdwDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _BreakgroundSzdwDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/ysgd_work_detail/breakground_ysgd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/ysgd_work_detail/breakground_ysgd_detail.dart index 9ad8176..c983462 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/ysgd_work_detail/breakground_ysgd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/ysgd_work_detail/breakground_ysgd_detail.dart @@ -85,14 +85,15 @@ class _BreakgroundYsgdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -248,7 +249,7 @@ class _BreakgroundYsgdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/zyfz_work_detail/breakground_zyfz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/zyfz_work_detail/breakground_zyfz_detail.dart index 4b52e36..544d2dd 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/zyfz_work_detail/breakground_zyfz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/zyfz_work_detail/breakground_zyfz_detail.dart @@ -50,14 +50,15 @@ class _BreakgroundZyfzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _BreakgroundZyfzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dt_work/zyr_work_detail/breakground_zyr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dt_work/zyr_work_detail/breakground_zyr_detail.dart index af08eb4..f36a0e9 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dt_work/zyr_work_detail/breakground_zyr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dt_work/zyr_work_detail/breakground_zyr_detail.dart @@ -63,14 +63,15 @@ class _BreakgroundZyrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -204,7 +205,7 @@ class _BreakgroundZyrDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/HoistworkDetailFormWidget.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/HoistworkDetailFormWidget.dart index a4f6247..208dcb2 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/HoistworkDetailFormWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/HoistworkDetailFormWidget.dart @@ -24,12 +24,32 @@ class HoistWorkDetailFormWidget extends StatefulWidget { final TextEditingController? relatedController; final TextEditingController? riskController; + // 新增 + final VoidCallback? onChooseVideoManager; + /// 选择摄像头 + final VoidCallback? onWorkStartTimeHandle; + final VoidCallback? onWorkEndTimeHandle; + /// 承包商 + final VoidCallback? onContractorHandle; + /// 作业区域 + final VoidCallback? onWorkAreaHandle; + /// 作业地点经纬度 + final VoidCallback? onWorkAreaLocationHandle; + const HoistWorkDetailFormWidget({ Key? key, required this.pd, required this.isEditable, required this.onChooseLevel, required this.signs, + /// 新增 + this.onChooseVideoManager, + this.onWorkStartTimeHandle, + this.onWorkEndTimeHandle, + this.onContractorHandle, + this.onWorkAreaHandle, + this.onWorkAreaLocationHandle, + this.locationController, this.hightController, this.nameController, @@ -239,7 +259,11 @@ class _HoistworkDetailFormWidgetState extends State { @override Widget build(BuildContext context) { + if (FormUtils.hasValue(widget.pd, 'LATITUDE')) { + widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map + } final pd = widget.pd; + return Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: const BoxDecoration( @@ -300,6 +324,7 @@ class _HoistworkDetailFormWidgetState extends State { hintText: '请输入吊物内容', text: pd['WORK_CONTENT'] ?? '', ), + if (!widget.isEditable && FormUtils.hasValue(pd, 'WORK_START_DATE')) ...[ ItemListWidget.singleLineTitleText( label: '作业开始时间:', @@ -316,10 +341,78 @@ class _HoistworkDetailFormWidgetState extends State { ), const Divider(), ], + ItemListWidget.selectableLineTitleTextRightButton( + label: '视频监控:', + isClean: true, + cleanText: '清除监控', + isRequired: false, + isEditable: widget.isEditable, + onTap: widget.onChooseVideoManager ?? () {}, + text: pd['VIDEONAME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业开始时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkStartTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_START_TIME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业结束时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkEndTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_END_TIME'] ?? '', + ), + const Divider(), + ListItemFactory.createYesNoSection( + verticalPadding: 0, + horizontalPadding: 0, + title: '是否承包商作业', + isEdit: widget.isEditable, + text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否', + isRequired: true, + groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1', + onChanged: (bool value) { + setState(() { + widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0"; + }); + }, + ), + if (pd['IS_CONTRACTOR_WORK'] == '1') + Column( + children: [ + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '承包商:', + isEditable: widget.isEditable, + onTap: widget.onContractorHandle ?? () {}, + text: pd['UNITS_NAME'] ?? '', + ), + ], + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '作业区域:', + isEditable: widget.isEditable, + onTap: widget.onWorkAreaHandle ?? () {}, + text: pd['PLS_NAME'] ?? '', + ), + const Divider(), + + ItemListWidget.twoRowButtonTitleText( + label: '作业地点经纬度', + isInput: false, + isEditable: widget.isEditable, + buttonText: '定位', + onTap: widget.onWorkAreaLocationHandle ?? (){}, + hintText: '', + text: pd['LATITUDE_LONGITUDE'] ?? '无', + ), const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '关联其他特殊作业及安全作业票编号:', + label: '关联其他特殊作业及安全作业票编号', isEditable: widget.isEditable, onTap: () async { final val = await showDialog( @@ -343,7 +436,7 @@ class _HoistworkDetailFormWidgetState extends State { ), const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '风险辨识结果:', + label: '风险辨识结果', isEditable: widget.isEditable, onTap: () async { await showDialog( diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/aqcs_work_detail/hoistwork_safe_func_sure.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/aqcs_work_detail/hoistwork_safe_func_sure.dart index b2d25f8..61decad 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/aqcs_work_detail/hoistwork_safe_func_sure.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/aqcs_work_detail/hoistwork_safe_func_sure.dart @@ -43,8 +43,7 @@ class _HoistworkSafeFuncSureState extends State { final TextEditingController _otherController = TextEditingController(); late Map signs = {}; - /// 动火人及证书编号 - late List workUserList = []; + /// 安全防护措施列表 List imagePaths = []; @@ -90,14 +89,15 @@ class _HoistworkSafeFuncSureState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -259,7 +259,7 @@ class _HoistworkSafeFuncSureState extends State { context, status == '1' ? '提交成功' : '已暂存', ); - Navigator.pop(context); + Navigator.of(context).pop(true); // 传 true 表示成功(可选) } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/aqjd_work_detail/hoistwork_aqjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/aqjd_work_detail/hoistwork_aqjd_detail.dart index bbb82b3..9d238ad 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/aqjd_work_detail/hoistwork_aqjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/aqjd_work_detail/hoistwork_aqjd_detail.dart @@ -78,14 +78,15 @@ class _HoistworkAqjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -217,7 +218,7 @@ class _HoistworkAqjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/dz_work_detai/hoistwork_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/dz_work_detai/hoistwork_apply_detail.dart index 440c584..922e2ac 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/dz_work_detai/hoistwork_apply_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/dz_work_detai/hoistwork_apply_detail.dart @@ -14,6 +14,10 @@ import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart'; +import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart'; +import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart'; enum EditUserType { @@ -64,7 +68,14 @@ class _HoistworkApplyDetailState extends State { late Map pd = {}; late Map signs = {}; late List> measuresList = []; - + /// ------------------- 新增 ------------------- + /// 视频监控摄像 + late List videoMonitoringList = []; + /// 承包商列表 + late List unitAllList = []; + /// 作业区域列表 + late List> workAreaList = []; + /// -------------------------------------- final TextEditingController _locationController = TextEditingController(); final TextEditingController _hightController = TextEditingController(); final TextEditingController _nameController = TextEditingController(); @@ -72,8 +83,7 @@ class _HoistworkApplyDetailState extends State { final TextEditingController _relatedController = TextEditingController(); final TextEditingController _riskController = TextEditingController(); - /// 动火人及证书编号 - late List workUserList = []; + // 存储各单位的人员列表 final Map>> _personCache = {}; @@ -92,6 +102,9 @@ class _HoistworkApplyDetailState extends State { pd['APPLY_USER_ID'] = SessionService.instance.loginUserId; pd['APPLY_USER_NAME'] = SessionService.instance.username; } + _getVideoList(); + _getUnitListAll(); + _getPlsList(); _contentController.addListener(() { setState(() { pd['WORK_CONTENT'] = _contentController.text.trim(); @@ -114,7 +127,151 @@ class _HoistworkApplyDetailState extends State { pd['RISK_IDENTIFICATION'] = _riskController.text.trim(); }); } + /// ---------------------------- 新增 -------------------------------- + /// 视频监控摄像头 + Future _chooseVideoManager() async { + final choice = await BottomPicker.show( + context, + items: + videoMonitoringList + .map((item) => item['VIDEONAME'] as String) + .toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['VIDEONAME'] = choice; + Map result = videoMonitoringList.firstWhere( + (item) => item['VIDEONAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) { + pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 选择承包商 + Future _chooseUnitManager() async { + final choice = await BottomPicker.show( + context, + items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['UNITS_NAME'] = choice; + + Map result = unitAllList.firstWhere( + (item) => item['UNITS_NAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'UNITS_ID')) { + pd['UNITS_ID'] = result['UNITS_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 预计作业开始时间 + Future _chooseWorkStartTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_START_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + + /// 开始时间必须早于结束时间 + if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') && + !isBeforeStr( + pd['WORK_EXPECTED_START_TIME'], + pd['WORK_EXPECTED_END_TIME'], + )) { + pd['WORK_EXPECTED_END_TIME'] = ''; + } + }); + } + } + /// 预计作业结束时间 + Future _chooseWorkEndTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '', + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_END_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + } + } + /// 选择经纬度 + Future _showLocationHandle() async{ + if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) { + ToastUtil.showNormal(context, '请选择作业区域'); + return; + } + Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context); + setState(() { + pd['LONGITUDE'] = mapData['longitue']; + pd['LATITUDE'] = mapData['latitude']; + pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}'; + }); + + } + /// 作业区域 + Future _getWorkArea() async { + FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => WorkAreaPicker( + onSelected: (String id, String POSITIONS, String name) { + pd['ELECTRONIC_FENCE_AREA_ID'] = id; + pd['POSITIONS'] = POSITIONS; + pd['PLS_NAME'] = name; + }, + ), + ).then((_) { + FocusHelper.clearFocus(context); + }); + } + /// 获取摄像头列表 + Future _getVideoList() async { + final result = await ApiService.getVideomanagerList(); + setState(() { + videoMonitoringList = result['varList'] ?? []; + }); + } + /// 获承包商列表 + Future _getUnitListAll() async { + final result = await ApiService.getUnitListAll(); + setState(() { + unitAllList = result['varList'] ?? []; + }); + } + /// 作业区域列表 + Future _getPlsList() async { + final result = await ApiService.getWorkAreaList(); + setState(() { + final String zTreeNodes = result['zTreeNodes'] ?? ''; + workAreaList = jsonDecode(zTreeNodes); + }); + } + /// ------------------------------------------------------------ void set_pd_DEPARTMENT_ID(EditUserType type, String id) { pd['${type.name}_DEPARTMENT_ID'] = id; } @@ -344,7 +501,24 @@ class _HoistworkApplyDetailState extends State { ToastUtil.showNormal(context, '请选择作业级别'); return; } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业开始时间'); + return; + } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业结束时间'); + return; + } + if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) { + ToastUtil.showNormal(context, '请选择承包商'); + return; + } + + if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) { + ToastUtil.showNormal(context, '请定位作业地点'); + return; + } for (var type in unitRules) { if (get_pd_DEPARTMENT_ID(type).length == 0) { ToastUtil.showNormal(context, '请选择${type.displayName}'); @@ -394,7 +568,7 @@ class _HoistworkApplyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); @@ -479,6 +653,13 @@ class _HoistworkApplyDetailState extends State { relatedController: _relatedController, riskController: _riskController, onChooseLevel: _chooseLevel, + // 新增 + onChooseVideoManager: _chooseVideoManager, + onWorkStartTimeHandle: _chooseWorkStartTime, + onWorkEndTimeHandle: _chooseWorkEndTime, + onContractorHandle: _chooseUnitManager, + onWorkAreaHandle: _getWorkArea, + onWorkAreaLocationHandle: _showLocationHandle, ), ), if (isEditable) diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/dzzh_work_detail/hoistwork_dzzh_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/dzzh_work_detail/hoistwork_dzzh_detail.dart index 468911a..7cd22ce 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/dzzh_work_detail/hoistwork_dzzh_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/dzzh_work_detail/hoistwork_dzzh_detail.dart @@ -50,14 +50,15 @@ class _HoistworkDzzhDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _HoistworkDzzhDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/hoistwork_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/hoistwork_list_page.dart index a5f52e0..58230e5 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/hoistwork_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/hoistwork_list_page.dart @@ -16,6 +16,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dz_work/szdw_ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dz_work/ysgd_work_detail/hoistwork_ysgd_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dz_work/zyfz_work_detail/hoistwork_zyfz_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dz_work/zyr_work_detail/hoistwork_zyr_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; @@ -170,7 +171,12 @@ class _HoistworkListPageState extends State { } void _goToDetail(Map item) async { - + final allowed = await WorkAreaHelper.checkInSpecialWorkArea( + context: context, + flow: widget.flow, + areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '', + ); + if (!allowed) return; switch (widget.flow) { case '提交申请': await pushPage(HoistworkApplyDetail(HOISTING_ID: item['HOISTING_ID'], flow: widget.flow), context); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/jhr_work_detail/hoistwork_jhr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/jhr_work_detail/hoistwork_jhr_detail.dart index eb97d50..5dab99c 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/jhr_work_detail/hoistwork_jhr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/jhr_work_detail/hoistwork_jhr_detail.dart @@ -49,13 +49,14 @@ class _HoistworkJhrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -182,7 +183,7 @@ class _HoistworkJhrDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/jsjd_work_detail/hoistwork_jsjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/jsjd_work_detail/hoistwork_jsjd_detail.dart index 55433ed..3219fbe 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/jsjd_work_detail/hoistwork_jsjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/jsjd_work_detail/hoistwork_jsjd_detail.dart @@ -47,14 +47,15 @@ class _HoistworkJsjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -178,7 +179,7 @@ class _HoistworkJsjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/jszy_work_detail/hoistwork_jszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/jszy_work_detail/hoistwork_jszy_detail.dart index b6ce8f2..01be868 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/jszy_work_detail/hoistwork_jszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/jszy_work_detail/hoistwork_jszy_detail.dart @@ -52,14 +52,15 @@ class _HoistworkJszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -136,26 +137,7 @@ class _HoistworkJszyDetailState extends State { return false; } - // 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') { - final diffMs = end.difference(start).inMilliseconds; - const max8h = 8 * 60 * 60 * 1000; - if (diffMs >= max8h) { - ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择'); - return false; - } - } - - // 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '二级') { - final diffMs = end.difference(start).inMilliseconds; - const max72h = 72 * 60 * 60 * 1000; - if (diffMs >= max72h) { - ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择'); - return false; - } - } return true; } /// 作废 -1 通过 1 @@ -223,7 +205,7 @@ class _HoistworkJszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/kszy_work_detail/hoistwork_kszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/kszy_work_detail/hoistwork_kszy_detail.dart index d5f31cc..d374486 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/kszy_work_detail/hoistwork_kszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/kszy_work_detail/hoistwork_kszy_detail.dart @@ -54,14 +54,15 @@ class _HoistworkKszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -190,7 +191,7 @@ class _HoistworkKszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/shbm_work_detail/hoistwork_shbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/shbm_work_detail/hoistwork_shbm_detail.dart index 6783023..175b7d7 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/shbm_work_detail/hoistwork_shbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/shbm_work_detail/hoistwork_shbm_detail.dart @@ -50,14 +50,15 @@ class _HoistworkShbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _HoistworkShbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/spbm_work_detail/hoistwork_spbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/spbm_work_detail/hoistwork_spbm_detail.dart index 9be0bb8..b788f2c 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/spbm_work_detail/hoistwork_spbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/spbm_work_detail/hoistwork_spbm_detail.dart @@ -49,14 +49,15 @@ class _HoistworkSpbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -186,7 +187,7 @@ class _HoistworkSpbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/ssr_work_detail/hoistwork_ssr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/ssr_work_detail/hoistwork_ssr_detail.dart index 46b7fa9..6dd488d 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/ssr_work_detail/hoistwork_ssr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/ssr_work_detail/hoistwork_ssr_detail.dart @@ -49,14 +49,15 @@ class _HoistworkSsrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -180,7 +181,7 @@ class _HoistworkSsrDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/szaq_work_detail/hoistwork_set_safe_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/szaq_work_detail/hoistwork_set_safe_detail.dart index 41487a4..db75b38 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/szaq_work_detail/hoistwork_set_safe_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/szaq_work_detail/hoistwork_set_safe_detail.dart @@ -164,13 +164,14 @@ class _HoistworkSetSafeDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -333,7 +334,7 @@ class _HoistworkSetSafeDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/szdw_work_detail/hoistwork_szdw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/szdw_work_detail/hoistwork_szdw_detail.dart index 3d5ae79..d19a67d 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/szdw_work_detail/hoistwork_szdw_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/szdw_work_detail/hoistwork_szdw_detail.dart @@ -50,14 +50,15 @@ class _HoistworkSzdwDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _HoistworkSzdwDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/ysgd_work_detail/hoistwork_ysgd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/ysgd_work_detail/hoistwork_ysgd_detail.dart index a028bfb..b2dd651 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/ysgd_work_detail/hoistwork_ysgd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/ysgd_work_detail/hoistwork_ysgd_detail.dart @@ -85,14 +85,15 @@ class _HoistworkYsgdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -248,7 +249,7 @@ class _HoistworkYsgdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/zyfz_work_detail/hoistwork_zyfz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/zyfz_work_detail/hoistwork_zyfz_detail.dart index 41940b4..3d32411 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/zyfz_work_detail/hoistwork_zyfz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/zyfz_work_detail/hoistwork_zyfz_detail.dart @@ -50,14 +50,15 @@ class _HoistworkZyfzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _HoistworkZyfzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/dz_work/zyr_work_detail/hoistwork_zyr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dz_work/zyr_work_detail/hoistwork_zyr_detail.dart index f3cda64..c0e17c4 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dz_work/zyr_work_detail/hoistwork_zyr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dz_work/zyr_work_detail/hoistwork_zyr_detail.dart @@ -49,14 +49,15 @@ class _HoistworkZyrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -180,7 +181,7 @@ class _HoistworkZyrDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/HighWorkDetailFormWidget.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/HighWorkDetailFormWidget.dart index 58e6c5f..b69515e 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/HighWorkDetailFormWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/HighWorkDetailFormWidget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; import '../../../../../../tools/tools.dart'; import '../../../item_list_widget.dart'; @@ -9,11 +10,22 @@ class HighWorkDetailFormWidget extends StatefulWidget { final Map pd; final bool isEditable; final VoidCallback onChooseLevel; +// 新增 + final VoidCallback? onChooseVideoManager; + /// 选择摄像头 + final VoidCallback? onWorkStartTimeHandle; + final VoidCallback? onWorkEndTimeHandle; + /// 承包商 + final VoidCallback? onContractorHandle; + /// 作业区域 + final VoidCallback? onWorkAreaHandle; + /// 作业地点经纬度 + final VoidCallback? onWorkAreaLocationHandle; /// 编辑模式下需提供以下控制器,非编辑模式可不传 final TextEditingController? locationController; // 地点 final TextEditingController? hightController; // 高度 - final TextEditingController? contentController; // 内容 + final TextEditingController? contentController; // 内容、 final TextEditingController? relatedController; final TextEditingController? riskController; @@ -22,6 +34,14 @@ class HighWorkDetailFormWidget extends StatefulWidget { required this.pd, required this.isEditable, required this.onChooseLevel, + /// 新增 + this.onChooseVideoManager, + this.onWorkStartTimeHandle, + this.onWorkEndTimeHandle, + this.onContractorHandle, + this.onWorkAreaHandle, + this.onWorkAreaLocationHandle, + this.locationController, this.hightController, this.contentController, @@ -55,7 +75,11 @@ class _HighWorkDetailFormWidgetState extends State { @override Widget build(BuildContext context) { + if (FormUtils.hasValue(widget.pd, 'LATITUDE')) { + widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map + } final pd = widget.pd; + return Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: const BoxDecoration( @@ -105,8 +129,78 @@ class _HighWorkDetailFormWidgetState extends State { text: pd['WORK_CONTENT'] ?? '', ), const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '视频监控:', + isClean: true, + cleanText: '清除监控', + isRequired: false, + isEditable: widget.isEditable, + onTap: widget.onChooseVideoManager ?? () {}, + text: pd['VIDEONAME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业开始时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkStartTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_START_TIME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业结束时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkEndTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_END_TIME'] ?? '', + ), + const Divider(), + ListItemFactory.createYesNoSection( + verticalPadding: 0, + horizontalPadding: 0, + title: '是否承包商作业', + isEdit: widget.isEditable, + text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否', + isRequired: true, + groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1', + onChanged: (bool value) { + setState(() { + widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0"; + }); + }, + ), + if (pd['IS_CONTRACTOR_WORK'] == '1') + Column( + children: [ + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '承包商:', + isEditable: widget.isEditable, + onTap: widget.onContractorHandle ?? () {}, + text: pd['UNITS_NAME'] ?? '', + ), + ], + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '作业区域:', + isEditable: widget.isEditable, + onTap: widget.onWorkAreaHandle ?? () {}, + text: pd['PLS_NAME'] ?? '', + ), + const Divider(), + ItemListWidget.twoRowButtonTitleText( - label: '关联其他特殊作业及安全作业票编号:', + label: '作业地点经纬度', + isInput: false, + isEditable: widget.isEditable, + buttonText: '定位', + onTap: widget.onWorkAreaLocationHandle ?? (){}, + hintText: '', + text: pd['LATITUDE_LONGITUDE'] ?? '无', + ), + const Divider(), + ItemListWidget.twoRowButtonTitleText( + label: '关联其他特殊作业及安全作业票编号', isEditable: widget.isEditable, onTap: () async { final val = await showDialog( @@ -130,7 +224,7 @@ class _HighWorkDetailFormWidgetState extends State { ), const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '风险辨识结果:', + label: '风险辨识结果', isEditable: widget.isEditable, onTap: () async { await showDialog( diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/aqcs_work_detail/highwork_safe_func_sure.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/aqcs_work_detail/highwork_safe_func_sure.dart index c457c94..279eb58 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/aqcs_work_detail/highwork_safe_func_sure.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/aqcs_work_detail/highwork_safe_func_sure.dart @@ -42,8 +42,7 @@ class _HighworkSafeFuncSureState extends State { /// 其他安全措施 final TextEditingController _otherController = TextEditingController(); - /// 动火人及证书编号 - late List workUserList = []; + /// 安全防护措施列表 List imagePaths = []; @@ -52,7 +51,7 @@ class _HighworkSafeFuncSureState extends State { void initState() { super.initState(); _getData(); - _getHotWorkNameList(); + } /// 弹出单位选择 @@ -90,14 +89,15 @@ class _HighworkSafeFuncSureState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -259,7 +259,7 @@ class _HighworkSafeFuncSureState extends State { context, status == '1' ? '提交成功' : '已暂存', ); - Navigator.pop(context); + Navigator.of(context).pop(true); // 传 true 表示成功(可选) } } catch (e) { LoadingDialogHelper.hide(); @@ -277,12 +277,7 @@ class _HighworkSafeFuncSureState extends State { } } - Future _getHotWorkNameList() async { - final result = await ApiService.getHotWorkNameList(); - setState(() { - workUserList = result['varList'] ?? ''; - }); - } + /// 初始化拉取数据 Future _getData() async { diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/aqjd_work_detail/highwork_aqjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/aqjd_work_detail/highwork_aqjd_detail.dart index 4a1af22..ebae1b7 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/aqjd_work_detail/highwork_aqjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/aqjd_work_detail/highwork_aqjd_detail.dart @@ -78,14 +78,15 @@ class _HighworkAqjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -217,7 +218,7 @@ class _HighworkAqjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/gc_work_detai/highwork_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/gc_work_detai/highwork_apply_detail.dart index 0a53377..facd8e2 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/gc_work_detai/highwork_apply_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/gc_work_detai/highwork_apply_detail.dart @@ -14,6 +14,10 @@ import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart'; +import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart'; +import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart'; enum EditUserType { @@ -62,15 +66,21 @@ class _HighworkApplyDetailState extends State { late Map pd = {}; late Map signs = {}; late List> measuresList = []; - + /// ------------------- 新增 ------------------- + /// 视频监控摄像 + late List videoMonitoringList = []; + /// 承包商列表 + late List unitAllList = []; + /// 作业区域列表 + late List> workAreaList = []; + /// -------------------------------------- final TextEditingController _locationController = TextEditingController(); final TextEditingController _hightController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); final TextEditingController _relatedController = TextEditingController(); final TextEditingController _riskController = TextEditingController(); - /// 动火人及证书编号 - late List workUserList = []; + // 存储各单位的人员列表 final Map>> _personCache = {}; @@ -89,8 +99,10 @@ class _HighworkApplyDetailState extends State { pd['APPLY_USER_ID'] = SessionService.instance.loginUserId; pd['APPLY_USER_NAME'] = SessionService.instance.username; } - _getHotWorkNameList(); + _getVideoList(); + _getUnitListAll(); + _getPlsList(); _contentController.addListener(() { setState(() { pd['WORK_CONTENT'] = _contentController.text.trim(); @@ -109,7 +121,151 @@ class _HighworkApplyDetailState extends State { pd['RISK_IDENTIFICATION'] = _riskController.text.trim(); }); } + /// ---------------------------- 新增 -------------------------------- + /// 视频监控摄像头 + Future _chooseVideoManager() async { + final choice = await BottomPicker.show( + context, + items: + videoMonitoringList + .map((item) => item['VIDEONAME'] as String) + .toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['VIDEONAME'] = choice; + Map result = videoMonitoringList.firstWhere( + (item) => item['VIDEONAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) { + pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 选择承包商 + Future _chooseUnitManager() async { + final choice = await BottomPicker.show( + context, + items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['UNITS_NAME'] = choice; + + Map result = unitAllList.firstWhere( + (item) => item['UNITS_NAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'UNITS_ID')) { + pd['UNITS_ID'] = result['UNITS_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 预计作业开始时间 + Future _chooseWorkStartTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_START_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + + /// 开始时间必须早于结束时间 + if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') && + !isBeforeStr( + pd['WORK_EXPECTED_START_TIME'], + pd['WORK_EXPECTED_END_TIME'], + )) { + pd['WORK_EXPECTED_END_TIME'] = ''; + } + }); + } + } + /// 预计作业结束时间 + Future _chooseWorkEndTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '', + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_END_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + } + } + /// 选择经纬度 + Future _showLocationHandle() async{ + if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) { + ToastUtil.showNormal(context, '请选择作业区域'); + return; + } + Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context); + setState(() { + pd['LONGITUDE'] = mapData['longitue']; + pd['LATITUDE'] = mapData['latitude']; + pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}'; + }); + + } + /// 作业区域 + Future _getWorkArea() async { + FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => WorkAreaPicker( + onSelected: (String id, String POSITIONS, String name) { + pd['ELECTRONIC_FENCE_AREA_ID'] = id; + pd['POSITIONS'] = POSITIONS; + pd['PLS_NAME'] = name; + }, + ), + ).then((_) { + FocusHelper.clearFocus(context); + }); + } + /// 获取摄像头列表 + Future _getVideoList() async { + final result = await ApiService.getVideomanagerList(); + setState(() { + videoMonitoringList = result['varList'] ?? []; + }); + } + /// 获承包商列表 + Future _getUnitListAll() async { + final result = await ApiService.getUnitListAll(); + setState(() { + unitAllList = result['varList'] ?? []; + }); + } + /// 作业区域列表 + Future _getPlsList() async { + final result = await ApiService.getWorkAreaList(); + setState(() { + final String zTreeNodes = result['zTreeNodes'] ?? ''; + workAreaList = jsonDecode(zTreeNodes); + }); + } + /// ------------------------------------------------------------ void set_pd_DEPARTMENT_ID(EditUserType type, String id) { pd['${type.name}_DEPARTMENT_ID'] = id; } @@ -344,7 +500,24 @@ class _HighworkApplyDetailState extends State { ToastUtil.showNormal(context, '请输入高处作业级别'); return; } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业开始时间'); + return; + } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业结束时间'); + return; + } + if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) { + ToastUtil.showNormal(context, '请选择承包商'); + return; + } + + if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) { + ToastUtil.showNormal(context, '请定位作业地点'); + return; + } for (var type in unitRules) { if (get_pd_DEPARTMENT_ID(type).length == 0) { ToastUtil.showNormal(context, '请选择${type.displayName}'); @@ -397,7 +570,7 @@ class _HighworkApplyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); @@ -412,14 +585,7 @@ class _HighworkApplyDetailState extends State { } } - Future _getHotWorkNameList() async { - final result = await ApiService.getHotWorkNameList(); - setState(() { - workUserList = result['varList'] ?? ''; - List names = - workUserList.map((item) => item['NAME'] as String).toList(); - }); - } + /// 初始化拉取数据 Future _getData() async { @@ -489,6 +655,13 @@ class _HighworkApplyDetailState extends State { relatedController: _relatedController, riskController: _riskController, onChooseLevel: _chooseLevel, + // 新增 + onChooseVideoManager: _chooseVideoManager, + onWorkStartTimeHandle: _chooseWorkStartTime, + onWorkEndTimeHandle: _chooseWorkEndTime, + onContractorHandle: _chooseUnitManager, + onWorkAreaHandle: _getWorkArea, + onWorkAreaLocationHandle: _showLocationHandle, ), ), if (isEditable) diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/highwork_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/highwork_list_page.dart index 9601114..d194542 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/highwork_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/highwork_list_page.dart @@ -13,6 +13,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/gc_work/szdw_ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/gc_work/ysgd_work_detail/highwork_ysgd_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/gc_work/zyfz_work_detail/highwork_zyfz_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/gc_work/zyr_work_detail/highwork_zyr_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; @@ -168,6 +169,12 @@ class _HighworkListPageState extends State { } void _goToDetail(Map item) async { + final allowed = await WorkAreaHelper.checkInSpecialWorkArea( + context: context, + flow: widget.flow, + areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '', + ); + if (!allowed) return; final Map data = { 'HIGHWORK_ID': item['HIGHWORK_ID'], 'flow': widget.flow, diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/jhr_work_detail/highwork_jhr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/jhr_work_detail/highwork_jhr_detail.dart index cdc0b2e..95f0fa2 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/jhr_work_detail/highwork_jhr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/jhr_work_detail/highwork_jhr_detail.dart @@ -49,13 +49,14 @@ class _HighworkJhrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -182,7 +183,7 @@ class _HighworkJhrDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/jsjd_work_detail/highwork_jsjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/jsjd_work_detail/highwork_jsjd_detail.dart index 2c849d3..a0da104 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/jsjd_work_detail/highwork_jsjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/jsjd_work_detail/highwork_jsjd_detail.dart @@ -50,14 +50,15 @@ class _HighworkJsjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -181,7 +182,7 @@ class _HighworkJsjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/jszy_work_detail/highwork_jszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/jszy_work_detail/highwork_jszy_detail.dart index 4869f0c..9d8a4c2 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/jszy_work_detail/highwork_jszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/jszy_work_detail/highwork_jszy_detail.dart @@ -52,14 +52,15 @@ class _HighworkJszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -136,26 +137,7 @@ class _HighworkJszyDetailState extends State { return false; } - // 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') { - final diffMs = end.difference(start).inMilliseconds; - const max8h = 8 * 60 * 60 * 1000; - if (diffMs >= max8h) { - ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择'); - return false; - } - } - - // 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '二级') { - final diffMs = end.difference(start).inMilliseconds; - const max72h = 72 * 60 * 60 * 1000; - if (diffMs >= max72h) { - ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择'); - return false; - } - } return true; } /// 作废 -1 通过 1 @@ -223,7 +205,7 @@ class _HighworkJszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/kszy_work_detail/highwork_kszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/kszy_work_detail/highwork_kszy_detail.dart index de3bea5..e68fd1e 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/kszy_work_detail/highwork_kszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/kszy_work_detail/highwork_kszy_detail.dart @@ -54,14 +54,15 @@ class _HighworkKszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -190,7 +191,7 @@ class _HighworkKszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/shbm_work_detail/highwork_shbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/shbm_work_detail/highwork_shbm_detail.dart index 02a9ce4..c5524f1 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/shbm_work_detail/highwork_shbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/shbm_work_detail/highwork_shbm_detail.dart @@ -50,14 +50,15 @@ class _HighworkShbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _HighworkShbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/spbm_work_detail/highwork_spbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/spbm_work_detail/highwork_spbm_detail.dart index 483350b..af39d51 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/spbm_work_detail/highwork_spbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/spbm_work_detail/highwork_spbm_detail.dart @@ -49,14 +49,15 @@ class _HighworkSpbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -186,7 +187,7 @@ class _HighworkSpbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/szaq_work_detail/highwork_set_safe_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/szaq_work_detail/highwork_set_safe_detail.dart index 9dcd9f0..52125eb 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/szaq_work_detail/highwork_set_safe_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/szaq_work_detail/highwork_set_safe_detail.dart @@ -163,13 +163,14 @@ class _HighworkSetSafeDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -332,7 +333,7 @@ class _HighworkSetSafeDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/szdw_work_detail/highwork_szdw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/szdw_work_detail/highwork_szdw_detail.dart index f1c3fee..e0457f4 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/szdw_work_detail/highwork_szdw_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/szdw_work_detail/highwork_szdw_detail.dart @@ -50,14 +50,15 @@ class _HighworkSzdwDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _HighworkSzdwDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/ysgd_work_detail/highwork_ysgd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/ysgd_work_detail/highwork_ysgd_detail.dart index 13b08e4..e3829db 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/ysgd_work_detail/highwork_ysgd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/ysgd_work_detail/highwork_ysgd_detail.dart @@ -81,13 +81,14 @@ class _HighworkYsgdDetailState extends State { } /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { signImages.add(path); signTimes.add(now); @@ -244,7 +245,7 @@ class _HighworkYsgdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/zyfz_work_detail/highwork_zyfz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/zyfz_work_detail/highwork_zyfz_detail.dart index c761baf..e516399 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/zyfz_work_detail/highwork_zyfz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/zyfz_work_detail/highwork_zyfz_detail.dart @@ -50,14 +50,15 @@ class _HighworkZyfzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _HighworkZyfzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/gc_work/zyr_work_detail/highwork_zyr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/gc_work/zyr_work_detail/highwork_zyr_detail.dart index 1d64d15..c278de0 100644 --- a/lib/pages/home/tap/tabList/special_wrok/gc_work/zyr_work_detail/highwork_zyr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/gc_work/zyr_work_detail/highwork_zyr_detail.dart @@ -49,14 +49,15 @@ class _HighworkZyrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -180,7 +181,7 @@ class _HighworkZyrDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart b/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart index a7100c6..64174be 100644 --- a/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart @@ -48,13 +48,14 @@ class _HomeGasTestPageState extends State { } Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ElectricityDetailFormWidget.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ElectricityDetailFormWidget.dart index 6d29ae0..0429c5e 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ElectricityDetailFormWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ElectricityDetailFormWidget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; import '../../../../../../tools/tools.dart'; import '../../../item_list_widget.dart'; @@ -11,6 +12,17 @@ class ElectricityDetailFormWidget extends StatefulWidget { final VoidCallback onChooseLevel; final VoidCallback onChooseHotworkUser; final VoidCallback onAnalyzeTap; +// 新增 + final VoidCallback? onChooseVideoManager; + /// 选择摄像头 + final VoidCallback? onWorkStartTimeHandle; + final VoidCallback? onWorkEndTimeHandle; + /// 承包商 + final VoidCallback? onContractorHandle; + /// 作业区域 + final VoidCallback? onWorkAreaHandle; + /// 作业地点经纬度 + final VoidCallback? onWorkAreaLocationHandle; /// 编辑模式下需提供以下控制器,非编辑可不传 final TextEditingController? contentController; @@ -28,6 +40,15 @@ class ElectricityDetailFormWidget extends StatefulWidget { required this.onChooseLevel, required this.onChooseHotworkUser, required this.onAnalyzeTap, + + /// 新增 + this.onChooseVideoManager, + this.onWorkStartTimeHandle, + this.onWorkEndTimeHandle, + this.onContractorHandle, + this.onWorkAreaHandle, + this.onWorkAreaLocationHandle, + this.contentController, this.locationController, this.cardController, @@ -55,7 +76,11 @@ class ElectricityDetailFormWidget extends StatefulWidget { class _ElectricityDetailFormWidgetState extends State { @override Widget build(BuildContext context) { + if (FormUtils.hasValue(widget.pd, 'LATITUDE')) { + widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map + } final pd = widget.pd; + return Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: const BoxDecoration( @@ -134,6 +159,76 @@ class _ElectricityDetailFormWidgetState extends State( @@ -191,7 +286,7 @@ class _ElectricityDetailFormWidgetState extends State( diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/aqcs_work_detail/electricity_safe_func_sure.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/aqcs_work_detail/electricity_safe_func_sure.dart index 0ea9176..95c3b83 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/aqcs_work_detail/electricity_safe_func_sure.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/aqcs_work_detail/electricity_safe_func_sure.dart @@ -44,8 +44,7 @@ class _ElectricitySafeFuncSureState extends State { /// 其他安全措施 final TextEditingController _otherController = TextEditingController(); - /// 动火人及证书编号 - late List workUserList = []; + /// 安全防护措施列表 List imagePaths = []; @@ -54,7 +53,7 @@ class _ElectricitySafeFuncSureState extends State { void initState() { super.initState(); _getData(); - _getHotWorkNameList(); + } /// 弹出单位选择 @@ -92,14 +91,15 @@ class _ElectricitySafeFuncSureState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -260,7 +260,7 @@ class _ElectricitySafeFuncSureState extends State { context, status == '1' ? '提交成功' : '已暂存', ); - Navigator.pop(context); + Navigator.of(context).pop(true); // 传 true 表示成功(可选) } } catch (e) { LoadingDialogHelper.hide(); @@ -278,12 +278,7 @@ class _ElectricitySafeFuncSureState extends State { } } - Future _getHotWorkNameList() async { - final result = await ApiService.getHotWorkNameList(); - setState(() { - workUserList = result['varList'] ?? ''; - }); - } + /// 初始化拉取数据 Future _getData() async { diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/aqjd_work_detail/electricity_aqjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/aqjd_work_detail/electricity_aqjd_detail.dart index af4cd5b..7e02e69 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/aqjd_work_detail/electricity_aqjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/aqjd_work_detail/electricity_aqjd_detail.dart @@ -79,14 +79,15 @@ class _ElectricityAqjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -217,7 +218,7 @@ class _ElectricityAqjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/dbbz_work_detail/electricity_dbbz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/dbbz_work_detail/electricity_dbbz_detail.dart index ad6ef59..1355c9f 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/dbbz_work_detail/electricity_dbbz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/dbbz_work_detail/electricity_dbbz_detail.dart @@ -53,14 +53,15 @@ class _ElectricityDbbzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -190,7 +191,7 @@ class _ElectricityDbbzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/electricity_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/electricity_list_page.dart index cb38ff0..c01b5c3 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/electricity_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/electricity_list_page.dart @@ -13,6 +13,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/lsyd_work/ydr import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/lsyd_work/ysgd_work_detail/electricity_ysgd_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/lsyd_work/zyfz_work_detail/electricity_zyfz_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/lsyd_work/zyr_work_detail/electricity_zyr_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; @@ -169,6 +170,12 @@ class _ElectricityListPageState extends State { } void _goToDetail(Map item) async { + final allowed = await WorkAreaHelper.checkInSpecialWorkArea( + context: context, + flow: widget.flow, + areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '', + ); + if (!allowed) return; switch (widget.flow) { case '提交申请': await pushPage( diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jhr_work_detail/electricity_jhr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jhr_work_detail/electricity_jhr_detail.dart index 5f55ead..bf86a9e 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jhr_work_detail/electricity_jhr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jhr_work_detail/electricity_jhr_detail.dart @@ -47,13 +47,14 @@ class _ElectricityJhrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -180,7 +181,7 @@ class _ElectricityJhrDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jsjd_work_detail/electricity_jsjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jsjd_work_detail/electricity_jsjd_detail.dart index b1017ab..918ae56 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jsjd_work_detail/electricity_jsjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jsjd_work_detail/electricity_jsjd_detail.dart @@ -47,14 +47,15 @@ class _ElectricityJsjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -178,7 +179,7 @@ class _ElectricityJsjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jszy_work_detail/electricity_jszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jszy_work_detail/electricity_jszy_detail.dart index 7e81d2a..377abbf 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jszy_work_detail/electricity_jszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/jszy_work_detail/electricity_jszy_detail.dart @@ -52,14 +52,15 @@ class _ElectricityJszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -136,26 +137,7 @@ class _ElectricityJszyDetailState extends State { return false; } - // 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') { - final diffMs = end.difference(start).inMilliseconds; - const max8h = 8 * 60 * 60 * 1000; - if (diffMs >= max8h) { - ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择'); - return false; - } - } - - // 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '二级') { - final diffMs = end.difference(start).inMilliseconds; - const max72h = 72 * 60 * 60 * 1000; - if (diffMs >= max72h) { - ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择'); - return false; - } - } return true; } /// 作废 -1 通过 1 @@ -223,7 +205,7 @@ class _ElectricityJszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/kszy_work_detail/electricity_kszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/kszy_work_detail/electricity_kszy_detail.dart index 6396e68..983ddaa 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/kszy_work_detail/electricity_kszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/kszy_work_detail/electricity_kszy_detail.dart @@ -52,14 +52,15 @@ class _ElectricityKszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -195,7 +196,7 @@ class _ElectricityKszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/lsyd_work_detai/electricity_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/lsyd_work_detai/electricity_apply_detail.dart index 9c86494..4a30240 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/lsyd_work_detai/electricity_apply_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/lsyd_work_detai/electricity_apply_detail.dart @@ -16,6 +16,10 @@ import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/lsyd_work/qtfx_work_detail/electricity_gas_list.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart'; +import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart'; +import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart'; enum EditUserType { // 12个 @@ -67,7 +71,14 @@ class _ElectricityApplyDetailState extends State { late Map pd = {}; late Map signs = {}; late List> measuresList = []; - + /// ------------------- 新增 ------------------- + /// 视频监控摄像 + late List videoMonitoringList = []; + /// 承包商列表 + late List unitAllList = []; + /// 作业区域列表 + late List> workAreaList = []; + /// -------------------------------------- final TextEditingController _contentController = TextEditingController(); final TextEditingController _locationController = TextEditingController(); final TextEditingController _cardController = TextEditingController(); @@ -96,7 +107,9 @@ class _ElectricityApplyDetailState extends State { pd['APPLY_USER_ID'] = SessionService.instance.loginUserId; pd['APPLY_USER_NAME'] = SessionService.instance.username; } - + _getVideoList(); + _getUnitListAll(); + _getPlsList(); _contentController.addListener(() { pd['WORK_CONTENT'] = _contentController.text.trim(); @@ -126,7 +139,151 @@ class _ElectricityApplyDetailState extends State { pd['RISK_IDENTIFICATION'] = _riskController.text.trim(); }); } + /// ---------------------------- 新增 -------------------------------- + /// 视频监控摄像头 + Future _chooseVideoManager() async { + final choice = await BottomPicker.show( + context, + items: + videoMonitoringList + .map((item) => item['VIDEONAME'] as String) + .toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['VIDEONAME'] = choice; + Map result = videoMonitoringList.firstWhere( + (item) => item['VIDEONAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) { + pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 选择承包商 + Future _chooseUnitManager() async { + final choice = await BottomPicker.show( + context, + items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['UNITS_NAME'] = choice; + + Map result = unitAllList.firstWhere( + (item) => item['UNITS_NAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'UNITS_ID')) { + pd['UNITS_ID'] = result['UNITS_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 预计作业开始时间 + Future _chooseWorkStartTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_START_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + + /// 开始时间必须早于结束时间 + if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') && + !isBeforeStr( + pd['WORK_EXPECTED_START_TIME'], + pd['WORK_EXPECTED_END_TIME'], + )) { + pd['WORK_EXPECTED_END_TIME'] = ''; + } + }); + } + } + /// 预计作业结束时间 + Future _chooseWorkEndTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '', + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_END_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + } + } + /// 选择经纬度 + Future _showLocationHandle() async{ + if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) { + ToastUtil.showNormal(context, '请选择作业区域'); + return; + } + Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context); + setState(() { + pd['LONGITUDE'] = mapData['longitue']; + pd['LATITUDE'] = mapData['latitude']; + pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}'; + }); + + } + /// 作业区域 + Future _getWorkArea() async { + FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => WorkAreaPicker( + onSelected: (String id, String POSITIONS, String name) { + pd['ELECTRONIC_FENCE_AREA_ID'] = id; + pd['POSITIONS'] = POSITIONS; + pd['PLS_NAME'] = name; + }, + ), + ).then((_) { + FocusHelper.clearFocus(context); + }); + } + /// 获取摄像头列表 + Future _getVideoList() async { + final result = await ApiService.getVideomanagerList(); + setState(() { + videoMonitoringList = result['varList'] ?? []; + }); + } + /// 获承包商列表 + Future _getUnitListAll() async { + final result = await ApiService.getUnitListAll(); + setState(() { + unitAllList = result['varList'] ?? []; + }); + } + /// 作业区域列表 + Future _getPlsList() async { + final result = await ApiService.getWorkAreaList(); + setState(() { + final String zTreeNodes = result['zTreeNodes'] ?? ''; + workAreaList = jsonDecode(zTreeNodes); + }); + } + /// ------------------------------------------------------------ void set_pd_DEPARTMENT_ID(EditUserType type, String id) { pd['${type.name}_DEPARTMENT_ID'] = id; } @@ -353,7 +510,24 @@ class _ElectricityApplyDetailState extends State { return; } } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业开始时间'); + return; + } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业结束时间'); + return; + } + if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) { + ToastUtil.showNormal(context, '请选择承包商'); + return; + } + + if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) { + ToastUtil.showNormal(context, '请定位作业地点'); + return; + } for (var type in unitRules) { if (get_pd_DEPARTMENT_ID(type).length == 0) { ToastUtil.showNormal(context, '请选择${type.displayName}'); @@ -398,7 +572,7 @@ class _ElectricityApplyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存'); - Navigator.pop(context); + Navigator.of(context).pop(true); } else { ToastUtil.showSuccess(context, '提交失败'); } @@ -492,6 +666,13 @@ class _ElectricityApplyDetailState extends State { context, ); }, + // 新增 + onChooseVideoManager: _chooseVideoManager, + onWorkStartTimeHandle: _chooseWorkStartTime, + onWorkEndTimeHandle: _chooseWorkEndTime, + onContractorHandle: _chooseUnitManager, + onWorkAreaHandle: _getWorkArea, + onWorkAreaLocationHandle: _showLocationHandle, ), ), SizedBox(height: 10), diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/psdw_work_detail/electricity_psdw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/psdw_work_detail/electricity_psdw_detail.dart index 44458fc..43863f8 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/psdw_work_detail/electricity_psdw_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/psdw_work_detail/electricity_psdw_detail.dart @@ -51,14 +51,15 @@ class _ElectricityPsdwDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _ElectricityPsdwDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/qtfx_work_detail/electricity_gas_test_page.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/qtfx_work_detail/electricity_gas_test_page.dart index 2cb1a32..4a54bc3 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/qtfx_work_detail/electricity_gas_test_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/qtfx_work_detail/electricity_gas_test_page.dart @@ -45,13 +45,14 @@ class _ElectricityGasTestPageState extends State { } Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/szaq_work_detail/electricity_set_safe_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/szaq_work_detail/electricity_set_safe_detail.dart index fbab659..14c473b 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/szaq_work_detail/electricity_set_safe_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/szaq_work_detail/electricity_set_safe_detail.dart @@ -164,13 +164,14 @@ class _ElectricitySetSafeDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -333,7 +334,7 @@ class _ElectricitySetSafeDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/yddw_work_detail/electricity_yddw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/yddw_work_detail/electricity_yddw_detail.dart index eec0a71..94a1d99 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/yddw_work_detail/electricity_yddw_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/yddw_work_detail/electricity_yddw_detail.dart @@ -50,14 +50,15 @@ class _ElectricityYddwDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _ElectricityYddwDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ydr_work_detail/electricity_ydr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ydr_work_detail/electricity_ydr_detail.dart index ab6935c..8ad3460 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ydr_work_detail/electricity_ydr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ydr_work_detail/electricity_ydr_detail.dart @@ -46,13 +46,14 @@ class _ElectricityYdrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -179,7 +180,7 @@ class _ElectricityYdrDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ysgd_work_detail/electricity_ysgd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ysgd_work_detail/electricity_ysgd_detail.dart index 0509cc7..bb36658 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ysgd_work_detail/electricity_ysgd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/ysgd_work_detail/electricity_ysgd_detail.dart @@ -85,14 +85,15 @@ class _ElectricityYsgdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -248,7 +249,7 @@ class _ElectricityYsgdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/zyfz_work_detail/electricity_zyfz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/zyfz_work_detail/electricity_zyfz_detail.dart index af446e8..a015f69 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/zyfz_work_detail/electricity_zyfz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/zyfz_work_detail/electricity_zyfz_detail.dart @@ -50,14 +50,15 @@ class _ElectricityZyfzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _ElectricityZyfzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/zyr_work_detail/electricity_zyr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/zyr_work_detail/electricity_zyr_detail.dart index 2a75afa..164c05b 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/zyr_work_detail/electricity_zyr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/zyr_work_detail/electricity_zyr_detail.dart @@ -47,14 +47,15 @@ class _ElectricityZyrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -178,7 +179,7 @@ class _ElectricityZyrDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/BlindboardDetailFormWidget.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/BlindboardDetailFormWidget.dart index 4671e1b..0490e48 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/BlindboardDetailFormWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/BlindboardDetailFormWidget.dart @@ -45,6 +45,17 @@ class BlindboardDetailFormWidget extends StatefulWidget { final bool isEditable; final VoidCallback onChooseLevel; +// 新增 + final VoidCallback? onChooseVideoManager; + /// 选择摄像头 + final VoidCallback? onWorkStartTimeHandle; + final VoidCallback? onWorkEndTimeHandle; + /// 承包商 + final VoidCallback? onContractorHandle; + /// 作业区域 + final VoidCallback? onWorkAreaHandle; + /// 作业地点经纬度 + final VoidCallback? onWorkAreaLocationHandle; /// 编辑模式下需提供以下控制器,非编辑模式可不传 final TextEditingController? nameController; // 名称 @@ -52,7 +63,6 @@ class BlindboardDetailFormWidget extends StatefulWidget { final TextEditingController? mediumController; // 介质 final TextEditingController? temperatureController; // 温度 final TextEditingController? pressureController; // 压力 - final TextEditingController? relatedController; final TextEditingController? riskController; @@ -62,6 +72,14 @@ class BlindboardDetailFormWidget extends StatefulWidget { required this.isEditable, required this.onChooseLevel, required this.boardList, + /// 新增 + this.onChooseVideoManager, + this.onWorkStartTimeHandle, + this.onWorkEndTimeHandle, + this.onContractorHandle, + this.onWorkAreaHandle, + this.onWorkAreaLocationHandle, + this.nameController, this.mediumController, this.temperatureController, @@ -104,8 +122,12 @@ class _BlindboardDetailFormWidgetState @override Widget build(BuildContext context) { final boardList = widget.boardList; + if (FormUtils.hasValue(widget.pd, 'LATITUDE')) { + widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map + } final pd = widget.pd; + return Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: const BoxDecoration( @@ -178,6 +200,76 @@ class _BlindboardDetailFormWidgetState ), ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '视频监控:', + isClean: true, + cleanText: '清除监控', + isRequired: false, + isEditable: widget.isEditable, + onTap: widget.onChooseVideoManager ?? () {}, + text: pd['VIDEONAME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业开始时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkStartTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_START_TIME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业结束时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkEndTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_END_TIME'] ?? '', + ), + const Divider(), + ListItemFactory.createYesNoSection( + verticalPadding: 0, + horizontalPadding: 0, + title: '是否承包商作业', + isEdit: widget.isEditable, + text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否', + isRequired: true, + groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1', + onChanged: (bool value) { + setState(() { + widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0"; + }); + }, + ), + if (pd['IS_CONTRACTOR_WORK'] == '1') + Column( + children: [ + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '承包商:', + isEditable: widget.isEditable, + onTap: widget.onContractorHandle ?? () {}, + text: pd['UNITS_NAME'] ?? '', + ), + ], + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '作业区域:', + isEditable: widget.isEditable, + onTap: widget.onWorkAreaHandle ?? () {}, + text: pd['PLS_NAME'] ?? '', + ), + const Divider(), + + ItemListWidget.twoRowButtonTitleText( + label: '作业地点经纬度', + isInput: false, + isEditable: widget.isEditable, + buttonText: '定位', + onTap: widget.onWorkAreaLocationHandle ?? (){}, + hintText: '', + text: pd['LATITUDE_LONGITUDE'] ?? '无', + ), const Divider(), ...boardList.map((board) { return Container( @@ -285,7 +377,7 @@ class _BlindboardDetailFormWidgetState const Divider(), ], ItemListWidget.twoRowButtonTitleText( - label: '关联其他特殊作业及安全作业票编号:', + label: '关联其他特殊作业及安全作业票编号', isEditable: widget.isEditable, onTap: () async { final val = await showDialog( @@ -310,7 +402,7 @@ class _BlindboardDetailFormWidgetState ), const Divider(), ItemListWidget.twoRowButtonTitleText( - label: '风险辨识结果:', + label: '风险辨识结果', isEditable: widget.isEditable, onTap: () async { await showDialog( diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/aqcs_work_detail/blindboard_safe_func_sure.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/aqcs_work_detail/blindboard_safe_func_sure.dart index 530a8ad..21d16c6 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/aqcs_work_detail/blindboard_safe_func_sure.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/aqcs_work_detail/blindboard_safe_func_sure.dart @@ -44,8 +44,7 @@ class _BlindboardSafeFuncSureState extends State { /// 其他安全措施 final TextEditingController _otherController = TextEditingController(); - /// 动火人及证书编号 - late List workUserList = []; + /// 安全防护措施列表 List imagePaths = []; @@ -54,7 +53,7 @@ class _BlindboardSafeFuncSureState extends State { void initState() { super.initState(); _getData(); - _getHotWorkNameList(); + } /// 弹出单位选择 @@ -92,14 +91,15 @@ class _BlindboardSafeFuncSureState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -261,7 +261,7 @@ class _BlindboardSafeFuncSureState extends State { context, status == '1' ? '提交成功' : '已暂存', ); - Navigator.pop(context); + Navigator.of(context).pop(true); // 传 true 表示成功(可选) } } catch (e) { LoadingDialogHelper.hide(); @@ -279,12 +279,7 @@ class _BlindboardSafeFuncSureState extends State { } } - Future _getHotWorkNameList() async { - final result = await ApiService.getHotWorkNameList(); - setState(() { - workUserList = result['varList'] ?? ''; - }); - } + /// 初始化拉取数据 Future _getData() async { diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/aqjd_work_detail/blindboard_aqjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/aqjd_work_detail/blindboard_aqjd_detail.dart index 7934d94..9c09f92 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/aqjd_work_detail/blindboard_aqjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/aqjd_work_detail/blindboard_aqjd_detail.dart @@ -79,14 +79,15 @@ class _BlindboardAqjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -218,7 +219,7 @@ class _BlindboardAqjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/blindboard_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/blindboard_list_page.dart index 443fe9c..8f9b9d1 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/blindboard_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/blindboard_list_page.dart @@ -15,6 +15,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/mbcd_work/szd import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/mbcd_work/ysgd_work_detail/blindboard_ysgd_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/mbcd_work/zyfz_work_detail/blindboard_zyfz_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/mbcd_work/zyr_work_detail/blindboard_zyr_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; @@ -168,7 +169,12 @@ class _BlindboardListPageState extends State { } void _goToDetail(Map item) async { - + final allowed = await WorkAreaHelper.checkInSpecialWorkArea( + context: context, + flow: widget.flow, + areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '', + ); + if (!allowed) return; switch (widget.flow) { case '提交申请': await pushPage(BlindboardApplyDetail(BLINDBOARD_ID: item['BLINDBOARD_ID'], flow: widget.flow), context); @@ -208,6 +214,10 @@ class _BlindboardListPageState extends State { break; case '开始作业': await pushPage(BlindboardKszyDetail(BLINDBOARD_ID: item['BLINDBOARD_ID'], flow: widget.flow), context); + break; + case '结束作业': + await pushPage(BlindboardJszyDetail(BLINDBOARD_ID: item['CUTROAD_ID'], flow: widget.flow), context); + break; case '验收签字': await pushPage(BlindboardYsgdDetail(BLINDBOARD_ID: item['BLINDBOARD_ID'], flow: widget.flow), context); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/cjry_work_detail/blindboard_cjry_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/cjry_work_detail/blindboard_cjry_detail.dart index ec1cc15..9f9341a 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/cjry_work_detail/blindboard_cjry_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/cjry_work_detail/blindboard_cjry_detail.dart @@ -62,14 +62,15 @@ class _BlindboardCjryDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -209,7 +210,7 @@ class _BlindboardCjryDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jhr_work_detail/blindboard_jhr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jhr_work_detail/blindboard_jhr_detail.dart index 6ab94bf..a6e7029 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jhr_work_detail/blindboard_jhr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jhr_work_detail/blindboard_jhr_detail.dart @@ -50,13 +50,14 @@ class _BlindboardJhrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -183,7 +184,7 @@ class _BlindboardJhrDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jsjd_work_detail/blindboard_jsjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jsjd_work_detail/blindboard_jsjd_detail.dart index cca3bd4..17bbeed 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jsjd_work_detail/blindboard_jsjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jsjd_work_detail/blindboard_jsjd_detail.dart @@ -51,14 +51,15 @@ class _BlindboardJsjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -182,7 +183,7 @@ class _BlindboardJsjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jszy_work_detail/blindboard_jszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jszy_work_detail/blindboard_jszy_detail.dart index 2c53694..f5bebb9 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jszy_work_detail/blindboard_jszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/jszy_work_detail/blindboard_jszy_detail.dart @@ -53,14 +53,15 @@ class _BlindboardJszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -137,26 +138,7 @@ class _BlindboardJszyDetailState extends State { return false; } - // 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') { - final diffMs = end.difference(start).inMilliseconds; - const max8h = 8 * 60 * 60 * 1000; - if (diffMs >= max8h) { - ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择'); - return false; - } - } - - // 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '二级') { - final diffMs = end.difference(start).inMilliseconds; - const max72h = 72 * 60 * 60 * 1000; - if (diffMs >= max72h) { - ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择'); - return false; - } - } return true; } /// 作废 -1 通过 1 @@ -224,7 +206,7 @@ class _BlindboardJszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/kszy_work_detail/blindboard_kszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/kszy_work_detail/blindboard_kszy_detail.dart index 59b2b49..e07178b 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/kszy_work_detail/blindboard_kszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/kszy_work_detail/blindboard_kszy_detail.dart @@ -55,14 +55,15 @@ class _BlindboardKszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -191,7 +192,7 @@ class _BlindboardKszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/mbcd_work_detai/blindboard_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/mbcd_work_detai/blindboard_apply_detail.dart index e292006..5593253 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/mbcd_work_detai/blindboard_apply_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/mbcd_work_detai/blindboard_apply_detail.dart @@ -14,6 +14,10 @@ import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart'; +import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart'; +import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart'; enum EditUserType { WORKSHOP('车间人员单位', '车间人员', true), @@ -60,7 +64,14 @@ class _BlindboardApplyDetailState extends State { late List boardList = [ {'BOARD_MATERIAL': '', 'BOARD_SPECIFICATION': '', 'BOARD_NO': ''}, ]; - + /// ------------------- 新增 ------------------- + /// 视频监控摄像 + late List videoMonitoringList = []; + /// 承包商列表 + late List unitAllList = []; + /// 作业区域列表 + late List> workAreaList = []; + /// -------------------------------------- late Map signs = {}; late List> measuresList = []; @@ -74,8 +85,7 @@ class _BlindboardApplyDetailState extends State { final TextEditingController _relatedController = TextEditingController(); final TextEditingController _riskController = TextEditingController(); - /// 动火人及证书编号 - late List workUserList = []; + // 存储各单位的人员列表 final Map>> _personCache = {}; @@ -94,8 +104,10 @@ class _BlindboardApplyDetailState extends State { pd['APPLY_USER_ID'] = SessionService.instance.loginUserId; pd['APPLY_USER_NAME'] = SessionService.instance.username; } - _getHotWorkNameList(); + _getVideoList(); + _getUnitListAll(); + _getPlsList(); _nameController.addListener(() { setState(() { pd['NAME'] = _nameController.text.trim(); @@ -117,7 +129,151 @@ class _BlindboardApplyDetailState extends State { pd['RISK_IDENTIFICATION'] = _riskController.text.trim(); }); } + /// ---------------------------- 新增 -------------------------------- + /// 视频监控摄像头 + Future _chooseVideoManager() async { + final choice = await BottomPicker.show( + context, + items: + videoMonitoringList + .map((item) => item['VIDEONAME'] as String) + .toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['VIDEONAME'] = choice; + Map result = videoMonitoringList.firstWhere( + (item) => item['VIDEONAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) { + pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 选择承包商 + Future _chooseUnitManager() async { + final choice = await BottomPicker.show( + context, + items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['UNITS_NAME'] = choice; + + Map result = unitAllList.firstWhere( + (item) => item['UNITS_NAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'UNITS_ID')) { + pd['UNITS_ID'] = result['UNITS_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 预计作业开始时间 + Future _chooseWorkStartTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_START_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + + /// 开始时间必须早于结束时间 + if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') && + !isBeforeStr( + pd['WORK_EXPECTED_START_TIME'], + pd['WORK_EXPECTED_END_TIME'], + )) { + pd['WORK_EXPECTED_END_TIME'] = ''; + } + }); + } + } + /// 预计作业结束时间 + Future _chooseWorkEndTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '', + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_END_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + } + } + /// 选择经纬度 + Future _showLocationHandle() async{ + if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) { + ToastUtil.showNormal(context, '请选择作业区域'); + return; + } + Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context); + setState(() { + pd['LONGITUDE'] = mapData['longitue']; + pd['LATITUDE'] = mapData['latitude']; + pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}'; + }); + + } + /// 作业区域 + Future _getWorkArea() async { + FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => WorkAreaPicker( + onSelected: (String id, String POSITIONS, String name) { + pd['ELECTRONIC_FENCE_AREA_ID'] = id; + pd['POSITIONS'] = POSITIONS; + pd['PLS_NAME'] = name; + }, + ), + ).then((_) { + FocusHelper.clearFocus(context); + }); + } + /// 获取摄像头列表 + Future _getVideoList() async { + final result = await ApiService.getVideomanagerList(); + setState(() { + videoMonitoringList = result['varList'] ?? []; + }); + } + /// 获承包商列表 + Future _getUnitListAll() async { + final result = await ApiService.getUnitListAll(); + setState(() { + unitAllList = result['varList'] ?? []; + }); + } + /// 作业区域列表 + Future _getPlsList() async { + final result = await ApiService.getWorkAreaList(); + setState(() { + final String zTreeNodes = result['zTreeNodes'] ?? ''; + workAreaList = jsonDecode(zTreeNodes); + }); + } + /// ------------------------------------------------------------ void set_pd_DEPARTMENT_ID(EditUserType type, String id) { pd['${type.name}_DEPARTMENT_ID'] = id; } @@ -342,7 +498,24 @@ class _BlindboardApplyDetailState extends State { ToastUtil.showNormal(context, '请选择盲板作业方式'); return; } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业开始时间'); + return; + } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业结束时间'); + return; + } + if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) { + ToastUtil.showNormal(context, '请选择承包商'); + return; + } + + if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) { + ToastUtil.showNormal(context, '请定位作业地点'); + return; + } for (var type in unitRules) { if (get_pd_DEPARTMENT_ID(type).length == 0) { ToastUtil.showNormal(context, '请选择${type.displayName}'); @@ -390,7 +563,7 @@ class _BlindboardApplyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); @@ -405,14 +578,7 @@ class _BlindboardApplyDetailState extends State { } } - Future _getHotWorkNameList() async { - final result = await ApiService.getHotWorkNameList(); - setState(() { - workUserList = result['varList'] ?? ''; - List names = - workUserList.map((item) => item['NAME'] as String).toList(); - }); - } + /// 初始化拉取数据 Future _getData() async { @@ -489,6 +655,13 @@ class _BlindboardApplyDetailState extends State { relatedController: _relatedController, riskController: _riskController, onChooseLevel: _chooseLevel, + // 新增 + onChooseVideoManager: _chooseVideoManager, + onWorkStartTimeHandle: _chooseWorkStartTime, + onWorkEndTimeHandle: _chooseWorkEndTime, + onContractorHandle: _chooseUnitManager, + onWorkAreaHandle: _getWorkArea, + onWorkAreaLocationHandle: _showLocationHandle, ), ), if (isEditable) diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/shbm_work_detail/blindboard_shbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/shbm_work_detail/blindboard_shbm_detail.dart index a852240..4310bf5 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/shbm_work_detail/blindboard_shbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/shbm_work_detail/blindboard_shbm_detail.dart @@ -51,14 +51,15 @@ class _BlindboardShbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -188,7 +189,7 @@ class _BlindboardShbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/spbm_work_detail/blindboard_spbm_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/spbm_work_detail/blindboard_spbm_detail.dart index 7f8a91c..e7f2b84 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/spbm_work_detail/blindboard_spbm_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/spbm_work_detail/blindboard_spbm_detail.dart @@ -50,14 +50,15 @@ class _BlindboardSpbmDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _BlindboardSpbmDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/szaq_work_detail/blindboard_set_safe_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/szaq_work_detail/blindboard_set_safe_detail.dart index 6556a14..c04895c 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/szaq_work_detail/blindboard_set_safe_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/szaq_work_detail/blindboard_set_safe_detail.dart @@ -165,13 +165,14 @@ class _BlindboardSetSafeDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -334,7 +335,7 @@ class _BlindboardSetSafeDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/szdw_work_detail/blindboard_szdw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/szdw_work_detail/blindboard_szdw_detail.dart index 1dbdfca..3f924e0 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/szdw_work_detail/blindboard_szdw_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/szdw_work_detail/blindboard_szdw_detail.dart @@ -51,14 +51,15 @@ class _BlindboardSzdwDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -188,7 +189,7 @@ class _BlindboardSzdwDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/ysgd_work_detail/blindboard_ysgd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/ysgd_work_detail/blindboard_ysgd_detail.dart index 9711e5f..c926a66 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/ysgd_work_detail/blindboard_ysgd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/ysgd_work_detail/blindboard_ysgd_detail.dart @@ -86,14 +86,15 @@ class _BlindboardYsgdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -239,7 +240,7 @@ class _BlindboardYsgdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/zyfz_work_detail/blindboard_zyfz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/zyfz_work_detail/blindboard_zyfz_detail.dart index 01cb1d4..4e9c2c3 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/zyfz_work_detail/blindboard_zyfz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/zyfz_work_detail/blindboard_zyfz_detail.dart @@ -51,14 +51,15 @@ class _BlindboardZyfzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -188,7 +189,7 @@ class _BlindboardZyfzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/zyr_work_detail/blindboard_zyr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/zyr_work_detail/blindboard_zyr_detail.dart index a2732c1..f9b6171 100644 --- a/lib/pages/home/tap/tabList/special_wrok/mbcd_work/zyr_work_detail/blindboard_zyr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/mbcd_work/zyr_work_detail/blindboard_zyr_detail.dart @@ -50,14 +50,15 @@ class _BlindboardZyrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -181,7 +182,7 @@ class _BlindboardZyrDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/SpaceWorkDetailFormWidget.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/SpaceWorkDetailFormWidget.dart index c4fb385..35e1461 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/SpaceWorkDetailFormWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/SpaceWorkDetailFormWidget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; import '../../../../../../tools/tools.dart'; import '../../../item_list_widget.dart'; @@ -12,6 +13,17 @@ class SpaceWorkDetailFormWidget extends StatefulWidget { final VoidCallback onChooseLevel; final VoidCallback onChooseSpaceName; final VoidCallback onAnalyzeTap; +// 新增 + final VoidCallback? onChooseVideoManager; + /// 选择摄像头 + final VoidCallback? onWorkStartTimeHandle; + final VoidCallback? onWorkEndTimeHandle; + /// 承包商 + final VoidCallback? onContractorHandle; + /// 作业区域 + final VoidCallback? onWorkAreaHandle; + /// 作业地点经纬度 + final VoidCallback? onWorkAreaLocationHandle; /// 编辑模式下需提供以下控制器,非编辑可不传 final TextEditingController? spaceNameController; @@ -28,6 +40,14 @@ class SpaceWorkDetailFormWidget extends StatefulWidget { required this.onChooseLevel, required this.onChooseSpaceName, required this.onAnalyzeTap, + /// 新增 + this.onChooseVideoManager, + this.onWorkStartTimeHandle, + this.onWorkEndTimeHandle, + this.onContractorHandle, + this.onWorkAreaHandle, + this.onWorkAreaLocationHandle, + this.isEditSpaceName = false, /// 受限空间可选择可手写,默认选择,受限空间编号也要跟着变化 this.contentController, this.spaceNameController, @@ -55,7 +75,11 @@ class SpaceWorkDetailFormWidget extends StatefulWidget { class _SpaceWorkDetailFormWidgetState extends State { @override Widget build(BuildContext context) { + if (FormUtils.hasValue(widget.pd, 'LATITUDE')) { + widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map + } final pd = widget.pd; + return Container( padding: EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( @@ -160,10 +184,79 @@ class _SpaceWorkDetailFormWidgetState extends State { text: pd['WORK_END_DATE'] ?? '', ), ], - Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '视频监控:', + isClean: true, + cleanText: '清除监控', + isRequired: false, + isEditable: widget.isEditable, + onTap: widget.onChooseVideoManager ?? () {}, + text: pd['VIDEONAME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业开始时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkStartTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_START_TIME'] ?? '', + ), + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '预计作业结束时间:', + isEditable: widget.isEditable, + onTap: widget.onWorkEndTimeHandle ?? () {}, + text: pd['WORK_EXPECTED_END_TIME'] ?? '', + ), + const Divider(), + ListItemFactory.createYesNoSection( + verticalPadding: 0, + horizontalPadding: 0, + title: '是否承包商作业', + isEdit: widget.isEditable, + text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否', + isRequired: true, + groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1', + onChanged: (bool value) { + setState(() { + widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0"; + }); + }, + ), + if (pd['IS_CONTRACTOR_WORK'] == '1') + Column( + children: [ + const Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + label: '承包商:', + isEditable: widget.isEditable, + onTap: widget.onContractorHandle ?? () {}, + text: pd['UNITS_NAME'] ?? '', + ), + ], + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '作业区域:', + isEditable: widget.isEditable, + onTap: widget.onWorkAreaHandle ?? () {}, + text: pd['PLS_NAME'] ?? '', + ), + const Divider(), + ItemListWidget.twoRowButtonTitleText( - label: '关联其他特殊作业及安全作业票编号:', + label: '作业地点经纬度', + isInput: false, + isEditable: widget.isEditable, + buttonText: '定位', + onTap: widget.onWorkAreaLocationHandle ?? (){}, + hintText: '', + text: pd['LATITUDE_LONGITUDE'] ?? '无', + ), + const Divider(), + ItemListWidget.twoRowButtonTitleText( + label: '关联其他特殊作业及安全作业票编号', isEditable: widget.isEditable, onTap: () async { final val = await showDialog( @@ -188,7 +281,7 @@ class _SpaceWorkDetailFormWidgetState extends State { ), Divider(), ItemListWidget.twoRowButtonTitleText( - label: '风险辨识结果:', + label: '风险辨识结果', isEditable: widget.isEditable, onTap: () async { await showDialog( diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqcs_work_detail/spacework_safe_func_sure.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqcs_work_detail/spacework_safe_func_sure.dart index 8b1c485..cf41b9e 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqcs_work_detail/spacework_safe_func_sure.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqcs_work_detail/spacework_safe_func_sure.dart @@ -88,14 +88,15 @@ class _SpaceworkSafeFuncSureState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -256,7 +257,7 @@ class _SpaceworkSafeFuncSureState extends State { context, status == '1' ? '提交成功' : '已暂存', ); - Navigator.pop(context); + Navigator.of(context).pop(true); // 传 true 表示成功(可选) } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqgl_work_detail/spacework_aqgl_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqgl_work_detail/spacework_aqgl_detail.dart index d768721..ceec71e 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqgl_work_detail/spacework_aqgl_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqgl_work_detail/spacework_aqgl_detail.dart @@ -53,14 +53,15 @@ class _SpaceworkAqglDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -189,7 +190,7 @@ class _SpaceworkAqglDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqjd_work_detail/spacework_aqjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqjd_work_detail/spacework_aqjd_detail.dart index d3ab50c..c33ad4b 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqjd_work_detail/spacework_aqjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/aqjd_work_detail/spacework_aqjd_detail.dart @@ -80,14 +80,15 @@ class _SpaceworkAqjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -218,7 +219,7 @@ class _SpaceworkAqjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/dbbz_work_detail/spacework_dbbz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/dbbz_work_detail/spacework_dbbz_detail.dart index cfdfa04..806640f 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/dbbz_work_detail/spacework_dbbz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/dbbz_work_detail/spacework_dbbz_detail.dart @@ -54,14 +54,15 @@ class _SpaceworkDbbzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -191,7 +192,7 @@ class _SpaceworkDbbzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/dhsp_work_detail/spacework_dhsp_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/dhsp_work_detail/spacework_dhsp_detail.dart index 8c3a778..cda3b06 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/dhsp_work_detail/spacework_dhsp_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/dhsp_work_detail/spacework_dhsp_detail.dart @@ -51,14 +51,15 @@ class _SpaceworkDhspDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -188,7 +189,7 @@ class _SpaceworkDhspDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jhr_work_detail/spacework_jhr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jhr_work_detail/spacework_jhr_detail.dart index ee4e9bf..be9978c 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jhr_work_detail/spacework_jhr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jhr_work_detail/spacework_jhr_detail.dart @@ -48,13 +48,14 @@ class _SpaceworkJhrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); @@ -181,7 +182,7 @@ class _SpaceworkJhrDetailState extends State { context, '保存成功', ); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jsjd_work_detail/spacework_jsjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jsjd_work_detail/spacework_jsjd_detail.dart index 3c9759b..c31fb09 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jsjd_work_detail/spacework_jsjd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jsjd_work_detail/spacework_jsjd_detail.dart @@ -48,14 +48,15 @@ class _SpaceworkJsjdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -179,7 +180,7 @@ class _SpaceworkJsjdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jszy_work_detail/spacework_jszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jszy_work_detail/spacework_jszy_detail.dart index dc501ee..189f2b9 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jszy_work_detail/spacework_jszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/jszy_work_detail/spacework_jszy_detail.dart @@ -53,14 +53,15 @@ class _SpaceworkJszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -137,26 +138,7 @@ class _SpaceworkJszyDetailState extends State { return false; } - // 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') { - final diffMs = end.difference(start).inMilliseconds; - const max8h = 8 * 60 * 60 * 1000; - if (diffMs >= max8h) { - ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择'); - return false; - } - } - - // 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms) - if (pd['WORK_LEVEL'] == '二级') { - final diffMs = end.difference(start).inMilliseconds; - const max72h = 72 * 60 * 60 * 1000; - if (diffMs >= max72h) { - ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择'); - return false; - } - } return true; } /// 作废 -1 通过 1 @@ -224,7 +206,7 @@ class _SpaceworkJszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/kszy_work_detail/spacework_kszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/kszy_work_detail/spacework_kszy_detail.dart index beaebbb..5e0afdb 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/kszy_work_detail/spacework_kszy_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/kszy_work_detail/spacework_kszy_detail.dart @@ -54,14 +54,15 @@ class _SpaceworkKszyDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -197,7 +198,7 @@ class _SpaceworkKszyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_test_page.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_test_page.dart index 60b80ae..87af690 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_test_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_test_page.dart @@ -63,13 +63,14 @@ class _SpaceworkGasTestPageState extends State { } Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/space_work_detai/spacework_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/space_work_detai/spacework_apply_detail.dart index dd2409b..cd50d35 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/space_work_detai/spacework_apply_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/space_work_detai/spacework_apply_detail.dart @@ -15,6 +15,10 @@ import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_edit.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart'; +import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart'; +import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart'; enum EditUserType { @@ -64,7 +68,14 @@ class _SpaceworkApplyDetailState extends State { /// 详情 late Map pd = {}; late List> measuresList = []; - + /// ------------------- 新增 ------------------- + /// 视频监控摄像 + late List videoMonitoringList = []; + /// 承包商列表 + late List unitAllList = []; + /// 作业区域列表 + late List> workAreaList = []; + /// -------------------------------------- final TextEditingController _contentController = TextEditingController(); final TextEditingController _spaceNameController = TextEditingController(); final TextEditingController _spaceNumberController = TextEditingController(); @@ -94,7 +105,9 @@ class _SpaceworkApplyDetailState extends State { pd['APPLY_USER_NAME'] = SessionService.instance.username; } _getSpaceWorkNameList(); - + _getVideoList(); + _getUnitListAll(); + _getPlsList(); _contentController.addListener(() { setState(() { pd['WORK_CONTENT'] = _contentController.text.trim(); @@ -116,7 +129,151 @@ class _SpaceworkApplyDetailState extends State { pd['RISK_IDENTIFICATION'] = _riskController.text.trim(); }); } + /// ---------------------------- 新增 -------------------------------- + /// 视频监控摄像头 + Future _chooseVideoManager() async { + final choice = await BottomPicker.show( + context, + items: + videoMonitoringList + .map((item) => item['VIDEONAME'] as String) + .toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['VIDEONAME'] = choice; + Map result = videoMonitoringList.firstWhere( + (item) => item['VIDEONAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) { + pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 选择承包商 + Future _chooseUnitManager() async { + final choice = await BottomPicker.show( + context, + items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + setState(() { + pd['UNITS_NAME'] = choice; + + Map result = unitAllList.firstWhere( + (item) => item['UNITS_NAME'] == choice, + orElse: () => {}, // 避免找不到时报错 + ); + if (FormUtils.hasValue(result, 'UNITS_ID')) { + pd['UNITS_ID'] = result['UNITS_ID']; + } + FocusHelper.clearFocus(context); + }); + } + } + /// 预计作业开始时间 + Future _chooseWorkStartTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_START_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + + /// 开始时间必须早于结束时间 + if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') && + !isBeforeStr( + pd['WORK_EXPECTED_START_TIME'], + pd['WORK_EXPECTED_END_TIME'], + )) { + pd['WORK_EXPECTED_END_TIME'] = ''; + } + }); + } + } + /// 预计作业结束时间 + Future _chooseWorkEndTime() async { + DateTime? picked = await BottomDateTimePicker.showDate( + context, + minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '', + allowFuture: true, + ); + if (picked != null) { + setState(() { + pd['WORK_EXPECTED_END_TIME'] = DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + } + } + /// 选择经纬度 + Future _showLocationHandle() async{ + if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) { + ToastUtil.showNormal(context, '请选择作业区域'); + return; + } + Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context); + setState(() { + pd['LONGITUDE'] = mapData['longitue']; + pd['LATITUDE'] = mapData['latitude']; + pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}'; + }); + + } + /// 作业区域 + Future _getWorkArea() async { + FocusHelper.clearFocus(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + barrierColor: Colors.black54, + backgroundColor: Colors.transparent, + builder: + (_) => WorkAreaPicker( + onSelected: (String id, String POSITIONS, String name) { + pd['ELECTRONIC_FENCE_AREA_ID'] = id; + pd['POSITIONS'] = POSITIONS; + pd['PLS_NAME'] = name; + }, + ), + ).then((_) { + FocusHelper.clearFocus(context); + }); + } + /// 获取摄像头列表 + Future _getVideoList() async { + final result = await ApiService.getVideomanagerList(); + setState(() { + videoMonitoringList = result['varList'] ?? []; + }); + } + /// 获承包商列表 + Future _getUnitListAll() async { + final result = await ApiService.getUnitListAll(); + setState(() { + unitAllList = result['varList'] ?? []; + }); + } + /// 作业区域列表 + Future _getPlsList() async { + final result = await ApiService.getWorkAreaList(); + setState(() { + final String zTreeNodes = result['zTreeNodes'] ?? ''; + workAreaList = jsonDecode(zTreeNodes); + }); + } + /// ------------------------------------------------------------ void set_pd_DEPARTMENT_ID(EditUserType type, String id) { pd['${type.name}_DEPARTMENT_ID'] = id; } @@ -331,7 +488,24 @@ class _SpaceworkApplyDetailState extends State { return; } } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业开始时间'); + return; + } + if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) { + ToastUtil.showNormal(context, '请选择预计作业结束时间'); + return; + } + if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) { + ToastUtil.showNormal(context, '请选择承包商'); + return; + } + + if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) { + ToastUtil.showNormal(context, '请定位作业地点'); + return; + } for (var type in unitRules) { if (get_pd_DEPARTMENT_ID(type).length == 0) { ToastUtil.showNormal(context, '请选择${type.displayName}'); @@ -371,7 +545,7 @@ class _SpaceworkApplyDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存'); - Navigator.pop(context); + Navigator.of(context).pop(true); }else{ ToastUtil.showSuccess(context, '提交失败'); @@ -469,11 +643,14 @@ class _SpaceworkApplyDetailState extends State { onChooseLevel: _chooseLevel, onChooseSpaceName: _chooseSpaceName, onAnalyzeTap: () { - // pushPage( - // HotworkGasList(WORK_ID: widget.WORK_ID), - // context, - // ); }, + // 新增 + onChooseVideoManager: _chooseVideoManager, + onWorkStartTimeHandle: _chooseWorkStartTime, + onWorkEndTimeHandle: _chooseWorkEndTime, + onContractorHandle: _chooseUnitManager, + onWorkAreaHandle: _getWorkArea, + onWorkAreaLocationHandle: _showLocationHandle, ), ), if (isEditable) diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/spacework_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/spacework_list_page.dart index fab1236..b9564cf 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/spacework_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/spacework_list_page.dart @@ -14,6 +14,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/sxkj_work/szd import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/sxkj_work/ysgd_work_detail/spacework_ysgd_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/sxkj_work/zyfz_work_detail/spacework_zyfz_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/sxkj_work/zyr_work_detail/spacework_zyr_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/customWidget/bottom_picker.dart'; @@ -172,6 +173,12 @@ class _SpaceworkListPageState extends State { } void _goToDetail(Map item) async { + final allowed = await WorkAreaHelper.checkInSpecialWorkArea( + context: context, + flow: widget.flow, + areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '', + ); + if (!allowed) return; switch (widget.flow) { case '提交申请': await pushPage( diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/szaq_work_detail/spacework_set_safe_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/szaq_work_detail/spacework_set_safe_detail.dart index 3c2e92a..93e43a7 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/szaq_work_detail/spacework_set_safe_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/szaq_work_detail/spacework_set_safe_detail.dart @@ -165,13 +165,14 @@ class _SpaceworkSetSafeDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - setState(() { imagePaths.add(path); signTimes.add(now); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/szdw_work_detail/spacework_szdw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/szdw_work_detail/spacework_szdw_detail.dart index 5092260..e00327b 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/szdw_work_detail/spacework_szdw_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/szdw_work_detail/spacework_szdw_detail.dart @@ -51,14 +51,15 @@ class _SpaceworkSzdwDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -188,7 +189,7 @@ class _SpaceworkSzdwDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/ysgd_work_detail/spacework_ysgd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/ysgd_work_detail/spacework_ysgd_detail.dart index 47beee3..1deeceb 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/ysgd_work_detail/spacework_ysgd_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/ysgd_work_detail/spacework_ysgd_detail.dart @@ -86,14 +86,15 @@ class _SpaceworkYsgdDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -249,7 +250,7 @@ class _SpaceworkYsgdDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/zyfz_work_detail/spacework_zyfz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/zyfz_work_detail/spacework_zyfz_detail.dart index 0726d01..7532863 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/zyfz_work_detail/spacework_zyfz_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/zyfz_work_detail/spacework_zyfz_detail.dart @@ -50,14 +50,15 @@ class _SpaceworkZyfzDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); - - setState(() { +setState(() { signImages.add(path); signTimes.add(now); FocusHelper.clearFocus(context); @@ -187,7 +188,7 @@ class _SpaceworkZyfzDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/zyr_work_detail/spacework_zyr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/zyr_work_detail/spacework_zyr_detail.dart index e3d32fe..292f2a2 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/zyr_work_detail/spacework_zyr_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/zyr_work_detail/spacework_zyr_detail.dart @@ -49,11 +49,13 @@ class _SpaceworkZyrDetailState extends State { /// 签字 Future _sign() async { + await NativeOrientation.setLandscape(); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); - if (path != null) { + await NativeOrientation.setPortrait(); +if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); setState(() { @@ -180,7 +182,7 @@ class _SpaceworkZyrDetailState extends State { LoadingDialogHelper.hide(); if (result['result'] == 'success') { ToastUtil.showSuccess(context, '保存成功'); - Navigator.pop(context); + Navigator.of(context).pop(true); } } catch (e) { LoadingDialogHelper.hide(); diff --git a/lib/pages/home/tap/tabList/special_wrok/work_area_helper.dart b/lib/pages/home/tap/tabList/special_wrok/work_area_helper.dart new file mode 100644 index 0000000..fa54217 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/work_area_helper.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/services/location_service.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/tools/tools.dart'; + +class WorkAreaHelper { + /// 检查是否需要并且能否在作业区域内继续 + static Future checkInSpecialWorkArea({ + required BuildContext context, + required String flow, + required String areaId, + bool checkTapConfig = true, + Duration timeout = const Duration(seconds: 10), + }) async { + // 如果是提交申请 或 配置关闭,则直接允许 + if (flow == '提交申请' || !checkTapConfig) { + return true; + } + try { + LoadingDialogHelper.show(); + + // 获取经纬度(字符串形式) + final LocationResult loc = + await LocationService.getCurrentLocation(timeout: timeout); + // 调用后端检查 + final result = await ApiService.checkSpecialWorkIsInPls( + areaId ?? '', + loc.longitude, + loc.latitude, + ); + + if (result != null && + result['result'] == 'success' && + (result['flag'] as bool? ?? false)) { + return true; + } else { + ToastUtil.showNormal(context, result?['msg'] ?? '作业区域不存在'); + return false; + } + } catch (e) { + // 友好提示:定位或其他异常 + final msg = (e is LocationException) ? e.message : '检查区域失败: ${e.toString()}'; + ToastUtil.showNormal(context, msg); + return false; + } finally { + LoadingDialogHelper.hide(); + } + } +} diff --git a/lib/pages/home/tap/tabList/work_tab_mbcd_list.dart b/lib/pages/home/tap/tabList/work_tab_mbcd_list.dart index ce32f33..1914974 100644 --- a/lib/pages/home/tap/tabList/work_tab_mbcd_list.dart +++ b/lib/pages/home/tap/tabList/work_tab_mbcd_list.dart @@ -84,6 +84,11 @@ class _WorkTabMbcdListState extends State { "title": "开始作业时间", "unreadCount": eight_work_count['WORK_START'] ?? '0', }, + { + "icon": "assets/icon-apps/icon-js-1.png", + "title": "结束作业时间", + "unreadCount": eight_work_count['WORK_END'] ?? '0', + }, { "icon": "assets/icon-apps/icon-js-1.png", "title": "验收归档", diff --git a/lib/pages/home/tap/workArea_picker.dart b/lib/pages/home/tap/workArea_picker.dart new file mode 100644 index 0000000..0d38a5e --- /dev/null +++ b/lib/pages/home/tap/workArea_picker.dart @@ -0,0 +1,287 @@ +import 'dart:convert'; +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/tools/tools.dart'; + +// ----- Models ----- +class Category { + final String ELECTRONIC_FENCE_AREA_ID; + final String POSITIONS; + final String name; + final List children; + + Category({ + required this.ELECTRONIC_FENCE_AREA_ID, + required this.name, + this.POSITIONS = '', + this.children = const [], + }); + + factory Category.fromJson(Map json) { + // 保护式读取,避免 null 转 List、String 时抛异常 + final id = (json['ELECTRONIC_FENCE_AREA_ID'] ?? '').toString(); + final name = (json['name'] ?? '').toString(); + final positions = (json['POSITIONS'] ?? '').toString(); + + // children 可能不存在、为 null、或已经是 List + final rawChildren = json['children']; + List childrenList = []; + if (rawChildren is List) { + childrenList = rawChildren + .where((e) => e != null) + .map((e) { + // e 可能已经是 Map 或 dynamic + if (e is Map) { + return Category.fromJson(e); + } else if (e is Map) { + return Category.fromJson(Map.from(e)); + } else { + // 无法解析的项跳过,或返回空占位 + return null; + } + }) + .whereType() + .toList(); + } + + return Category( + ELECTRONIC_FENCE_AREA_ID: id, + name: name, + POSITIONS: positions, + children: childrenList, + ); + } +} + + +typedef DeptSelectCallback = + void Function(String id, String POSITIONS, String name); + +class WorkAreaPicker extends StatefulWidget { + final DeptSelectCallback onSelected; + + const WorkAreaPicker({Key? key, required this.onSelected}) : super(key: key); + + @override + _WorkAreaPickerState createState() => _WorkAreaPickerState(); +} + +class _WorkAreaPickerState extends State { + String selectedId = ''; + String selectedName = ''; + String selected_POSITIONS = ''; + + Set expandedSet = {}; + + List original = []; + List filtered = []; + bool loading = true; + + final TextEditingController _searchController = TextEditingController(); + + @override + void initState() { + super.initState(); + // 初始均为空 + selectedId = ''; + selectedName = ''; + selected_POSITIONS = ''; + expandedSet = {}; + _searchController.addListener(_onSearchChanged); + _loadData(); + } + + @override + void dispose() { + _searchController.removeListener(_onSearchChanged); + _searchController.dispose(); + super.dispose(); + } + + Future _loadData() async { + try { + final result = await ApiService.getWorkAreaList(); + final dynamic nodesField = result['zTreeNodes']; + + // nodesField 可能是已经解析过的 List 或者是 String(JSON 字符串) + List raw; + if (nodesField is String) { + raw = json.decode(nodesField) as List; + } else if (nodesField is List) { + raw = nodesField; + } else { + raw = []; + } + + // debug 打印(运行中可查看控制台) + // print('raw length = ${raw.length}'); + + setState(() { + original = raw + .map((e) { + if (e is Map) return Category.fromJson(e); + if (e is Map) return Category.fromJson(Map.from(e)); + return null; + }) + .whereType() + .toList(); + filtered = original; + loading = false; + }); + } catch (e, st) { + // 打印错误以便调试 + // print('loadData error: $e\n$st'); + setState(() => loading = false); + } + } + + void _onSearchChanged() { + final query = _searchController.text.toLowerCase().trim(); + setState(() { + filtered = query.isEmpty ? original : _filterCategories(original, query); + }); + } + + List _filterCategories(List list, String query) { + List result = []; + for (var cat in list) { + final children = _filterCategories(cat.children, query); + if (cat.name.toLowerCase().contains(query) || children.isNotEmpty) { + result.add( + Category( + ELECTRONIC_FENCE_AREA_ID: cat.ELECTRONIC_FENCE_AREA_ID, + name: cat.name, + children: children, + ), + ); + } + } + return result; + } + + Widget _buildRow(Category cat, int indent) { + final hasChildren = cat.children.isNotEmpty; + final isExpanded = expandedSet.contains(cat.ELECTRONIC_FENCE_AREA_ID); + final isSelected = cat.ELECTRONIC_FENCE_AREA_ID == selectedId; + return Column( + children: [ + InkWell( + onTap: () { + setState(() { + if (hasChildren) { + isExpanded + ? expandedSet.remove(cat.ELECTRONIC_FENCE_AREA_ID) + : expandedSet.add(cat.ELECTRONIC_FENCE_AREA_ID); + } + selectedId = cat.ELECTRONIC_FENCE_AREA_ID; + selectedName = cat.name; + selected_POSITIONS = cat.POSITIONS; + }); + }, + child: Container( + color: Colors.white, + child: Row( + children: [ + SizedBox(width: 16.0 * indent), + SizedBox( + width: 24, + child: + hasChildren + ? Icon( + isExpanded + ? Icons.arrow_drop_down_rounded + : Icons.arrow_right_rounded, + size: 35, + color: Colors.grey[600], + ) + : const SizedBox.shrink(), + ), + const SizedBox(width: 5), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Text(cat.name), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Icon( + isSelected + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, + color: Colors.green, + ), + ), + ], + ), + ), + ), + if (hasChildren && isExpanded) + ...cat.children.map((c) => _buildRow(c, indent + 1)), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height * 0.7, + color: Colors.white, + child: Column( + children: [ + Container( + color: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: const Text('取消', style: TextStyle(fontSize: 16)), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: SearchBarWidget( + controller: _searchController, + isShowSearchButton: false, + onSearch: (keyboard) {}, + ), + ), + ), + GestureDetector( + onTap: () { + Navigator.of(context).pop(); + widget.onSelected( + selectedId, + selected_POSITIONS, + selectedName, + ); + }, + child: const Text( + '确定', + style: TextStyle(fontSize: 16, color: Colors.green), + ), + ), + ], + ), + ), + Divider(), + Expanded( + child: + loading + ? const Center(child: CircularProgressIndicator()) + : Container( + color: Colors.white, + child: ListView.builder( + itemCount: filtered.length, + itemBuilder: (ctx, idx) => _buildRow(filtered[idx], 0), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index eea3cef..6d78d84 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -3,9 +3,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; -import 'package:qhd_prevention/tools/auth_service.dart'; +import 'package:qhd_prevention/services/auth_service.dart'; import '../tools/tools.dart'; import 'main_tab.dart'; +import 'mine/webViewPage.dart'; void main() => runApp(const MyApp()); @@ -223,14 +224,17 @@ class _LoginPageState extends State { ), ), TextSpan( - text: '《用户协议》', + text: '《服务协议》', style: const TextStyle( color: Color(0xFF0D1D8C), ), recognizer: TapGestureRecognizer() ..onTap = () { - // 打开用户协议 + pushPage(const WebViewPage( + name: "用户服务协议", + url: 'http://47.92.102.56:7811/file/xieyi/zsyhxy.htm', // 替换为目标网址 + ), context); }, ), TextSpan( @@ -247,7 +251,11 @@ class _LoginPageState extends State { recognizer: TapGestureRecognizer() ..onTap = () { - // 打开隐私政策 + pushPage(const WebViewPage( + name: "隐私政策", + url: 'http://47.92.102.56:7811/file/xieyi/zsysq.htm', // 替换为目标网址 + ), context); + }, ), ], diff --git a/lib/pages/mine/mine_first_sign_page.dart b/lib/pages/mine/mine_first_sign_page.dart index 5d60d1d..425563e 100644 --- a/lib/pages/mine/mine_first_sign_page.dart +++ b/lib/pages/mine/mine_first_sign_page.dart @@ -36,13 +36,7 @@ class _SignatureUpdatePageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - GestureDetector( - onTap: () { - pushPage(MineSignPage(), context); - }, - child: Text("用户",style: TextStyle(color: Colors.black,fontSize: 16)), - ), - + Text("用户",style: TextStyle(color: Colors.black,fontSize: 16)), _buildConfirmButton(), ], ), @@ -91,10 +85,14 @@ class _SignatureUpdatePageState extends State { height: 30, child: ElevatedButton( onPressed: () async { + await NativeOrientation.setLandscape(); + final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), ); + await NativeOrientation.setPortrait(); + // 更新状态(当子页面关闭时) setState(() { imagePath = path ?? ''; diff --git a/lib/pages/mine/mine_set_page.dart b/lib/pages/mine/mine_set_page.dart index 204daf8..36506fd 100644 --- a/lib/pages/mine/mine_set_page.dart +++ b/lib/pages/mine/mine_set_page.dart @@ -6,7 +6,7 @@ import 'package:qhd_prevention/pages/login_page.dart'; import 'package:qhd_prevention/pages/mine/mine_first_sign_page.dart'; import 'package:qhd_prevention/pages/mine/mine_set_pwd_page.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; -import 'package:qhd_prevention/tools/auth_service.dart'; +import 'package:qhd_prevention/services/auth_service.dart'; import 'package:qhd_prevention/tools/h_colors.dart'; import '../../tools/tools.dart'; diff --git a/lib/tools/StorageService.dart b/lib/services/StorageService.dart similarity index 100% rename from lib/tools/StorageService.dart rename to lib/services/StorageService.dart diff --git a/lib/tools/auth_service.dart b/lib/services/auth_service.dart similarity index 99% rename from lib/tools/auth_service.dart rename to lib/services/auth_service.dart index c6b5646..8846d13 100644 --- a/lib/tools/auth_service.dart +++ b/lib/services/auth_service.dart @@ -61,7 +61,6 @@ class AuthService { } - /// 验证是否已登录(通过重新登录) static Future isLoggedIn() async { final prefs = await SharedPreferences.getInstance(); diff --git a/lib/services/location_service.dart b/lib/services/location_service.dart new file mode 100644 index 0000000..61d671d --- /dev/null +++ b/lib/services/location_service.dart @@ -0,0 +1,71 @@ +import 'package:geolocator/geolocator.dart'; + +class LocationResult { + final String latitude; + final String longitude; + + LocationResult({ + required this.latitude, + required this.longitude, + }); + + factory LocationResult.fromPosition(Position p) { + return LocationResult( + latitude: p.latitude.toString(), + longitude: p.longitude.toString(), + ); + } + + double? get latitudeAsDouble => double.tryParse(latitude); + double? get longitudeAsDouble => double.tryParse(longitude); + + @override + String toString() => 'LocationResult(latitude: $latitude, longitude: $longitude)'; +} + +class LocationException implements Exception { + final String message; + LocationException(this.message); + @override + String toString() => message; +} + +/// 统一处理定位权限与获取经纬度(字符串形式) +class LocationService { + /// 获取当前经纬度(字符串形式),成功返回 LocationResult,失败抛出 LocationException + static Future getCurrentLocation({ + Duration timeout = const Duration(seconds: 10), + }) async { + // 检查定位服务是否开启 + bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled) { + await Geolocator.openLocationSettings(); + throw LocationException('定位服务未开启,请打开设备定位后重试'); + } + + // 检查/请求权限 + LocationPermission permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied) { + throw LocationException('定位权限被拒绝'); + } + } + + if (permission == LocationPermission.deniedForever) { + throw LocationException( + '定位权限被永久拒绝,请去系统设置中为应用打开定位权限', + ); + } + + // 获取位置(带超时) + try { + final position = await Geolocator + .getCurrentPosition(desiredAccuracy: LocationAccuracy.high) + .timeout(timeout); + return LocationResult.fromPosition(position); + } on Exception catch (e) { + throw LocationException('获取位置失败: ${e.toString()}'); + } + } +} diff --git a/lib/services/update_service.dart b/lib/services/update_service.dart new file mode 100644 index 0000000..6462305 --- /dev/null +++ b/lib/services/update_service.dart @@ -0,0 +1,168 @@ +// update_service.dart +import 'dart:async'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:path_provider/path_provider.dart'; + +/// 安装 MethodChannel 名称(与原生保持一致) +const String _kInstallChannel = 'app.install'; + +/// 更新事件类型 +enum UpdateState { idle, starting, downloading, completed, installing, installed, failed, canceled } + +class UpdateEvent { + final UpdateState state; + final String? message; // 可选的错误/提示消息 + + UpdateEvent(this.state, {this.message}); +} + +/// 单例服务类:管理下载、进度与安装 +class UpdateService { + UpdateService._internal(); + + static final UpdateService _instance = UpdateService._internal(); + + factory UpdateService() => _instance; + + // 进度通知器(0.0 ~ 1.0) + final ValueNotifier progress = ValueNotifier(0.0); + + // 状态流控制器(广播) + final StreamController _statusController = + StreamController.broadcast(); + + Stream get statusStream => _statusController.stream; + + // Dio cancel token 用于取消下载 + CancelToken? _cancelToken; + + final MethodChannel _channel = const MethodChannel(_kInstallChannel); + + /// 启动下载并在下载完成后尝试安装 + /// apkUrl: 下载地址 + /// apkFileName: 保存的文件名(可选) + Future downloadAndInstall({ + required String apkUrl, + String? apkFileName, + }) async { + // 防止重复下载 + if (_cancelToken != null && !_cancelToken!.isCancelled) { + _statusController.add(UpdateEvent(UpdateState.failed, message: '已有下载进行中')); + return; + } + + _statusController.add(UpdateEvent(UpdateState.starting)); + progress.value = 0.0; + + _cancelToken = CancelToken(); + + // 准备保存路径(app 专属外部目录) + String savePath; + try { + final extDir = await getExternalStorageDirectory(); + if (extDir == null) { + _statusController.add(UpdateEvent(UpdateState.failed, message: '无法获取存储目录')); + return; + } + apkFileName ??= 'app_update_${DateTime.now().millisecondsSinceEpoch}.apk'; + savePath = '${extDir.path}/$apkFileName'; + } catch (e) { + _statusController.add(UpdateEvent(UpdateState.failed, message: '获取存储路径失败: $e')); + return; + } + + final dio = Dio(); + + try { + _statusController.add(UpdateEvent(UpdateState.downloading)); + await dio.download( + apkUrl, + savePath, + cancelToken: _cancelToken, + onReceiveProgress: (received, total) { + if (total > 0) { + final p = received / total; + progress.value = p; + } + }, + options: Options( + responseType: ResponseType.stream, + followRedirects: true, + // 不设置超时(可能大文件下载) + receiveTimeout: Duration(seconds: 0), + headers: {"Accept": "application/vnd.android.package-archive"}, + ), + ); + + // 下载完成 + progress.value = 1.0; + _statusController.add(UpdateEvent(UpdateState.completed)); + + // 延迟让 UI 显示 100% + await Future.delayed(const Duration(milliseconds: 200)); + + // 调用原生安装 + _statusController.add(UpdateEvent(UpdateState.installing)); + try { + final Map args = {'path': savePath}; + await _channel.invokeMethod('installApk', args); + // 成功发起安装(系统会弹出安装界面),我们不能保证用户安装成功,但这里当做已触发安装 + _statusController.add(UpdateEvent(UpdateState.installed)); + } on PlatformException catch (e) { + _statusController.add(UpdateEvent(UpdateState.failed, message: '安装失败: ${e.message}')); + } catch (e) { + _statusController.add(UpdateEvent(UpdateState.failed, message: '安装异常: $e')); + } + } on DioError catch (e) { + if (CancelToken.isCancel(e)) { + progress.value = 0.0; + _statusController.add(UpdateEvent(UpdateState.canceled)); + } else { + progress.value = 0.0; + _statusController.add(UpdateEvent(UpdateState.failed, message: e.message)); + } + } catch (e) { + progress.value = 0.0; + _statusController.add(UpdateEvent(UpdateState.failed, message: e.toString())); + } finally { + // 清理 token + _cancelToken = null; + } + } + + /// 取消当前下载(若有) + void cancelDownload() { + if (_cancelToken != null && !_cancelToken!.isCancelled) { + _cancelToken!.cancel(); + } + } + + /// 仅触发安装(如果你已经手动下载好了文件),传入本地 apk 路径 + Future installApk(String apkPath) async { + try { + _statusController.add(UpdateEvent(UpdateState.installing)); + final Map args = {'path': apkPath}; + await _channel.invokeMethod('installApk', args); + _statusController.add(UpdateEvent(UpdateState.installed)); + } on PlatformException catch (e) { + _statusController.add(UpdateEvent(UpdateState.failed, message: '安装失败: ${e.message}')); + } catch (e) { + _statusController.add(UpdateEvent(UpdateState.failed, message: '安装异常: $e')); + } + } + + /// 释放资源(页面销毁或 app 退出时调用) + Future dispose() async { + try { + _statusController.add(UpdateEvent(UpdateState.idle)); + await _statusController.close(); + } catch (_) {} + try { + progress.dispose(); + } catch (_) {} + } +} diff --git a/lib/tools/tools.dart b/lib/tools/tools.dart index 88f7533..3012e7f 100644 --- a/lib/tools/tools.dart +++ b/lib/tools/tools.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter/services.dart'; int getRandomWithNum(int min, int max) { final random = Random(); @@ -261,7 +262,45 @@ String formatDate(DateTime? date, String fmt) { }); return result; } +/// 把 'yyyy-MM-dd HH:mm'(或 'yyyy-MM-ddTHH:mm')解析为 DateTime,失败返回 null +DateTime? _parseYMdHm(String s) { + if (s.isEmpty) return null; + // 允许传入 'yyyy-MM-dd HH:mm' 或 'yyyy-MM-ddTHH:mm' + // 如果没有秒,补上 :00 + String t = s.trim(); + // 如果只有日期部分,直接返回 null(你也可以自行扩展) + if (!RegExp(r'^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}$').hasMatch(t)) { + // 试着容错:如果是 'yyyy-MM-dd HH:mm:ss' 也接受 + if (!RegExp(r'^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}$').hasMatch(t)) { + return null; + } else { + // 已经带秒,直接 parse + return DateTime.tryParse(t.replaceFirst(' ', 'T')); + } + } + // 如果是 'yyyy-MM-dd HH:mm',补齐秒并使用 ISO 格式 + final iso = t.replaceFirst(' ', 'T') + ':00'; + return DateTime.tryParse(iso); +} +/// 比较两个 'yyyy-MM-dd HH:mm' 格式字符串 +/// 返回 1 (a>b), 0 (a==b), -1 (a compareYMdHmStrings(a, b) == 1; + +/// 便捷:a 是否 早于 b +bool isBeforeStr(String a, String b) => compareYMdHmStrings(a, b) == -1; /// ------------------------------------------------------ /// 防多次点击 /// ------------------------------------------------------ @@ -390,4 +429,14 @@ class NoDataWidget { ); } +} +class NativeOrientation { + static const MethodChannel _channel = MethodChannel('app.orientation'); + + static Future setLandscape() async { + await _channel.invokeMethod('setOrientation', 'landscape'); + } + static Future setPortrait() async { + await _channel.invokeMethod('setOrientation', 'portrait'); + } } \ No newline at end of file diff --git a/lib/tools/update/update_dialogs.dart b/lib/tools/update/update_dialogs.dart new file mode 100644 index 0000000..ba0cec6 --- /dev/null +++ b/lib/tools/update/update_dialogs.dart @@ -0,0 +1,188 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/services/update_service.dart'; + +/// 显示“发现新版本”确认对话框,点击更新后会弹出下载进度弹窗 +Future showUpdateConfirmDialog(BuildContext context, { + required String apkUrl, + String title = '发现新版本', + String content = '检测到新版本,是否立即更新?', + String updateButtonText = '更新', + String cancelButtonText = '稍后', +}) async { + final confirmed = await showDialog( + context: context, + barrierDismissible: false, + builder: (ctx) { + return AlertDialog( + title: Text(title), + content: Text(content), + actions: [ + TextButton( + onPressed: () => Navigator.of(ctx).pop(false), + child: Text(cancelButtonText), + ), + ElevatedButton( + onPressed: () => Navigator.of(ctx).pop(true), + child: Text(updateButtonText), + ), + ], + ); + }, + ); + + if (confirmed == true) { + // 跳出下载进度弹窗(阻塞式,直到弹窗被关闭) + await showDialog( + context: context, + barrierDismissible: false, + builder: (ctx) => DownloadProgressDialog(apkUrl: apkUrl), + ); + } +} + +/// 下载进度弹窗(会在 initState 里自动开始下载) +class DownloadProgressDialog extends StatefulWidget { + final String apkUrl; + final String? apkFileName; + const DownloadProgressDialog({Key? key, required this.apkUrl, this.apkFileName}) : super(key: key); + + @override + State createState() => _DownloadProgressDialogState(); +} + +class _DownloadProgressDialogState extends State { + final UpdateService _service = UpdateService(); + StreamSubscription? _sub; + double _progress = 0.0; + String _statusText = '准备下载...'; + bool _isWorking = true; // 下载或安装过程中禁止重复操作 + + @override + void initState() { + super.initState(); + + // 监听状态流 + _sub = _service.statusStream.listen((event) { + // 根据事件更新 UI + setState(() { + switch (event.state) { + case UpdateState.idle: + _statusText = '空闲'; + break; + case UpdateState.starting: + _statusText = '准备中...'; + break; + case UpdateState.downloading: + _statusText = '下载中...'; + break; + case UpdateState.completed: + _statusText = '下载完成,正在准备安装...'; + break; + case UpdateState.installing: + _statusText = '发起安装...'; + break; + case UpdateState.installed: + _statusText = '已触发安装'; + break; + case UpdateState.failed: + _statusText = '失败: ${event.message ?? ''}'; + break; + case UpdateState.canceled: + _statusText = '已取消'; + break; + } + }); + + // 根据不同状态决定是否关闭弹窗或提示 + if (event.state == UpdateState.installing || event.state == UpdateState.installed) { + // 已发起安装,关闭弹窗让系统安装界面出现 + if (mounted) Future.delayed(const Duration(milliseconds: 300), () { + if (mounted) Navigator.of(context).pop(); + }); + } else if (event.state == UpdateState.failed) { + // 显示错误并在短时间后自动关闭弹窗 + _isWorking = false; + Future.delayed(const Duration(milliseconds: 800), () { + if (mounted) Navigator.of(context).pop(); + if (mounted) ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('更新失败:${event.message ?? '未知错误'}')), + ); + }); + } else if (event.state == UpdateState.canceled) { + _isWorking = false; + if (mounted) { + Future.delayed(const Duration(milliseconds: 200), () { + if (mounted) Navigator.of(context).pop(); + if (mounted) ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('下载已取消')), + ); + }); + } + } + }); + + // 监听进度 ValueNotifier + _service.progress.addListener(_onProgressChanged); + + // 启动下载 + _service.downloadAndInstall(apkUrl: widget.apkUrl, apkFileName: widget.apkFileName); + } + + void _onProgressChanged() { + setState(() { + _progress = _service.progress.value; + }); + } + + @override + void dispose() { + _sub?.cancel(); + _service.progress.removeListener(_onProgressChanged); + super.dispose(); + } + + void _onCancel() { + if (!_isWorking) { + // 如果已经不是工作中,直接关闭 + if (Navigator.of(context).canPop()) Navigator.of(context).pop(); + return; + } + + // 取消下载 + _service.cancelDownload(); + // 标记为非工作中(后续状态回调会关闭并提示) + _isWorking = false; + } + + @override + Widget build(BuildContext context) { + final percent = (_progress * 100).clamp(0.0, 100.0); + return WillPopScope( + onWillPop: () async => false, // 禁止物理返回 + child: AlertDialog( + title: const Text('正在更新'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + LinearProgressIndicator(value: _progress), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('${percent.toStringAsFixed(1)}%'), + Flexible(child: Text(_statusText, overflow: TextOverflow.ellipsis, textAlign: TextAlign.right)), + ], + ), + ], + ), + actions: [ + TextButton( + onPressed: _onCancel, + child: const Text('取消'), + ), + ], + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 3ff5f30..1d96f72 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -796,10 +796,10 @@ packages: dependency: transitive description: name: provider - sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "6.1.5" + version: "6.1.5+1" shared_preferences: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 1c19da8..aa43be1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.1 +version: 1.0.1+5 environment: sdk: ^3.7.0