2025.7.29 隐患快报完成
							parent
							
								
									d672f1d944
								
							
						
					
					
						commit
						0238689f1a
					
				|  | @ -1,4 +1,5 @@ | |||
| import 'package:flutter/material.dart'; | ||||
| import 'package:qhd_prevention/customWidget/toast_util.dart'; | ||||
| 
 | ||||
| import '../http/ApiService.dart'; | ||||
| import '../tools/tools.dart'; | ||||
|  | @ -365,4 +366,77 @@ class ListItemFactory { | |||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   ///  分类头部 | ||||
|   static Widget createYesNoSectionTwo({ | ||||
|     required String title, | ||||
|     required String yesLabel, | ||||
|     required String noLabel, | ||||
|     required bool groupValue, | ||||
|     required bool canClick, | ||||
|     required BuildContext context, | ||||
|     required ValueChanged<bool> onChanged, | ||||
|     double verticalPadding = 15, | ||||
|     double horizontalPadding = 10, | ||||
|   }) { | ||||
|     return Padding( | ||||
|       padding: EdgeInsets.only(top: 0, right: horizontalPadding, left: horizontalPadding, bottom: verticalPadding), | ||||
|       child: Container( | ||||
|         padding: EdgeInsets.symmetric(horizontal: 10), | ||||
|         decoration: BoxDecoration( | ||||
|           color: Colors.white, | ||||
|           borderRadius: BorderRadius.circular(5), | ||||
|         ), | ||||
|         child: Row( | ||||
|           children: [ | ||||
|             Expanded( | ||||
|               child: Text( | ||||
|                 title, | ||||
|                 style: TextStyle( | ||||
|                   fontSize: 15, | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                   color: Colors.black, | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|             Row( | ||||
|               children: [ | ||||
|                 Row( | ||||
|                   children: [ | ||||
|                     Radio<bool>( | ||||
|                       activeColor: Colors.blue, | ||||
|                       value: true, | ||||
|                       groupValue: groupValue, | ||||
|                       onChanged:(val) { | ||||
|                         if(canClick){ | ||||
|                           onChanged(val!); | ||||
|                         }else{ | ||||
|                           ToastUtil.showNormal(context, "重大隐患不允许选此项"); | ||||
|                         } | ||||
|                       } | ||||
|                       // (val) => onChanged(val!), | ||||
|                     ), | ||||
|                     Text(yesLabel), | ||||
|                   ], | ||||
|                 ), | ||||
|                 const SizedBox(width: 16), | ||||
|                 Row( | ||||
|                   children: [ | ||||
|                     Radio<bool>( | ||||
|                       activeColor: Colors.blue, | ||||
|                       value: false, | ||||
|                       groupValue: groupValue, | ||||
|                       onChanged: (val) => onChanged(val!), | ||||
|                     ), | ||||
|                     Text(noLabel), | ||||
|                   ], | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ class BottomPickerTwo { | |||
|         double height = 250, | ||||
|       }) { | ||||
|     // 当前选中项 | ||||
|     T selected = items[initialIndex]; | ||||
|     dynamic selected = items[initialIndex]; | ||||
| 
 | ||||
|     return showModalBottomSheet<T>( | ||||
|       context: context, | ||||
|  | @ -55,7 +55,7 @@ class BottomPickerTwo { | |||
|                       child: const Text('取消'), | ||||
|                     ), | ||||
|                     TextButton( | ||||
|                       onPressed: () => Navigator.of(ctx).pop(selected), | ||||
|                       onPressed: () => Navigator.of(ctx).pop(selected["NAME"]), | ||||
|                       child: const Text('确定'), | ||||
|                     ), | ||||
|                   ], | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ class DepartmentPickerHiddenType extends StatefulWidget { | |||
| class _DepartmentPickerHiddenTypeState | ||||
|     extends State<DepartmentPickerHiddenType> { | ||||
|   String selectedId = ''; | ||||
|   String selectedName = ''; | ||||
|   Set<String> expandedSet = {}; | ||||
|   List<Category> original = []; | ||||
|   List<Category> filtered = []; | ||||
|  |  | |||
|  | @ -0,0 +1,226 @@ | |||
| import 'dart:convert'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:qhd_prevention/customWidget/search_bar_widget.dart'; | ||||
| import 'package:qhd_prevention/http/ApiService.dart'; | ||||
| import '../tools/tools.dart'; // 包含 SessionService | ||||
| 
 | ||||
| // 数据模型 | ||||
| class Category { | ||||
|   final String id; | ||||
|   final String name; | ||||
|   final String pdId; | ||||
|   final List<Category> children; | ||||
| 
 | ||||
|   Category({ | ||||
|     required this.id, | ||||
|     required this.name, | ||||
|     required this.pdId, | ||||
|     this.children = const [], | ||||
|   }); | ||||
| 
 | ||||
|   factory Category.fromJson(Map<String, dynamic> json) { | ||||
|     return Category( | ||||
|       id: json['id'] as String, | ||||
|       name: json['name'] as String, | ||||
|       pdId: json['PARENT_ID'] as String, | ||||
|       children: (json['children'] as List<dynamic>) | ||||
|           .map((e) => Category.fromJson(e as Map<String, dynamic>)) | ||||
|           .toList(), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /// 弹窗回调签名:返回选中项的 id 和 name | ||||
| typedef DeptSelectCallback = void Function(String id, String name,String pdId); | ||||
| 
 | ||||
| class DepartmentPickerTwo extends StatefulWidget { | ||||
|   /// 回调,返回选中部门 id 与 name | ||||
|   final DeptSelectCallback onSelected; | ||||
| 
 | ||||
|   const DepartmentPickerTwo({Key? key, required this.onSelected}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _DepartmentPickerTwoState createState() => _DepartmentPickerTwoState(); | ||||
| } | ||||
| 
 | ||||
| class _DepartmentPickerTwoState extends State<DepartmentPickerTwo> { | ||||
|   String selectedId = ''; | ||||
|   String selectedPDId = ''; | ||||
|   String selectedName = ''; | ||||
|   Set<String> expandedSet = {}; | ||||
| 
 | ||||
|   List<Category> original = []; | ||||
|   List<Category> filtered = []; | ||||
|   bool loading = true; | ||||
| 
 | ||||
|   final TextEditingController _searchController = TextEditingController(); | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     // 初始均为空 | ||||
|     selectedId = ''; | ||||
|     selectedName = ''; | ||||
|     selectedPDId = ''; | ||||
|     expandedSet = {}; | ||||
|     _searchController.addListener(_onSearchChanged); | ||||
|     _loadData(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _searchController.removeListener(_onSearchChanged); | ||||
|     _searchController.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   Future<void> _loadData() async { | ||||
|     try { | ||||
|       List<dynamic> raw; | ||||
|       if (SessionService.instance.departmentJsonStr?.isNotEmpty ?? false) { | ||||
|         raw = json.decode(SessionService.instance.departmentJsonStr!) as List<dynamic>; | ||||
|       } else { | ||||
|         final result = await ApiService.getHiddenTreatmentListTree(); | ||||
|         final String nodes = result['zTreeNodes'] as String; | ||||
|         SessionService.instance.departmentJsonStr = nodes; | ||||
|         raw = json.decode(nodes) as List<dynamic>; | ||||
|       } | ||||
|       setState(() { | ||||
|         original = raw.map((e) => Category.fromJson(e as Map<String, dynamic>)).toList(); | ||||
|         filtered = original; | ||||
|         loading = false; | ||||
|       }); | ||||
|     } catch (e) { | ||||
|       setState(() => loading = false); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   void _onSearchChanged() { | ||||
|     final query = _searchController.text.toLowerCase().trim(); | ||||
|     setState(() { | ||||
|       filtered = query.isEmpty ? original : _filterCategories(original, query); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   List<Category> _filterCategories(List<Category> list, String query) { | ||||
|     List<Category> result = []; | ||||
|     for (var cat in list) { | ||||
|       final children = _filterCategories(cat.children, query); | ||||
|       if (cat.name.toLowerCase().contains(query) || children.isNotEmpty) { | ||||
|         result.add(Category(id: cat.id, name: cat.name,pdId:cat.pdId, children: children)); | ||||
|       } | ||||
|     } | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   Widget _buildRow(Category cat, int indent) { | ||||
|     final hasChildren = cat.children.isNotEmpty; | ||||
|     final isExpanded = expandedSet.contains(cat.id); | ||||
|     final isSelected = cat.id == selectedId; | ||||
|     return Column( | ||||
|       children: [ | ||||
|         InkWell( | ||||
|           onTap: () { | ||||
|             setState(() { | ||||
|               if (hasChildren) { | ||||
|                 isExpanded ? expandedSet.remove(cat.id) : expandedSet.add(cat.id); | ||||
|                 selectedPDId=cat.pdId; | ||||
|               }else{ | ||||
|                 selectedPDId=cat.id; | ||||
|               } | ||||
|               selectedId = cat.id; | ||||
|               selectedName = cat.name; | ||||
| 
 | ||||
|             }); | ||||
|           }, | ||||
|           child: Container( | ||||
|             color: Colors.white, | ||||
|             child: Row( | ||||
|               children: [ | ||||
|                 SizedBox(width: 16.0 * indent), | ||||
|                 SizedBox( | ||||
|                   width: 24, | ||||
|                   child: hasChildren | ||||
|                       ? Icon(isExpanded ? Icons.arrow_drop_down_rounded : Icons.arrow_right_rounded, | ||||
|                       size: 35, color: Colors.grey[600]) | ||||
|                       : const SizedBox.shrink(), | ||||
|                 ), | ||||
|                 const SizedBox(width: 5), | ||||
|                 Expanded( | ||||
|                   child: Padding( | ||||
|                     padding: const EdgeInsets.symmetric(vertical: 12), | ||||
|                     child: Text(cat.name), | ||||
|                   ), | ||||
|                 ), | ||||
|                 Padding( | ||||
|                   padding: const EdgeInsets.symmetric(horizontal: 16), | ||||
|                   child: Icon( | ||||
|                     isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, | ||||
|                     color: Colors.green, | ||||
|                   ), | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|         if (hasChildren && isExpanded) | ||||
|           ...cat.children.map((c) => _buildRow(c, indent + 1)), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       width: MediaQuery.of(context).size.width, | ||||
|       height: MediaQuery.of(context).size.height * 0.7, | ||||
|       color: Colors.white, | ||||
|       child: Column( | ||||
|         children: [ | ||||
|           Container( | ||||
|             color: Colors.white, | ||||
|             padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), | ||||
|             child: Row( | ||||
|               children: [ | ||||
|                 GestureDetector( | ||||
|                   onTap: () => Navigator.of(context).pop(), | ||||
|                   child: const Text('取消', style: TextStyle(fontSize: 16)), | ||||
|                 ), | ||||
|                 Expanded( | ||||
|                   child: Padding( | ||||
|                     padding: const EdgeInsets.symmetric(horizontal: 12), | ||||
|                     child: SearchBarWidget( | ||||
|                       controller: _searchController, | ||||
|                       isShowSearchButton: false, | ||||
|                       onSearch: (keyboard) { | ||||
|                       }, | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|                 GestureDetector( | ||||
|                   onTap: () { | ||||
|                     Navigator.of(context).pop(); | ||||
|                     widget.onSelected(selectedId, selectedName,selectedPDId); | ||||
|                   }, | ||||
|                   child: const Text('确定', style: TextStyle(fontSize: 16, color: Colors.green)), | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           Divider(), | ||||
|           Expanded( | ||||
|             child: loading | ||||
|                 ? const Center(child: CircularProgressIndicator()) | ||||
|                 : Container( | ||||
|               color: Colors.white, | ||||
|               child: ListView.builder( | ||||
|                 itemCount: filtered.length, | ||||
|                 itemBuilder: (ctx, idx) => _buildRow(filtered[idx], 0), | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | @ -819,7 +819,7 @@ U6Hzm1ninpWeE+awIDAQAB | |||
|     final fileName = file.path.split(Platform.pathSeparator).last; | ||||
|     return HttpManager().uploadFaceImage( | ||||
|         baseUrl: basePath, | ||||
|         path: '/app/feedback/upload', | ||||
|         path: '/app/imgfiles/add', | ||||
|         fromData: { | ||||
|           'FOREIGN_KEY': id, | ||||
|           'TYPE': type, | ||||
|  | @ -834,6 +834,28 @@ U6Hzm1ninpWeE+awIDAQAB | |||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// ai识别图片隐患 | ||||
|   static Future<Map<String, dynamic>> identifyImg(String imagePath) async { | ||||
|     final file = File(imagePath); | ||||
|     if (!await file.exists()) { | ||||
|       throw ApiException('file_not_found', '图片不存在:$imagePath'); | ||||
|     } | ||||
|     final fileName = file.path.split(Platform.pathSeparator).last; | ||||
|     return HttpManager().uploadFaceImage( | ||||
|         baseUrl: basePath, | ||||
|         path: '/app/hidden/identifyImg', | ||||
|         fromData: { | ||||
|           'CORPINFO_ID': SessionService.instance.corpinfoId, | ||||
|           'USER_ID': SessionService.instance.loginUserId, | ||||
|           'FFILE': await MultipartFile.fromFile( | ||||
|               file.path, | ||||
|               filename: fileName | ||||
|           ), | ||||
|         } | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   /// 修改密码 | ||||
|   static Future<Map<String, dynamic>> changePassWord(String oldPwd,String confirmPwd) { | ||||
|     return HttpManager().request( | ||||
|  | @ -1411,6 +1433,45 @@ U6Hzm1ninpWeE+awIDAQAB | |||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// 上传隐患快报 | ||||
|   static Future<Map<String, dynamic>> addRiskListCheckApp( | ||||
|       String hazardDescription,String partDescription,String latitude,String longitude, | ||||
|       String dangerDetail,String dataTime,String type,String responsibleId, | ||||
|       String yinHuanTypeIds,String hazardLeve,String buMenId,String buMenPDId, | ||||
|       String yinHuanTypeNames,String hiddenType1,String hiddenType2,String hiddenType3,) { | ||||
|     return HttpManager().request( | ||||
|       basePath, | ||||
|       '/app/hidden/riskListCheckAppAdd', | ||||
|       method: Method.post, | ||||
|       data: { | ||||
|         "HIDDEN_ID": "", | ||||
|         "SOURCE": '1', | ||||
|         "HIDDENDESCR": hazardDescription, | ||||
|         "HIDDENPART": partDescription, | ||||
|         "LATITUDE": latitude, | ||||
|         "LONGITUDE": longitude, | ||||
| 
 | ||||
|         "RECTIFYDESCR": dangerDetail, | ||||
|         "RECTIFICATIONDEADLINE": dataTime, | ||||
|         "RECTIFICATIONTYPE": type, | ||||
|         "RECTIFICATIONOR": responsibleId, | ||||
| 
 | ||||
|         "HIDDENTYPE": yinHuanTypeIds, | ||||
|         "HIDDENLEVEL":hazardLeve, | ||||
|         "RECTIFICATIONDEPT": buMenId, | ||||
|         "HIDDENFINDDEPT": buMenPDId.isNotEmpty?buMenPDId:buMenId, | ||||
| 
 | ||||
|         "CREATOR": SessionService.instance.loginUserId, | ||||
|         "HIDDENTYPE_NAME": yinHuanTypeNames, | ||||
|         "HIDDENTYPE1": hiddenType1 , | ||||
|         "HIDDENTYPE2": hiddenType2 , | ||||
|         "HIDDENTYPE3": hiddenType3 , | ||||
|         "CORPINFO_ID": SessionService.instance.corpinfoId, | ||||
|         "USER_ID": SessionService.instance.loginUserId, | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,15 +1,20 @@ | |||
| import 'dart:convert'; | ||||
| import 'dart:io'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:geolocator/geolocator.dart'; | ||||
| 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_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/toast_util.dart'; | ||||
| import 'package:qhd_prevention/pages/my_appbar.dart'; | ||||
| import 'package:qhd_prevention/tools/tools.dart'; | ||||
| import '../../../customWidget/photo_picker_row.dart'; | ||||
| import '../../../http/ApiService.dart'; | ||||
| 
 | ||||
|  | @ -25,21 +30,31 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|   final _partController = TextEditingController(); | ||||
|   final _dangerDetailController = TextEditingController(); | ||||
| 
 | ||||
|    String _repairLevel = ""; | ||||
| 
 | ||||
| 
 | ||||
|    String _repairLevelName = ""; | ||||
|   late bool _isDanger = false; | ||||
| 
 | ||||
|   late bool _canClick = true; | ||||
|   late List<dynamic> _hazardLeveLlist = []; //隐患级别 | ||||
|   // 存储各单位的人员列表 | ||||
|   List<Map<String, dynamic>> _personCache = []; | ||||
| 
 | ||||
| 
 | ||||
|   List<String> _yinHuanImages = []; | ||||
|   String _yinHuanVido=""; | ||||
|   dynamic _hazardLeve; | ||||
|   String yinHuanId = ""; | ||||
|   String yinHuanName = ""; | ||||
|   String buMenId = ""; | ||||
|   String buMenPDId = ""; | ||||
|   String buMenName = ""; | ||||
|   String responsibleId=""; | ||||
|   String responsibleName=""; | ||||
|   String dataTime = ""; | ||||
| 
 | ||||
|   List<String> _zhengGaiImages = []; | ||||
|   List<String> _yinHuanTypeIds = []; | ||||
|   List<String> _yinHuanTypeNames = []; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     // TODO: implement initState | ||||
|  | @ -52,7 +67,7 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|     try { | ||||
|       final result = await ApiService.getHazardLevel(); | ||||
|       if (result['result'] == 'success') { | ||||
|         final List<dynamic> newList = result['varList'] ?? []; | ||||
|         final List<dynamic> newList = result['list'] ?? []; | ||||
|         setState(() { | ||||
|           _hazardLeveLlist.addAll(newList); | ||||
|         }); | ||||
|  | @ -116,9 +131,22 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|                 isShowAI: true, | ||||
|                 onChanged: (List<File> files) { | ||||
|                   // 上传图片 files | ||||
|                   _yinHuanImages.clear(); | ||||
|                   for(int i=0;i<files.length;i++){ | ||||
|                     _yinHuanImages.add(files[i].path); | ||||
|                   } | ||||
|                 }, | ||||
|                 onAiIdentify: () { | ||||
|                   // AI 识别逻辑 | ||||
|                   if(_yinHuanImages.isEmpty){ | ||||
|                     ToastUtil.showNormal(context, "请先上传一张图片"); | ||||
|                     return; | ||||
|                   } | ||||
|                   if(_yinHuanImages.length>1){ | ||||
|                     ToastUtil.showNormal(context, "识别暂时只能上传一张图片"); | ||||
|                     return; | ||||
|                   } | ||||
|                   _identifyImg(_yinHuanImages[0]); | ||||
|                 }, | ||||
|               ), | ||||
|             ), | ||||
|  | @ -129,6 +157,7 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|                 mediaType: MediaType.video, | ||||
|                 onChanged: (List<File> files) { | ||||
|                   // 上传视频 files | ||||
|                   _yinHuanVido=files[0].path; | ||||
|                 }, | ||||
|                 onAiIdentify: () { | ||||
|                   // AI 视频识别逻辑 | ||||
|  | @ -151,23 +180,35 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|             ), | ||||
|             GestureDetector( | ||||
|                 onTap: () async { | ||||
|                   final choice = await BottomPickerTwo.show<String>( | ||||
|                   String choice = await BottomPickerTwo.show<String>( | ||||
|                     context, | ||||
|                     items: _hazardLeveLlist, | ||||
|                     itemBuilder: (item) => Text(item, textAlign: TextAlign.center), | ||||
|                     itemBuilder: (item) => Text(item["NAME"], textAlign: TextAlign.center), | ||||
|                     initialIndex: 0, | ||||
|                   ); | ||||
|                   if (choice != null) { | ||||
|                     for(int i=0;i<_hazardLeveLlist.length;i++){ | ||||
|                       if(choice==_hazardLeveLlist[i]["NAME"]){ | ||||
|                         _hazardLeve = _hazardLeveLlist[i]; | ||||
|                       } | ||||
|                     } | ||||
| 
 | ||||
|                     setState(() { | ||||
|                       _hazardLeve = choice; | ||||
|                       _repairLevel=_hazardLeve[""]; | ||||
|                       _repairLevelName=_hazardLeve["NAME"]; | ||||
|                       if("5ff9daf78e9a4fb1b40d77980656799d"==_hazardLeve["DICTIONARIES_ID"]){ | ||||
|                         _isDanger=false; | ||||
|                         _canClick=false; | ||||
|                       }else{ | ||||
|                         _canClick=true; | ||||
|                       } | ||||
| 
 | ||||
|                     }); | ||||
|                   } | ||||
|                 }, | ||||
|                 child: _buildSectionContainer( | ||||
|               child: ListItemFactory.createRowSpaceBetweenItem( | ||||
|                 leftText: "隐患级别", | ||||
|                 rightText: _repairLevel.isNotEmpty?_repairLevel:"请选择", | ||||
|                 rightText: _repairLevelName.isNotEmpty?_repairLevelName:"请选择", | ||||
|                 isRight: true, | ||||
|               ), | ||||
|             ), | ||||
|  | @ -181,9 +222,18 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|                   backgroundColor: Colors.transparent, | ||||
|                   builder: | ||||
|                       (ctx) => DepartmentPickerHiddenType( | ||||
|                         onSelected: (json) { | ||||
|                           print(jsonEncode(json)); | ||||
|                         onSelected: (jsonString) { | ||||
|                           Map<String, List<String>> result = jsonString; | ||||
|                            _yinHuanTypeIds = List<String>.from(result['id']!); | ||||
|                            _yinHuanTypeNames = List<String>.from(result['name']!); | ||||
|                           setState(() { | ||||
|                             yinHuanName=_yinHuanTypeNames[_yinHuanTypeNames.length-1]; | ||||
|                           }); | ||||
| 
 | ||||
| 
 | ||||
|                           // print(jsonEncode(json)); | ||||
|                           // List<String> _yinHuanTypeIds = []; | ||||
|                           // List<String> _yinHuanTypeNames = []; | ||||
|                         }, | ||||
|                       ), | ||||
|                 ); | ||||
|  | @ -197,13 +247,15 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|               ), | ||||
|             ), | ||||
|             _buildSectionContainer( | ||||
|               child: ListItemFactory.createYesNoSection( | ||||
|               child: ListItemFactory.createYesNoSectionTwo( | ||||
|                 title: "是否立即整改", | ||||
|                 horizontalPadding: 0, | ||||
|                 verticalPadding: 0, | ||||
|                 yesLabel: "是", | ||||
|                 noLabel: "否", | ||||
|                 groupValue: _isDanger, | ||||
|                 canClick: _canClick, | ||||
|                 context:context, | ||||
|                 onChanged: (val) { | ||||
|                   setState(() { | ||||
|                     _isDanger = val; | ||||
|  | @ -231,8 +283,15 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|                       isShowAI: false, | ||||
|                       onChanged: (List<File> files) { | ||||
|                         // 上传图片 files | ||||
|                         _zhengGaiImages.clear(); | ||||
|                         for(int i=0;i<files.length;i++){ | ||||
|                           _zhengGaiImages.add(files[i].path); | ||||
|                         } | ||||
| 
 | ||||
|                       }, | ||||
|                       onAiIdentify: () { | ||||
| 
 | ||||
|                       }, | ||||
|                       onAiIdentify: () {}, | ||||
|                     ), | ||||
|                   ), | ||||
|                 ], | ||||
|  | @ -248,12 +307,21 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|                         barrierColor: Colors.black54, | ||||
|                         backgroundColor: Colors.transparent, | ||||
|                         builder: | ||||
|                             (ctx) => DepartmentPicker( | ||||
|                               onSelected: (id, name) async { | ||||
|                             (ctx) => DepartmentPickerTwo( | ||||
|                               onSelected: (id, name,pdId) async { | ||||
|                                 setState(() { | ||||
|                                   buMenId = id; | ||||
|                                   buMenName = name; | ||||
|                                   buMenPDId=pdId; | ||||
| 
 | ||||
|                                   responsibleId=""; | ||||
|                                   responsibleName=""; | ||||
|                                 }); | ||||
|                                 // 拉取该单位的人员列表并缓存 | ||||
|                                 final result = await ApiService.getListTreePersonList(id); | ||||
|                                 _personCache=List<Map<String, dynamic>>.from( | ||||
|                                   result['userList'] as List, | ||||
|                                 ); | ||||
|                               }, | ||||
|                             ), | ||||
|                       ); | ||||
|  | @ -266,6 +334,46 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
| 
 | ||||
| 
 | ||||
|                   SizedBox(height: 10), | ||||
| 
 | ||||
|                   GestureDetector( | ||||
|                     onTap: () { | ||||
|                       if ( buMenId.isEmpty) { | ||||
|                         ToastUtil.showNormal(context, '请先选择部门'); | ||||
|                         return; | ||||
|                       } | ||||
|                       DepartmentPersonPicker.show( | ||||
|                         context, | ||||
|                         personsData: _personCache, | ||||
|                         onSelected: (userId, name) { | ||||
|                           setState(() { | ||||
|                             // renYuanId = userId; | ||||
|                             // renYuanName = name; | ||||
| 
 | ||||
|                             responsibleId=userId; | ||||
|                             responsibleName=name; | ||||
| 
 | ||||
|                           }); | ||||
| 
 | ||||
|                         }, | ||||
|                       ); | ||||
|                     }, | ||||
|                     child:Container( | ||||
|                       padding: EdgeInsets.symmetric(horizontal: 15), | ||||
|                       decoration: BoxDecoration( | ||||
|                         color: Colors.white, | ||||
|                         borderRadius: BorderRadius.circular(5), | ||||
|                       ), | ||||
|                       child: ListItemFactory.createRowSpaceBetweenItem( | ||||
|                         leftText: "整改负责人", | ||||
|                         rightText: responsibleName.isNotEmpty?responsibleName:"请选择", | ||||
|                         isRight: true, | ||||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
| 
 | ||||
|                   GestureDetector( | ||||
|                     onTap: () { | ||||
|                       showDialog( | ||||
|  | @ -274,13 +382,15 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|                             (_) => HDatePickerDialog( | ||||
|                               initialDate: DateTime.now(), | ||||
|                               onCancel: () => Navigator.of(context).pop(), | ||||
|                               onConfirm: (selected) { | ||||
|                               onConfirm: (selected) async { | ||||
|                                 Navigator.of(context).pop(); | ||||
|                                 setState(() { | ||||
|                                   dataTime = DateFormat( | ||||
|                                     'yyyy-MM-dd', | ||||
|                                   ).format(selected); | ||||
|                                 }); | ||||
| 
 | ||||
| 
 | ||||
|                               }, | ||||
|                             ), | ||||
|                       ); | ||||
|  | @ -298,7 +408,9 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
| 
 | ||||
|             SizedBox(height: 30), | ||||
|             CustomButton( | ||||
|               onPressed: () {}, | ||||
|               onPressed: () { | ||||
|                 _riskListCheckAppAdd(); | ||||
|               }, | ||||
|               text: "提交", | ||||
|               backgroundColor: Colors.blue, | ||||
|             ), | ||||
|  | @ -307,4 +419,237 @@ class _QuickReportPageState extends State<QuickReportPage> { | |||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Future<void> _riskListCheckAppAdd() async { | ||||
|     if(_yinHuanImages.isEmpty){ | ||||
|       ToastUtil.showNormal(context, "请上传隐患图片"); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     String hazardDescription=_standardController.text.trim(); | ||||
|     if(hazardDescription.isEmpty){ | ||||
|       ToastUtil.showNormal(context, "请填隐患描述"); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     String partDescription=_partController.text.trim(); | ||||
|     if(partDescription.isEmpty){ | ||||
|       ToastUtil.showNormal(context, "请填隐患部位"); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     if(_hazardLeve.isEmpty){ | ||||
|       ToastUtil.showNormal(context, "请选择隐患级别"); | ||||
|       return; | ||||
|     } | ||||
|     String hazardLeve = _hazardLeve["BIANMA"]; | ||||
| 
 | ||||
|     if(_yinHuanTypeIds.isEmpty){ | ||||
|       ToastUtil.showNormal(context, "请选择隐患类型"); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     String type="1"; | ||||
|     String dangerDetail=""; | ||||
|     if(_isDanger){ | ||||
|       type="1"; | ||||
|        dangerDetail=_dangerDetailController.text.trim(); | ||||
|       if(dangerDetail.isEmpty){ | ||||
|         ToastUtil.showNormal(context, "请填整改描述"); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if(_zhengGaiImages.isEmpty){ | ||||
|         ToastUtil.showNormal(context, "请上传整改后图片"); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     }else{ | ||||
|       type="2"; | ||||
|       if(buMenId.isEmpty){ | ||||
|         ToastUtil.showNormal(context, "请选择整改部门"); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if(responsibleId.isEmpty){ | ||||
|         ToastUtil.showNormal(context, "请选择整改人"); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if(dataTime.isEmpty){ | ||||
|         ToastUtil.showNormal(context, "请选择整改期限"); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     String yinHuanTypeIds=""; | ||||
|     String yinHuanTypeNames=""; | ||||
|     for(int i=0;i<_yinHuanTypeIds.length;i++){ | ||||
|      String yinHuanTypeId= _yinHuanTypeIds[i]; | ||||
|      String yinHuanTypeName= _yinHuanTypeNames[i]; | ||||
|      if(yinHuanTypeIds.isEmpty){ | ||||
|        yinHuanTypeIds=yinHuanTypeId; | ||||
|      }else{ | ||||
|        yinHuanTypeIds="$yinHuanTypeIds,$yinHuanTypeId"; | ||||
|      } | ||||
|      if(yinHuanTypeNames.isEmpty){ | ||||
|        yinHuanTypeNames=yinHuanTypeName; | ||||
|      }else{ | ||||
|        yinHuanTypeNames="$yinHuanTypeNames/$yinHuanTypeName"; | ||||
|      } | ||||
|     } | ||||
| 
 | ||||
|     String hiddenType1=""; | ||||
|     if(_yinHuanTypeIds.length>1){ | ||||
|       hiddenType1=_yinHuanTypeIds[0]; | ||||
|     } | ||||
|     String hiddenType2=""; | ||||
|     if(_yinHuanTypeIds.length>2){ | ||||
|       hiddenType1=_yinHuanTypeIds[1]; | ||||
|     } | ||||
|     String hiddenType3=""; | ||||
|     if(_yinHuanTypeIds.length>3){ | ||||
|       hiddenType1=_yinHuanTypeIds[2]; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     //获取定位 | ||||
|     Position position = await _determinePosition(); | ||||
|     String longitude=position.longitude.toString(); | ||||
|     String latitude=position.latitude.toString(); | ||||
|      | ||||
|     try { | ||||
|       final result = await ApiService.addRiskListCheckApp( | ||||
|          hazardDescription, partDescription, latitude, longitude, | ||||
|          dangerDetail, dataTime, type, responsibleId, | ||||
|          yinHuanTypeIds, hazardLeve, buMenId, buMenPDId, | ||||
|          yinHuanTypeNames, hiddenType1, hiddenType2, hiddenType3,); | ||||
|       if (result['result'] == 'success') { | ||||
| 
 | ||||
|         String hiddenId = result['pd']['HIDDEN_ID'] ; | ||||
| 
 | ||||
| 
 | ||||
|             for (int i=0;i<_yinHuanImages.length;i++){ | ||||
|               _addImgFiles(_yinHuanImages[i],"3",hiddenId); | ||||
|             } | ||||
| 
 | ||||
|             _addImgFiles(_yinHuanVido,"3",hiddenId); | ||||
| 
 | ||||
|           if(_isDanger){ | ||||
|             for (int i=0;i<_zhengGaiImages.length;i++){ | ||||
|               _addImgFiles(_zhengGaiImages[i],"4",hiddenId); | ||||
|             } | ||||
|           } | ||||
|         setState(() { | ||||
|           ToastUtil.showNormal(context, "提交成功"); | ||||
|           Navigator.pop(context); | ||||
|         }); | ||||
|       } | ||||
|     } catch (e) { | ||||
|       print('Error fetching data: $e'); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|   Future<String> _addImgFiles(String imagePath,String type,String id) async { | ||||
|     try { | ||||
| 
 | ||||
|       final raw = await ApiService.addImgFiles( imagePath, type, id); | ||||
|       if (raw['result'] == 'success') { | ||||
|         return raw['imgPath']; | ||||
|       }else{ | ||||
|         // _showMessage('反馈提交失败'); | ||||
|         return ""; | ||||
|       } | ||||
| 
 | ||||
|     } catch (e) { | ||||
|       // 出错时可以 Toast 或者在页面上显示错误状态 | ||||
|       print('加载首页数据失败:$e'); | ||||
|       return ""; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   Future<void> _identifyImg(String imagePath) async { | ||||
|     try { | ||||
|       LoadingDialogHelper.show(context); | ||||
|       final raw = await ApiService.identifyImg( imagePath); | ||||
|       if (raw['result'] == 'success') { | ||||
|         final List<dynamic> newList = raw['aiHiddens'] ?? []; | ||||
| 
 | ||||
|         String miaoShuText=""; | ||||
|         String zhengGaiText=""; | ||||
|         for(int i=0;i<newList.length;i++){ | ||||
|           // 1. 将字符串解析为 Map | ||||
|           final Map<String, dynamic> item1 = jsonDecode(newList[i]); | ||||
|           if(miaoShuText.isEmpty){ | ||||
|             miaoShuText=item1["hiddenDescr"]; | ||||
|           }else{ | ||||
|             miaoShuText=miaoShuText+";"+item1["hiddenDescr"]; | ||||
|           } | ||||
| 
 | ||||
|           if(zhengGaiText.isEmpty){ | ||||
|             zhengGaiText=item1["rectificationSuggestions"]; | ||||
|           }else{ | ||||
|             zhengGaiText=zhengGaiText+";"+item1["rectificationSuggestions"]; | ||||
|           } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         LoadingDialogHelper.hide(context); | ||||
|         setState(() { | ||||
|           _isDanger=true; | ||||
|           _standardController.text=miaoShuText; | ||||
|           _dangerDetailController.text=zhengGaiText; | ||||
|         }); | ||||
|       }else{ | ||||
|         ToastUtil.showNormal(context, "识别失败"); | ||||
|         LoadingDialogHelper.hide(context); | ||||
|         // _showMessage('反馈提交失败'); | ||||
|         // return ""; | ||||
|       } | ||||
| 
 | ||||
|     } catch (e) { | ||||
|       // 出错时可以 Toast 或者在页面上显示错误状态 | ||||
|       print('加载首页数据失败:$e'); | ||||
|       // return ""; | ||||
|       LoadingDialogHelper.hide(context); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   Future<Position> _determinePosition() async { | ||||
|     bool serviceEnabled; | ||||
|     LocationPermission permission; | ||||
| 
 | ||||
|     // 检查定位服务是否启用 | ||||
|     serviceEnabled = await Geolocator.isLocationServiceEnabled(); | ||||
|     if (!serviceEnabled) { | ||||
|       return Future.error('Location services are disabled.'); | ||||
|     } | ||||
| 
 | ||||
|     // 获取权限 | ||||
|     permission = await Geolocator.checkPermission(); | ||||
|     if (permission == LocationPermission.denied) { | ||||
|       permission = await Geolocator.requestPermission(); | ||||
|       if (permission == LocationPermission.denied) { | ||||
|         return Future.error('Location permissions are denied'); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if (permission == LocationPermission.deniedForever) { | ||||
|       return Future.error( | ||||
|           'Location permissions are permanently denied, we cannot request permissions.'); | ||||
|     } | ||||
| 
 | ||||
|     // 获取当前位置 | ||||
|     return await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										278
									
								
								pubspec.lock
								
								
								
								
							
							
						
						
									
										278
									
								
								pubspec.lock
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
		Reference in New Issue