From ff39ac4737d56fbbae4fa49843b62a36385b97b0 Mon Sep 17 00:00:00 2001 From: hs <873121290@qq.com> Date: Thu, 31 Jul 2025 17:33:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AB=E9=A1=B9=E4=BD=9C=E4=B8=9A=E5=8A=A8?= =?UTF-8?q?=E7=81=AB=E4=BD=9C=E4=B8=9A=E6=B5=81=E7=A8=8B=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner/AppDelegate.swift | 1 + lib/customWidget/ItemWidgetFactory.dart | 26 +- lib/customWidget/photo_picker_row.dart | 97 ++-- lib/http/ApiService.dart | 5 +- lib/pages/home/tap/item_list_widget.dart | 10 + .../MeasuresListWidget.dart | 143 +++--- .../special_wrok/SpecialWorkFormBaseWork.dart | 399 +++++++++++++++++ .../special_wrok/WorkDetailFormWidget.dart | 3 +- .../dangerous_options_page.dart | 204 ++++++--- .../hotwork_safe_func_sure.dart | 167 +++---- .../aqgl_work_detail/hotwork_aqgl_detail.dart | 335 ++++++++++++++ .../aqjd_work_detail/hotwork_aqjd_detail.dart | 370 ++++++++++++++++ .../dbbz_work_detail/hotwork_dbbz_detail.dart | 335 ++++++++++++++ .../dh_work_detai/hotwork_apply_detail.dart | 2 +- .../dhsp_work_detail/hotwork_dhsp_detail.dart | 335 ++++++++++++++ .../jhr_work_detail/hotwork_jhr_detail.dart | 358 +++++++++++++++ .../jsjd_work_detail/hotwork_jsjd_detail.dart | 317 +++++++++++++ .../jszy_work_detail/hotwork_jszy_detail.dart | 378 ++++++++++++++++ .../kszy_work_detail/hotwork_kszy_detail.dart | 351 +++++++++++++++ .../special_wrok/special_work_list_page.dart | 49 +- .../hotwork_set_safe_detail.dart | 21 +- .../szdw_work_detail/hotwork_szdw_detail.dart | 335 ++++++++++++++ .../ysgd/hotwork_ysgd_detail.dart | 418 ++++++++++++++++++ .../zyfz_work_detail/hotwork_zyfz_detail.dart | 334 ++++++++++++++ 24 files changed, 4701 insertions(+), 292 deletions(-) rename lib/pages/home/tap/tabList/special_wrok/{dh_work_detai => }/MeasuresListWidget.dart (91%) create mode 100644 lib/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/aqgl_work_detail/hotwork_aqgl_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/aqjd_work_detail/hotwork_aqjd_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/dbbz_work_detail/hotwork_dbbz_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/dhsp_work_detail/hotwork_dhsp_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/jhr_work_detail/hotwork_jhr_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/jsjd_work_detail/hotwork_jsjd_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/jszy_work_detail/hotwork_jszy_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/kszy_work_detail/hotwork_kszy_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/szdw_work_detail/hotwork_szdw_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/ysgd/hotwork_ysgd_detail.dart create mode 100644 lib/pages/home/tap/tabList/special_wrok/zyfz_work_detail/hotwork_zyfz_detail.dart diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 6266644..59bf2ac 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -7,6 +7,7 @@ import UIKit _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { + GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } diff --git a/lib/customWidget/ItemWidgetFactory.dart b/lib/customWidget/ItemWidgetFactory.dart index ee63d78..246d6c8 100644 --- a/lib/customWidget/ItemWidgetFactory.dart +++ b/lib/customWidget/ItemWidgetFactory.dart @@ -36,7 +36,7 @@ class ListItemFactory { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - _truncateText(rightText,14), + _truncateText(rightText,100), // rightText, style: TextStyle(fontSize: 15, color: Colors.grey), ), @@ -233,7 +233,7 @@ class ListItemFactory { ); } - /// 分类头部 + /// YesNo static Widget createYesNoSection({ required String title, required String yesLabel, @@ -317,7 +317,23 @@ class ListItemFactory { ), ); } - + /// 单纯标题 + static Widget headerTitle(String title) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Text( + title, + style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), + ), + ], + ), + ); + } /// 扩展项(根据需求自定义) static Widget createCustomItem({ required Widget child, @@ -344,8 +360,10 @@ class ListItemFactory { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // 标题 HhTextStyleUtils.mainTitle(label, fontSize: 15), const SizedBox(height: 8), + // 文本输入框,清除默认内边距以与标题左对齐 Expanded( child: TextField( autofocus: false, @@ -357,6 +375,7 @@ class ListItemFactory { decoration: InputDecoration( hintText: hint, border: InputBorder.none, + contentPadding: EdgeInsets.zero, // 去除默认内边距 ), ), ), @@ -365,7 +384,6 @@ class ListItemFactory { ); } - /// 分类头部 static Widget createYesNoSectionTwo({ required String title, diff --git a/lib/customWidget/photo_picker_row.dart b/lib/customWidget/photo_picker_row.dart index 6f0a007..69d80ed 100644 --- a/lib/customWidget/photo_picker_row.dart +++ b/lib/customWidget/photo_picker_row.dart @@ -244,8 +244,8 @@ class _MediaPickerRowState extends State { } } -/// 整改后照片上传区域组件 -class RepairedPhotoSection extends StatelessWidget { +/// 照片上传区域组件 +class RepairedPhotoSection extends StatefulWidget { final int maxCount; final MediaType mediaType; final String title; @@ -271,54 +271,85 @@ class RepairedPhotoSection extends StatelessWidget { this.onMediaRemoved, }) : super(key: key); + @override + _RepairedPhotoSectionState createState() => _RepairedPhotoSectionState(); +} + +class _RepairedPhotoSectionState extends State { + late List _mediaPaths; + + @override + void initState() { + super.initState(); + // 初始化已有地址 + _mediaPaths = widget.initialMediaPaths?.take(widget.maxCount).toList() ?? []; + // 初始回调 + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.onChanged( + _mediaPaths.map((p) => File(p)).toList(), + ); + }); + } + @override Widget build(BuildContext context) { return Container( color: Colors.white, - padding: const EdgeInsets.only(left: 5, right: 10), + padding: const EdgeInsets.only(left: 0, right: 10), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: EdgeInsets.symmetric(horizontal: horizontalPadding), + padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding), child: ListItemFactory.createRowSpaceBetweenItem( - leftText: title, - rightText: '${initialMediaPaths?.length ?? 0}/$maxCount', + leftText: widget.title, + // 动态展示已选数量 + rightText: '${_mediaPaths.length}/${widget.maxCount}', ), ), + const SizedBox(height: 8), Padding( - padding: EdgeInsets.symmetric(horizontal: horizontalPadding), + padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding), child: MediaPickerRow( - maxCount: maxCount, - mediaType: mediaType, - initialMediaPaths: initialMediaPaths, - onChanged: onChanged, - onMediaAdded: onMediaAdded, - onMediaRemoved: onMediaRemoved, + maxCount: widget.maxCount, + mediaType: widget.mediaType, + initialMediaPaths: _mediaPaths, + // 统一在 onChanged 中维护 mediaPaths,避免重复添加 + onChanged: (files) { + final newPaths = files.map((f) => f.path).toList(); + setState(() { + _mediaPaths = newPaths; + }); + widget.onChanged(files); + }, + // 仅回调外部,不再修改本地 _mediaPaths + onMediaAdded: widget.onMediaAdded, + onMediaRemoved: widget.onMediaRemoved, ), ), const SizedBox(height: 20), - if (isShowAI) - Row( - children: [ - GestureDetector( - onTap: onAiIdentify, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 15), - height: 36, - decoration: BoxDecoration( - color: const Color(0xFFDFEAFF), - borderRadius: BorderRadius.circular(18), - ), - child: Row( - children: [ - Image.asset('assets/images/ai_img.png', width: 20), - const SizedBox(width: 5), - const Text('AI隐患识别与处理'), - ], - ), + if (widget.isShowAI) + Padding( + padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding), + child: GestureDetector( + onTap: widget.onAiIdentify, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 15), + height: 36, + decoration: BoxDecoration( + color: const Color(0xFFDFEAFF), + borderRadius: BorderRadius.circular(18), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset('assets/images/ai_img.png', width: 20), + const SizedBox(width: 5), + const Text('AI隐患识别与处理'), + ], ), ), - ], + ), ), ], ), diff --git a/lib/http/ApiService.dart b/lib/http/ApiService.dart index 49515b1..1fb2b5c 100644 --- a/lib/http/ApiService.dart +++ b/lib/http/ApiService.dart @@ -801,7 +801,7 @@ U6Hzm1ninpWeE+awIDAQAB ); } - /// 上传图片 + /// 上传图片或视频 static Future> uploadSaveFile ( String filePath, ) async { @@ -842,6 +842,9 @@ U6Hzm1ninpWeE+awIDAQAB // 把文件路径填成 MultipartFile for (var i = 0; i < filePaths.length; i++) { final path = filePaths[i]; + if (path.contains('uploadFiles')) { + continue; + } data['file$i'] = await MultipartFile.fromFile( path, filename: path.split(Platform.pathSeparator).last, diff --git a/lib/pages/home/tap/item_list_widget.dart b/lib/pages/home/tap/item_list_widget.dart index 40be6f4..27c12ce 100644 --- a/lib/pages/home/tap/item_list_widget.dart +++ b/lib/pages/home/tap/item_list_widget.dart @@ -430,4 +430,14 @@ class ItemListWidget { ), ); } + static Widget itemContainer(Widget child) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: child, + ); + } } diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart b/lib/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart similarity index 91% rename from lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart rename to lib/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart index d9c95d9..d96cad8 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/MeasuresListWidget.dart @@ -1,20 +1,17 @@ import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; +import '../../../../../customWidget/date_picker_dialog.dart'; +import '../../../../../http/ApiService.dart'; +import '../../../../../tools/tools.dart'; +import 'dh_work_detai/hotwork_apply_detail.dart'; -import '../../../../../../customWidget/date_picker_dialog.dart'; -import '../../../../../../http/ApiService.dart'; -import '../../../../../../tools/tools.dart'; -import 'hotwork_apply_detail.dart'; - -/// 表格组件,用于展示“安全防护措施”列表 -import 'package:flutter/material.dart'; +/// 八项作业先关组件,用于展示“安全防护措施”列表 import 'package:flutter/services.dart'; -/// 主安全措施列表组件,支持编辑与只读模式 +/// 主安全措施表格组件,支持编辑与只读模式 class MeasuresListWidget extends StatelessWidget { const MeasuresListWidget({ super.key, @@ -22,6 +19,8 @@ class MeasuresListWidget extends StatelessWidget { required this.baseImgPath, required this.isAllowEdit, this.onSign, + this.isShowSign = false, + }); /// 接口返回的原始 Map 列表 final List> measuresList; @@ -35,6 +34,8 @@ class MeasuresListWidget extends StatelessWidget { /// 签字按钮回调,回调当前行 item final void Function(Map item)? onSign; + /// 操作那里是否显示签名图片 + final bool isShowSign; @override @@ -128,17 +129,15 @@ class MeasuresListWidget extends StatelessWidget { isAllowEdit ? TextButton( onPressed: () { - if ((item['STATUS'] as String?) != '-1') { - onSign?.call(item); - } + onSign?.call(item); }, child: Text( (item['SIGN_ITEM'] ?? '').toString().isNotEmpty ? '已签字' : '签字', style: TextStyle( - color: (item['STATUS'] as String?) == '-1' - ? Colors.grey + color: (item['SIGN_ITEM'] ?? '').toString().isNotEmpty + ? Colors.grey.shade600 : Colors.blue, ), ), @@ -156,7 +155,7 @@ class MeasuresListWidget extends StatelessWidget { // 操作图片 if (item.containsKey('IMG_PATH') && - (item['IMG_PATH'] as String).isNotEmpty) + (item['IMG_PATH'] as String).isNotEmpty && isShowSign) ..._buildImageRows( context, (item['IMG_PATH'] as String).split(','), @@ -258,15 +257,10 @@ class MeasuresListWidget extends StatelessWidget { children: [ GestureDetector( onTap: () { - Navigator.of(context).push( - PageRouteBuilder( - opaque: false, - pageBuilder: (_, __, ___) => - SingleImageViewer(imageUrl: '$baseImgPath\$p'), - ), - ); + presentOpaque(SingleImageViewer(imageUrl: '$baseImgPath$p'), context); + }, - child: Image.network('$baseImgPath\$p', width: 80, height: 80), + child: Image.network('$baseImgPath$p', width: 80, height: 80), ), if (time.isNotEmpty) ...[const SizedBox(width: 8), Text(time)], ], @@ -276,7 +270,6 @@ class MeasuresListWidget extends StatelessWidget { } } - /// 其他安全防护措施表格组件 class OtherMeasuresWidget extends StatelessWidget { /// 其他安全防护措施数据列表 @@ -363,36 +356,71 @@ class OtherMeasuresWidget extends StatelessWidget { /// 处理 SIGN_PATH 可能是 List 或 String Widget _buildSignImages(BuildContext context, Map item) { - final dynamic raw = item['SIGN_PATH']; + final rawPath = item['SIGN_PATH']; List paths = []; - if (raw is String && raw.isNotEmpty) { - paths = [raw]; - } else if (raw is List) { - paths = raw.cast(); + if (rawPath is String && rawPath.isNotEmpty) { + paths = [rawPath]; + } else if (rawPath is List) { + paths = rawPath.whereType().toList(); } + + // 健壮判断:SIGN_TIME 可能不存在或类型非 String/List + List times = []; + if (item.containsKey('SIGN_TIME')) { + final rawTime = item['SIGN_TIME']; + if (rawTime is String && rawTime.isNotEmpty) { + times = [rawTime]; + } else if (rawTime is List) { + times = rawTime.whereType().toList(); + } + } + + // 没有图片,直接返回空 if (paths.isEmpty) return const SizedBox.shrink(); - return Wrap( - spacing: 8, - runSpacing: 8, - children: - paths.map((p) { - return GestureDetector( + + // 构造每一对“图片 + 时间” + final children = []; + for (var i = 0; i < paths.length; i++) { + final path = paths[i]; + // 时间对应同下标,越界则使用空字符串 + final timeLabel = (i < times.length) ? times[i] : ''; + + children.add( + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: - (_) => SingleImageViewer(imageUrl: '$baseImgPath$p'), - ), - ); + presentOpaque(SingleImageViewer(imageUrl: '$baseImgPath$path'), context); }, child: Image.network( - '$baseImgPath$p', + '$baseImgPath$path', width: 60, height: 60, - errorBuilder: (c, o, s) => const Icon(Icons.broken_image), + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => const Icon(Icons.broken_image), ), - ); - }).toList(), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + timeLabel, + style: const TextStyle(fontSize: 12, color: Colors.black54), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + if (i != paths.length - 1) { + children.add(const SizedBox(height: 8)); + } + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: children, ); } } @@ -568,14 +596,9 @@ class SignaturesListWidget extends StatelessWidget { children: [ GestureDetector( onTap: - () => Navigator.of(context).push( - MaterialPageRoute( - builder: - (_) => SingleImageViewer( - imageUrl: '$baseImgPath${signPaths[i]}', - ), - ), - ), + () => presentOpaque(SingleImageViewer( + imageUrl: '$baseImgPath${signPaths[i]}', + ), context), child: Image.network( '$baseImgPath${signPaths[i]}', width: 100, @@ -664,14 +687,9 @@ class SignaturesListWidget extends StatelessWidget { children: [ GestureDetector( onTap: - () => Navigator.of(context).push( - MaterialPageRoute( - builder: - (_) => SingleImageViewer( - imageUrl: '$baseImgPath${signPaths[i]}', - ), - ), - ), + () => presentOpaque(SingleImageViewer( + imageUrl: '$baseImgPath${signPaths[i]}', + ), context), child: Image.network( '$baseImgPath${signPaths[i]}', width: 100, @@ -714,7 +732,6 @@ class SelectionPopup extends StatefulWidget { @override _SelectionPopupState createState() => _SelectionPopupState(); } - class _SelectionPopupState extends State { late List> workList; late String selectedWorkType; @@ -986,3 +1003,5 @@ class _SelectionPopupState extends State { ); } } + + diff --git a/lib/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart b/lib/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart new file mode 100644 index 0000000..4508dde --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart @@ -0,0 +1,399 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import '../../../../../customWidget/ItemWidgetFactory.dart'; +import '../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../tools/tools.dart'; +import '../special_Wrok/MeasuresListWidget.dart'; +import 'WorkDetailFormWidget.dart'; + +class SpecialWorkFormBaseWork extends StatelessWidget { + /// 基础数据 + final Map pd; + + /// 安全防护措施列表 + final List> measuresList; + + /// 其他签字数据 + final Map signs; + + /// 图片前缀 + final String baseImgPath; + + /// 是否可编辑 + final bool isEditable; + + /// 回调:选择级别 + final VoidCallback onChooseLevel; + + /// 回调:选择动火作业人员 + final VoidCallback onChooseHotworkUser; + + /// 回调:分析按钮 + final VoidCallback onAnalyzeTap; + + const SpecialWorkFormBaseWork({ + Key? key, + required this.pd, + required this.measuresList, + required this.signs, + required this.baseImgPath, + this.isEditable = false, + required this.onChooseLevel, + required this.onChooseHotworkUser, + required this.onAnalyzeTap, + }) : super(key: key); + + Widget _itemContainer(Widget child) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8), + child: child, + ); + } + + Widget signItemWidget( + String signKey, + String nameKey, + String name, + BuildContext context, + ) { + return _itemContainer( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 标题行 + Padding( + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), + child: Row( + children: [ + Expanded( + child: ListItemFactory.headerTitle(name), + ), + if (FormUtils.hasValue(pd, nameKey)) + Text( + pd[nameKey]?.toString() ?? '', + style: const TextStyle(fontSize: 16), + ), + ], + ), + ), + const SizedBox(height: 8), + + if (FormUtils.hasValue(signs, signKey)) + ...((signs[signKey] as List) + .cast>() + .map((item) { + // 解析 SIGN_PATH + List signPaths = []; + final rawSP = FormUtils.hasValue(item, 'SIGN_PATH') ? item['SIGN_PATH'] : null; + if (rawSP is String && rawSP.isNotEmpty) { + signPaths = rawSP.split(',').map((s) => s.trim()).toList(); + } else if (rawSP is List) { + signPaths = rawSP.cast(); + } + + // 解析 SIGN_TIME 保留空格 + List signTimes = []; + final rawST = FormUtils.hasValue(item, 'SIGN_TIME') ? item['SIGN_TIME'] : null; + if (rawST is String && rawST.isNotEmpty) { + signTimes = rawST.split(','); + } else if (rawST is List) { + signTimes = rawST.cast(); + } + + final pairCount = math.min(signPaths.length, signTimes.length); + + // 解析 IMG_PATH, 最多 2 张 + List imgPaths = []; + final rawIP = FormUtils.hasValue(item, 'IMG_PATH') ? item['IMG_PATH'] : null; + if (rawIP is String && rawIP.isNotEmpty) { + imgPaths = rawIP.split(',').map((s) => s.trim()).take(2).toList(); + } else if (rawIP is List) { + imgPaths = rawIP.cast().take(2).toList(); + } + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (imgPaths.isNotEmpty) ...[ + Row( + children: imgPaths.map((p) { + final fullUrl = '$baseImgPath$p'; + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: ClipRRect( + borderRadius: BorderRadius.circular(2), + child: Image.network( + fullUrl, + width: 50, + height: 50, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => const Icon( + Icons.broken_image, + size: 40, + ), + ), + ), + ); + }).toList(), + ), + const SizedBox(height: 8), + ], + + // 签名及时间 + ...List.generate(pairCount, (index) { + final imgPath = signPaths[index]; + final timeLabel = signTimes[index]; + final fullUrl = '$baseImgPath$imgPath'; + const imageWidth = 200.0; + const imageHeight = 100.0; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () => presentOpaque( + SingleImageViewer(imageUrl: fullUrl), + context, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(4), + child: Image.network( + fullUrl, + width: imageWidth, + height: imageHeight, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => + const Icon(Icons.broken_image, size: 60), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: SizedBox( + height: imageHeight, + child: Align( + alignment: Alignment.bottomRight, + child: Text( + timeLabel, + style: const TextStyle( + fontSize: 14, + color: Colors.black87, + ), + ), + ), + ), + ), + ], + ), + if (index < pairCount - 1) + const Padding( + padding: EdgeInsets.only(top: 8.0), + child: Divider(height: 1), + ), + ], + ), + ); + }), + ], + ), + ); + }).toList()), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Column( + spacing: 12, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 1. 作业详情表单 + WorkDetailFormWidget( + pd: pd, + isEditable: isEditable, + onChooseLevel: onChooseLevel, + onChooseHotworkUser: onChooseHotworkUser, + onAnalyzeTap: onAnalyzeTap, + ), + + // 2. 安全防护措施 + if (measuresList.isNotEmpty) + _itemContainer( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListItemFactory.createBuildSimpleSection('安全防护措施'), + MeasuresListWidget( + measuresList: measuresList, + baseImgPath: baseImgPath, + isAllowEdit: isEditable, + isShowSign: true, + ), + ], + ), + ), + + // 3. 其他安全防护措施 + if (FormUtils.hasValue(signs, 'MEASURES_CONFIRM')) + _itemContainer( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListItemFactory.createBuildSimpleSection('其他安全防护措施'), + OtherMeasuresWidget( + otherMeasures: signs['MEASURES_CONFIRM'], + baseImgPath: baseImgPath, + ), + ], + ), + ), + + // 4. 各环节签字及意见 + if (FormUtils.hasValue(signs, 'GUARDIAN')) + signItemWidget('GUARDIAN', 'GUARDIAN_USER_NAME', '监护人', context), + + if (FormUtils.hasValue(signs, 'CONFESS')) + signItemWidget('CONFESS', 'CONFESS_USER_NAME', '安全交底人', context), + + if (FormUtils.hasValue(signs, 'ACCEPT_CONFESS')) + signItemWidget('ACCEPT_CONFESS', 'ACCEPT_CONFESS_USER_NAME', '接受交底人', context), + + // 作业负责人意见 + if (FormUtils.hasValue(signs, 'CONFIRM')) ...[ + Column( + children: [ + _itemContainer( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('作业负责人意见'),), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(signs['CONFIRM'][0]['DESCR'] ?? ''), + ), + ], + ), + ), + Divider(height: 1,), + signItemWidget('CONFIRM', 'CONFIRM_USER_NAME', '作业负责人', context), + ], + ) + + ], + + // 所在单位负责人意见 + if (FormUtils.hasValue(signs, 'LEADER')) ...[ + Column( + children: [ + _itemContainer( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('所在单位意见'),), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(signs['LEADER'][0]['DESCR'] ?? ''), + ), + ], + ), + ), + Divider(height: 1,), + signItemWidget('LEADER', 'LEADER_USER_NAME', '所在单位负责人', context), + ], + ) + ], + + // 安全管理部门负责人意见 + if (FormUtils.hasValue(signs, 'AUDIT')) ...[ + Column( + children: [ + _itemContainer( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('安全管理部门意见'),), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(signs['AUDIT'][0]['DESCR'] ?? ''), + ), + ], + ), + ), + Divider(height: 1,), + signItemWidget('AUDIT', 'AUDIT_USER_NAME', '安全管理部门负责人', context), + ], + ) + ], + + // 动火审批人意见 + if (FormUtils.hasValue(signs, 'APPROVE')) ...[ + Column( + children: [ + _itemContainer( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('动火审批人意见'),), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(signs['APPROVE'][0]['DESCR'] ?? ''), + ), + ], + ), + ), + Divider(height: 1,), + signItemWidget('APPROVE', 'APPROVE_USER_NAME', '动火审批人', context), + ], + ) + ], + + // 作业开始负责人签字 + if (FormUtils.hasValue(signs, 'MONITOR')) + signItemWidget('MONITOR', 'MONITOR_USER_NAME', '动火前在岗班长', context), + + // 作业开始负责人签字 + if (FormUtils.hasValue(signs, 'WORK_START')) + signItemWidget('WORK_START', 'WORK_START_USER_NAME', '作业开始负责人', context), + + // 作业结束负责人签字 + if (FormUtils.hasValue(signs, 'WORK_END')) + signItemWidget('WORK_END', 'WORK_END_USER_NAME', '作业结束负责人', context), + + // 完工验收意见和签字 + if (FormUtils.hasValue(signs, 'ACCEPT')) ...[ + Column( + children: [ + _itemContainer( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding(padding: EdgeInsets.symmetric(horizontal: 8), child: ListItemFactory.headerTitle('完工验收意见'),), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(signs['ACCEPT'][0]['DESCR'] ?? ''), + ), + ], + ), + ), + Divider(height: 1,), + signItemWidget('ACCEPT', 'ACCEPT_USER_NAME', '验收部门负责人', context), + ], + ) + ], + ], + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/WorkDetailFormWidget.dart b/lib/pages/home/tap/tabList/special_wrok/WorkDetailFormWidget.dart index c5e8a35..7d90a0d 100644 --- a/lib/pages/home/tap/tabList/special_wrok/WorkDetailFormWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/WorkDetailFormWidget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../../tools/tools.dart'; import '../../item_list_widget.dart'; -import '../special_Wrok/dh_work_detai/MeasuresListWidget.dart'; +import '../special_Wrok/MeasuresListWidget.dart'; /// 通用明细表单组件 /// 编辑模式时需传入对应的 TextEditingController;非编辑模式可不传 @@ -12,7 +12,6 @@ class WorkDetailFormWidget extends StatelessWidget { final VoidCallback onChooseLevel; final VoidCallback onChooseHotworkUser; final VoidCallback onAnalyzeTap; - /// 编辑模式下需提供以下控制器,非编辑可不传 final TextEditingController? contentController; final TextEditingController? locationController; diff --git a/lib/pages/home/tap/tabList/special_wrok/aqcs_work_detail/dangerous_options_page.dart b/lib/pages/home/tap/tabList/special_wrok/aqcs_work_detail/dangerous_options_page.dart index ad5cda6..889ae31 100644 --- a/lib/pages/home/tap/tabList/special_wrok/aqcs_work_detail/dangerous_options_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/aqcs_work_detail/dangerous_options_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/http/HttpManager.dart'; import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; @@ -19,8 +20,38 @@ import '../../../../../mine/mine_sign_page.dart'; /// 本地路径 + 线上路径模型 class ImageData { String localPath; - String? serverPath; - ImageData({required this.localPath, this.serverPath}); + String serverPath; + + ImageData({required this.localPath, required this.serverPath}); +} + +class SignImageData { + String SIGNER_TIME; + String? filePath; + int? key = 0; + + SignImageData({ + required this.SIGNER_TIME, + required this.filePath, + required this.key, + }); + + Map toJson() => { + 'SIGNER_TIME': SIGNER_TIME, + 'filePath': filePath, + 'key': key, + }; + + factory SignImageData.fromJson(Map json) { + return SignImageData( + SIGNER_TIME: json['SIGNER_TIME'] as String, + filePath: json['filePath'] as String?, + key: + json['key'] is int + ? json['key'] as int + : int.tryParse(json['key']?.toString() ?? ''), + ); + } } class DangerousOptionsPage extends StatefulWidget { @@ -28,7 +59,7 @@ class DangerousOptionsPage extends StatefulWidget { final int status; final String measures; final List imgList; - final List signImgList; + final List> signImgList; const DangerousOptionsPage({ Key? key, @@ -48,12 +79,10 @@ class _DangerousOptionsPageState extends State { late int status; late String measures; late List imgList; - late List signImgList; + late List signImgList; List signTimes = []; bool buttonLoading = false; - final Dio _dio = Dio(); - final _picker = ImagePicker(); @override void initState() { @@ -62,7 +91,8 @@ class _DangerousOptionsPageState extends State { status = widget.status; measures = widget.measures; imgList = List.from(widget.imgList); - signImgList = List.from(widget.signImgList); + signImgList = + widget.signImgList.map((map) => SignImageData.fromJson(map)).toList(); } /// 拍照或选图后的回调 @@ -87,29 +117,61 @@ class _DangerousOptionsPageState extends State { Future _submit() async { if (signImgList.isEmpty) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text('请签字'))); + ToastUtil.showNormal(context, '请签字'); return; } - await ApiService.saveDangerousOptionsFile(signImgList); + List filePaths = + signImgList.map((img) => img.filePath ?? '').toList(); + final result = await ApiService.saveDangerousOptionsFile(filePaths); + final List signList = result['FILE_PATH_LIST']; + List> sineImageList = []; + for (SignImageData data in signImgList) { + for (Map img in signList) { + String imgName = 'file${data.key}'; + if (data.filePath!.contains('uploadFiles')) { + final idata = { + 'filePath': data.filePath, + 'SIGNER_TIME': data.SIGNER_TIME, + 'key': data.key, + }; + sineImageList.add(idata); + } + if (imgName == img['key']) { + final idata = { + 'filePath': img['filePath'] ?? '', + 'SIGNER_TIME': data.SIGNER_TIME, + 'key': data.key, + }; + sineImageList.add(idata); + } + } + } setState(() => buttonLoading = true); Navigator.pop(context, { - 'imgList': imgList.map((e) => {'local': e.localPath, 'remote': e.serverPath}).toList(), - 'signImgList': signImgList, + 'imgList': + imgList + .map((e) => {'local': e.localPath, 'remote': e.serverPath}) + .toList(), + 'signImgList': sineImageList, 'index': index, 'status': status, }); } Future _sign() async { - final path = await Navigator.push( + final String path = await Navigator.push( context, MaterialPageRoute(builder: (c) => MineSignPage()), ); if (path != null) { final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); setState(() { - signImgList.add(path); + final imageData = SignImageData( + SIGNER_TIME: now, + filePath: path, + key: signImgList.length, + ); + signImgList.add(imageData); signTimes.add(now); }); FocusHelper.clearFocus(context); @@ -118,41 +180,57 @@ class _DangerousOptionsPageState extends State { Widget _signListWidget() { return Column( - children: signImgList.map((path) { - final idx = signImgList.indexOf(path); - return Column( - children: [ - const SizedBox(height: 10), - const Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: + signImgList.map((imgData) { + final idx = signImgList.indexOf(imgData); + return Column( children: [ - GestureDetector( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 200, maxHeight: 150), - child: Image.file(File(path), fit: BoxFit.contain), - ), - onTap: () => presentOpaque(SingleImageViewer(imageUrl: path), context), - ), - Column( + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - CustomButton( - text: '删除', - height: 30, - padding: const EdgeInsets.symmetric(horizontal: 10), - backgroundColor: Colors.red, - onPressed: () { - setState(() => signImgList.removeAt(idx)); - }, + GestureDetector( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 230, + maxHeight: 150, + ), + child: + (imgData.filePath ?? '').contains('uploadFiles') + ? Image.network( + '${ApiService.baseImgPath}${imgData.filePath}', + ) + : Image.file( + File(imgData.filePath ?? ''), + fit: BoxFit.contain, + ), + ), + onTap: + () => presentOpaque( + SingleImageViewer(imageUrl: imgData.filePath ?? ''), + context, + ), + ), + Column( + children: [ + CustomButton( + text: '删除', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() => signImgList.removeAt(idx)); + }, + ), + const SizedBox(height: 80), + ], ), - const SizedBox(height: 80), ], ), ], - ), - ], - ); - }).toList(), + ); + }).toList(), ); } @@ -177,13 +255,19 @@ class _DangerousOptionsPageState extends State { Padding( padding: EdgeInsets.all(10), child: Center( - child: Text('主要安全措施', style: TextStyle(fontWeight: FontWeight.bold)), + child: Text( + '主要安全措施', + style: TextStyle(fontWeight: FontWeight.bold), + ), ), ), Padding( padding: EdgeInsets.all(10), child: Center( - child: Text('操作', style: TextStyle(fontWeight: FontWeight.bold)), + child: Text( + '操作', + style: TextStyle(fontWeight: FontWeight.bold), + ), ), ), ], @@ -191,7 +275,10 @@ class _DangerousOptionsPageState extends State { TableRow( children: [ Padding( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 5), + padding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 5, + ), child: Text(measures), ), Padding( @@ -202,16 +289,28 @@ class _DangerousOptionsPageState extends State { value: -1, groupValue: status, title: Text('不涉及'), - contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 8.0), - visualDensity: VisualDensity(vertical: -4, horizontal: 0), + contentPadding: const EdgeInsets.symmetric( + vertical: 0, + horizontal: 8.0, + ), + visualDensity: VisualDensity( + vertical: -4, + horizontal: 0, + ), onChanged: (v) => setState(() => status = v!), ), RadioListTile( value: 1, groupValue: status, title: Text('涉及'), - contentPadding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0), - visualDensity: VisualDensity(vertical: -4, horizontal: 0), + contentPadding: const EdgeInsets.symmetric( + vertical: 4.0, + horizontal: 8.0, + ), + visualDensity: VisualDensity( + vertical: -4, + horizontal: 0, + ), onChanged: (v) => setState(() => status = v!), ), ], @@ -226,7 +325,10 @@ class _DangerousOptionsPageState extends State { title: '上传图片', maxCount: 2, mediaType: MediaType.image, - initialMediaPaths: imgList.map((e) => e.localPath).toList(), + initialMediaPaths: + imgList + .map((e) => '${ApiService.baseImgPath}${e.serverPath}') + .toList(), onChanged: (paths) {}, onMediaAdded: _onImageAdded, onMediaRemoved: (path) { diff --git a/lib/pages/home/tap/tabList/special_wrok/aqcs_work_detail/hotwork_safe_func_sure.dart b/lib/pages/home/tap/tabList/special_wrok/aqcs_work_detail/hotwork_safe_func_sure.dart index 1a38d7d..204d1b2 100644 --- a/lib/pages/home/tap/tabList/special_wrok/aqcs_work_detail/hotwork_safe_func_sure.dart +++ b/lib/pages/home/tap/tabList/special_wrok/aqcs_work_detail/hotwork_safe_func_sure.dart @@ -16,7 +16,7 @@ import '../../../../../../customWidget/single_image_viewer.dart'; import '../../../../../../http/ApiService.dart'; import '../../../../../mine/mine_sign_page.dart'; import '../../../../../my_appbar.dart'; -import '../../special_Wrok/dh_work_detai/MeasuresListWidget.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; import '../WorkDetailFormWidget.dart'; @@ -52,7 +52,6 @@ class _HotworkSafeFuncSureState extends State { late List workUserList = []; /// 安全防护措施列表 - late List measuresListCopy = []; List imagePaths = []; List signTimes = []; // 签字时间列表 @override @@ -60,13 +59,6 @@ class _HotworkSafeFuncSureState extends State { super.initState(); _getData(); _getHotWorkNameList(); - addMeasuresListCopy(); - } - - String measuresListToJson() { - final List> jsonList = - measuresListCopy.map((item) => item.toJson()).toList(); - return jsonEncode(jsonList); } /// 弹出单位选择 @@ -100,42 +92,11 @@ class _HotworkSafeFuncSureState extends State { }); } - /// 弹出人员选择,需先选择单位 - void choosePersonHandle(MeasureItem item) async { - String unitId = item.DEPARTMENT_ID; - final personList = item.userList; - if (!unitId.isNotEmpty) { - ToastUtil.showNormal(context, '请先选择确认单位'); - return; - } - if (personList.isEmpty) { - // 一般这种情况是因为重新编辑没有缓存对应部门的负责人,所以先拉取一下接口 - await _getPersonListForUnitId(item); - final list = item.userList; - if (list.isEmpty) { - // 如果还是没数据,说明该部门没有可选的人 - ToastUtil.showNormal(context, '暂无数据,请选择其他单位'); - } else { - choosePersonHandle(item); - } - return; - } - DepartmentPersonPicker.show( - context, - personsData: personList, - onSelected: (userId, name) { - setState(() { - item.USER_NAME = name; - item.USER_ID = userId; - print(json.encode(measuresListCopy)); - }); - }, - ).then((_) {}); - } /// 签字 Future _sign() async { + FocusHelper.clearFocus(context); final path = await Navigator.push( context, MaterialPageRoute(builder: (context) => MineSignPage()), @@ -208,49 +169,47 @@ class _HotworkSafeFuncSureState extends State { }).toList(), ); } + bool _validateAndProceed(BuildContext context) { + for (var i = 0; i < measuresList.length; i++) { + final m = measuresList[i]; - /// 提交 1 提交 0暂存 + // 1. 未签字 + if (!FormUtils.hasValue(m, 'SIGN_ITEM')) { + ToastUtil.showNormal(context, '第${i + 1}项未签字'); + return false; + } + + // 只有 STATUS == '1' 时才检查后续 ANSWER + if (m['STATUS'] == '1') { + for (var j = 1; j <= 4; j++) { + if (FormUtils.hasValue(m, 'QUESTION${j}') + ) { + if (!FormUtils.hasValue(m, 'ANSWER${j}') + || (m['ANSWER${j}'] as String?)!.length == 0) { + ToastUtil.showNormal(context, '第${i + 1}项未填写第${j}项'); + return false; + } + } + } + } + } + return true; + } + /// 提交 1 提交 0暂存 Future _submit(String status) async { + if (_otherController.text.trim().isEmpty) { + ToastUtil.showNormal(context, '请输入其他安全措施,没有请填“无”'); + return; + } if (imagePaths.isEmpty) { ToastUtil.showNormal(context, '请签字'); return; } - List> signers = []; String reasonText = ''; if (status == '1') { - int index = 0; - for (var item in measuresListCopy) { - if (item.USER_ID.isEmpty) { - ToastUtil.showNormal( - context, - '第${index + 1}项未设置确认人', - ); - return; - } - if (item.selectMeasures.isEmpty) { - ToastUtil.showNormal( - context, - '第${index + 1}项未选择安全措施', - ); - return; - } - final userId = item.USER_ID; - final selectMeasures = item.selectMeasures as List? ?? []; - - for (var item in selectMeasures) { - signers.add({ - 'BUS_HOTWORK_MEASURES_ID': item['BUS_HOTWORK_MEASURES_ID'], - 'USER_ID': userId, - }); - } - // 检查长度是否一致 - if (signers.length != measuresList.length) { - // 使用 ScaffoldMessenger 弹出提示 - ToastUtil.showNormal(context, '请为每个安全措施选择确认人'); - return; - } - index++; + if (!_validateAndProceed(context)) { + return; } } else { await showDialog( @@ -273,15 +232,15 @@ class _HotworkSafeFuncSureState extends State { return; } } - final Map formData = {}; // 提交参数 + formData['APPLY_STATUS'] = status; formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['DESCR'] = _otherController.text.trim().isEmpty ? _otherController.text.trim() : "无"; formData['SIGNTIME'] = signTimes.join(','); formData['USER_ID'] = SessionService.instance.loginUserId; - formData['APPLY_STATUS'] = status; formData['STEP_REASON'] = reasonText; - formData['PREPARERS'] = json.encode(signers); + formData['MEASURES'] = json.encode(measuresList); await showDialog( context: context, @@ -327,8 +286,6 @@ class _HotworkSafeFuncSureState extends State { final result = await ApiService.getHotWorkNameList(); setState(() { workUserList = result['varList'] ?? ''; - List names = - workUserList.map((item) => item['NAME'] as String).toList(); }); } @@ -350,42 +307,20 @@ class _HotworkSafeFuncSureState extends State { }); } - void removeMeasuresListCopy(int index) { - setState(() { - measuresListCopy.removeAt(index); - }); - } - - void addMeasuresListCopy() { - setState(() { - measuresListCopy.add( - MeasureItem( - id: Random().nextDouble(), - DEPARTMENT_ID: '', - DEPARTMENT_NAME: '', - USER_ID: '', - USER_NAME: '', - userList: [], - userIndex: -1, - selectMeasures: [], - ), - ); - }); - } Future _itemToSign(Map measures, int index) async { // 签名图片列表 - final List signImgList = measures['SIGN_ITEM'] ?? []; + final List> signImgList = measures['SIGN_ITEM'] ?? []; // 操作图片路径列表 final List imgList = []; if ((measures['IMG_PATH'] as String?)?.isNotEmpty ?? false) { for (var path in (measures['IMG_PATH'] as String).split(',')) { - - imgList.add(ImageData(localPath: path)); + imgList.add(ImageData(localPath:path, serverPath: path)); } } - final result = await pushPage(DangerousOptionsPage(index: index,status: measures['STATUS'] ?? 1, + //measures['STATUS'] + final result = await pushPage(DangerousOptionsPage(index: index,status: int.parse(FormUtils.hasValue(measures, 'STATUS') ? measures['STATUS'] : '1'), measures: measures['PROTECTIVE_MEASURES'] ?? '', imgList: imgList, signImgList: signImgList), context); @@ -393,18 +328,28 @@ class _HotworkSafeFuncSureState extends State { setState(() { // 更新返回的图片列表(local + remote) final returned = result['imgList'] as List; - measures['IMG_PATH'] = returned.map((m) { + // 先映射成 ImageData 列表 + final imgDataList = returned.map((m) { return ImageData( localPath: m['local'] as String, - serverPath: m['remote'] as String?, + serverPath: m['remote'] as String, ); }).toList(); - // 更新签字列表 - measures['SIGN_ITEM'] = List.from(result['signImgList'] as List); + // 抽取所有非空的 serverPath 并拼成逗号分隔的字符串 + final serverPathString = imgDataList + .map((e) => e.serverPath) + .where((s) => s.isNotEmpty) + .map((s) => s) // 将 String? 转回 String + .join(','); + // 存回 measures + measures['IMG_PATH'] = serverPathString; + + // 更新签字列表 + measures['SIGN_ITEM'] = List>.from(result['signImgList'] as List); // 更新状态、序号等 - measures['STATUS'] = result['status']; + measures['STATUS'] = result['status'].toString(); index = result['index'] as int; }); } diff --git a/lib/pages/home/tap/tabList/special_wrok/aqgl_work_detail/hotwork_aqgl_detail.dart b/lib/pages/home/tap/tabList/special_wrok/aqgl_work_detail/hotwork_aqgl_detail.dart new file mode 100644 index 0000000..9329ae9 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/aqgl_work_detail/hotwork_aqgl_detail.dart @@ -0,0 +1,335 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; + +/// 安全管理部门意见 +class HotworkAqglDetail extends StatefulWidget { + const HotworkAqglDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkAqglDetailState(); +} + +class _HotworkAqglDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + final TextEditingController _contentController = TextEditingController(); + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + String DESCR = _contentController.text.trim(); + + if (DESCR.isEmpty) { + ToastUtil.showNormal(context, '请输入安全管理部门意见'); + return; + } + if (status == '1') { + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + formData['DESCR'] = DESCR; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '安全管理部门意见'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + // _setSafeDetailWidget(), + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + ItemListWidget.itemContainer( + Column( + children: [ + ListItemFactory.createBuildMultilineInput( + '安全管理部门意见', + '请输入意见', + _contentController, + ), + Divider(), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('安全管理部门负责人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ) + ], + ), + ), + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/aqjd_work_detail/hotwork_aqjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/aqjd_work_detail/hotwork_aqjd_detail.dart new file mode 100644 index 0000000..c11ecf8 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/aqjd_work_detail/hotwork_aqjd_detail.dart @@ -0,0 +1,370 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; + +/// 本地路径 + 线上路径模型 +class ImageData { + String localPath; + String serverPath; + + ImageData({required this.localPath, required this.serverPath}); +} + +/// 安全措施确认人确认 +class HotworkAqjdDetail extends StatefulWidget { + const HotworkAqjdDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkAqjdDetailState(); +} + +class _HotworkAqjdDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + late List imgList = []; + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 拍照或选图后的回调 + Future _onImageAdded(String localPath) async { + // 上传到服务器 + final res = await ApiService.uploadSaveFile(localPath); + final url = res['FILE_PATH'] as String; + setState(() { + imgList.add(ImageData(localPath: localPath, serverPath: url)); + }); + } + + /// 删除图片处理:调用删除接口并更新列表 + Future _onImageRemoved(ImageData item) async { + if (item.serverPath != null) { + await ApiService.deleteSaveFile(item.serverPath!); + } + setState(() { + imgList.remove(item); + }); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + + if (status == '1') { + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } +// 抽取所有非空的 serverPath 并拼成逗号分隔的字符串 + final serverPathString = imgList + .map((e) => e.serverPath) + .where((s) => s.isNotEmpty) + .map((s) => s) // 将 String? 转回 String + .join(','); + + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + formData['IMG_PATH'] = serverPathString; + + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '安全交底人签字确认'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + // _setSafeDetailWidget(), + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + ItemListWidget.itemContainer(Column( + children: [RepairedPhotoSection( + title: '上传图片', + maxCount: 2, + mediaType: MediaType.image, + onChanged: (paths) {}, + onMediaAdded: _onImageAdded, + onMediaRemoved: (path) { + final item = imgList.firstWhere((e) => e.localPath == path); + _onImageRemoved(item); + }, + onAiIdentify: () {}, + ), + ItemListWidget.itemContainer( + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('安全交底人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ), + ),], + ),), + + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/dbbz_work_detail/hotwork_dbbz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dbbz_work_detail/hotwork_dbbz_detail.dart new file mode 100644 index 0000000..1f6efde --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/dbbz_work_detail/hotwork_dbbz_detail.dart @@ -0,0 +1,335 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; + +/// 班长验票 +class HotworkDbbzDetail extends StatefulWidget { + const HotworkDbbzDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkDbbzDetailState(); +} + +class _HotworkDbbzDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + final TextEditingController _contentController = TextEditingController(); + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + String DESCR = _contentController.text.trim(); + + if (DESCR.isEmpty) { + ToastUtil.showNormal(context, '请输入安全管理部门意见'); + return; + } + if (status == '1') { + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + formData['DESCR'] = DESCR; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '班长验票'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + // _setSafeDetailWidget(), + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + ItemListWidget.itemContainer( + Column( + children: [ + ListItemFactory.createBuildMultilineInput( + '动火前在岗班长意见', + '请输入意见', + _contentController, + ), + Divider(), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('动火前在岗班长'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ) + ], + ), + ), + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_apply_detail.dart index 73ff3f2..5013236 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_apply_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/hotwork_apply_detail.dart @@ -14,7 +14,7 @@ import '../../../../../../customWidget/bottom_picker.dart'; import '../../../../../../http/ApiService.dart'; import '../../../../../my_appbar.dart'; import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; -import 'MeasuresListWidget.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; enum EditUserType { ANALYZE('分析单位', '分析单位负责人'), diff --git a/lib/pages/home/tap/tabList/special_wrok/dhsp_work_detail/hotwork_dhsp_detail.dart b/lib/pages/home/tap/tabList/special_wrok/dhsp_work_detail/hotwork_dhsp_detail.dart new file mode 100644 index 0000000..3d8088e --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/dhsp_work_detail/hotwork_dhsp_detail.dart @@ -0,0 +1,335 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; + +/// 审批部门意见 +class HotworkDhspDetail extends StatefulWidget { + const HotworkDhspDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkDhspDetailState(); +} + +class _HotworkDhspDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + final TextEditingController _contentController = TextEditingController(); + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + String DESCR = _contentController.text.trim(); + + if (DESCR.isEmpty) { + ToastUtil.showNormal(context, '请输入安全管理部门意见'); + return; + } + if (status == '1') { + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + formData['DESCR'] = DESCR; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '审批部门意见'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + // _setSafeDetailWidget(), + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + ItemListWidget.itemContainer( + Column( + children: [ + ListItemFactory.createBuildMultilineInput( + '动火审批部门意见', + '请输入意见', + _contentController, + ), + Divider(), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('动火审批人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ) + ], + ), + ), + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/jhr_work_detail/hotwork_jhr_detail.dart b/lib/pages/home/tap/tabList/special_wrok/jhr_work_detail/hotwork_jhr_detail.dart new file mode 100644 index 0000000..fdbc40d --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/jhr_work_detail/hotwork_jhr_detail.dart @@ -0,0 +1,358 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; +/// 监护人签字 +class HotworkJhrDetail extends StatefulWidget { + const HotworkJhrDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkJhrDetailState(); +} + +class _HotworkJhrDetailState extends State { + late bool isEditable = false; + /// 详情 + late Map pd = {}; + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + + List imagePaths = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + imagePaths.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + imagePaths.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + imagePaths.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (imagePaths.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + + if (status == '1') { + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + imagePaths, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess( + context, + '保存成功', + ); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 其他安全防护措施 + Widget _setSafeDetailWidget() { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + padding: EdgeInsets.symmetric(horizontal: 5), + child: Column( + children: [ + if (measuresList.isNotEmpty) + Column( + children: [ + SizedBox(height: 5), + ListItemFactory.createBuildSimpleSection('安全防护措施'), + Container( + color: Colors.white, + child: MeasuresListWidget( + measuresList: + measuresList, // List> + baseImgPath: ApiService.baseImgPath, + isAllowEdit: false, + isShowSign:true, + ), + ), + ], + ), + if (FormUtils.hasValue(signs, 'MEASURES_CONFIRM')) + Column( + children: [ + SizedBox(height: 20), + ListItemFactory.createBuildSimpleSection('其他安全防护措施'), + Container( + color: Colors.white, + child: OtherMeasuresWidget( + otherMeasures: signs['MEASURES_CONFIRM'], + baseImgPath: ApiService.baseImgPath, + ), + ), + ], + ), + SizedBox(height: 20), + + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.createBuildSimpleSection('监护人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + SizedBox(height: 10), + if (imagePaths.isNotEmpty) _signListWidget(), + ], + ), + ); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '监护人签字确认'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + children: [ + // _card(_defaultDetail()), + WorkDetailFormWidget( + pd: pd, + isEditable: false, + onChooseLevel: (){}, + onChooseHotworkUser: (){}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + SizedBox(height: 20), + _setSafeDetailWidget(), + SizedBox(height: 20), + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/jsjd_work_detail/hotwork_jsjd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/jsjd_work_detail/hotwork_jsjd_detail.dart new file mode 100644 index 0000000..e873af8 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/jsjd_work_detail/hotwork_jsjd_detail.dart @@ -0,0 +1,317 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; +/// 接受交底人签字 +class HotworkJsjdDetail extends StatefulWidget { + const HotworkJsjdDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkJsjdDetailState(); +} + +class _HotworkJsjdDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + + if (status == '1') { + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '接受交底人签字确认'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + // _setSafeDetailWidget(), + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + ItemListWidget.itemContainer( + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('接受交底人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ), + ), + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/jszy_work_detail/hotwork_jszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/jszy_work_detail/hotwork_jszy_detail.dart new file mode 100644 index 0000000..98288d6 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/jszy_work_detail/hotwork_jszy_detail.dart @@ -0,0 +1,378 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/picker/CupertinoDatePicker.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; + +/// 作业结束时间选择 +class HotworkJszyDetail extends StatefulWidget { + const HotworkJszyDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkJszyDetailState(); +} + +class _HotworkJszyDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + /// 选择的时间 + String endTime = ''; + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + bool checkWorkTime(Map pd, BuildContext context) { + // 解析开始和结束时间 + final start = DateTime.parse(pd['WORK_START_DATE'] as String); + final end = DateTime.parse(endTime); + + // 校验:结束时间必须晚于开始时间 + if (end.isAtSameMomentAs(start) || end.isBefore(start)) { + ToastUtil.showNormal(context, '作业开始时间不能晚于或等于结束时间,请重新选择'); + + return false; + } + + // 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms) + if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') { + final diffMs = end.difference(start).inMilliseconds; + const max8h = 8 * 60 * 60 * 1000; + if (diffMs >= max8h) { + ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择'); + + return false; + } + } + + // 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms) + if (pd['WORK_LEVEL'] == '二级') { + final diffMs = end.difference(start).inMilliseconds; + const max72h = 72 * 60 * 60 * 1000; + if (diffMs >= max72h) { + ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择'); + return false; + } + } + return true; + } + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + if (status == '1') { + if (endTime.isEmpty) { + ToastUtil.showNormal(context, '请选择作业结束时间'); + return; + } + if (!checkWorkTime(pd, context)) { + return; + } + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + formData['WORK_END_DATE'] = endTime; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '作业结束时间选择'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + // _setSafeDetailWidget(), + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + ItemListWidget.itemContainer( + Column( + children: [ + GestureDetector( + child: ListItemFactory.createRowSpaceBetweenItem(leftText: '作业结束时间', rightText: endTime.isEmpty?'请选择':endTime, isRight: true), + onTap: () async { + DateTime? picked = await BottomDateTimePicker.showDate(context); + if (picked != null) { + setState(() { + endTime = DateFormat('yyyy-MM-dd HH:mm').format(picked); + }); + } + }, + ), + + Divider(), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('作业结束时间负责人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ), + ], + ), + ), + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/kszy_work_detail/hotwork_kszy_detail.dart b/lib/pages/home/tap/tabList/special_wrok/kszy_work_detail/hotwork_kszy_detail.dart new file mode 100644 index 0000000..84e3fc7 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/kszy_work_detail/hotwork_kszy_detail.dart @@ -0,0 +1,351 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/picker/CupertinoDatePicker.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; + +/// 作业开始时间选择 +class HotworkKszyDetail extends StatefulWidget { + const HotworkKszyDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkKszyDetailState(); +} + +class _HotworkKszyDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + /// 选择的时间 + String startTime = ''; + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + if (status == '1') { + if (startTime.isEmpty) { + ToastUtil.showNormal(context, '请选择作业开始时间'); + return; + } + final workStart = DateTime.parse(pd['WORK_START_DATE'] as String); + final analyzeTime = DateTime.parse(pd['ANALYZE_TIME'] as String); + final intervalMs = workStart.difference(analyzeTime).inMilliseconds; + const thresholdMs = 30 * 60 * 1000; + + if (intervalMs >= thresholdMs) { + ToastUtil.showNormal(context, '气体分析时间与开始时间间隔超过30分钟,请重新上传气体检测结果'); + return; + } + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + formData['WORK_START_DATE'] = startTime; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '作业开始时间选择'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + // _setSafeDetailWidget(), + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + ItemListWidget.itemContainer( + Column( + children: [ + GestureDetector( + child: ListItemFactory.createRowSpaceBetweenItem(leftText: '作业开始时间', rightText: startTime.isEmpty?'请选择':startTime, isRight: true), + onTap: () async { + DateTime? picked = await BottomDateTimePicker.showDate(context); + if (picked != null) { + setState(() { + startTime = DateFormat('yyyy-MM-dd HH:mm').format(picked); + }); + } + }, + ), + + Divider(), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('作业开始时间负责人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ), + ], + ), + ), + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/special_work_list_page.dart b/lib/pages/home/tap/tabList/special_wrok/special_work_list_page.dart index cce2cf0..88a070c 100644 --- a/lib/pages/home/tap/tabList/special_wrok/special_work_list_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/special_work_list_page.dart @@ -2,7 +2,17 @@ import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/dh_work_detai/hotwork_apply_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/aqcs_work_detail/hotwork_safe_func_sure.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/aqgl_work_detail/hotwork_aqgl_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/aqjd_work_detail/hotwork_aqjd_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dbbz_work_detail/hotwork_dbbz_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dhsp_work_detail/hotwork_dhsp_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/jhr_work_detail/hotwork_jhr_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/jszy_work_detail/hotwork_jszy_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/kszy_work_detail/hotwork_kszy_detail.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/szaq_work_detail/hotwork_set_safe_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/szdw_work_detail/hotwork_szdw_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/ysgd/hotwork_ysgd_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/zyfz_work_detail/hotwork_zyfz_detail.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; import '../../../../../customWidget/bottom_picker.dart'; @@ -10,6 +20,7 @@ import '../../../../../customWidget/custom_button.dart'; import '../../../../../customWidget/search_bar_widget.dart'; import '../../../../../http/ApiService.dart'; import '../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import 'jsjd_work_detail/hotwork_jsjd_detail.dart'; class SpecialWorkListPage extends StatefulWidget { final String flow; @@ -191,7 +202,7 @@ class _SpecialWorkListPageState extends State { _fetchData(); break; case '气体检测': - pushPage(HotworkGasList(HOTWORK_ID: item['HOTWORK_ID'], addFlag:true), context); + await pushPage(HotworkGasList(HOTWORK_ID: item['HOTWORK_ID'], addFlag:true), context); break; case '设置安全措施确认人': await pushPage(HotworkSetSafeDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); @@ -201,46 +212,70 @@ class _SpecialWorkListPageState extends State { break; case '监护人签字': routeName = '/hotwork-guardian-detail'; + await pushPage(HotworkJhrDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); break; case '安全交底人签字': routeName = '/hotwork-confess-detail'; + await pushPage(HotworkAqjdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; case '接受交底人签字': routeName = '/hotwork-acceptconfess-detail'; + await pushPage(HotworkJsjdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; case '作业负责人签字': routeName = '/hotwork-confirm-detail'; + await pushPage(HotworkZyfzDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; case '所在单位签字': routeName = '/hotwork-leader-detail'; + await pushPage(HotworkSzdwDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; case '安全管理部门签字': routeName = '/hotwork-audit-detail'; + await pushPage(HotworkAqglDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; case '审批人签字': routeName = '/hotwork-approve-detail'; + await pushPage(HotworkDhspDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; case '当班班长验票': routeName = '/hotwork-monitor-detail'; + await pushPage(HotworkDbbzDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; case '开始作业': routeName = '/hotwork-startwork-detail'; + await pushPage(HotworkKszyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; case '结束作业': routeName = '/hotwork-endwork-detail'; + await pushPage(HotworkJszyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; case '验收签字': routeName = '/hotwork-accept-detail'; + await pushPage(HotworkYsgdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context); + break; default: routeName = '/hotwork-detail'; } - - Navigator.pushNamed( - context, - routeName, - arguments: {'HOTWORK_ID': item['HOTWORK_ID'], 'flow': widget.flow}, - ); + setState(() { + _fetchSteps(); + _fetchData(); + }); + // Navigator.pushNamed( + // context, + // routeName, + // arguments: {'HOTWORK_ID': item['HOTWORK_ID'], 'flow': widget.flow}, + // ); } Widget _buildFlowStepItem({ diff --git a/lib/pages/home/tap/tabList/special_wrok/szaq_work_detail/hotwork_set_safe_detail.dart b/lib/pages/home/tap/tabList/special_wrok/szaq_work_detail/hotwork_set_safe_detail.dart index 50dbbf5..a0ca773 100644 --- a/lib/pages/home/tap/tabList/special_wrok/szaq_work_detail/hotwork_set_safe_detail.dart +++ b/lib/pages/home/tap/tabList/special_wrok/szaq_work_detail/hotwork_set_safe_detail.dart @@ -11,13 +11,11 @@ import 'package:qhd_prevention/customWidget/department_picker.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; import 'package:qhd_prevention/tools/tools.dart'; -import '../../../../../../customWidget/bottom_picker.dart'; import '../../../../../../customWidget/custom_alert_dialog.dart'; import '../../../../../../customWidget/single_image_viewer.dart'; import '../../../../../../http/ApiService.dart'; import '../../../../../mine/mine_sign_page.dart'; import '../../../../../my_appbar.dart'; -import '../../special_Wrok/dh_work_detai/MeasuresListWidget.dart'; import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; import '../WorkDetailFormWidget.dart'; import 'SafeFunctionDialog.dart'; @@ -38,18 +36,11 @@ class HotworkSetSafeDetail extends StatefulWidget { class _HotworkSetSafeDetailState extends State { late bool isEditable = false; - final levelList = ["特级", "一级", "二级"]; - - /// 编辑还是新增 - late String msg = 'add'; /// 详情 late Map pd = {}; late List> measuresList = []; - /// 动火人及证书编号 - late List workUserList = []; - /// 安全防护措施列表 late List measuresListCopy = []; List imagePaths = []; @@ -58,7 +49,6 @@ class _HotworkSetSafeDetailState extends State { void initState() { super.initState(); _getData(); - _getHotWorkNameList(); addMeasuresListCopy(); } @@ -339,7 +329,7 @@ class _HotworkSetSafeDetailState extends State { if (result['result'] == 'success') { ToastUtil.showSuccess( context, - status == '1' ? '提交成功' : '已暂存', + '保存成功', ); Navigator.pop(context); } @@ -359,15 +349,6 @@ class _HotworkSetSafeDetailState extends State { } } - Future _getHotWorkNameList() async { - final result = await ApiService.getHotWorkNameList(); - setState(() { - workUserList = result['varList'] ?? ''; - List names = - workUserList.map((item) => item['NAME'] as String).toList(); - }); - } - /// 初始化拉取数据 Future _getData() async { final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); diff --git a/lib/pages/home/tap/tabList/special_wrok/szdw_work_detail/hotwork_szdw_detail.dart b/lib/pages/home/tap/tabList/special_wrok/szdw_work_detail/hotwork_szdw_detail.dart new file mode 100644 index 0000000..ee1facd --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/szdw_work_detail/hotwork_szdw_detail.dart @@ -0,0 +1,335 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; + +/// 所在单位意见 +class HotworkSzdwDetail extends StatefulWidget { + const HotworkSzdwDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkSzdwDetailState(); +} + +class _HotworkSzdwDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + final TextEditingController _contentController = TextEditingController(); + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + String DESCR = _contentController.text.trim(); + + if (DESCR.isEmpty) { + ToastUtil.showNormal(context, '请输入所在单位负责人意见'); + return; + } + if (status == '1') { + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + formData['DESCR'] = DESCR; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '所在单位意见'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + // _setSafeDetailWidget(), + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + ItemListWidget.itemContainer( + Column( + children: [ + ListItemFactory.createBuildMultilineInput( + '所在单位负责人意见', + '请输入意见', + _contentController, + ), + Divider(), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('所在单位负责人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ) + ], + ), + ), + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/ysgd/hotwork_ysgd_detail.dart b/lib/pages/home/tap/tabList/special_wrok/ysgd/hotwork_ysgd_detail.dart new file mode 100644 index 0000000..ed3519e --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/ysgd/hotwork_ysgd_detail.dart @@ -0,0 +1,418 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/picker/CupertinoDatePicker.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; + +/// 完工验收 +class ImageData { + String localPath; + String serverPath; + + ImageData({required this.localPath, required this.serverPath}); +} + +class HotworkYsgdDetail extends StatefulWidget { + const HotworkYsgdDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkYsgdDetailState(); +} + +class _HotworkYsgdDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + /// 选择的时间 + String startTime = ''; + /// 验收意见 + final TextEditingController _contentController = TextEditingController(); + late List imgList = []; + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + /// 拍照或选图后的回调 + Future _onImageAdded(String localPath) async { + // 上传到服务器 + final res = await ApiService.uploadSaveFile(localPath); + final url = res['FILE_PATH'] as String; + setState(() { + imgList.add(ImageData(localPath: localPath, serverPath: url)); + }); + } + + /// 删除图片处理:调用删除接口并更新列表 + Future _onImageRemoved(ImageData item) async { + if (item.serverPath != null) { + await ApiService.deleteSaveFile(item.serverPath!); + } + setState(() { + imgList.remove(item); + }); + } + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String DESCR = _contentController.text.trim(); + + if (DESCR.isEmpty) { + ToastUtil.showNormal(context, '请输入负责人意见'); + return; + } + String reasonText = ''; + if (status == '1') { + if (startTime.isEmpty) { + ToastUtil.showNormal(context, '请选择验收时间'); + return; + } + // final workStart = DateTime.parse(pd['WORK_START_DATE'] as String); + final endTime = DateTime.parse(pd['WORK_END_DATE'] as String); + final acceptTime = DateTime.parse(startTime); + + final intervalMs = endTime.difference(acceptTime).inMilliseconds; + const thresholdMs = 30 * 60 * 1000; + + if (intervalMs >= thresholdMs) { + ToastUtil.showNormal(context, '请在作业结束30分钟内完成验收'); + return; + } + if (imgList.isNotEmpty) { + ToastUtil.showNormal(context, '请上传验收视频'); + + return; + } + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + final serverPathString = imgList + .map((e) => e.serverPath) + .where((s) => s.isNotEmpty) + .map((s) => s) // 将 String? 转回 String + .join(','); + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + formData['ACCEPT_TIME'] = startTime; + formData['DESCR'] = DESCR; + formData['VIDEO_PATH'] = serverPathString; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '完工验收'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + // _setSafeDetailWidget(), + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + + ItemListWidget.itemContainer( + Column( + children: [ + ListItemFactory.createBuildMultilineInput( + '完工验收', + '请输入意见', + _contentController, + ), + Divider(), + GestureDetector( + child: ListItemFactory.createRowSpaceBetweenItem(leftText: '验收时间', rightText: startTime.isEmpty?'请选择':startTime, isRight: true), + onTap: () async { + DateTime? picked = await BottomDateTimePicker.showDate(context); + if (picked != null) { + setState(() { + startTime = DateFormat('yyyy-MM-dd HH:mm').format(picked); + }); + FocusHelper.clearFocus(context); + } + }, + ), + Divider(), + RepairedPhotoSection( + title: '验收视频', + maxCount: 1, + mediaType: MediaType.video, + horizontalPadding: 0, + onChanged: (paths) {}, + onMediaAdded: _onImageAdded, + onMediaRemoved: (path) { + final item = imgList.firstWhere((e) => e.localPath == path); + _onImageRemoved(item); + }, + onAiIdentify: () {}, + ), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('验收部门负责人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ), + ], + ), + ), + _bottomButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/home/tap/tabList/special_wrok/zyfz_work_detail/hotwork_zyfz_detail.dart b/lib/pages/home/tap/tabList/special_wrok/zyfz_work_detail/hotwork_zyfz_detail.dart new file mode 100644 index 0000000..6e8f8b3 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/zyfz_work_detail/hotwork_zyfz_detail.dart @@ -0,0 +1,334 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/department_person_picker.dart'; +import 'package:qhd_prevention/customWidget/department_picker.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/SpecialWorkFormBaseWork.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import '../../../../../../customWidget/bottom_picker.dart'; +import '../../../../../../customWidget/custom_alert_dialog.dart'; +import '../../../../../../customWidget/photo_picker_row.dart'; +import '../../../../../../customWidget/single_image_viewer.dart'; +import '../../../../../../http/ApiService.dart'; +import '../../../../../mine/mine_sign_page.dart'; +import '../../../../../my_appbar.dart'; +import '../../special_Wrok/MeasuresListWidget.dart'; +import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; +import '../WorkDetailFormWidget.dart'; +import '../szaq_work_detail/SafeFunctionDialog.dart'; + +/// 作业负责人意见 +class HotworkZyfzDetail extends StatefulWidget { + const HotworkZyfzDetail({ + super.key, + required this.HOTWORK_ID, + required this.flow, + }); + + final String HOTWORK_ID; + final String flow; + + @override + State createState() => _HotworkZyfzDetailState(); +} + +class _HotworkZyfzDetailState extends State { + late bool isEditable = false; + + /// 详情 + late Map pd = {}; + final TextEditingController _contentController = TextEditingController(); + + /// 安全防护措施列表 + late List> measuresList = []; + late Map signs = {}; + + List signImages = []; + List signTimes = []; // 签字时间列表 + @override + void initState() { + super.initState(); + _getData(); + } + + /// 签字 + Future _sign() async { + final path = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => MineSignPage()), + ); + if (path != null) { + final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now()); + + setState(() { + signImages.add(path); + signTimes.add(now); + FocusHelper.clearFocus(context); + }); + } + } + + Widget _signListWidget() { + return Column( + children: + signImages.map((path) { + return Column( + children: [ + const SizedBox(height: 10), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + maxHeight: 150, + ), + child: Image.file( + File(path), + // 改为完整显示 + fit: BoxFit.contain, + ), + ), + onTap: () { + presentOpaque( + SingleImageViewer(imageUrl: path), + context, + ); + }, + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(right: 5), + child: CustomButton( + text: 'X', + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 10), + backgroundColor: Colors.red, + onPressed: () { + setState(() { + signImages.remove(path); + }); + }, + ), + ), + const SizedBox(height: 80), + ], + ), + ], + ), + ], + ); + }).toList(), + ); + } + + /// 作废 -1 通过 1 + Future _submit(String status) async { + if (signImages.isEmpty) { + ToastUtil.showNormal(context, '请签字'); + return; + } + String reasonText = ''; + String DESCR = _contentController.text.trim(); + + if (DESCR.isEmpty) { + ToastUtil.showNormal(context, '请输入负责人意见'); + return; + } + if (status == '1') { + } else { + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '作废原因', + mode: DialogMode.input, + hintText: '请输入作废原因', + cancelText: '取消', + confirmText: '确定', + onInputConfirm: (text) { + reasonText = text; + }, + ), + ); + if (reasonText.isEmpty) { + ToastUtil.showNormal(context, '请填写作废原因'); + return; + } + } + + // 存回 measures + final Map formData = {}; + // 提交参数 + formData['HOTWORK_ID'] = widget.HOTWORK_ID; + formData['SIGNTIME'] = signTimes.join(','); + formData['USER_ID'] = SessionService.instance.loginUserId; + formData['APPLY_STATUS'] = status; + formData['STEP_REASON'] = reasonText; + formData['DESCR'] = DESCR; + + await showDialog( + context: context, + builder: + (_) => CustomAlertDialog( + title: '提示', + content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?', + cancelText: '取消', + confirmText: '确定', + onConfirm: () async { + LoadingDialogHelper.show(context); + try { + final result = await ApiService.saveSafeFunctionSure( + formData, + signImages, + ); + LoadingDialogHelper.hide(context); + if (result['result'] == 'success') { + ToastUtil.showSuccess(context, '保存成功'); + Navigator.pop(context); + } + } catch (e) { + LoadingDialogHelper.hide(context); + ToastUtil.showNormal(context, '操作失败:$e'); + } + }, + ), + ); + } + + /// 初始化拉取数据 + Future _getData() async { + final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); + setState(() { + pd = data['pd']; + + _getSigns(pd['HOTWORK_ID'] ?? ''); + _getMeasures(pd['HOTWORK_ID'] ?? ''); + }); + } + + Future _getMeasures(String homework_id) async { + final data = await ApiService.listSignFinishMeasures( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + measuresList = List>.from( + data['finishMeasuresList'] ?? >[], + ); + }); + } + + Future _getSigns(String homework_id) async { + final data = await ApiService.listSignFinished( + homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, + ); + setState(() { + signs = data['signs'] ?? {}; + }); + } + + /// 底部按钮 + Widget _bottomButtons() { + return Row( + spacing: 10, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + height: 45, + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '作废', + backgroundColor: Colors.red, + onPressed: () { + _submit('-1'); + }, + ), + ), + Expanded( + child: CustomButton( + textStyle: TextStyle(fontSize: 16, color: Colors.white), + text: '通过', + backgroundColor: Colors.green, + onPressed: () { + _submit('1'); + }, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: '作业负责人意见'), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.all(12), + child: Column( + spacing: 12, + children: [ + SpecialWorkFormBaseWork( + pd: pd, + measuresList: measuresList, + signs: signs, + baseImgPath: ApiService.baseImgPath, + isEditable: false, + onChooseLevel: () {}, + onChooseHotworkUser: () {}, + onAnalyzeTap: () { + pushPage( + HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), + context, + ); + }, + ), + ItemListWidget.itemContainer( + Column( + children: [ + ListItemFactory.createBuildMultilineInput( + '作业负责人意见', + '请输入意见', + _contentController, + ), + Divider(), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('作业负责人'), + CustomButton( + text: '新增手写签字', + height: 36, + backgroundColor: Colors.green, + onPressed: () { + _sign(); + }, + ), + ], + ), + if (signImages.isNotEmpty) _signListWidget(), + ], + ) + ], + ), + ), + _bottomButtons(), + ], + ), + ), + ), + ); + } +}