Compare commits
2 Commits
3167e2f930
...
c75dfba154
| Author | SHA1 | Date |
|---|---|---|
|
|
c75dfba154 | |
|
|
ff41cbce60 |
|
|
@ -6,9 +6,13 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
|
||||
<!-- 相册权限 -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- Android 13+ -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Android 12及以下 -->
|
||||
<!-- Android 13+ -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
|
||||
<!-- Android 12 及以下 -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<!-- 相机 -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
|
|
|||
|
|
@ -8,6 +8,40 @@ PODS:
|
|||
- Flutter
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- DKImagePickerController/Core (4.3.9):
|
||||
- DKImagePickerController/ImageDataManager
|
||||
- DKImagePickerController/Resource
|
||||
- DKImagePickerController/ImageDataManager (4.3.9)
|
||||
- DKImagePickerController/PhotoGallery (4.3.9):
|
||||
- DKImagePickerController/Core
|
||||
- DKPhotoGallery
|
||||
- DKImagePickerController/Resource (4.3.9)
|
||||
- DKPhotoGallery (0.0.19):
|
||||
- DKPhotoGallery/Core (= 0.0.19)
|
||||
- DKPhotoGallery/Model (= 0.0.19)
|
||||
- DKPhotoGallery/Preview (= 0.0.19)
|
||||
- DKPhotoGallery/Resource (= 0.0.19)
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Core (0.0.19):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Preview
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Model (0.0.19):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Preview (0.0.19):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Resource
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Resource (0.0.19):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- file_picker (0.0.1):
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_baidu_mapapi_base (3.9.0):
|
||||
- BaiduMapKit/Utils (= 6.6.4)
|
||||
|
|
@ -39,12 +73,18 @@ PODS:
|
|||
- FlutterMacOS
|
||||
- pdfx (1.0.0):
|
||||
- Flutter
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- photo_manager (3.7.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- SDWebImage (5.21.1):
|
||||
- SDWebImage/Core (= 5.21.1)
|
||||
- SDWebImage/Core (5.21.1)
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- SwiftyGif (5.4.5)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- video_compress (0.3.0):
|
||||
|
|
@ -61,6 +101,7 @@ PODS:
|
|||
DEPENDENCIES:
|
||||
- camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/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`)
|
||||
|
|
@ -74,6 +115,7 @@ DEPENDENCIES:
|
|||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- pdfx (from `.symlinks/plugins/pdfx/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
|
|
@ -85,12 +127,18 @@ DEPENDENCIES:
|
|||
SPEC REPOS:
|
||||
trunk:
|
||||
- BaiduMapKit
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
camera_avfoundation:
|
||||
:path: ".symlinks/plugins/camera_avfoundation/ios"
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_baidu_mapapi_base:
|
||||
|
|
@ -117,6 +165,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
pdfx:
|
||||
:path: ".symlinks/plugins/pdfx/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
photo_manager:
|
||||
:path: ".symlinks/plugins/photo_manager/ios"
|
||||
shared_preferences_foundation:
|
||||
|
|
@ -136,6 +186,9 @@ SPEC CHECKSUMS:
|
|||
BaiduMapKit: 84991811cb07b24c6ead7d59022c13245427782c
|
||||
camera_avfoundation: be3be85408cd4126f250386828e9b1dfa40ab436
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_baidu_mapapi_base: 24dd82034374c6f52a73e90316834c63ff8d4f64
|
||||
flutter_baidu_mapapi_map: f799cc1bb3d39196b8d3d59399ca8635e690bd44
|
||||
|
|
@ -149,8 +202,11 @@ SPEC CHECKSUMS:
|
|||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
pdfx: 77f4dddc48361fbb01486fa2bdee4532cbb97ef3
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
photo_manager: 1d80ae07a89a67dfbcae95953a1e5a24af7c3e62
|
||||
SDWebImage: f29024626962457f3470184232766516dee8dfea
|
||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||
video_compress: f2133a07762889d67f0711ac831faa26f956980e
|
||||
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class ListItemFactory {
|
|||
Text(
|
||||
leftText,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: textColor,
|
||||
),
|
||||
|
|
@ -47,7 +47,7 @@ class ListItemFactory {
|
|||
fit: FlexFit.loose,
|
||||
child: Text(
|
||||
rightText,
|
||||
style: TextStyle(fontSize: 15, color: Colors.grey),
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
|
@ -65,7 +65,7 @@ class ListItemFactory {
|
|||
fit: FlexFit.loose,
|
||||
child: Text(
|
||||
rightText,
|
||||
style: TextStyle(fontSize: 15, color: Colors.grey),
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.right,
|
||||
|
|
@ -101,7 +101,7 @@ class ListItemFactory {
|
|||
Text(
|
||||
topText,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
|
@ -109,7 +109,7 @@ class ListItemFactory {
|
|||
const SizedBox(height: 5),
|
||||
Text(
|
||||
bottomText,
|
||||
style: TextStyle(fontSize: 15, color: Colors.grey),
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey),
|
||||
softWrap: true,
|
||||
maxLines: null, // 允许无限行数
|
||||
),
|
||||
|
|
@ -139,7 +139,7 @@ class ListItemFactory {
|
|||
Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
|
@ -202,7 +202,7 @@ class ListItemFactory {
|
|||
Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
|
@ -250,7 +250,7 @@ class ListItemFactory {
|
|||
children: [
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(fontSize: 15, color: Colors.grey),
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey),
|
||||
softWrap: true,
|
||||
maxLines: null, // 允许无限行数
|
||||
),
|
||||
|
|
@ -296,7 +296,7 @@ class ListItemFactory {
|
|||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
|
@ -418,7 +418,7 @@ class ListItemFactory {
|
|||
if (isRequired) Text('* ', style: TextStyle(color: Colors.red)),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -458,7 +458,7 @@ class ListItemFactory {
|
|||
if (isRequired) Text('* ', style: TextStyle(color: Colors.red)),
|
||||
// 标题
|
||||
Expanded(
|
||||
child:HhTextStyleUtils.mainTitle(label, fontSize: 15),
|
||||
child:HhTextStyleUtils.mainTitle(label, fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -472,7 +472,7 @@ class ListItemFactory {
|
|||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
style: const TextStyle(fontSize: 15),
|
||||
style: const TextStyle(fontSize: 13),
|
||||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
border: InputBorder.none,
|
||||
|
|
@ -519,7 +519,7 @@ class ListItemFactory {
|
|||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class BottomPicker {
|
|||
required List<T> items,
|
||||
required Widget Function(T item) itemBuilder,
|
||||
int initialIndex = 0,
|
||||
double itemExtent = 40.0,
|
||||
double itemExtent = 50.0,
|
||||
double height = 250,
|
||||
}) {
|
||||
if (items.isEmpty) return Future.value(null);
|
||||
|
|
@ -82,13 +82,15 @@ class BottomPicker {
|
|||
scrollController: FixedExtentScrollController(
|
||||
initialItem: initialIndex,
|
||||
),
|
||||
itemExtent: 30,
|
||||
itemExtent: itemExtent,
|
||||
onSelectedItemChanged: (index) {
|
||||
selected = items[index];
|
||||
},
|
||||
children: items.map(itemBuilder).toList(),
|
||||
// 把 itemBuilder 返回的 Widget 用 Center 包一层
|
||||
children: items.map((item) => Center(child: itemBuilder(item))).toList(),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class BottomPickerTwo {
|
|||
required List<dynamic> items,
|
||||
required Widget Function(dynamic item) itemBuilder,
|
||||
int initialIndex = 0,
|
||||
double itemExtent = 40.0,
|
||||
double itemExtent = 50.0,
|
||||
double height = 250,
|
||||
double desiredSpacing = 16.0,
|
||||
}) {
|
||||
|
|
@ -72,7 +72,7 @@ class BottomPickerTwo {
|
|||
scrollController: FixedExtentScrollController(
|
||||
initialItem: initialIndex,
|
||||
),
|
||||
itemExtent: 35,
|
||||
itemExtent: itemExtent,
|
||||
onSelectedItemChanged: (index) {
|
||||
selected = items[index];
|
||||
},
|
||||
|
|
@ -88,7 +88,7 @@ class BottomPickerTwo {
|
|||
// '选项 $index',
|
||||
items[index]["NAME"],
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontSize: 15,
|
||||
color:
|
||||
index == selected
|
||||
? CupertinoColors.activeBlue
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class _VideoPlayerPopupState extends State<VideoPlayerPopup> {
|
|||
playedColor: Colors.blue,
|
||||
backgroundColor: Colors.white,
|
||||
handleColor: Colors.blue,
|
||||
bufferedColor: Colors.red,
|
||||
bufferedColor: Colors.white,
|
||||
),
|
||||
aspectRatio: _videoController.value.aspectRatio > 0
|
||||
? _videoController.value.aspectRatio
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:qhd_prevention/tools/VideoConverter.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||
import 'package:video_compress/video_compress.dart';
|
||||
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
|
@ -54,12 +51,15 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_mediaPaths = widget.initialMediaPaths != null
|
||||
? widget.initialMediaPaths!.take(widget.maxCount).toList()
|
||||
: [];
|
||||
_mediaPaths =
|
||||
widget.initialMediaPaths != null
|
||||
? widget.initialMediaPaths!.take(widget.maxCount).toList()
|
||||
: [];
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onChanged(
|
||||
_mediaPaths.map((p) => p.startsWith('http') ? File('') : File(p)).toList(),
|
||||
_mediaPaths
|
||||
.map((p) => p.startsWith('http') ? File('') : File(p))
|
||||
.toList(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -114,70 +114,55 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _cameraAction() async {
|
||||
if (!widget.isEdit || _mediaPaths.length >= widget.maxCount) return;
|
||||
|
||||
try {
|
||||
if (widget.mediaType == MediaType.image) {
|
||||
XFile? picked = await _picker.pickImage(source: ImageSource.camera);
|
||||
if (picked != null) {
|
||||
final path = picked.path;
|
||||
setState(() => _mediaPaths.add(path));
|
||||
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
|
||||
widget.onMediaAdded?.call(path);
|
||||
}
|
||||
} else {
|
||||
// video from camera
|
||||
XFile? picked = await _picker.pickVideo(source: ImageSource.camera);
|
||||
if (picked != null) {
|
||||
await _handlePickedPath(picked.path);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('拍摄失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showPickerOptions() async {
|
||||
if (!widget.isEdit) return; // 不可编辑时直接返回
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.white,
|
||||
builder: (_) => SafeArea(
|
||||
child: Wrap(
|
||||
children: [
|
||||
ListTile(
|
||||
titleAlignment: ListTileTitleAlignment.center,
|
||||
leading: Icon(
|
||||
widget.mediaType == MediaType.image ? Icons.camera_alt : Icons.videocam,
|
||||
),
|
||||
title: Text(widget.mediaType == MediaType.image ? '拍照' : '拍摄视频'),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
_pickCamera();
|
||||
},
|
||||
builder:
|
||||
(_) => SafeArea(
|
||||
child: Wrap(
|
||||
children: [
|
||||
ListTile(
|
||||
titleAlignment: ListTileTitleAlignment.center,
|
||||
leading: Icon(
|
||||
widget.mediaType == MediaType.image
|
||||
? Icons.camera_alt
|
||||
: Icons.videocam,
|
||||
),
|
||||
title: Text(
|
||||
widget.mediaType == MediaType.image ? '拍照' : '拍摄视频',
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
_pickCamera();
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
titleAlignment: ListTileTitleAlignment.center,
|
||||
leading: Icon(
|
||||
widget.mediaType == MediaType.image
|
||||
? Icons.photo_library
|
||||
: Icons.video_library,
|
||||
),
|
||||
title: Text(
|
||||
widget.mediaType == MediaType.image ? '从相册选择' : '从相册选择视频',
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
_pickGallery();
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
titleAlignment: ListTileTitleAlignment.center,
|
||||
leading: const Icon(Icons.close),
|
||||
title: const Text('取消'),
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
ListTile(
|
||||
titleAlignment: ListTileTitleAlignment.center,
|
||||
leading: Icon(
|
||||
widget.mediaType == MediaType.image ? Icons.photo_library : Icons.video_library,
|
||||
),
|
||||
title: Text(widget.mediaType == MediaType.image ? '从相册选择' : '从相册选择视频'),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
_pickGallery();
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
titleAlignment: ListTileTitleAlignment.center,
|
||||
leading: const Icon(Icons.close),
|
||||
title: const Text('取消'),
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -205,52 +190,181 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
|
|||
}
|
||||
}
|
||||
|
||||
// 修改 _pickGallery 方法
|
||||
Future<void> _pickGallery() async {
|
||||
if (!widget.isEdit || _mediaPaths.length >= widget.maxCount) return;
|
||||
|
||||
final permission = await PhotoManager.requestPermissionExtend();
|
||||
if (permission != PermissionState.authorized &&
|
||||
permission != PermissionState.limited) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('请到设置中开启相册访问权限')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final remaining = widget.maxCount - _mediaPaths.length;
|
||||
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
|
||||
context,
|
||||
pickerConfig: AssetPickerConfig(
|
||||
requestType: widget.mediaType == MediaType.image ? RequestType.image : RequestType.video,
|
||||
maxAssets: remaining,
|
||||
gridCount: 4,
|
||||
),
|
||||
);
|
||||
if (assets != null) {
|
||||
for (final asset in assets) {
|
||||
if (_mediaPaths.length >= widget.maxCount) break;
|
||||
final file = await asset.file;
|
||||
if (file != null) {
|
||||
final path = file.path;
|
||||
// 交给统一处理(会转码视频)
|
||||
await _handlePickedPath(path);
|
||||
// iOS: 使用 PhotoManager.requestPermissionExtend() 唤起系统权限弹窗(支持 limited)
|
||||
if (Platform.isIOS) {
|
||||
final permission = await PhotoManager.requestPermissionExtend();
|
||||
debugPrint('iOS photo permission state: $permission');
|
||||
|
||||
if (permission != PermissionState.authorized &&
|
||||
permission != PermissionState.limited) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('请到设置中开启相册访问权限')),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 授权或 limited:继续打开 AssetPicker
|
||||
final remaining = widget.maxCount - _mediaPaths.length;
|
||||
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
|
||||
context,
|
||||
pickerConfig: AssetPickerConfig(
|
||||
requestType:
|
||||
widget.mediaType == MediaType.image ? RequestType.image : RequestType.video,
|
||||
maxAssets: remaining,
|
||||
gridCount: 4,
|
||||
),
|
||||
);
|
||||
|
||||
if (assets != null) {
|
||||
for (final asset in assets) {
|
||||
if (_mediaPaths.length >= widget.maxCount) break;
|
||||
try {
|
||||
final file = await asset.file;
|
||||
if (file != null) {
|
||||
final path = file.path;
|
||||
await _handlePickedPath(path);
|
||||
} else {
|
||||
debugPrint('资产获取 file 为空,asset id: ${asset.id}');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('读取 asset 文件失败: $e');
|
||||
}
|
||||
}
|
||||
if (mounted) setState(() {});
|
||||
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
|
||||
}
|
||||
|
||||
} else {
|
||||
// Android: 使用 permission_handler 请求存储权限(简单处理)
|
||||
final PermissionStatus current = await Permission.storage.status;
|
||||
PermissionStatus status;
|
||||
if (current.isGranted) {
|
||||
status = PermissionStatus.granted;
|
||||
} else {
|
||||
status = await Permission.storage.request();
|
||||
}
|
||||
|
||||
debugPrint('Android storage permission: $status');
|
||||
|
||||
if (status == PermissionStatus.granted) {
|
||||
final remaining = widget.maxCount - _mediaPaths.length;
|
||||
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
|
||||
context,
|
||||
pickerConfig: AssetPickerConfig(
|
||||
requestType:
|
||||
widget.mediaType == MediaType.image ? RequestType.image : RequestType.video,
|
||||
maxAssets: remaining,
|
||||
gridCount: 4,
|
||||
),
|
||||
);
|
||||
|
||||
if (assets != null) {
|
||||
for (final asset in assets) {
|
||||
if (_mediaPaths.length >= widget.maxCount) break;
|
||||
try {
|
||||
final file = await asset.file;
|
||||
if (file != null) {
|
||||
final path = file.path;
|
||||
await _handlePickedPath(path);
|
||||
} else {
|
||||
debugPrint('资产获取 file 为空,asset id: ${asset.id}');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('读取 asset 文件失败: $e');
|
||||
}
|
||||
}
|
||||
if (mounted) setState(() {});
|
||||
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
|
||||
}
|
||||
} else if (status == PermissionStatus.permanentlyDenied) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('请到设置中开启相册访问权限')),
|
||||
);
|
||||
}
|
||||
await openAppSettings();
|
||||
} else {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('相册访问权限被拒绝')),
|
||||
);
|
||||
}
|
||||
}
|
||||
setState(() {});
|
||||
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('相册选择失败: $e');
|
||||
} catch (e, st) {
|
||||
debugPrint('相册选择失败: $e\n$st');
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('相册选择失败')),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _removeMedia(int index) {
|
||||
if (!widget.isEdit) return; // 不可编辑时不允许删除
|
||||
// 修改拍照方法,使用 permission_handler 请求相机权限
|
||||
Future<void> _cameraAction() async {
|
||||
if (!widget.isEdit || _mediaPaths.length >= widget.maxCount) return;
|
||||
|
||||
final removed = _mediaPaths[index];
|
||||
setState(() => _mediaPaths.removeAt(index));
|
||||
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
|
||||
widget.onMediaRemoved?.call(removed);
|
||||
// 请求相机权限
|
||||
final PermissionStatus status = await Permission.camera.request();
|
||||
|
||||
if (status != PermissionStatus.granted) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('相机权限被拒绝')));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (widget.mediaType == MediaType.image) {
|
||||
XFile? picked = await _picker.pickImage(source: ImageSource.camera);
|
||||
if (picked != null) {
|
||||
final path = picked.path;
|
||||
setState(() => _mediaPaths.add(path));
|
||||
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
|
||||
widget.onMediaAdded?.call(path);
|
||||
}
|
||||
} else {
|
||||
// video from camera
|
||||
XFile? picked = await _picker.pickVideo(source: ImageSource.camera);
|
||||
if (picked != null) {
|
||||
await _handlePickedPath(picked.path);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('拍摄失败: $e');
|
||||
// 友好提示
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('拍摄失败,请检查相机权限或设备状态')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _removeMedia(int index) async {
|
||||
final ok = await CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
title: '温馨提示',
|
||||
content:
|
||||
widget.mediaType == MediaType.image ? '确定要删除这张图片吗?' : '确定要删除这个视频吗?',
|
||||
cancelText: '取消',
|
||||
);
|
||||
if (ok) {
|
||||
final removed = _mediaPaths[index];
|
||||
setState(() => _mediaPaths.removeAt(index));
|
||||
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
|
||||
widget.onMediaRemoved?.call(removed);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -282,28 +396,42 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
|
|||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: widget.mediaType == MediaType.image
|
||||
? (isNetwork
|
||||
? Image.network(path, fit: BoxFit.cover, width: 80, height: 80)
|
||||
: Image.file(File(path), width: 80, height: 80, fit: BoxFit.cover))
|
||||
: Container(
|
||||
color: Colors.black12,
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.videocam,
|
||||
color: Colors.white70,
|
||||
),
|
||||
|
||||
),
|
||||
child: SizedBox.expand(
|
||||
// 让内容强制填满格子
|
||||
child:
|
||||
widget.mediaType == MediaType.image
|
||||
? (isNetwork
|
||||
? Image.network(
|
||||
path,
|
||||
fit: BoxFit.cover, // 铺满格子并裁剪
|
||||
)
|
||||
: Image.file(
|
||||
File(path),
|
||||
fit: BoxFit.cover, // 铺满格子并裁剪
|
||||
))
|
||||
: Container(
|
||||
color: Colors.black12,
|
||||
child: const Center(
|
||||
child: Icon(
|
||||
Icons.videocam,
|
||||
color: Colors.white70,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 只在可编辑状态下显示删除按钮
|
||||
if (widget.isEdit)
|
||||
Positioned(
|
||||
top: -15,
|
||||
right: -15,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.cancel, size: 20, color: Colors.red),
|
||||
icon: const Icon(
|
||||
Icons.cancel,
|
||||
size: 20,
|
||||
color: Colors.red,
|
||||
),
|
||||
onPressed: () => _removeMedia(index),
|
||||
),
|
||||
),
|
||||
|
|
@ -336,9 +464,7 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
|
|||
Positioned.fill(
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
child: const Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -346,12 +472,6 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// 照片上传区域组件,使用纵向四列Grid展示
|
||||
/// 新增 isEdit 属性控制编辑状态
|
||||
class RepairedPhotoSection extends StatefulWidget {
|
||||
|
|
@ -400,7 +520,8 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_mediaPaths = widget.initialMediaPaths?.take(widget.maxCount).toList() ?? [];
|
||||
_mediaPaths =
|
||||
widget.initialMediaPaths?.take(widget.maxCount).toList() ?? [];
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onChanged(_mediaPaths.map((p) => File(p)).toList());
|
||||
});
|
||||
|
|
@ -418,7 +539,10 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
|
|||
padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding),
|
||||
child: ListItemFactory.createRowSpaceBetweenItem(
|
||||
leftText: widget.title,
|
||||
rightText: widget.isShowNum ? '${_mediaPaths.length}/${widget.maxCount}' : '',
|
||||
rightText:
|
||||
widget.isShowNum
|
||||
? '${_mediaPaths.length}/${widget.maxCount}'
|
||||
: '',
|
||||
isRequired: widget.isRequired,
|
||||
),
|
||||
),
|
||||
|
|
@ -439,14 +563,17 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
|
|||
},
|
||||
onMediaAdded: widget.onMediaAdded,
|
||||
onMediaRemoved: widget.onMediaRemoved,
|
||||
onMediaTapped: widget.onMediaTapped, // 传递点击回调
|
||||
onMediaTapped: widget.onMediaTapped,
|
||||
// 传递点击回调
|
||||
isEdit: widget.isEdit, // 传递编辑状态
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 8),
|
||||
if (widget.isShowAI && widget.isEdit) // 只在可编辑状态下显示AI按钮
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: widget.horizontalPadding,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: widget.onAiIdentify,
|
||||
child: Container(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||
/// context,
|
||||
/// mode: BottomPickerMode.date, // 或 BottomPickerMode.dateTime(默认)
|
||||
/// allowFuture: true,
|
||||
/// allowPast: false, // 是否允许选择过去(false 表示只能选择现在或未来)
|
||||
/// minTimeStr: '2025-08-20 08:30',
|
||||
/// );
|
||||
/// if (picked != null) {
|
||||
|
|
@ -16,7 +17,8 @@ enum BottomPickerMode { dateTime, date }
|
|||
class BottomDateTimePicker {
|
||||
static Future<DateTime?> showDate(
|
||||
BuildContext context, {
|
||||
bool allowFuture = false,
|
||||
bool allowFuture = true,
|
||||
bool allowPast = true, // 新增:是否允许选择过去(默认允许)
|
||||
String? minTimeStr, // 可选:'yyyy-MM-dd HH:mm'
|
||||
BottomPickerMode mode = BottomPickerMode.dateTime,
|
||||
}) {
|
||||
|
|
@ -29,6 +31,7 @@ class BottomDateTimePicker {
|
|||
),
|
||||
builder: (_) => _InlineDateTimePickerContent(
|
||||
allowFuture: allowFuture,
|
||||
allowPast: allowPast,
|
||||
minTimeStr: minTimeStr,
|
||||
mode: mode,
|
||||
),
|
||||
|
|
@ -38,12 +41,14 @@ class BottomDateTimePicker {
|
|||
|
||||
class _InlineDateTimePickerContent extends StatefulWidget {
|
||||
final bool allowFuture;
|
||||
final bool allowPast;
|
||||
final String? minTimeStr;
|
||||
final BottomPickerMode mode;
|
||||
|
||||
const _InlineDateTimePickerContent({
|
||||
Key? key,
|
||||
this.allowFuture = false,
|
||||
this.allowFuture = true,
|
||||
this.allowPast = true,
|
||||
this.minTimeStr,
|
||||
this.mode = BottomPickerMode.dateTime,
|
||||
}) : super(key: key);
|
||||
|
|
@ -87,13 +92,25 @@ class _InlineDateTimePickerContentState
|
|||
// 解析 minTimeStr(若提供)
|
||||
_minTime = _parseMinTime(widget.minTimeStr);
|
||||
|
||||
// 初始时间:取 now 与 _minTime 的较大者
|
||||
// 初始时间:取 now 与 _minTime 的较大者(但要考虑 allowPast)
|
||||
final now = DateTime.now();
|
||||
DateTime initial = now;
|
||||
|
||||
// 如果指定了最小时间并且比 now 晚,则以最小时间为初始
|
||||
if (_minTime != null && _minTime!.isAfter(initial)) {
|
||||
initial = _minTime!;
|
||||
}
|
||||
|
||||
// 如果不允许选择过去,则确保 initial 至少为 now(或当天的 00:00,取决于模式)
|
||||
if (!widget.allowPast) {
|
||||
if (widget.mode == BottomPickerMode.date) {
|
||||
final today = DateTime(now.year, now.month, now.day);
|
||||
if (initial.isBefore(today)) initial = today;
|
||||
} else {
|
||||
if (initial.isBefore(now)) initial = now;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是 date 模式,只保留日期部分(时分归零)
|
||||
if (widget.mode == BottomPickerMode.date) {
|
||||
initial = DateTime(initial.year, initial.month, initial.day);
|
||||
|
|
@ -120,7 +137,7 @@ class _InlineDateTimePickerContentState
|
|||
minuteCtrl = FixedExtentScrollController(
|
||||
initialItem: selectedMinute.clamp(0, minutes.length - 1));
|
||||
|
||||
// 确保初始选择满足约束(例如 minTime 或禁止未来)
|
||||
// 确保初始选择满足约束(例如 minTime 或禁止未来/禁止过去)
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_enforceConstraintsAndUpdateControllers();
|
||||
});
|
||||
|
|
@ -169,7 +186,7 @@ class _InlineDateTimePickerContentState
|
|||
});
|
||||
}
|
||||
|
||||
// 检查并限制时间(模式感知)
|
||||
// 检查并限制时间(模式感知),支持 allowPast 与 allowFuture
|
||||
void _enforceConstraintsAndUpdateControllers() {
|
||||
final now = DateTime.now();
|
||||
final isDateOnly = widget.mode == BottomPickerMode.date;
|
||||
|
|
@ -179,35 +196,48 @@ class _InlineDateTimePickerContentState
|
|||
: DateTime(
|
||||
selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute);
|
||||
|
||||
// 处理 _minTime(如果存在),在 date 模式下只比较日期部分
|
||||
if (_minTime != null) {
|
||||
final DateTime minRef = isDateOnly
|
||||
? DateTime(_minTime!.year, _minTime!.month, _minTime!.day)
|
||||
: _minTime!;
|
||||
if (picked.isBefore(minRef)) {
|
||||
// 把选中项调整为 minRef
|
||||
selectedYear = minRef.year;
|
||||
selectedMonth = minRef.month;
|
||||
selectedDay = minRef.day;
|
||||
if (!isDateOnly) {
|
||||
selectedHour = minRef.hour;
|
||||
selectedMinute = minRef.minute;
|
||||
} else {
|
||||
selectedHour = 0;
|
||||
selectedMinute = 0;
|
||||
}
|
||||
|
||||
// 更新天数及控制器
|
||||
_updateDays(jumpDay: false);
|
||||
yearCtrl.jumpToItem(years.indexOf(selectedYear));
|
||||
monthCtrl.jumpToItem(selectedMonth - 1);
|
||||
dayCtrl.jumpToItem(selectedDay - 1);
|
||||
if (!isDateOnly) {
|
||||
hourCtrl.jumpToItem(selectedHour);
|
||||
minuteCtrl.jumpToItem(selectedMinute);
|
||||
}
|
||||
return;
|
||||
// 处理最小时间约束:结合 _minTime 与 allowPast
|
||||
DateTime? minRef;
|
||||
if (!widget.allowPast) {
|
||||
// 不允许选择过去:最小时间至少为 now(或当天 00:00)
|
||||
minRef = isDateOnly
|
||||
? DateTime(now.year, now.month, now.day)
|
||||
: now;
|
||||
// 如果用户也指定了 _minTime 且比 now 晚,则以 _minTime 为准
|
||||
if (_minTime != null && _minTime!.isAfter(minRef)) {
|
||||
minRef = isDateOnly
|
||||
? DateTime(_minTime!.year, _minTime!.month, _minTime!.day)
|
||||
: _minTime;
|
||||
}
|
||||
} else if (_minTime != null) {
|
||||
// 允许选择过去,但若指定了 _minTime,则以 _minTime 为最小参考
|
||||
minRef = isDateOnly
|
||||
? DateTime(_minTime!.year, _minTime!.month, _minTime!.day)
|
||||
: _minTime;
|
||||
}
|
||||
|
||||
if (minRef != null && picked.isBefore(minRef)) {
|
||||
// 把选中项调整为 minRef
|
||||
selectedYear = minRef.year;
|
||||
selectedMonth = minRef.month;
|
||||
selectedDay = minRef.day;
|
||||
if (!isDateOnly) {
|
||||
selectedHour = minRef.hour;
|
||||
selectedMinute = minRef.minute;
|
||||
} else {
|
||||
selectedHour = 0;
|
||||
selectedMinute = 0;
|
||||
}
|
||||
|
||||
_updateDays(jumpDay: false);
|
||||
yearCtrl.jumpToItem(years.indexOf(selectedYear));
|
||||
monthCtrl.jumpToItem(selectedMonth - 1);
|
||||
dayCtrl.jumpToItem(selectedDay - 1);
|
||||
if (!isDateOnly) {
|
||||
hourCtrl.jumpToItem(selectedHour);
|
||||
minuteCtrl.jumpToItem(selectedMinute);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理禁止选择未来(当 allowFuture == false)
|
||||
|
|
@ -370,7 +400,7 @@ class _InlineDateTimePickerContentState
|
|||
return Expanded(
|
||||
child: CupertinoPicker.builder(
|
||||
scrollController: controller,
|
||||
itemExtent: 32,
|
||||
itemExtent: 40,
|
||||
childCount: items.length,
|
||||
onSelectedItemChanged: onSelected,
|
||||
itemBuilder: (context, index) {
|
||||
|
|
|
|||
|
|
@ -237,20 +237,29 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 检查记录详情地图信息
|
||||
static Future<Map<String, dynamic>> getRecordMapInfo(String CHECKRECORD_ID) {
|
||||
/// 检查记录详情地图信息(标准排查)
|
||||
static Future<Map<String, dynamic>> getNormalRecordMapInfo(Map data) {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/customCheckRecord/goMap',
|
||||
method: Method.post,
|
||||
data: {
|
||||
'USER_ID': SessionService.instance.loginUserId,
|
||||
'CORPINFO_ID': SessionService.instance.corpinfoId,
|
||||
'CHECKRECORD_ID': CHECKRECORD_ID,
|
||||
...data
|
||||
},
|
||||
);
|
||||
}
|
||||
/// 检查记录详情地图信息(隐患排查)
|
||||
static Future<Map<String, dynamic>> getDangerRecordMapInfo(Map data) {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/checkrecord/goMap',
|
||||
method: Method.post,
|
||||
data: {
|
||||
...data
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// 月隐患 1 月隐患,2年隐患
|
||||
static Future<Map<String, dynamic>> getDanger(int type) {
|
||||
|
|
@ -301,7 +310,7 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
'userId': SessionService.instance.loginUserId,
|
||||
'USER_NAME': SessionService.instance.username,
|
||||
'CHECK_DEPARTMENT_ID': SessionService.instance.deptId,
|
||||
'IS_MAIN': SessionService.instance.isRest,
|
||||
'IS_MAIN': SessionService.instance.loginUser?['ISMAIN']??'',
|
||||
'CORPINFO_ID': SessionService.instance.corpinfoId,
|
||||
'USER_ID': SessionService.instance.loginUserId,
|
||||
},
|
||||
|
|
@ -312,7 +321,7 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
static Future<Map<String, dynamic>> getHiddenRoll() {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/offduty/isRest',
|
||||
'/app/hidden/getHiddenByCorp',
|
||||
method: Method.post,
|
||||
data: {
|
||||
'CORPINFO_ID': SessionService.instance.corpinfoId,
|
||||
|
|
@ -431,7 +440,7 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 人脸成功告知服务器
|
||||
static Future<Map<String, dynamic>> fnSetUserFaceTime(int FACE_TIME) {
|
||||
return HttpManager().request(
|
||||
baseFacePath,
|
||||
|
|
@ -445,16 +454,28 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> fnGetUserFaceTime(int FACE_TIME) {
|
||||
/// 是否需要人脸识别
|
||||
static Future<Map<String, dynamic>> fnGetUserFaceTime() {
|
||||
return HttpManager().request(
|
||||
baseFacePath,
|
||||
'/app/user/getUserFaceTime',
|
||||
method: Method.post,
|
||||
withToken: true,
|
||||
data: {
|
||||
'loading': false,
|
||||
'FACE_TIME': FACE_TIME,
|
||||
'CORPINFO_ID': SessionService.instance.corpinfoId,
|
||||
'USER_ID': SessionService.instance.loginUserId,
|
||||
},
|
||||
);
|
||||
}
|
||||
/// 人脸状态,是否上传过人脸
|
||||
static Future<Map<String, dynamic>> fnGetUserFace() {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/user/getUserFace',
|
||||
method: Method.post,
|
||||
withToken: true,
|
||||
data: {
|
||||
'USERNAME': SessionService.instance.username,
|
||||
'CORPINFO_ID': SessionService.instance.corpinfoId,
|
||||
'USER_ID': SessionService.instance.loginUserId,
|
||||
},
|
||||
|
|
@ -474,7 +495,7 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 获取视频进度
|
||||
static Future<Map<String, dynamic>> fnGetVideoPlayProgress(
|
||||
String VIDEOCOURSEWARE_ID,
|
||||
String CURRICULUM_ID,
|
||||
|
|
@ -968,7 +989,7 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
);
|
||||
}
|
||||
|
||||
/// 保存安全措施确认
|
||||
/// 作业票签字下一步
|
||||
static Future<Map<String, dynamic>> saveSafeFunctionSure(
|
||||
String workType,
|
||||
Map<String, dynamic> formData,
|
||||
|
|
@ -3877,19 +3898,7 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
},
|
||||
);
|
||||
}
|
||||
/// 删除已上传图片
|
||||
static Future<Map<String, dynamic>> deleteNFCImage(Map data) {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/app/imgfiles/delete',
|
||||
method: Method.post,
|
||||
data: {
|
||||
...data,
|
||||
"CORPINFO_ID": SessionService.instance.corpinfoId,
|
||||
"USER_ID": SessionService.instance.loginUserId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// nfc巡检记录
|
||||
static Future<Map<String, dynamic>> nfcInspectionRecord(
|
||||
int showCount,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// main.dart
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:qhd_prevention/pages/badge_manager.dart';
|
||||
import 'package:qhd_prevention/services/auth_service.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
|
@ -34,7 +35,10 @@ class GlobalMessage {
|
|||
}
|
||||
|
||||
/// 全局 helper:在弹窗前取消焦点并尽量隐藏键盘,避免弹窗后键盘自动弹起
|
||||
Future<T?> showDialogAfterUnfocus<T>(BuildContext context, Widget dialog) async {
|
||||
Future<T?> showDialogAfterUnfocus<T>(
|
||||
BuildContext context,
|
||||
Widget dialog,
|
||||
) async {
|
||||
// 取消焦点并尝试隐藏键盘
|
||||
FocusScope.of(context).unfocus();
|
||||
try {
|
||||
|
|
@ -65,7 +69,10 @@ Future<T?> showModalBottomSheetAfterUnfocus<T>({
|
|||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp,
|
||||
DeviceOrientation.portraitDown,
|
||||
]);
|
||||
// 初始化 EasyLoading
|
||||
EasyLoading.instance
|
||||
..displayDuration = const Duration(seconds: 20)
|
||||
|
|
@ -89,7 +96,7 @@ void main() async {
|
|||
await prefs.remove('token');
|
||||
navigatorKey.currentState?.pushNamedAndRemoveUntil(
|
||||
'/login',
|
||||
(route) => false,
|
||||
(route) => false,
|
||||
);
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
|
|
@ -123,9 +130,21 @@ class MyApp extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: '',
|
||||
localizationsDelegates: [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
// 如果使用了其他本地化包,请添加对应的 delegate
|
||||
],
|
||||
supportedLocales: [
|
||||
const Locale('zh', 'CN'), // 中文
|
||||
const Locale('en', 'US'), // 英文(备用)
|
||||
],
|
||||
locale: const Locale('zh', 'CN'),
|
||||
// 强制使用中文
|
||||
navigatorKey: navigatorKey,
|
||||
// 在路由变化时统一取消焦点(防止 push/pop 时焦点回到 TextField)
|
||||
navigatorObservers: [KeyboardUnfocusNavigatorObserver(),routeObserver],
|
||||
navigatorObservers: [KeyboardUnfocusNavigatorObserver(), routeObserver],
|
||||
builder: (context, child) {
|
||||
return EasyLoading.init(
|
||||
builder: (context, widget) {
|
||||
|
|
@ -133,7 +152,11 @@ class MyApp extends StatelessWidget {
|
|||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () {
|
||||
// 全局点击空白处取消焦点(隐藏键盘)
|
||||
FocusHelper.clearFocus(context);
|
||||
try {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
} catch (e) {
|
||||
debugPrint('NavigatorObserver unfocus error: $e');
|
||||
}
|
||||
},
|
||||
child: widget,
|
||||
);
|
||||
|
|
@ -142,7 +165,7 @@ class MyApp extends StatelessWidget {
|
|||
},
|
||||
theme: ThemeData(
|
||||
textTheme: const TextTheme(
|
||||
bodyMedium: TextStyle(fontSize: 14), // 默认字体大小
|
||||
bodyMedium: TextStyle(fontSize: 13), // 默认字体大小
|
||||
),
|
||||
dividerTheme: const DividerThemeData(
|
||||
color: Colors.black12,
|
||||
|
|
@ -169,9 +192,7 @@ class MyApp extends StatelessWidget {
|
|||
),
|
||||
home: isLoggedIn ? const MainPage() : const LoginPage(),
|
||||
debugShowCheckedModeBanner: false,
|
||||
routes: {
|
||||
'/login': (_) => const LoginPage(),
|
||||
},
|
||||
routes: {'/login': (_) => const LoginPage()},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,19 +151,23 @@ class _DangerManagerDetailPageState extends State<DangerManagerDetailPage> {
|
|||
}
|
||||
|
||||
Future<void> _submit() async {
|
||||
|
||||
if (!FormUtils.hasValue(hiddenForm, 'CHECKTIME')) {
|
||||
ToastUtil.showNormal(context, '请选择验收时间');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LoadingDialogHelper.show();
|
||||
final result = await ApiService.checkKeyprojectDanger(widget.info['HIDDEN_ID'] ?? '', hiddenForm);
|
||||
final result = await ApiService.checkKeyprojectDanger(
|
||||
widget.info['HIDDEN_ID'] ?? '',
|
||||
hiddenForm,
|
||||
);
|
||||
if (result['result'] == 'success') {
|
||||
final List<String> files = _getServerPath(hiddenForm['ysImgs']);
|
||||
for (String p in files) {
|
||||
final upResult = await ApiService.addNormalImgFiles(p,{'TYPE':'5','FOREIGN_KEY':widget.info['HIDDEN_ID']});
|
||||
final upResult = await ApiService.addNormalImgFiles(p, {
|
||||
'TYPE': '5',
|
||||
'FOREIGN_KEY': widget.info['HIDDEN_ID'],
|
||||
});
|
||||
if (FormUtils.hasValue(upResult, 'network_error')) {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, '上传文件出错');
|
||||
|
|
@ -272,7 +276,8 @@ class _DangerManagerDetailPageState extends State<DangerManagerDetailPage> {
|
|||
ItemListWidget.singleLineTitleText(
|
||||
label: '整改部门',
|
||||
isEditable: false,
|
||||
text: hiddenForm['RECTIFICATIONDEPT_NAME'] ?? '',
|
||||
text:
|
||||
hiddenForm['RECTIFICATIONDEPT_NAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
|
|
@ -342,31 +347,47 @@ class _DangerManagerDetailPageState extends State<DangerManagerDetailPage> {
|
|||
),
|
||||
const Divider(),
|
||||
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 8),child: RepairedPhotoSection(
|
||||
title: '验收照片',
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
isShowAI: false,
|
||||
isEdit: widget.info['TabCur'] == '1'
|
||||
? true
|
||||
: false,
|
||||
isRequired:false,
|
||||
initialMediaPaths: _getServerPath(
|
||||
hiddenForm['ysImgs'],
|
||||
).map((path) => '${ApiService.baseImgPath}$path').toList(),
|
||||
onMediaTapped: (p) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: p), context);
|
||||
},
|
||||
onChanged:
|
||||
(files) => setState(() {
|
||||
hiddenForm['ysImgs'] =
|
||||
files
|
||||
.map((f) => {'FILEPATH': f.path})
|
||||
.toList();
|
||||
}), onAiIdentify: () { },
|
||||
|
||||
),)
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
),
|
||||
child: RepairedPhotoSection(
|
||||
title: '验收照片',
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
isShowAI: false,
|
||||
isEdit:
|
||||
widget.info['TabCur'] == '1'
|
||||
? true
|
||||
: false,
|
||||
isRequired: false,
|
||||
initialMediaPaths:
|
||||
_getServerPath(hiddenForm['ysImgs'])
|
||||
.map(
|
||||
(path) =>
|
||||
'${ApiService.baseImgPath}$path',
|
||||
)
|
||||
.toList(),
|
||||
onMediaTapped: (p) {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: p),
|
||||
context,
|
||||
);
|
||||
},
|
||||
onChanged:
|
||||
(files) => setState(() {
|
||||
hiddenForm['ysImgs'] =
|
||||
files
|
||||
.map(
|
||||
(f) => {
|
||||
'FILEPATH': f.path,
|
||||
},
|
||||
)
|
||||
.toList();
|
||||
}),
|
||||
onAiIdentify: () {},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:intl/intl.dart';
|
|||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker_two.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/date_picker_dialog.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_person_picker.dart';
|
||||
|
|
@ -19,6 +20,17 @@ import 'package:qhd_prevention/tools/tools.dart';
|
|||
import '../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../http/ApiService.dart';
|
||||
|
||||
class ImgData {
|
||||
final String path;
|
||||
final String id;
|
||||
|
||||
const ImgData({
|
||||
required this.path,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
class CheckInformationOneItem extends StatefulWidget {
|
||||
const CheckInformationOneItem(this.id,this.item, this.result, this.type, {super.key,required this.onClose});
|
||||
|
||||
|
|
@ -46,8 +58,8 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
List<Map<String, dynamic>> _personCache = [];
|
||||
|
||||
|
||||
List<String> _yinHuanImages = [];
|
||||
List<String> _yinHuanVido = [];
|
||||
List<ImgData> _yinHuanImages = [];
|
||||
List<ImgData> _yinHuanVido = [];
|
||||
// String _yinHuanVido="";
|
||||
dynamic _hazardLeve;
|
||||
String yinHuanId = "";
|
||||
|
|
@ -59,7 +71,7 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
String responsibleName="";
|
||||
String dataTime = "";
|
||||
|
||||
List<String> _zhengGaiImages = [];
|
||||
List<ImgData> _zhengGaiImages = [];
|
||||
List<String> _yinHuanTypeIds = [];
|
||||
List<String> _yinHuanTypeNames = [];
|
||||
|
||||
|
|
@ -70,10 +82,6 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
|
||||
|
||||
|
||||
|
||||
// unqualifiedInspectionItemID=SessionService.instance.unqualifiedInspectionItemID.toString();
|
||||
unqualifiedInspectionItemID=widget.item.departmentId;
|
||||
setState(() {
|
||||
|
|
@ -83,10 +91,12 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
dynamic pd=widget.result["pd"];
|
||||
|
||||
for(int i=0;i<hImgs.length;i++){
|
||||
if (hImgs[i]['FILEPATH'].toString().endsWith('.mp4')) {
|
||||
_yinHuanVido.add(ApiService.baseImgPath +hImgs[i]['FILEPATH']);
|
||||
final ImgData = hImgs[i];
|
||||
if (ImgData['FILEPATH'].toString().endsWith('.mp4')) {
|
||||
_yinHuanVido.add(ImgData(path: ApiService.baseImgPath +hImgs[i]['FILEPATH'], id: ImgData['IMGFILES_ID']));
|
||||
} else {
|
||||
_yinHuanImages.add(ApiService.baseImgPath +hImgs[i]["FILEPATH"]);
|
||||
_yinHuanImages.add(ImgData(path: ApiService.baseImgPath +hImgs[i]['FILEPATH'], id: ImgData['IMGFILES_ID']));
|
||||
|
||||
}
|
||||
}
|
||||
// _yinHuanVido视频
|
||||
|
|
@ -113,7 +123,7 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
dataTime=pd["RECTIFICATIONDEADLINE"];
|
||||
|
||||
_dangerDetailController.text=pd["RECTIFYDESCR"];
|
||||
_zhengGaiImages= rImgs.map((e) => ApiService.baseImgPath +e["FILEPATH"]).toList();
|
||||
_zhengGaiImages= rImgs.map((e) => ImgData(path: ApiService.baseImgPath +e["FILEPATH"], id: e['IMGFILES_ID'])).toList();
|
||||
// _zhengGaiImages整改图片"rImgs" -> [_GrowableList]
|
||||
|
||||
}else{
|
||||
|
|
@ -170,7 +180,34 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _getIDForPath(String path, List<ImgData> imgList) {
|
||||
for (ImgData data in imgList) {
|
||||
if (data.path == path) {
|
||||
return data.id;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
Future<bool> _removeFileWithType(String type, String id) async{
|
||||
if (id.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
final result = await ApiService.onImageRemoved(id);
|
||||
if (result['result'] == 'success') {
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
// return
|
||||
}
|
||||
ImgData _getImageDataForPath(String path, List<ImgData> list) {
|
||||
for (ImgData data in list) {
|
||||
if (data.path == path) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return ImgData(path: '', id: '');
|
||||
}
|
||||
Widget _buildSectionContainer({required Widget child}) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 10),
|
||||
|
|
@ -201,21 +238,29 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
title: "隐患照片",
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
initialMediaPaths:_yinHuanImages,
|
||||
initialMediaPaths:_yinHuanImages.map((item) => item.path).toList(),
|
||||
isShowAI: true,
|
||||
onMediaAdded: (value) {
|
||||
_yinHuanImages.add(value);
|
||||
_yinHuanImages.add(ImgData(path: value, id: ''));
|
||||
},
|
||||
onMediaRemoved: (value) {
|
||||
onMediaRemoved: (value) async {
|
||||
|
||||
if (value.startsWith('http')) {
|
||||
final result = await _removeFileWithType('img', _getIDForPath(value, _yinHuanImages));
|
||||
if (result) {
|
||||
setState(() {
|
||||
_yinHuanImages.remove(_getImageDataForPath(value, _yinHuanImages));
|
||||
});
|
||||
}
|
||||
}else{
|
||||
setState(() {
|
||||
_yinHuanImages.remove(_getImageDataForPath(value, _yinHuanImages));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
onChanged: (List<File> files) {
|
||||
// // 上传图片 files
|
||||
// _yinHuanImages.clear();
|
||||
// for(int i=0;i<files.length;i++){
|
||||
// _yinHuanImages.add(files[i].path);
|
||||
// }
|
||||
},
|
||||
onChanged: (List<File> files) {},
|
||||
onAiIdentify: () {
|
||||
// AI 识别逻辑
|
||||
if(_yinHuanImages.isEmpty){
|
||||
|
|
@ -226,7 +271,7 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
ToastUtil.showNormal(context, "识别暂时只能上传一张图片");
|
||||
return;
|
||||
}
|
||||
_identifyImg(_yinHuanImages[0]);
|
||||
_identifyImg(_yinHuanImages[0].path);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
@ -235,10 +280,26 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
title: "隐患视频",
|
||||
maxCount: 1,
|
||||
mediaType: MediaType.video,
|
||||
initialMediaPaths: _yinHuanVido,
|
||||
initialMediaPaths: _yinHuanVido.map((item) => item.path).toList(),
|
||||
onMediaAdded: (value) {
|
||||
_yinHuanVido.add(value);
|
||||
_yinHuanVido.add(ImgData(path: value, id: ''));
|
||||
},
|
||||
onMediaRemoved: (value) async {
|
||||
|
||||
if (value.startsWith('http')) {
|
||||
final result = await _removeFileWithType('video', _getIDForPath(value, _yinHuanVido));
|
||||
if (result) {
|
||||
setState(() {
|
||||
_yinHuanVido.remove(_getImageDataForPath(value, _yinHuanVido));
|
||||
});
|
||||
}
|
||||
}else{
|
||||
setState(() {
|
||||
_yinHuanVido.remove(_getImageDataForPath(value, _yinHuanVido));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onChanged: (List<File> files) {
|
||||
// 上传视频 files
|
||||
// _yinHuanVido=files[0].path;
|
||||
|
|
@ -366,17 +427,28 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
horizontalPadding: 0,
|
||||
mediaType: MediaType.image,
|
||||
isShowAI: false,
|
||||
initialMediaPaths: _zhengGaiImages,
|
||||
initialMediaPaths:_zhengGaiImages.map((item) => item.path).toList(),
|
||||
onMediaAdded: (value) {
|
||||
_zhengGaiImages.add(value);
|
||||
_zhengGaiImages.add(ImgData(path: value, id: ''));
|
||||
},
|
||||
onMediaRemoved: (value) async {
|
||||
|
||||
if (value.startsWith('http')) {
|
||||
final result = await _removeFileWithType('img', _getIDForPath(value, _zhengGaiImages));
|
||||
if (result) {
|
||||
setState(() {
|
||||
_zhengGaiImages.remove(_getImageDataForPath(value, _zhengGaiImages));
|
||||
});
|
||||
}
|
||||
}else{
|
||||
setState(() {
|
||||
_zhengGaiImages.remove(_getImageDataForPath(value, _zhengGaiImages));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
onChanged: (List<File> files) {
|
||||
// 上传图片 files
|
||||
// _zhengGaiImages.clear();
|
||||
// for(int i=0;i<files.length;i++){
|
||||
// _zhengGaiImages.add(files[i].path);
|
||||
// }
|
||||
|
||||
},
|
||||
onAiIdentify: () {
|
||||
|
||||
|
|
@ -651,27 +723,29 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
|
||||
String hiddenId = result['pd']['HIDDEN_ID'] ;
|
||||
// SessionService.instance.setUnqualifiedInspectionItemIDJson(hiddenId);
|
||||
|
||||
bool success = true;
|
||||
for (int i=0;i<_yinHuanImages.length;i++){
|
||||
_addImgFiles(_yinHuanImages[i],"3",hiddenId);
|
||||
success = await _addImgFiles(_yinHuanImages[i],"3",hiddenId);
|
||||
}
|
||||
|
||||
if(_yinHuanVido.isNotEmpty) {
|
||||
_addImgFiles(_yinHuanVido[0],"3",hiddenId);
|
||||
success = await _addImgFiles(_yinHuanVido[0],"3",hiddenId);
|
||||
}
|
||||
|
||||
if(_isDanger){
|
||||
for (int i=0;i<_zhengGaiImages.length;i++){
|
||||
_addImgFiles(_zhengGaiImages[i],"4",hiddenId);
|
||||
success = await _addImgFiles(_zhengGaiImages[i],"4",hiddenId);
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
setState(() {
|
||||
ToastUtil.showNormal(context, "提交成功");
|
||||
Navigator.pop(context);
|
||||
widget.onClose(hiddenId,_standardController.text.trim());
|
||||
|
||||
setState(() {
|
||||
ToastUtil.showNormal(context, "提交成功");
|
||||
Navigator.pop(context);
|
||||
widget.onClose(hiddenId,_standardController.text.trim());
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -679,24 +753,31 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<bool> _addImgFiles(ImgData imgData,String type,String id) async {
|
||||
if (imgData.id.isEmpty) {
|
||||
try {
|
||||
|
||||
final raw = await ApiService.addImgFiles( imgData.path, type, id);
|
||||
if (raw['result'] == 'success') {
|
||||
Map pd = raw['pd'];
|
||||
imgData = ImgData(path: pd['FILEPATH'], id: pd['IMGFILES_ID']);
|
||||
return true;
|
||||
}else{
|
||||
// _showMessage('反馈提交失败');
|
||||
ToastUtil.showError(context, raw['message'] ?? '图片上传失败,请重试');
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<String> _addImgFiles(String imagePath,String type,String id) async {
|
||||
try {
|
||||
} catch (e) {
|
||||
// 出错时可以 Toast 或者在页面上显示错误状态
|
||||
print('加载数据失败:$e');
|
||||
ToastUtil.showError(context, '图片上传失败,请重试');
|
||||
|
||||
final raw = await ApiService.addImgFiles( imagePath, type, id);
|
||||
if (raw['result'] == 'success') {
|
||||
return raw['imgPath'];
|
||||
}else{
|
||||
// _showMessage('反馈提交失败');
|
||||
return "";
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// 出错时可以 Toast 或者在页面上显示错误状态
|
||||
print('加载数据失败:$e');
|
||||
return "";
|
||||
}
|
||||
// 如果已经是服务器返回的图片,这里不走上传逻辑
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,17 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
|
|||
Future<void> _getMapData() async {
|
||||
setState(() => _loadingMap = true);
|
||||
try {
|
||||
final resData = await ApiService.getRecordMapInfo(widget.id);
|
||||
Map data = {
|
||||
'USER_ID': SessionService.instance.loginUserId,
|
||||
'CORPINFO_ID': SessionService.instance.corpinfoId,
|
||||
'CHECKRECORD_ID': widget.id,
|
||||
};
|
||||
Map resData = {};
|
||||
if (widget.type == 1) {
|
||||
resData = await ApiService.getDangerRecordMapInfo(data);
|
||||
} else {
|
||||
resData = await ApiService.getNormalRecordMapInfo(data);
|
||||
}
|
||||
final built = buildCoversFromRes(resData);
|
||||
double lat = centerLat;
|
||||
double lng = centerLng;
|
||||
|
|
@ -109,7 +119,7 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
|
|||
}
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> buildCoversFromRes(Map<String, dynamic> res) {
|
||||
List<Map<String, dynamic>> buildCoversFromRes(Map res) {
|
||||
final list = <Map<String, dynamic>>[];
|
||||
|
||||
final cinfo = res['cinfo'];
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:qhd_prevention/customWidget/photo_picker_row.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
|
||||
class PhotoItem {
|
||||
|
|
@ -79,33 +80,35 @@ class _DangerImageUpdataPageState extends State<DangerImageUpdataPage> {
|
|||
appBar: MyAppbar(title: "检查照片"),
|
||||
body: Column(
|
||||
children: [
|
||||
RepairedPhotoSection(
|
||||
title: "检查照片",
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
isShowAI: false,
|
||||
initialMediaPaths: _imgList.map((e)=> e.filePath).toList(),
|
||||
onMediaAdded: (value) {
|
||||
setState(() {
|
||||
_imgList.add(
|
||||
PhotoItem(
|
||||
id: "", // 新图片没有ID
|
||||
filePath: value,
|
||||
type: "2",
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
ItemListWidget.itemContainer(
|
||||
RepairedPhotoSection(
|
||||
title: "检查照片",
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
isShowAI: false,
|
||||
initialMediaPaths: _imgList.map((e)=> e.filePath).toList(),
|
||||
onMediaAdded: (value) {
|
||||
setState(() {
|
||||
_imgList.add(
|
||||
PhotoItem(
|
||||
id: "", // 新图片没有ID
|
||||
filePath: value,
|
||||
type: "2",
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
onMediaRemoved: (path) {
|
||||
int delete = 0;
|
||||
_onImageRemoved(_imgList[delete]);
|
||||
_imgList.removeAt(delete);
|
||||
},
|
||||
onAiIdentify: () {},
|
||||
onChanged: (List<File> value) {},
|
||||
),
|
||||
|
||||
onMediaRemoved: (path) {
|
||||
int delete = 0;
|
||||
_onImageRemoved(_imgList[delete]);
|
||||
_imgList.removeAt(delete);
|
||||
},
|
||||
onAiIdentify: () {},
|
||||
onChanged: (List<File> value) {},
|
||||
),
|
||||
|
||||
// 下一步按钮
|
||||
Container(
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@ import 'package:intl/intl.dart';
|
|||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker_two.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/date_picker_dialog.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker_hidden_type.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker_two.dart';
|
||||
import 'package:qhd_prevention/customWidget/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/single_image_viewer.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/my_appbar.dart';
|
||||
|
|
@ -19,6 +22,17 @@ import 'package:qhd_prevention/tools/tools.dart';
|
|||
import '../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../http/ApiService.dart';
|
||||
|
||||
class ImgData {
|
||||
final String path;
|
||||
final String id;
|
||||
|
||||
const ImgData({
|
||||
required this.path,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
class HazardRegistrationPage extends StatefulWidget {
|
||||
const HazardRegistrationPage(this.item, this.result, this.type, {super.key,required this.onClose});
|
||||
|
||||
|
|
@ -45,8 +59,8 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
List<Map<String, dynamic>> _personCache = [];
|
||||
|
||||
|
||||
List<String> _yinHuanImages = [];
|
||||
List<String> _yinHuanVido = [];
|
||||
List<ImgData> _yinHuanImages = [];
|
||||
List<ImgData> _yinHuanVido = [];
|
||||
// String _yinHuanVido="";
|
||||
dynamic _hazardLeve;
|
||||
String yinHuanId = "";
|
||||
|
|
@ -58,7 +72,7 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
String responsibleName="";
|
||||
String dataTime = "";
|
||||
|
||||
List<String> _zhengGaiImages = [];
|
||||
List<ImgData> _zhengGaiImages = [];
|
||||
List<String> _yinHuanTypeIds = [];
|
||||
List<String> _yinHuanTypeNames = [];
|
||||
|
||||
|
|
@ -69,63 +83,85 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
|
||||
|
||||
|
||||
setState(() {
|
||||
neiRong=widget.item['CHECK_CONTENT'] ?? '';
|
||||
_standardController.text=widget.item["CHECK_UNQUALIFIED"]??"";
|
||||
});
|
||||
neiRong=widget.item['CHECK_CONTENT'] ?? '';
|
||||
_standardController.text=widget.item["CHECK_UNQUALIFIED"]??"";
|
||||
unqualifiedInspectionItemID=SessionService.instance.unqualifiedInspectionItemID.toString();
|
||||
setState(() {
|
||||
if(unqualifiedInspectionItemID.isNotEmpty){
|
||||
List<dynamic> hImgs=widget.result["hImgs"];
|
||||
List<dynamic> rImgs=widget.result["rImgs"];
|
||||
dynamic pd=widget.result["pd"];
|
||||
if(unqualifiedInspectionItemID.isNotEmpty && widget.result.isNotEmpty){
|
||||
List<dynamic> hImgs=widget.result["hImgs"] ?? [];
|
||||
List<dynamic> rImgs=widget.result["rImgs"]?? [];
|
||||
dynamic pd=widget.result["pd"];
|
||||
|
||||
for(int i=0;i<hImgs.length;i++){
|
||||
final item = hImgs[i];
|
||||
if (item['FILEPATH'].toString().endsWith('.mp4')) {
|
||||
_yinHuanVido.add(ImgData(path: ApiService.baseImgPath +item['FILEPATH'], id: item['IMGFILES_ID']));
|
||||
} else {
|
||||
_yinHuanImages.add(ImgData(path: ApiService.baseImgPath +item['FILEPATH'], id: item['IMGFILES_ID']));
|
||||
|
||||
for(int i=0;i<hImgs.length;i++){
|
||||
if (hImgs[i]['FILEPATH'].toString().endsWith('.mp4')) {
|
||||
_yinHuanVido.add(ApiService.baseImgPath +hImgs[i]['FILEPATH']);
|
||||
} else {
|
||||
_yinHuanImages.add(ApiService.baseImgPath +hImgs[i]["FILEPATH"]);
|
||||
}
|
||||
}
|
||||
// _yinHuanVido视频
|
||||
// _yinHuanImages= hImgs.map((e) => ApiService.baseImgPath +e["FILEPATH"]).toList();
|
||||
_standardController.text=pd["HIDDENDESCR"];
|
||||
_partController.text=pd["HIDDENPART"];
|
||||
_repairLevelName =pd["HIDDENLEVELNAME"];
|
||||
hazardLeve =pd["HIDDENLEVEL"];
|
||||
_yinHuanTypeIds= pd["HIDDENTYPE"].toString().split(",");
|
||||
_yinHuanTypeNames= pd["HIDDENTYPE_NAME"].toString().split("/");
|
||||
yinHuanName=pd["HIDDENTYPE_NAME"].toString();
|
||||
|
||||
if(pd["RECTIFICATIONTYPE"]=="1"){
|
||||
_isDanger=true;
|
||||
}else{
|
||||
_isDanger=false;
|
||||
}
|
||||
|
||||
buMenId=pd["RECTIFICATIONDEPT"];
|
||||
buMenPDId=pd["HIDDENFINDDEPT"];
|
||||
buMenName=pd["RECTIFICATIONDEPTNAME"];
|
||||
responsibleId=pd["RECTIFICATIONOR"];
|
||||
responsibleName=pd["RECTIFICATIONORNAME"];
|
||||
dataTime=pd["RECTIFICATIONDEADLINE"];
|
||||
|
||||
_dangerDetailController.text=pd["RECTIFYDESCR"];
|
||||
_zhengGaiImages= rImgs.map((e) => ApiService.baseImgPath +e["FILEPATH"]).toList();
|
||||
// _zhengGaiImages整改图片"rImgs" -> [_GrowableList]
|
||||
|
||||
}
|
||||
});
|
||||
// _yinHuanVido视频
|
||||
// _yinHuanImages= hImgs.map((e) => ApiService.baseImgPath +e["FILEPATH"]).toList();
|
||||
_standardController.text=pd["HIDDENDESCR"];
|
||||
_partController.text=pd["HIDDENPART"];
|
||||
_repairLevelName =pd["HIDDENLEVELNAME"];
|
||||
hazardLeve =pd["HIDDENLEVEL"];
|
||||
_yinHuanTypeIds= pd["HIDDENTYPE"].toString().split(",");
|
||||
_yinHuanTypeNames= pd["HIDDENTYPE_NAME"].toString().split("/");
|
||||
yinHuanName=pd["HIDDENTYPE_NAME"].toString();
|
||||
|
||||
if(pd["RECTIFICATIONTYPE"]=="1"){
|
||||
_isDanger=true;
|
||||
}else{
|
||||
_isDanger=false;
|
||||
}
|
||||
|
||||
buMenId=pd["RECTIFICATIONDEPT"];
|
||||
buMenPDId=pd["HIDDENFINDDEPT"];
|
||||
buMenName=pd["RECTIFICATIONDEPTNAME"];
|
||||
responsibleId=pd["RECTIFICATIONOR"];
|
||||
responsibleName=pd["RECTIFICATIONORNAME"];
|
||||
dataTime=pd["RECTIFICATIONDEADLINE"];
|
||||
|
||||
_dangerDetailController.text=pd["RECTIFYDESCR"];
|
||||
_zhengGaiImages= rImgs.map((e) => ImgData(path: ApiService.baseImgPath +e["FILEPATH"], id: '')).toList();
|
||||
// _zhengGaiImages整改图片"rImgs" -> [_GrowableList]
|
||||
|
||||
}
|
||||
|
||||
|
||||
_getHazardLevel();
|
||||
|
||||
|
||||
}
|
||||
|
||||
String _getIDForPath(String path, List<ImgData> imgList) {
|
||||
for (ImgData data in imgList) {
|
||||
if (data.path == path) {
|
||||
return data.id;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
Future<bool> _removeFileWithType(String type, String id) async{
|
||||
if (id.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
final result = await ApiService.onImageRemoved(id);
|
||||
if (result['result'] == 'success') {
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
// return
|
||||
}
|
||||
ImgData _getImageDataForPath(String path, List<ImgData> list) {
|
||||
for (ImgData data in list) {
|
||||
if (data.path == path) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return ImgData(path: '', id: '');
|
||||
}
|
||||
Future<void> _getHazardLevel() async {
|
||||
try {
|
||||
final result = await ApiService.getHazardLevel();
|
||||
|
|
@ -200,20 +236,28 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
title: "隐患照片",
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
initialMediaPaths:_yinHuanImages,
|
||||
initialMediaPaths:_yinHuanImages.map((item) => item.path).toList(),
|
||||
isShowAI: true,
|
||||
onMediaAdded: (value) {
|
||||
_yinHuanImages.add(value);
|
||||
_yinHuanImages.add(ImgData(path: value, id: ''));
|
||||
},
|
||||
onMediaRemoved: (value) {
|
||||
|
||||
onMediaRemoved: (value) async {
|
||||
if (value.startsWith('http')) {
|
||||
final result = await _removeFileWithType('img', _getIDForPath(value, _yinHuanImages));
|
||||
if (result) {
|
||||
setState(() {
|
||||
_yinHuanImages.remove(_getImageDataForPath(value, _yinHuanImages));
|
||||
});
|
||||
}
|
||||
}else{
|
||||
setState(() {
|
||||
_yinHuanImages.remove(_getImageDataForPath(value, _yinHuanImages));
|
||||
});
|
||||
}
|
||||
},
|
||||
onChanged: (List<File> files) {
|
||||
// // 上传图片 files
|
||||
// _yinHuanImages.clear();
|
||||
// for(int i=0;i<files.length;i++){
|
||||
// _yinHuanImages.add(files[i].path);
|
||||
// }
|
||||
onChanged: (List<File> files) {},
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onAiIdentify: () {
|
||||
// AI 识别逻辑
|
||||
|
|
@ -225,7 +269,7 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
ToastUtil.showNormal(context, "识别暂时只能上传一张图片");
|
||||
return;
|
||||
}
|
||||
_identifyImg(_yinHuanImages[0]);
|
||||
_identifyImg(_yinHuanImages[0].path);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
@ -234,17 +278,23 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
title: "隐患视频",
|
||||
maxCount: 1,
|
||||
mediaType: MediaType.video,
|
||||
initialMediaPaths: _yinHuanVido,
|
||||
initialMediaPaths: _yinHuanVido.map((item) => item.path).toList(),
|
||||
onMediaAdded: (value) {
|
||||
_yinHuanVido.add(value);
|
||||
_yinHuanVido.add(ImgData(path: value, id: ''));
|
||||
},
|
||||
onMediaRemoved: (value){
|
||||
_yinHuanVido.remove(value);
|
||||
},
|
||||
onMediaTapped: (path) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: Colors.black54,
|
||||
builder: (_) => VideoPlayerPopup(videoUrl:path),
|
||||
);
|
||||
},
|
||||
onChanged: (List<File> files) {
|
||||
// 上传视频 files
|
||||
// _yinHuanVido=files[0].path;
|
||||
},
|
||||
onAiIdentify: () {
|
||||
// AI 视频识别逻辑
|
||||
},
|
||||
onAiIdentify: () {},
|
||||
),
|
||||
),
|
||||
_buildSectionContainer(
|
||||
|
|
@ -365,18 +415,27 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
horizontalPadding: 0,
|
||||
mediaType: MediaType.image,
|
||||
isShowAI: false,
|
||||
initialMediaPaths: _zhengGaiImages,
|
||||
initialMediaPaths:_zhengGaiImages.map((item) => item.path).toList(),
|
||||
onMediaAdded: (value) {
|
||||
_zhengGaiImages.add(value);
|
||||
_zhengGaiImages.add(ImgData(path: value, id: ''));
|
||||
},
|
||||
onChanged: (List<File> files) {
|
||||
// 上传图片 files
|
||||
// _zhengGaiImages.clear();
|
||||
// for(int i=0;i<files.length;i++){
|
||||
// _zhengGaiImages.add(files[i].path);
|
||||
// }
|
||||
onMediaRemoved: (value) async {
|
||||
if (value.startsWith('http')) {
|
||||
final result = await _removeFileWithType('img', _getIDForPath(value, _zhengGaiImages));
|
||||
if (result) {
|
||||
setState(() {
|
||||
_zhengGaiImages.remove(_getImageDataForPath(value, _zhengGaiImages));
|
||||
});
|
||||
}
|
||||
}else{
|
||||
setState(() {
|
||||
_zhengGaiImages.remove(_getImageDataForPath(value, _zhengGaiImages));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
onChanged: (List<File> files) {},
|
||||
onAiIdentify: () {
|
||||
|
||||
},
|
||||
|
|
@ -612,7 +671,7 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
Position position = await _determinePosition();
|
||||
String longitude=position.longitude.toString();
|
||||
String latitude=position.latitude.toString();
|
||||
|
||||
|
||||
try {
|
||||
|
||||
final Map<String, dynamic> result;
|
||||
|
|
@ -640,25 +699,27 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
String hiddenId = result['pd']['HIDDEN_ID'] ;
|
||||
SessionService.instance.setUnqualifiedInspectionItemIDJson(hiddenId);
|
||||
|
||||
for (int i=0;i<_yinHuanImages.length;i++){
|
||||
_addImgFiles(_yinHuanImages[i],"3",hiddenId);
|
||||
}
|
||||
bool success = true;
|
||||
for (int i=0;i<_yinHuanImages.length;i++){
|
||||
success = await _addImgFiles(_yinHuanImages[i],"3",hiddenId);
|
||||
}
|
||||
|
||||
if(_yinHuanVido.isNotEmpty) {
|
||||
_addImgFiles(_yinHuanVido[0],"3",hiddenId);
|
||||
}
|
||||
if(_yinHuanVido.isNotEmpty) {
|
||||
success = await _addImgFiles(_yinHuanVido[0],"3",hiddenId);
|
||||
}
|
||||
|
||||
if(_isDanger){
|
||||
for (int i=0;i<_zhengGaiImages.length;i++){
|
||||
_addImgFiles(_zhengGaiImages[i],"4",hiddenId);
|
||||
}
|
||||
if(_isDanger){
|
||||
for (int i=0;i<_zhengGaiImages.length;i++){
|
||||
success = await _addImgFiles(_zhengGaiImages[i],"4",hiddenId);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
ToastUtil.showNormal(context, "提交成功");
|
||||
Navigator.pop(context);
|
||||
widget.onClose(hiddenId,_standardController.text.trim());
|
||||
});
|
||||
}
|
||||
if (success) {
|
||||
setState(() {
|
||||
ToastUtil.showNormal(context, "提交成功");
|
||||
Navigator.pop(context);
|
||||
widget.onClose(hiddenId, _standardController.text.trim());
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -668,22 +729,31 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
|
||||
|
||||
|
||||
Future<String> _addImgFiles(String imagePath,String type,String id) async {
|
||||
try {
|
||||
Future<bool> _addImgFiles(ImgData imgData,String type,String id) async {
|
||||
if (imgData.id.isEmpty) {
|
||||
try {
|
||||
|
||||
final raw = await ApiService.addImgFiles( imagePath, type, id);
|
||||
if (raw['result'] == 'success') {
|
||||
return raw['imgPath'];
|
||||
}else{
|
||||
// _showMessage('反馈提交失败');
|
||||
return "";
|
||||
final raw = await ApiService.addImgFiles( imgData.path, type, id);
|
||||
if (raw['result'] == 'success') {
|
||||
Map pd = raw['pd'];
|
||||
imgData = ImgData(path: pd['FILEPATH'], id: pd['IMGFILES_ID']);
|
||||
return true;
|
||||
}else{
|
||||
// _showMessage('反馈提交失败');
|
||||
ToastUtil.showError(context, raw['message'] ?? '图片上传失败,请重试');
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// 出错时可以 Toast 或者在页面上显示错误状态
|
||||
print('加载数据失败:$e');
|
||||
ToastUtil.showError(context, '图片上传失败,请重试');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// 出错时可以 Toast 或者在页面上显示错误状态
|
||||
print('加载首页数据失败:$e');
|
||||
return "";
|
||||
}
|
||||
// 如果已经是服务器返回的图片,这里不走上传逻辑
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,16 +22,6 @@ class _InspectRecordsListPageState extends State<InspectRecordsListPage>
|
|||
int _selectedTab = 0;
|
||||
int pageNum = 1;
|
||||
|
||||
// 模拟数据
|
||||
final List<Map<String, dynamic>> _notifications = List.generate(10, (i) {
|
||||
bool read = i % 3 == 0;
|
||||
return {
|
||||
'title': '测试数据标题标题 ${i + 1}',
|
||||
'time': '2025-06-${10 + i} 12:3${i}',
|
||||
'read': read,
|
||||
};
|
||||
});
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -145,7 +135,7 @@ class _InspectRecordsListPageState extends State<InspectRecordsListPage>
|
|||
),
|
||||
labelColor: Colors.blue,
|
||||
unselectedLabelColor: Colors.grey,
|
||||
tabs: const [Tab(text: '政府公告'), Tab(text: '企业公告')],
|
||||
tabs: const [Tab(text: '平台公告'), Tab(text: '企业公告')],
|
||||
),
|
||||
|
||||
// Search bar
|
||||
|
|
|
|||
|
|
@ -218,9 +218,14 @@ class _HiddenDangerAcceptancePageState extends State<HiddenDangerAcceptancePage>
|
|||
Divider(height: 1),
|
||||
_buildInfoItem('发现时间', pd['CREATTIME'] ?? ''),
|
||||
Divider(height: 1),
|
||||
_buildInfoItem('隐患级别', pd['HIDDENLEVELNAME'] ?? ''),
|
||||
Divider(height: 1),
|
||||
|
||||
if (pd['HIDDEN_CATEGORY']?.isNotEmpty == true)
|
||||
_buildInfoItem('隐患类别', pd['HIDDEN_CATEGORY_NAME'] ?? ''),
|
||||
Column(children: [
|
||||
_buildInfoItem('隐患类别', pd['HIDDEN_CATEGORY_NAME'] ?? ''),
|
||||
Divider(height: 1),
|
||||
],),
|
||||
|
||||
_buildInfoItem('隐患类型', pd['HIDDENTYPE_NAME'] ?? ''),
|
||||
Divider(height: 1),
|
||||
|
|
@ -467,29 +472,13 @@ class _HiddenDangerAcceptancePageState extends State<HiddenDangerAcceptancePage>
|
|||
GestureDetector(
|
||||
onTap: () async {
|
||||
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(context);
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(context, allowPast: false,);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
dataTime = DateFormat('yyyy-MM-dd HH:mm').format(picked);
|
||||
});
|
||||
}
|
||||
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder:
|
||||
// (_) => HDatePickerDialog(
|
||||
// initialDate: DateTime.now(),
|
||||
// onCancel: () => Navigator.of(context).pop(),
|
||||
// onConfirm: (selected) {
|
||||
// Navigator.of(context).pop();
|
||||
// setState(() {
|
||||
// // _selectData = selected;
|
||||
// dataTime= DateFormat('yyyy-MM-dd').format(selected);
|
||||
//
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 0),
|
||||
|
|
@ -563,7 +552,7 @@ class _HiddenDangerAcceptancePageState extends State<HiddenDangerAcceptancePage>
|
|||
|
||||
LoadingDialogHelper.show();
|
||||
|
||||
final data = await ApiService.addHazardAcceptance( type, miaoshu, dataTime,widget.item['HIDDEN_ID']);
|
||||
final data = await ApiService.addHazardAcceptance( type, miaoshu, dataTime,widget.item['HIDDEN_ID'] ?? '');
|
||||
if (data['result'] == 'success') {
|
||||
|
||||
String hiddenCheckId="";
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ class _EquipmentInspectionListPageState
|
|||
|
||||
return ListView.builder(
|
||||
controller: _scrollController,
|
||||
itemCount: _periodList.length,
|
||||
itemCount: _equipList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final period = _periodList[index];
|
||||
final items = groupedItems[period] ?? [];
|
||||
|
|
@ -298,7 +298,7 @@ class _EquipmentInspectionListPageState
|
|||
),
|
||||
labelColor: Colors.blue,
|
||||
unselectedLabelColor: Colors.grey,
|
||||
tabs: const [Tab(text: '待反馈'), Tab(text: '已反馈')],
|
||||
tabs: const [Tab(text: '开始巡检'), Tab(text: '巡检记录')],
|
||||
),
|
||||
),
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,8 @@ import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
|||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
||||
|
||||
|
||||
class ImmediatelyInspection extends StatefulWidget {
|
||||
const ImmediatelyInspection(this. id, {super.key});
|
||||
const ImmediatelyInspection(this.id, {super.key});
|
||||
|
||||
final String id;
|
||||
|
||||
|
|
@ -23,13 +21,12 @@ class ImmediatelyInspection extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ImmediatelyInspectionState extends State<ImmediatelyInspection> {
|
||||
|
||||
|
||||
final TextEditingController _xunController = TextEditingController();
|
||||
final TextEditingController _neiRongController = TextEditingController();
|
||||
|
||||
// 是否有故障
|
||||
bool _isFault = false;
|
||||
String dataTime="";
|
||||
String dataTime = "";
|
||||
|
||||
List<String> _guZhangImages = [];
|
||||
List<String> _xiuGaiImages = [];
|
||||
|
|
@ -46,36 +43,43 @@ class _ImmediatelyInspectionState extends State<ImmediatelyInspection> {
|
|||
children: [
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: Column(children: [
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '巡检人:',
|
||||
isEditable: true,
|
||||
controller: _xunController,
|
||||
hintText: '请输入巡检人',
|
||||
text: '',
|
||||
),
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15,vertical: 10),
|
||||
child: GestureDetector(
|
||||
child: ItemListWidget.selectableLineTitleTextRightButton(label: '巡检日期', text: dataTime.isEmpty?
|
||||
'请选择':dataTime??'', isEditable: true, isRequired: true,horizontalnum: 0),
|
||||
child: Column(
|
||||
children: [
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '巡检人:',
|
||||
isEditable: true,
|
||||
isRequired: false,
|
||||
controller: _xunController,
|
||||
hintText: '请输入巡检人',
|
||||
text: '',
|
||||
),
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
child: GestureDetector(
|
||||
child: ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '巡检日期',
|
||||
text: dataTime.isEmpty ? '请选择' : dataTime ?? '',
|
||||
isEditable: true,
|
||||
isRequired: false,
|
||||
horizontalnum: 0,
|
||||
),
|
||||
onTap: () async {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => HDatePickerDialog(
|
||||
initialDate: DateTime.now(),
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
onConfirm: (selected) {
|
||||
Navigator.of(context).pop();
|
||||
setState(() {
|
||||
|
||||
dataTime= DateFormat('yyyy-MM-dd').format(selected);
|
||||
|
||||
});
|
||||
},
|
||||
),
|
||||
initialDate: DateTime.now(),
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
onConfirm: (selected) {
|
||||
Navigator.of(context).pop();
|
||||
setState(() {
|
||||
dataTime = DateFormat(
|
||||
'yyyy-MM-dd',
|
||||
).format(selected);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -85,29 +89,28 @@ class _ImmediatelyInspectionState extends State<ImmediatelyInspection> {
|
|||
// isRight: true,
|
||||
// ),
|
||||
),
|
||||
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
SizedBox(height: 15,),
|
||||
SizedBox(height: 15),
|
||||
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: ItemListWidget.multiLineTitleTextField(
|
||||
child: ItemListWidget.multiLineTitleTextField(
|
||||
label: '巡检内容:',
|
||||
isEditable: true,
|
||||
isRequired: false,
|
||||
controller: _neiRongController,
|
||||
hintText: "(必填)",
|
||||
text:'',
|
||||
text: '',
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 15,),
|
||||
SizedBox(height: 15),
|
||||
ListItemFactory.createYesNoSection(
|
||||
title: '是否有故障',
|
||||
horizontalPadding: 0,
|
||||
horizontalPadding: 2,
|
||||
groupValue: _isFault,
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
|
|
@ -118,48 +121,40 @@ class _ImmediatelyInspectionState extends State<ImmediatelyInspection> {
|
|||
|
||||
// if(_isFault)
|
||||
// SizedBox(height: 15,),
|
||||
if(_isFault)
|
||||
RepairedPhotoSection(
|
||||
horizontalPadding: 15,
|
||||
title: "故障照片",
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
onMediaAdded: (value) {
|
||||
_guZhangImages.add(value);
|
||||
},
|
||||
onMediaRemoved: (value) {
|
||||
_guZhangImages.remove(value);
|
||||
},
|
||||
onChanged: (files) {
|
||||
},
|
||||
onAiIdentify: () {
|
||||
if (_isFault)
|
||||
RepairedPhotoSection(
|
||||
horizontalPadding: 15,
|
||||
title: "故障照片",
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
onMediaAdded: (value) {
|
||||
_guZhangImages.add(value);
|
||||
},
|
||||
onMediaRemoved: (value) {
|
||||
_guZhangImages.remove(value);
|
||||
},
|
||||
onChanged: (files) {},
|
||||
onAiIdentify: () {},
|
||||
),
|
||||
|
||||
},
|
||||
),
|
||||
SizedBox(height: _isFault ? 15 : 0),
|
||||
if (_isFault)
|
||||
RepairedPhotoSection(
|
||||
horizontalPadding: 15,
|
||||
title: "故障处理后照片",
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
onMediaAdded: (value) {
|
||||
_xiuGaiImages.add(value);
|
||||
},
|
||||
onMediaRemoved: (value) {
|
||||
_xiuGaiImages.remove(value);
|
||||
},
|
||||
onChanged: (files) {},
|
||||
onAiIdentify: () {},
|
||||
),
|
||||
|
||||
|
||||
SizedBox(height:_isFault? 15:0,),
|
||||
if(_isFault)
|
||||
RepairedPhotoSection(
|
||||
horizontalPadding: 15,
|
||||
title: "故障处理后照片",
|
||||
maxCount: 4,
|
||||
mediaType: MediaType.image,
|
||||
onMediaAdded: (value) {
|
||||
_xiuGaiImages.add(value);
|
||||
},
|
||||
onMediaRemoved: (value) {
|
||||
_xiuGaiImages.remove(value);
|
||||
},
|
||||
onChanged: (files) {
|
||||
},
|
||||
onAiIdentify: () {
|
||||
|
||||
},
|
||||
),
|
||||
|
||||
|
||||
SizedBox(height:_isFault? 15:0,),
|
||||
SizedBox(height: _isFault ? 15 : 0),
|
||||
RepairedPhotoSection(
|
||||
horizontalPadding: 15,
|
||||
title: "巡检照片",
|
||||
|
|
@ -171,14 +166,11 @@ class _ImmediatelyInspectionState extends State<ImmediatelyInspection> {
|
|||
onMediaRemoved: (value) {
|
||||
_xunJianImages.remove(value);
|
||||
},
|
||||
onChanged: (files) {
|
||||
},
|
||||
onAiIdentify: () {
|
||||
|
||||
},
|
||||
onChanged: (files) {},
|
||||
onAiIdentify: () {},
|
||||
),
|
||||
|
||||
SizedBox(height:25,),
|
||||
SizedBox(height: 25),
|
||||
CustomButton(
|
||||
text: "提交",
|
||||
backgroundColor: Colors.blue,
|
||||
|
|
@ -187,7 +179,7 @@ class _ImmediatelyInspectionState extends State<ImmediatelyInspection> {
|
|||
_startInspection();
|
||||
},
|
||||
),
|
||||
SizedBox(height:25,),
|
||||
SizedBox(height: 25),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
@ -195,79 +187,74 @@ class _ImmediatelyInspectionState extends State<ImmediatelyInspection> {
|
|||
|
||||
Future<void> _startInspection() async {
|
||||
try {
|
||||
|
||||
String person=_xunController.text.trim();
|
||||
if(person.isEmpty){
|
||||
String person = _xunController.text.trim();
|
||||
if (person.isEmpty) {
|
||||
ToastUtil.showNormal(context, "请填巡检人");
|
||||
return;
|
||||
}
|
||||
|
||||
if(dataTime.isEmpty){
|
||||
if (dataTime.isEmpty) {
|
||||
ToastUtil.showNormal(context, "请选择巡检时间");
|
||||
return;
|
||||
}
|
||||
|
||||
String content=_neiRongController.text.trim();
|
||||
if(content.isEmpty){
|
||||
String content = _neiRongController.text.trim();
|
||||
if (content.isEmpty) {
|
||||
ToastUtil.showNormal(context, "请选巡检内容");
|
||||
return;
|
||||
}
|
||||
|
||||
if(_isFault&&_guZhangImages.isEmpty){
|
||||
if (_isFault && _guZhangImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, "请上传故障照片");
|
||||
return;
|
||||
}
|
||||
|
||||
if(_isFault&&_xiuGaiImages.isEmpty){
|
||||
if (_isFault && _xiuGaiImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, "请上传故障处理后照片");
|
||||
return;
|
||||
}
|
||||
|
||||
if(_xunJianImages.isEmpty){
|
||||
if (_xunJianImages.isEmpty) {
|
||||
ToastUtil.showNormal(context, "请上传巡检图片");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LoadingDialogHelper.show(message: "提交中...");
|
||||
|
||||
for(int i=0;i<_guZhangImages.length;i++){
|
||||
for (int i = 0; i < _guZhangImages.length; i++) {
|
||||
allImagePaths.add(_guZhangImages[i]);
|
||||
allImageTypes.add("24");
|
||||
}
|
||||
|
||||
for(int m=0;m<_xiuGaiImages.length;m++){
|
||||
for (int m = 0; m < _xiuGaiImages.length; m++) {
|
||||
allImagePaths.add(_xiuGaiImages[m]);
|
||||
allImageTypes.add("25");
|
||||
}
|
||||
|
||||
for(int n=0;n<_xunJianImages.length;n++){
|
||||
for (int n = 0; n < _xunJianImages.length; n++) {
|
||||
allImagePaths.add(_xunJianImages[n]);
|
||||
allImageTypes.add("26");
|
||||
}
|
||||
|
||||
|
||||
|
||||
final formData = {
|
||||
'SPECIALEQUIPMENT_ID': widget.id,
|
||||
'CREATOR': SessionService.instance.username,
|
||||
'ISFAULT': _isFault?"是":"否",
|
||||
'ISFAULT': _isFault ? "是" : "否",
|
||||
'INSPECTOR': person,
|
||||
'INSPECTIONCONTENT': content,
|
||||
'INSPECTIONTIME': dataTime,
|
||||
|
||||
'TYPES':allImageTypes.join(','),
|
||||
'TYPES': allImageTypes.join(','),
|
||||
"CORPINFO_ID": SessionService.instance.corpinfoId,
|
||||
'USER_ID': SessionService.instance.loginUserId,
|
||||
};
|
||||
|
||||
final result = await ApiService.startInspection(formData,allImagePaths);
|
||||
final result = await ApiService.startInspection(formData, allImagePaths);
|
||||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
setState(() {
|
||||
ToastUtil.showNormal(context, "提交成功");
|
||||
Navigator.pop(context);
|
||||
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -275,7 +262,4 @@ class _ImmediatelyInspectionState extends State<ImmediatelyInspection> {
|
|||
print('Error fetching data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -526,8 +526,7 @@ class _NfcCheckDangerDetailState extends State<NfcCheckDangerDetail> {
|
|||
if (id.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
Map data = {'IMGFILES_ID': id};
|
||||
final result = await ApiService.deleteNFCImage(data);
|
||||
final result = await ApiService.onImageRemoved(id);
|
||||
if (result['result'] == 'success') {
|
||||
return true;
|
||||
}else{
|
||||
|
|
|
|||
|
|
@ -1,15 +1,9 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/SafeCheck/CheckPersonSure/check_person_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/SafeCheck/DangeCheck/safeCheck_assignment_detail_page.dart';
|
||||
import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
|
||||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
|
||||
class SafecheckAssignmentList extends StatefulWidget {
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class _SafecheckDangerDetailState extends State<SafecheckDangerDetail> {
|
|||
inspectedForm = {
|
||||
'INSPECTION_EXPLAIN_ID': '', // 安全检查人员主键ID
|
||||
'INSPECTION_STATUS': '3',
|
||||
'INSPECTION_ID': widget.INSPECTION_ID, // 安全检查ID
|
||||
'INSPECTION_ID': widget.INSPECTION_ID ?? '', // 安全检查ID
|
||||
'INSPECTED_EXPLAIN': '', // 申辩内容
|
||||
'INSPECTED_EXPLAIN_FILENAME': '', // 申辩附件名称
|
||||
'INSPECTED_SITEUSER_SIGN_IMG': '', // 被检查单位现场负责人签字
|
||||
|
|
@ -261,11 +261,11 @@ class _SafecheckDangerDetailState extends State<SafecheckDangerDetail> {
|
|||
),
|
||||
ItemListWidget.OneRowImageTitle(
|
||||
label: '被检查单位现场负责人签字',
|
||||
text: form['INSPECTED_SITEUSER_SIGN_TIME'],
|
||||
text: form['INSPECTED_SITEUSER_SIGN_TIME'] ?? '',
|
||||
onTapCallBack: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
imgPath: form['INSPECTED_SITEUSER_SIGN_IMG'],
|
||||
imgPath: form['INSPECTED_SITEUSER_SIGN_IMG'] ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -732,7 +732,7 @@ class _SafecheckStartDetailState extends State<SafecheckStartDetail> {
|
|||
ItemListWidget.multiLineTitleTextField(
|
||||
label: '检查人意见:',
|
||||
isEditable: false,
|
||||
text: item['opinion'],
|
||||
text: item['opinion'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.OneRowImageTitle(
|
||||
|
|
@ -750,7 +750,7 @@ class _SafecheckStartDetailState extends State<SafecheckStartDetail> {
|
|||
ItemListWidget.multiLineTitleTextField(
|
||||
label: '被检查单位现场负责人意见:',
|
||||
isEditable: false,
|
||||
text: item['INSPECTED_EXPLAIN'],
|
||||
text: item['INSPECTED_EXPLAIN'] ?? '',
|
||||
),
|
||||
if (FormUtils.hasValue(form, 'INSPECTED_EXPLAIN'))
|
||||
ItemListWidget.multiLineTitleTextField(
|
||||
|
|
@ -761,11 +761,11 @@ class _SafecheckStartDetailState extends State<SafecheckStartDetail> {
|
|||
if (form['INSPECTION_STATUS'] == '3')
|
||||
ItemListWidget.OneRowImageTitle(
|
||||
label: '检查人签字',
|
||||
text: form['INSPECTED_SITEUSER_SIGN_TIME'],
|
||||
text: form['INSPECTED_SITEUSER_SIGN_TIME'] ?? '',
|
||||
onTapCallBack: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
imgPath: item['INSPECTED_SITEUSER_SIGN_IMG'],
|
||||
imgPath: item['INSPECTED_SITEUSER_SIGN_IMG'] ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -1028,7 +1028,7 @@ class _SafecheckStartDetailState extends State<SafecheckStartDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
form['INSPECTION_TIME_START'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -1048,7 +1048,7 @@ class _SafecheckStartDetailState extends State<SafecheckStartDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
form['INSPECTION_TIME_END'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
|
|||
|
|
@ -279,9 +279,12 @@ class _SafecheckStartListPageState extends State<SafecheckStartListPage> {
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"检查时间: ${item['INSPECTION_TIME_START'] ?? ''}至${item['INSPECTION_TIME_END'] ?? ''}",
|
||||
Flexible(
|
||||
child: Text(
|
||||
"检查时间: ${item['INSPECTION_TIME_START'] ?? ''}至${item['INSPECTION_TIME_END'] ?? ''}",maxLines: 2,overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ class _HomeDangerPageState extends State<HomeDangerPage>
|
|||
Future<void> _goScan() async {
|
||||
final result = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => ScanPage()),
|
||||
MaterialPageRoute(builder: (_) => ScanPage(totalList: [])),
|
||||
);
|
||||
|
||||
dynamic item;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ 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/pages/home/scan_page.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.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';
|
||||
|
|
@ -33,13 +35,18 @@ class HomePage extends StatefulWidget {
|
|||
const HomePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_HomePageState createState() => _HomePageState();
|
||||
HomePageState createState() => HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
class HomePageState extends State<HomePage> {
|
||||
int _eight_work_count = 0;
|
||||
int _safetyEnvironmentalInspection = 0;
|
||||
|
||||
void startScan() {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => ScanPage(totalList: totalList)),
|
||||
);
|
||||
}
|
||||
// 缓存 key
|
||||
static const String _hiddenCacheKey = 'hidden_roll_cache';
|
||||
|
||||
|
|
@ -59,7 +66,7 @@ class _HomePageState extends State<HomePage> {
|
|||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
List totalList = [];
|
||||
/// 按钮信息
|
||||
List<Map<String, dynamic>> buttonInfos = [
|
||||
{
|
||||
|
|
@ -74,7 +81,7 @@ class _HomePageState extends State<HomePage> {
|
|||
},
|
||||
{
|
||||
"icon": "assets/icon-apps/home-risk.png",
|
||||
"title": "风险布控",
|
||||
"title": "风险分布",
|
||||
"unreadCount": 0,
|
||||
},
|
||||
{"icon": "assets/icon-apps/home-fl.png", "title": "法律法规", "unreadCount": 0},
|
||||
|
|
@ -109,7 +116,7 @@ class _HomePageState extends State<HomePage> {
|
|||
"title": "安全例会",
|
||||
"unreadCount": 0,
|
||||
},
|
||||
{"icon": "assets/icon-apps/home-xj.png", "title": "燃气巡检", "unreadCount": 0},
|
||||
// {"icon": "assets/icon-apps/home-xj.png", "title": "燃气巡检", "unreadCount": 0},
|
||||
];
|
||||
|
||||
/// 我的工作
|
||||
|
|
@ -182,6 +189,17 @@ class _HomePageState extends State<HomePage> {
|
|||
|
||||
/// 首次加载:先恢复缓存(如果有),然后在后台去刷新(只有当无缓存时才显示 loading)
|
||||
Future<void> _initialLoad() async {
|
||||
/// 清单列表
|
||||
final data = await ApiService.getListData();
|
||||
if (data['result'] == 'success') {
|
||||
final content = data['varList'] ?? [];
|
||||
for (Map item in content) {
|
||||
if (item['checkCount'] == 0) {
|
||||
totalList.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final result = await AuthService.checkUpdate();
|
||||
if (FormUtils.hasValue(result, 'pd')) {
|
||||
// 有更新 提示更新
|
||||
|
|
@ -573,7 +591,7 @@ class _HomePageState extends State<HomePage> {
|
|||
children: [
|
||||
Image.asset(iconPath, width: 40, height: 40),
|
||||
const SizedBox(height: 5),
|
||||
Text(label, style: const TextStyle(fontSize: 14)),
|
||||
Text(label, style: const TextStyle(fontSize: 13)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -596,7 +614,7 @@ class _HomePageState extends State<HomePage> {
|
|||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
unreadCount > 99 ? '99+' : '$unreadCount',
|
||||
'$unreadCount',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
|
|
@ -715,7 +733,7 @@ class _HomePageState extends State<HomePage> {
|
|||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
|
|
@ -724,7 +742,7 @@ class _HomePageState extends State<HomePage> {
|
|||
const SizedBox(height: 4),
|
||||
Text(
|
||||
subtitle,
|
||||
style: const TextStyle(fontSize: 14, color: Colors.black),
|
||||
style: const TextStyle(fontSize: 13, color: Colors.black),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/study/face_ecognition_page.dart';
|
||||
import 'package:qhd_prevention/pages/home/work/risk_list_page.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
||||
class ScanPage extends StatefulWidget {
|
||||
const ScanPage({Key? key}) : super(key: key);
|
||||
|
||||
// const ScanPage({Key? key}) : super(key: key,);
|
||||
const ScanPage({super.key, required this.totalList});
|
||||
final List totalList;
|
||||
@override
|
||||
State<ScanPage> createState() => _ScanPageState();
|
||||
}
|
||||
|
|
@ -47,24 +53,77 @@ class _ScanPageState extends State<ScanPage> {
|
|||
}
|
||||
}
|
||||
|
||||
void _showResult(String code) {
|
||||
Navigator.of(context).pop(code);
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (_) => AlertDialog(
|
||||
// title: const Text('扫描结果'),
|
||||
// content: Text(code),
|
||||
// actions: [
|
||||
// TextButton(
|
||||
// onPressed: () {
|
||||
// Navigator.of(context).pop(code);
|
||||
// _controller.start(); // 恢复扫码
|
||||
// },
|
||||
// child: const Text('确定'),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
void _showResult(String result) {
|
||||
try {
|
||||
if (result.contains('STUDENT_ID')) {
|
||||
final Map<String, dynamic> stuInfo = jsonDecode(result);
|
||||
print('stuInfo: $stuInfo');
|
||||
// 兼容性提取:res.result.split("@")[1]
|
||||
String? stuId;
|
||||
final parts = result.split('@');
|
||||
if (parts.length > 1) stuId = parts[1];
|
||||
|
||||
// userId = res.result.substring(0, res.result.indexOf('%_face'))
|
||||
String? userId;
|
||||
final idx = result.indexOf('%_face');
|
||||
if (idx >= 0) {
|
||||
userId = result.substring(0, idx);
|
||||
}
|
||||
|
||||
print('stuId: $stuId, userId: $userId');
|
||||
|
||||
// 比较登录用户 id 与解析到的 stuInfo.USER_ID
|
||||
if (SessionService.instance.loginUserId == stuInfo['USER_ID']) {
|
||||
goToFace(stuInfo);
|
||||
} else {
|
||||
ToastUtil.showNormal(context, '当前登录账号不匹配,无法扫码学习,请切换至正确的账号后再尝试人脸识别!');
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 不是 STUDENT_ID 的情况:按列表 id 匹配
|
||||
bool found = false;
|
||||
final listId = result;
|
||||
for (final item in widget.totalList) {
|
||||
if (item['LISTMANAGER_ID'] == listId) {
|
||||
found = true;
|
||||
goToList(listId: item['LISTMANAGER_ID'], listName: item['NAME']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ToastUtil.showError(context, '无法检查该清单');
|
||||
}
|
||||
}
|
||||
} catch (e, st) {
|
||||
// 捕获解析或运行时错误
|
||||
print('handleScanResult error: $e\n$st');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('扫码处理失败: ${e.toString()}')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 人脸识别跳转
|
||||
void goToFace(Map<String, dynamic> stuInfo) async {
|
||||
print('navigate to face with $stuInfo');
|
||||
final passed = await pushPage<bool>(
|
||||
FaceRecognitionPage(studentId: stuInfo['STUDENT_ID'], mode: FaceMode.auto),
|
||||
context,
|
||||
);
|
||||
if (passed == true) {
|
||||
ToastUtil.showSuccess(context, '验证成功');
|
||||
|
||||
} else {
|
||||
ToastUtil.showError(context, '验证失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转到清单页面
|
||||
void goToList({required String listId, required String listName}) {
|
||||
print('navigate to list: $listId, name: $listName');
|
||||
Navigator.pop(context,Animation);
|
||||
pushPage(RiskListPage(1, listId), context);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -184,12 +184,12 @@ class _FaceRecognitionPageState extends State<FaceRecognitionPage> {
|
|||
children: [
|
||||
Text(
|
||||
'请将人脸置于圆圈内',
|
||||
style: const TextStyle(fontSize: 18, color: Colors.black87),
|
||||
style: const TextStyle(fontSize: 16, color: Colors.black87),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
_errMsg,
|
||||
style: const TextStyle(fontSize: 18, color: Colors.red),
|
||||
style: const TextStyle(fontSize: 14, color: Colors.red),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
title: '温馨提示',
|
||||
content: '重要提醒:尊敬的用户,根据规定我们会在您学习过程中多次进行人脸识别认证,为了保护您的隐私请您在摄像设备视野内确保衣冠整齐。',
|
||||
cancelText: '取消',
|
||||
confirmText: '同意并继续'
|
||||
confirmText: '同意并继续',
|
||||
);
|
||||
if (ok) {}
|
||||
}
|
||||
|
|
@ -113,9 +113,7 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
_faceTime = int.tryParse(pd['FACE_TIME']?.toString() ?? '10') ?? 10;
|
||||
_videoList = List.from(pd['VIDEOLIST'] ?? []);
|
||||
// set initial face time
|
||||
if (pd['ISFACE'] == '1') {
|
||||
await ApiService.fnSetUserFaceTime(_faceTime);
|
||||
}
|
||||
|
||||
// compute percent
|
||||
for (var item in _videoList) {
|
||||
if (item['nodes'] is List && (item['nodes'] as List).isNotEmpty) {
|
||||
|
|
@ -158,11 +156,11 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
}
|
||||
|
||||
Future<void> _onVideoTap(
|
||||
Map<String, dynamic> data,
|
||||
bool hasNodes,
|
||||
int fi,
|
||||
int ni,
|
||||
) async {
|
||||
Map<String, dynamic> data,
|
||||
bool hasNodes,
|
||||
int fi,
|
||||
int ni,
|
||||
) async {
|
||||
// 后端清除人脸计时
|
||||
await ApiService.fnClearUserFaceTime();
|
||||
_faceTimer?.cancel();
|
||||
|
|
@ -210,16 +208,35 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
|
||||
Future<void> _navigateFaceIfNeeded(FutureOr<void> Function() onPass) async {
|
||||
if (_info?['ISFACE'] == '1') {
|
||||
final passed = await pushPage<bool>(
|
||||
FaceRecognitionPage(studentId: widget.studentId, mode: FaceMode.auto),
|
||||
context,
|
||||
);
|
||||
if (passed == true) {
|
||||
await onPass();
|
||||
} else {
|
||||
ScaffoldMessenger.of(
|
||||
LoadingDialogHelper.show();
|
||||
final resData = await ApiService.fnGetUserFace();
|
||||
LoadingDialogHelper.hide();
|
||||
final pd_data = resData['pd'];
|
||||
if (FormUtils.hasValue(pd_data, 'USERAVATARURL')) {
|
||||
final passed = await pushPage<bool>(
|
||||
FaceRecognitionPage(studentId: widget.studentId, mode: FaceMode.auto),
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('人脸验证未通过,无法继续')));
|
||||
);
|
||||
if (passed == true) {
|
||||
await ApiService.fnSetUserFaceTime(_faceTime);
|
||||
await onPass();
|
||||
} else {
|
||||
ToastUtil.showError(context, '人脸验证未通过,无法继续');
|
||||
}
|
||||
} else {
|
||||
final ok = await CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
title: '温馨提示',
|
||||
content: '您当前还未进行人脸认证,请先进行认证',
|
||||
cancelText: '取消',
|
||||
);
|
||||
if (ok) {
|
||||
await pushPage(
|
||||
const FaceRecognitionPage(studentId: '', mode: FaceMode.manual),
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
await onPass();
|
||||
|
|
@ -240,14 +257,14 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
|
||||
final raw = prog['pd']?['RESOURCETIME'];
|
||||
final seen =
|
||||
(() {
|
||||
if (raw == null) return 0;
|
||||
// 如果本身就是数字
|
||||
if (raw is num) return raw.toInt();
|
||||
// 否则转成字符串再 parse
|
||||
final s = raw.toString();
|
||||
return (double.tryParse(s) ?? 0.0).toInt();
|
||||
})();
|
||||
(() {
|
||||
if (raw == null) return 0;
|
||||
// 如果本身就是数字
|
||||
if (raw is num) return raw.toInt();
|
||||
// 否则转成字符串再 parse
|
||||
final s = raw.toString();
|
||||
return (double.tryParse(s) ?? 0.0).toInt();
|
||||
})();
|
||||
|
||||
// 先销毁旧 controller
|
||||
try {
|
||||
|
|
@ -290,12 +307,14 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
if (secondsSinceLast >= _uploadIntervalSeconds && !_endReported) {
|
||||
_lastReported = pos;
|
||||
// 串行上报:通过 _ongoingSubmit 链式调用来保证顺序
|
||||
_ongoingSubmit = (_ongoingSubmit ?? Future.value()).then((_) {
|
||||
return _submitPlayTime(end: false, seconds: pos.inSeconds);
|
||||
}).whenComplete(() {
|
||||
// 完成后清理引用
|
||||
_ongoingSubmit = null;
|
||||
});
|
||||
_ongoingSubmit = (_ongoingSubmit ?? Future.value())
|
||||
.then((_) {
|
||||
return _submitPlayTime(end: false, seconds: pos.inSeconds);
|
||||
})
|
||||
.whenComplete(() {
|
||||
// 完成后清理引用
|
||||
_ongoingSubmit = null;
|
||||
});
|
||||
}
|
||||
|
||||
// 接近结束的检测,容差 800ms,触发一次最终结束上报
|
||||
|
|
@ -305,13 +324,15 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
if (endedOrClose && !_endReported) {
|
||||
_endReported = true;
|
||||
final finalSeconds = (dur.inMilliseconds / 1000.0).ceil();
|
||||
_ongoingSubmit = (_ongoingSubmit ?? Future.value()).then((_) async {
|
||||
await _submitPlayTime(end: true, seconds: finalSeconds);
|
||||
}).whenComplete(() {
|
||||
_ongoingSubmit = null;
|
||||
// 最终上报完成后尝试退出顶层 route(退出全屏)
|
||||
_exitTopRouteIfPresent();
|
||||
});
|
||||
_ongoingSubmit = (_ongoingSubmit ?? Future.value())
|
||||
.then((_) async {
|
||||
await _submitPlayTime(end: true, seconds: finalSeconds);
|
||||
})
|
||||
.whenComplete(() {
|
||||
_ongoingSubmit = null;
|
||||
// 最终上报完成后尝试退出顶层 route(退出全屏)
|
||||
_exitTopRouteIfPresent();
|
||||
});
|
||||
|
||||
ApiService.fnClearUserFaceTime();
|
||||
_faceTimer?.cancel();
|
||||
|
|
@ -350,20 +371,23 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
|
||||
// 解析后端返回的 RESOURCETIME(可能是数字或字符串)
|
||||
final resTraw = pd['RESOURCETIME'];
|
||||
final resT = (resTraw is num)
|
||||
? resTraw.toDouble()
|
||||
: double.tryParse('$resTraw') ?? seconds.toDouble();
|
||||
final resT =
|
||||
(resTraw is num)
|
||||
? resTraw.toDouble()
|
||||
: double.tryParse('$resTraw') ?? seconds.toDouble();
|
||||
|
||||
// 从当前视频数据中安全解析 videoTime
|
||||
final videoTimeRaw = _currentVideoData!['VIDEOTIME'];
|
||||
final videoTime = (videoTimeRaw is String)
|
||||
? double.tryParse(videoTimeRaw) ?? 1.0
|
||||
: (videoTimeRaw is num ? videoTimeRaw.toDouble() : 1.0);
|
||||
final videoTime =
|
||||
(videoTimeRaw is String)
|
||||
? double.tryParse(videoTimeRaw) ?? 1.0
|
||||
: (videoTimeRaw is num ? videoTimeRaw.toDouble() : 1.0);
|
||||
|
||||
final comp = pd['PLAYCOUNT'] != null && pd['PLAYCOUNT'] > 0;
|
||||
|
||||
final pctDouble = comp ? 100.0 : ((resT / (videoTime > 0 ? videoTime : 1.0)) * 100.0);
|
||||
final pct = pctDouble.clamp(0.0, 100.0).toDouble().round();
|
||||
final pctDouble =
|
||||
comp ? 100.0 : ((resT / (videoTime > 0 ? videoTime : 1.0)) * 100.0);
|
||||
final pct = (pctDouble.clamp(0.00, 100.00) * 100).round() / 100;
|
||||
final str = '${pct}%';
|
||||
|
||||
if (mounted) {
|
||||
|
|
@ -382,11 +406,11 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
_videoController?.pause();
|
||||
|
||||
final ok = await CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
title: '温馨提示',
|
||||
content: '当前任务内所有课程均已学完,是否直接参加考试?',
|
||||
cancelText: '否',
|
||||
confirmText: '是'
|
||||
context,
|
||||
title: '温馨提示',
|
||||
content: '当前任务内所有课程均已学完,是否直接参加考试?',
|
||||
cancelText: '否',
|
||||
confirmText: '是',
|
||||
);
|
||||
if (ok) {
|
||||
_startExam(resData);
|
||||
|
|
@ -434,7 +458,7 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
'POST_ID': pd['POST_ID'] ?? '',
|
||||
'STUDENT_ID': widget.studentId,
|
||||
'STRENGTHEN_PAPER_QUESTION_ID':
|
||||
paper['STAGEEXAMPAPERINPUT_ID'] ?? '',
|
||||
paper['STAGEEXAMPAPERINPUT_ID'] ?? '',
|
||||
...data,
|
||||
},
|
||||
examType: TakeExamType.video_study,
|
||||
|
|
@ -459,14 +483,16 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _startFaceTimer() {
|
||||
_faceTimer = Timer.periodic(Duration(seconds: _faceTime), (_) async {
|
||||
final res = await ApiService.fnGetUserFaceTime(_faceTime);
|
||||
final res = await ApiService.fnGetUserFaceTime();
|
||||
final isPlaying = _videoController?.value.isPlaying == true;
|
||||
final isVideo = _currentVideoData?['IS_VIDEO'] == 0;
|
||||
final needAuth = FormUtils.hasValue(res, 'data');
|
||||
|
||||
if (isPlaying && isVideo && needAuth) {
|
||||
if (isPlaying && isVideo && !needAuth) {
|
||||
_exitTopRouteIfPresent();
|
||||
_videoController!.pause();
|
||||
_videoController!.removeListener(_onTimeUpdate);
|
||||
await _showFaceAuthOnce();
|
||||
|
|
@ -475,17 +501,66 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
}
|
||||
|
||||
Future<void> _showFaceAuthOnce() async {
|
||||
// 先 cancel 定时器,避免跳转过程中再次触发
|
||||
_faceTimer?.cancel();
|
||||
_faceTimer = null;
|
||||
|
||||
final passed = await pushPage<bool>(
|
||||
FaceRecognitionPage(studentId: widget.studentId, mode: FaceMode.auto),
|
||||
context,
|
||||
);
|
||||
|
||||
if (passed == true) {
|
||||
await _videoController?.play();
|
||||
setState(() {});
|
||||
await ApiService.fnSetUserFaceTime(_faceTime);
|
||||
// 认证通过后 —— 恢复播放、重新添加 listener,并更新上报基准
|
||||
try {
|
||||
// 如果 controller 还存在且已初始化
|
||||
if (_videoController != null && _videoController!.value.isInitialized) {
|
||||
// 先确保 listener 没有重复添加
|
||||
try {
|
||||
_videoController!.removeListener(_onTimeUpdate);
|
||||
} catch (_) {}
|
||||
|
||||
// 更新上报基准,避免立即触发一次短时间内的重复上报
|
||||
try {
|
||||
final currentPos = _videoController!.value.position;
|
||||
_lastReported = currentPos;
|
||||
debugPrint('Face auth passed — resetting _lastReported to $currentPos');
|
||||
} catch (_) {
|
||||
_lastReported = Duration.zero;
|
||||
}
|
||||
|
||||
// 恢复播放并绑定 listener(保证周期性进度上报恢复)
|
||||
await _videoController!.play();
|
||||
_videoController!.addListener(_onTimeUpdate);
|
||||
} else {
|
||||
// 如果 controller 为空或未初始化,仅尝试更新 UI
|
||||
debugPrint('Face auth passed but _videoController is null or not initialized');
|
||||
}
|
||||
setState(() {});
|
||||
} catch (e, st) {
|
||||
debugPrint('Error resuming playback after face auth: $e\n$st');
|
||||
}
|
||||
// 恢复人脸验证计时器
|
||||
_startFaceTimer();
|
||||
} else {
|
||||
ToastUtil.showError(context, '人脸验证未通过,无法继续');
|
||||
if (_videoController != null) {
|
||||
try {
|
||||
_videoController?.removeListener(_onTimeUpdate);
|
||||
} catch (_) {}
|
||||
try {
|
||||
_videoController?.dispose();
|
||||
} catch (_) {}
|
||||
_videoController = null;
|
||||
}
|
||||
_faceTimer?.cancel();
|
||||
setState(() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Widget _buildVideoOrCover(double containerW, double containerH) {
|
||||
final c = _videoController;
|
||||
if (c != null && c.value.isInitialized) {
|
||||
|
|
@ -493,9 +568,9 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
allowSeek: false,
|
||||
controller: _videoController,
|
||||
coverUrl:
|
||||
_videoCoverUrl.isNotEmpty
|
||||
? ApiService.baseImgPath + _videoCoverUrl
|
||||
: ApiService.baseImgPath + (_info?['COVERPATH'] ?? ''),
|
||||
_videoCoverUrl.isNotEmpty
|
||||
? ApiService.baseImgPath + _videoCoverUrl
|
||||
: ApiService.baseImgPath + (_info?['COVERPATH'] ?? ''),
|
||||
aspectRatio: _videoController?.value.aspectRatio ?? 16 / 9,
|
||||
);
|
||||
} else {
|
||||
|
|
@ -524,7 +599,7 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
child: Column(
|
||||
children: [
|
||||
_buildVideoOrCover(screenWidth(context), 250),
|
||||
const SizedBox(height: 5,),
|
||||
const SizedBox(height: 5),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
color: Colors.white,
|
||||
|
|
@ -541,12 +616,14 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
Container(
|
||||
color: Colors.white,
|
||||
child: TabBar(
|
||||
indicatorColor: Colors.blue,
|
||||
labelStyle: const TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.black87,
|
||||
),
|
||||
controller: _tabController,
|
||||
labelStyle: const TextStyle(fontSize: 16),
|
||||
indicator: const UnderlineTabIndicator(
|
||||
borderSide: BorderSide(width: 3.0, color: Colors.blue),
|
||||
insets: EdgeInsets.symmetric(horizontal: 0.0),
|
||||
),
|
||||
labelColor: Colors.blue,
|
||||
unselectedLabelColor: Colors.grey,
|
||||
tabs: const [Tab(text: '课件目录'), Tab(text: '详情')],
|
||||
),
|
||||
),
|
||||
|
|
@ -575,7 +652,7 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...nodes.asMap().entries.map(
|
||||
(e) => _buildVideoItem(
|
||||
(e) => _buildVideoItem(
|
||||
item,
|
||||
e.value as Map<String, dynamic>,
|
||||
true,
|
||||
|
|
@ -594,12 +671,12 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
}
|
||||
|
||||
Widget _buildVideoItem(
|
||||
Map<String, dynamic> item,
|
||||
Map<String, dynamic> m,
|
||||
bool hasNodes,
|
||||
int fi,
|
||||
int ni,
|
||||
) {
|
||||
Map<String, dynamic> item,
|
||||
Map<String, dynamic> m,
|
||||
bool hasNodes,
|
||||
int fi,
|
||||
int ni,
|
||||
) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.all(20),
|
||||
|
|
@ -640,23 +717,26 @@ class _StudyDetailPageState extends State<StudyDetailPage>
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"进度:${m['percent']}",
|
||||
style: const TextStyle(color: Colors.blue),
|
||||
Expanded(
|
||||
child: Text(
|
||||
"进度:${m['percent']}",
|
||||
style: const TextStyle(color: Colors.blue),
|
||||
),
|
||||
),
|
||||
if (m['IS_VIDEO'] == 0) ...[
|
||||
Text(secondsCount(m['VIDEOTIME'])),
|
||||
const SizedBox(width: 6),
|
||||
const SizedBox(width: 20),
|
||||
const Icon(Icons.play_circle, color: Colors.blue),
|
||||
],
|
||||
SizedBox(width: 20),
|
||||
CustomButton(
|
||||
onPressed:
|
||||
() => pushPage(
|
||||
StudyPractisePage(
|
||||
videoCoursewareId: m['VIDEOCOURSEWARE_ID'],
|
||||
),
|
||||
context,
|
||||
),
|
||||
StudyPractisePage(
|
||||
videoCoursewareId: m['VIDEOCOURSEWARE_ID'],
|
||||
),
|
||||
context,
|
||||
),
|
||||
text: "课后练习",
|
||||
backgroundColor: Colors.blue,
|
||||
height: 30,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class ItemListWidget {
|
|||
TextEditingController? controller, // 编辑时使用的控制器
|
||||
String? text, // 不可编辑时显示的文本
|
||||
String hintText = '请输入',
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
bool isRequired = true,
|
||||
bool strongRequired = false,
|
||||
ValueChanged<String>? onChanged,
|
||||
|
|
@ -90,7 +90,7 @@ class ItemListWidget {
|
|||
required bool isEditable, // 是否可编辑
|
||||
TextEditingController? controller, // 编辑时使用的控制器
|
||||
String? text, // 不可编辑时显示的文本
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
double height = 110, // 整体高度
|
||||
bool isRequired = true,
|
||||
String hintText = '请输入',
|
||||
|
|
@ -164,7 +164,7 @@ class ItemListWidget {
|
|||
required bool isEditable, // 是否可点击
|
||||
required String text, // 显示内容或提示
|
||||
VoidCallback? onTap, // 点击回调
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
bool isClean = false,
|
||||
VoidCallback? onTapClean, // 清除回调
|
||||
bool isRequired = true,
|
||||
|
|
@ -245,7 +245,7 @@ class ItemListWidget {
|
|||
required bool isEditable,
|
||||
required String text,
|
||||
VoidCallback? onTap,
|
||||
double fontSize = 15,
|
||||
double fontSize = 14,
|
||||
bool isClean = false,
|
||||
VoidCallback? onTapClean,
|
||||
bool isRequired = true,
|
||||
|
|
@ -334,7 +334,7 @@ class ItemListWidget {
|
|||
required String text, // 显示内容或提示
|
||||
TextEditingController? controller, // 第二行编辑控制器
|
||||
VoidCallback? onTap, // 第一行点击回调
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
double row2Height = 80, // 第二行高度
|
||||
bool isRequired = true,
|
||||
}) {
|
||||
|
|
@ -426,7 +426,7 @@ class ItemListWidget {
|
|||
required VoidCallback? onTap, // 第一行点击回调
|
||||
String buttonText = '选择其他',
|
||||
required String hintText,
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
double row2Height = 80, // 第二行高度
|
||||
bool isRequired = true,
|
||||
}) {
|
||||
|
|
@ -517,7 +517,7 @@ class ItemListWidget {
|
|||
required String label, // 标题
|
||||
required String text, // 显示内容或提示
|
||||
required VoidCallback? onTap, // 第一行点击回调
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
String buttonText = '分析详情'
|
||||
}) {
|
||||
return Container(
|
||||
|
|
@ -572,7 +572,7 @@ class ItemListWidget {
|
|||
required String label, // 标题
|
||||
required String buttonText, // 按钮文字
|
||||
required VoidCallback onTap, // 第一行点击回调
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
Color btnColor = Colors.blue,
|
||||
bool isRequired = false,
|
||||
}) {
|
||||
|
|
@ -610,7 +610,7 @@ class ItemListWidget {
|
|||
static Widget OneRowImageTitle({
|
||||
required String label, // 标题
|
||||
required String imgPath, // 图片路径
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
Color btnColor = Colors.blue,
|
||||
bool isRequired = false,
|
||||
String text = '',
|
||||
|
|
@ -663,7 +663,7 @@ class ItemListWidget {
|
|||
required String title, // 第一行标题
|
||||
required List<dynamic>? imageUrls,
|
||||
double row2Height = 80, // 第二行高度
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
void Function(String)? onTapCallBack,
|
||||
bool isRequired = true,
|
||||
}) {
|
||||
|
|
@ -742,7 +742,7 @@ class ItemListWidget {
|
|||
TextEditingController? controller, // 第二行编辑控制器
|
||||
required VoidCallback? onTap, // 第一行点击回调
|
||||
required String hintText,
|
||||
double fontSize = 15, // 字体大小
|
||||
double fontSize = 14, // 字体大小
|
||||
double row2Height = 80, // 第二行高度
|
||||
bool isRequired = true,
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -329,6 +329,9 @@ if (path != null) {
|
|||
.toList(),
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere((e) => e.localPath == path);
|
||||
_onImageRemoved(item);
|
||||
|
|
|
|||
|
|
@ -215,6 +215,12 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
cleanText: '清除监控',
|
||||
isRequired: pd['WORK_LEVEL'] == '特级',
|
||||
isEditable: widget.isEditable,
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _HotworkAqglDetailState extends State<HotworkAqglDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -212,6 +212,8 @@ class _HotworkAqjdDetailState extends State<HotworkAqjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -322,6 +324,9 @@ class _HotworkAqjdDetailState extends State<HotworkAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ class _HotworkDbbzDetailState extends State<HotworkDbbzDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
|
|
@ -298,7 +298,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
@ -633,7 +633,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存');
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
ToastUtil.showSuccess(context, '提交失败');
|
||||
ToastUtil.showError(context, '提交失败');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _HotworkDhspDetailState extends State<HotworkDhspDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -174,6 +174,8 @@ class _HotworkJhrDetailState extends State<HotworkJhrDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ class _HotworkJsjdDetailState extends State<HotworkJsjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -221,6 +221,8 @@ class _HotworkJszyDetailState extends State<HotworkJszyDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -339,7 +341,7 @@ class _HotworkJszyDetailState extends State<HotworkJszyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
endTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,6 +193,8 @@ class _HotworkKszyDetailState extends State<HotworkKszyDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -311,7 +313,7 @@ class _HotworkKszyDetailState extends State<HotworkKszyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _HotworkSzdwDetailState extends State<HotworkSzdwDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -243,6 +243,8 @@ class _HotworkYsgdDetailState extends State<HotworkYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -369,7 +371,7 @@ class _HotworkYsgdDetailState extends State<HotworkYsgdDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -385,6 +387,9 @@ class _HotworkYsgdDetailState extends State<HotworkYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _HotworkZyfzDetailState extends State<HotworkZyfzDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -364,6 +364,12 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
label: '视频监控:',
|
||||
isClean: widget.isEditable,
|
||||
cleanText: '清除监控',
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
|
|
|
|||
|
|
@ -211,6 +211,8 @@ class _CutroadAqjdDetailState extends State<CutroadAqjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -315,6 +317,9 @@ class _CutroadAqjdDetailState extends State<CutroadAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
|
|
@ -474,7 +474,7 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ setState(() {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -362,6 +362,9 @@ setState(() {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere((e) => e.localPath == path);
|
||||
_onImageRemoved(item);
|
||||
|
|
|
|||
|
|
@ -328,6 +328,9 @@ setState(() {
|
|||
horizontalPadding: 0,
|
||||
isRequired: true,
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere((e) => e.localPath == path);
|
||||
_onImageRemoved(item);
|
||||
|
|
|
|||
|
|
@ -411,6 +411,12 @@ class _BreakgroundDetailFormWidgetState
|
|||
label: '视频监控:',
|
||||
isClean: widget.isEditable,
|
||||
cleanText: '清除监控',
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
|
|
|
|||
|
|
@ -211,6 +211,8 @@ class _BreakgroundAqjdDetailState extends State<BreakgroundAqjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -315,6 +317,9 @@ class _BreakgroundAqjdDetailState extends State<BreakgroundAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
|
|
@ -250,7 +250,7 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _BreakgroundDzzhDetailState extends State<BreakgroundDzzhDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ class _BreakgroundJhrDetailState extends State<BreakgroundJhrDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ class _BreakgroundJsjdDetailState extends State<BreakgroundJsjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -201,6 +201,8 @@ class _BreakgroundJszyDetailState extends State<BreakgroundJszyDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -313,7 +315,7 @@ class _BreakgroundJszyDetailState extends State<BreakgroundJszyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
endTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ class _BreakgroundKszyDetailState extends State<BreakgroundKszyDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -302,7 +304,7 @@ class _BreakgroundKszyDetailState extends State<BreakgroundKszyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ class _BreakgroundShbmDetailState extends State<BreakgroundShbmDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ class _BreakgroundSpbmDetailState extends State<BreakgroundSpbmDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -175,6 +175,8 @@ class _BreakgroundSsrDetailState extends State<BreakgroundSsrDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -319,6 +319,8 @@ class _BreakgroundSetSafeDetailState extends State<BreakgroundSetSafeDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _BreakgroundSzdwDetailState extends State<BreakgroundSzdwDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -243,6 +243,8 @@ class _BreakgroundYsgdDetailState extends State<BreakgroundYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -363,7 +365,7 @@ class _BreakgroundYsgdDetailState extends State<BreakgroundYsgdDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -379,6 +381,9 @@ class _BreakgroundYsgdDetailState extends State<BreakgroundYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _BreakgroundZyfzDetailState extends State<BreakgroundZyfzDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -202,6 +202,8 @@ class _BreakgroundZyrDetailState extends State<BreakgroundZyrDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -351,6 +353,9 @@ class _BreakgroundZyrDetailState extends State<BreakgroundZyrDetail> {
|
|||
isShowNum: false,
|
||||
isRequired: true,
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = workImages.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -393,6 +393,12 @@ class _HoistworkDetailFormWidgetState extends State<HoistWorkDetailFormWidget> {
|
|||
label: '视频监控:',
|
||||
isClean: widget.isEditable,
|
||||
cleanText: '清除监控',
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
|
|
|
|||
|
|
@ -211,6 +211,8 @@ class _HoistworkAqjdDetailState extends State<HoistworkAqjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -315,6 +317,9 @@ class _HoistworkAqjdDetailState extends State<HoistworkAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
|
|
@ -212,7 +212,7 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _HoistworkDzzhDetailState extends State<HoistworkDzzhDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ class _HoistworkJhrDetailState extends State<HoistworkJhrDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ class _HoistworkJsjdDetailState extends State<HoistworkJsjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -201,6 +201,8 @@ class _HoistworkJszyDetailState extends State<HoistworkJszyDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -313,7 +315,7 @@ class _HoistworkJszyDetailState extends State<HoistworkJszyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
endTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ class _HoistworkKszyDetailState extends State<HoistworkKszyDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -302,7 +304,7 @@ class _HoistworkKszyDetailState extends State<HoistworkKszyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ class _HoistworkShbmDetailState extends State<HoistworkShbmDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ class _HoistworkSpbmDetailState extends State<HoistworkSpbmDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -175,6 +175,8 @@ class _HoistworkSsrDetailState extends State<HoistworkSsrDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -318,6 +318,8 @@ class _HoistworkSetSafeDetailState extends State<HoistworkSetSafeDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _HoistworkSzdwDetailState extends State<HoistworkSzdwDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -243,6 +243,8 @@ class _HoistworkYsgdDetailState extends State<HoistworkYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -363,7 +365,7 @@ class _HoistworkYsgdDetailState extends State<HoistworkYsgdDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -379,6 +381,9 @@ class _HoistworkYsgdDetailState extends State<HoistworkYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _HoistworkZyfzDetailState extends State<HoistworkZyfzDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -175,6 +175,8 @@ class _HoistworkZyrDetailState extends State<HoistworkZyrDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -182,6 +182,12 @@ class _HighWorkDetailFormWidgetState extends State<HighWorkDetailFormWidget> {
|
|||
label: '视频监控:',
|
||||
isClean: widget.isEditable,
|
||||
cleanText: '清除监控',
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
|
|
|
|||
|
|
@ -211,6 +211,8 @@ class _HighworkAqjdDetailState extends State<HighworkAqjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -315,6 +317,9 @@ class _HighworkAqjdDetailState extends State<HighworkAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ class _HighworkApplyDetailState extends State<HighworkApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
|
|
@ -206,7 +206,7 @@ class _HighworkApplyDetailState extends State<HighworkApplyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ class _HighworkJhrDetailState extends State<HighworkJhrDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ class _HighworkJsjdDetailState extends State<HighworkJsjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -201,6 +201,8 @@ class _HighworkJszyDetailState extends State<HighworkJszyDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -313,7 +315,7 @@ class _HighworkJszyDetailState extends State<HighworkJszyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
endTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ class _HighworkKszyDetailState extends State<HighworkKszyDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -302,7 +304,7 @@ class _HighworkKszyDetailState extends State<HighworkKszyDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ class _HighworkShbmDetailState extends State<HighworkShbmDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ class _HighworkSpbmDetailState extends State<HighworkSpbmDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -317,6 +317,8 @@ class _HighworkSetSafeDetailState extends State<HighworkSetSafeDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _HighworkSzdwDetailState extends State<HighworkSzdwDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -243,6 +243,8 @@ class _HighworkYsgdDetailState extends State<HighworkYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -363,7 +365,7 @@ class _HighworkYsgdDetailState extends State<HighworkYsgdDetail> {
|
|||
if (picked != null) {
|
||||
setState(() {
|
||||
startTime = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
).format(picked);
|
||||
});
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -379,6 +381,9 @@ class _HighworkYsgdDetailState extends State<HighworkYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ class _HighworkZyfzDetailState extends State<HighworkZyfzDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -175,6 +175,8 @@ class _HighworkZyrDetailState extends State<HighworkZyrDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -250,6 +250,12 @@ class _ElectricityDetailFormWidgetState extends State<ElectricityDetailFormWidge
|
|||
label: '视频监控:',
|
||||
isClean: widget.isEditable,
|
||||
cleanText: '清除监控',
|
||||
onTapClean: () {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = '';
|
||||
pd['VIDEOMANAGER_ID'] = '';
|
||||
});
|
||||
},
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
|
|
|
|||
|
|
@ -212,6 +212,8 @@ class _ElectricityAqjdDetailState extends State<ElectricityAqjdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -322,6 +324,9 @@ class _ElectricityAqjdDetailState extends State<ElectricityAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ class _ElectricityDbbzDetailState extends State<ElectricityDbbzDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue