diff --git a/lib/customWidget/ItemWidgetFactory.dart b/lib/customWidget/ItemWidgetFactory.dart index 918f57a..72fb021 100644 --- a/lib/customWidget/ItemWidgetFactory.dart +++ b/lib/customWidget/ItemWidgetFactory.dart @@ -37,16 +37,17 @@ class ListItemFactory { ), ], ), - if (isRight) Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - _truncateText(rightText, 100), + Expanded(child: Text( + rightText, // rightText, style: TextStyle(fontSize: 15, color: Colors.grey), - ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ),), SizedBox(width: 2), Icon( Icons.arrow_forward_ios_rounded, @@ -56,7 +57,8 @@ class ListItemFactory { ], ) else - Text(rightText, style: TextStyle(fontSize: 15, color: Colors.grey)), + Expanded(child: Text(rightText, style: TextStyle(fontSize: 15, color: Colors.grey,),maxLines: 1, + overflow: TextOverflow.ellipsis, textAlign: TextAlign.right,)), ], ), ); diff --git a/lib/customWidget/dotted_border_box.dart b/lib/customWidget/dotted_border_box.dart new file mode 100644 index 0000000..312a67c --- /dev/null +++ b/lib/customWidget/dotted_border_box.dart @@ -0,0 +1,46 @@ +import 'dart:ffi'; + +import 'package:flutter/material.dart'; +import 'package:dotted_border/dotted_border.dart'; + +class DottedBorderBox extends StatelessWidget { + final Widget? child; + final Color color; + final double strokeWidth; + final List dashPattern; + final BorderRadius borderRadius; + final EdgeInsets padding; + final StrokeCap strokeCap; + + const DottedBorderBox({ + super.key, + this.child, + this.color = Colors.black26, + this.strokeWidth = 1.5, + this.dashPattern = const [6, 3], + this.borderRadius = const BorderRadius.all(Radius.circular(8)), + this.padding = const EdgeInsets.all(8), + this.strokeCap = StrokeCap.butt, + }); + + @override + Widget build(BuildContext context) { + return DottedBorder( + options: RoundedRectDottedBorderOptions( + // 控制内边距(虚线与外部的间隔) + borderPadding: EdgeInsets.zero, + // 控制虚线与 child 的间距(虚线内侧留白) + padding: padding, + color: color, + strokeWidth: strokeWidth, + dashPattern: dashPattern, + strokeCap: strokeCap, radius: Radius.circular(0), + // 如果需要,可以传 gradient 等 + ), + child: ClipRRect( + borderRadius: borderRadius, + child: child ?? const SizedBox.shrink(), + ), + ); + } +} diff --git a/lib/customWidget/picker/CupertinoDatePicker.dart b/lib/customWidget/picker/CupertinoDatePicker.dart index 53e7dad..e90c067 100644 --- a/lib/customWidget/picker/CupertinoDatePicker.dart +++ b/lib/customWidget/picker/CupertinoDatePicker.dart @@ -1,14 +1,16 @@ - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; /// 调用示例: -/// DateTime? picked = await BottomDateTimePicker.show(context); +/// DateTime? picked = await BottomDateTimePicker.showDate( +/// context, +/// allowFuture: true, // 添加此参数允许选择未来时间 +/// ); /// if (picked != null) { /// print('用户选择的时间:$picked'); /// } class BottomDateTimePicker { - static Future showDate(BuildContext context) { + static Future showDate(BuildContext context, {bool allowFuture = false}) { return showModalBottomSheet( context: context, backgroundColor: Colors.white, @@ -16,12 +18,16 @@ class BottomDateTimePicker { shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(12)), ), - builder: (_) => _InlineDateTimePickerContent(), + builder: (_) => _InlineDateTimePickerContent(allowFuture: allowFuture), ); } } class _InlineDateTimePickerContent extends StatefulWidget { + final bool allowFuture; // 允许未来 + + const _InlineDateTimePickerContent({Key? key, this.allowFuture = false}) : super(key: key); + @override State<_InlineDateTimePickerContent> createState() => _InlineDateTimePickerContentState(); } @@ -30,10 +36,12 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte // 数据源 final List years = List.generate(101, (i) => 1970 + i); final List months = List.generate(12, (i) => i + 1); - final List days = List.generate(31, (i) => i + 1); final List hours = List.generate(24, (i) => i); final List minutes = List.generate(60, (i) => i); + // 动态天数列表(根据年月变化) + late List days; + // Controllers late FixedExtentScrollController yearCtrl; late FixedExtentScrollController monthCtrl; @@ -58,6 +66,9 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte selectedHour = now.hour; selectedMinute = now.minute; + // 初始化天数列表 + days = _getDaysInMonth(selectedYear, selectedMonth); + yearCtrl = FixedExtentScrollController(initialItem: years.indexOf(selectedYear)); monthCtrl = FixedExtentScrollController(initialItem: selectedMonth - 1); dayCtrl = FixedExtentScrollController(initialItem: selectedDay - 1); @@ -65,19 +76,33 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte minuteCtrl = FixedExtentScrollController(initialItem: selectedMinute); } - @override - void dispose() { - yearCtrl.dispose(); - monthCtrl.dispose(); - dayCtrl.dispose(); - hourCtrl.dispose(); - minuteCtrl.dispose(); - super.dispose(); + // 根据年月获取当月天数 + List _getDaysInMonth(int year, int month) { + final lastDay = DateUtils.getDaysInMonth(year, month); + return List.generate(lastDay, (i) => i + 1); } + // 更新天数列表并调整选中日期 + void _updateDays() { + final newDays = _getDaysInMonth(selectedYear, selectedMonth); + final isDayValid = selectedDay <= newDays.length; + + setState(() { + days = newDays; + if (!isDayValid) { + selectedDay = newDays.last; + dayCtrl.jumpToItem(selectedDay - 1); + } + }); + } + + // 检查并限制时间(当不允许未来时间时) void _checkAndClampToNow() { + if (widget.allowFuture) return; // 允许未来时间时跳过检查 + final picked = DateTime(selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute); final now = DateTime.now(); + if (picked.isAfter(now)) { // 回滚到当前时间 selectedYear = now.year; @@ -92,9 +117,22 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte dayCtrl.jumpToItem(selectedDay - 1); hourCtrl.jumpToItem(selectedHour); minuteCtrl.jumpToItem(selectedMinute); + + // 更新天数列表 + _updateDays(); } } + @override + void dispose() { + yearCtrl.dispose(); + monthCtrl.dispose(); + dayCtrl.dispose(); + hourCtrl.dispose(); + minuteCtrl.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return SizedBox( @@ -140,6 +178,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte onSelected: (idx) { setState(() { selectedYear = years[idx]; + _updateDays(); // 年份变化时更新天数 _checkAndClampToNow(); }); }, @@ -152,6 +191,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte onSelected: (idx) { setState(() { selectedMonth = months[idx]; + _updateDays(); // 月份变化时更新天数 _checkAndClampToNow(); }); }, @@ -217,4 +257,4 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte ), ); } -} +} \ No newline at end of file diff --git a/lib/http/ApiService.dart b/lib/http/ApiService.dart index d249122..3b4c14d 100644 --- a/lib/http/ApiService.dart +++ b/lib/http/ApiService.dart @@ -1174,6 +1174,61 @@ U6Hzm1ninpWeE+awIDAQAB ); } + ///TODO -------------–-------------------- 安全检查 -------------–-------------------- + static Future> getSafeCheckCount() { + return HttpManager().request( + basePath, + '/app/safetyenvironmental/countCheck', + method: Method.post, + data: { + "CORPINFO_ID":SessionService.instance.corpinfoId, + 'INSPECTION_USER_ID': SessionService.instance.loginUserId, + 'INSPECTED_SITEUSER_ID': SessionService.instance.loginUserId, + 'INSPECTION_ORIGINATOR_ID': SessionService.instance.loginUserId, + "tm": DateTime.now().millisecondsSinceEpoch, + }, + ); + } + /// 安全检查流程图 + static Future> safeCheckFlowList(String ID) { + return HttpManager().request( + basePath, + '/app/safetyenvironmental/showFlowChart', + method: Method.post, + data: { + 'ID' :ID + }, + ); + } + /// 安全检查列表 + static Future> getSafeCheckSearchList(Map data, String url) { + return HttpManager().request( + basePath, + url, + method: Method.post, + data: { + "CORPINFO_ID":SessionService.instance.corpinfoId, + "loginUserId":SessionService.instance.loginUserId, + 'tm': DateTime.now().millisecondsSinceEpoch.toString(), + 'roleLevel' : SessionService.instance.loginUser?['roleLevel'] ?? '', + 'supDeparIds' : SessionService.instance.loginUser?['supDeparIds'] ?? '', + + ...data + }, + ); + } + // 安全检查列表详情 + static Future> getSafeCheckStartGoEdit(String INSPECTION_ID) { + return HttpManager().request( + basePath, + '/app/safetyenvironmental/goEdit', + method: Method.post, + data: { + "INSPECTION_ID":INSPECTION_ID, + }, + ); + } + diff --git a/lib/pages/KeyProjects/SafeCheck/custom/MultiTextFieldWithTitle.dart b/lib/pages/KeyProjects/SafeCheck/custom/MultiTextFieldWithTitle.dart index 3ab0efb..6e3b1cb 100644 --- a/lib/pages/KeyProjects/SafeCheck/custom/MultiTextFieldWithTitle.dart +++ b/lib/pages/KeyProjects/SafeCheck/custom/MultiTextFieldWithTitle.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/dotted_border_box.dart'; class MultiTextFieldWithTitle extends StatefulWidget { final String label; @@ -160,21 +161,20 @@ class _MultiTextFieldWithTitleState extends State { ...widget.texts.map((c) { return Padding( padding: const EdgeInsets.only(bottom: 8.0), - child: Container( - padding: const EdgeInsets.all(12), + + child: SizedBox( width: double.maxFinite, - decoration: BoxDecoration( - border: Border.all(color: Colors.grey), - borderRadius: BorderRadius.circular(4), - ), - child: Text( - c, - style: TextStyle( - fontSize: widget.fontSize, - color: Colors.grey[600], + child: DottedBorderBox( + child: + Text( + c, + style: TextStyle( + fontSize: widget.fontSize, + color: Colors.grey[600], + ), ), ), - ), + ) ); }).toList(), ], @@ -192,21 +192,19 @@ class _MultiTextFieldWithTitleState extends State { // 输入框 Padding( padding: EdgeInsets.symmetric(horizontal: 7, vertical: 7), - child: Container( - decoration: BoxDecoration( - border: BoxBorder.all(color: Colors.grey.shade300, width: 1), - borderRadius: BorderRadius.circular(4), - ), - padding: EdgeInsets.symmetric(vertical: 10), - child: TextField( - controller: _controllers[index], - decoration: InputDecoration(hintText: widget.hintText), - focusNode: _focusNodes[index], - keyboardType: TextInputType.multiline, - maxLines: 3, - minLines: 3, - style: TextStyle(fontSize: widget.fontSize), - ), + child: SizedBox( + width: double.maxFinite, + child: DottedBorderBox( + child: TextField( + controller: _controllers[index], + decoration: InputDecoration(hintText: widget.hintText), + focusNode: _focusNodes[index], + keyboardType: TextInputType.multiline, + maxLines: 3, + minLines: 3, + style: TextStyle(fontSize: widget.fontSize), + ), + ) ), ), diff --git a/lib/pages/home/SafeCheck/Start/safeCheck_start_detail.dart b/lib/pages/home/SafeCheck/Start/safeCheck_start_detail.dart new file mode 100644 index 0000000..e7df6ab --- /dev/null +++ b/lib/pages/home/SafeCheck/Start/safeCheck_start_detail.dart @@ -0,0 +1,848 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart'; +import 'package:qhd_prevention/customWidget/bottom_picker.dart'; +import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/dotted_border_box.dart'; +import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart'; +import 'package:qhd_prevention/customWidget/single_image_viewer.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; +import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/custom/MultiTextFieldWithTitle.dart'; +import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/custom/safeCheck_table.dart'; +import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/custom/safe_drawer_page.dart'; +import 'package:qhd_prevention/pages/app/Danger_paicha/quick_report_page.dart'; +import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart'; +import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/tools/tools.dart'; + +class SafecheckStartDetail extends StatefulWidget { + const SafecheckStartDetail({ + super.key, + required this.INSPECTION_ID, + required this.isEdit, + }); + + final String INSPECTION_ID; + final bool isEdit; + + @override + State createState() => _SafecheckStartDetailState(); +} + +class _SafecheckStartDetailState extends State { + /// 被检查单位负责人 + late List personList = []; + + /// 检查类型 + late List typeList = []; + + /// 被检查单位 + late List toCheckUnitList = []; + + bool? chooseTitleType = null; + late List inspectorList = []; + + // 存储多行输入的内容 + List multiTexts = []; + + List delInspectors = []; + List delSituations = []; + List delHiddens = []; + List delHiddenFiles = []; + + // rules 格式: [{ 'name': 'INSPECTION_CATEGORY', 'message': '请填写检查题目' }, ...] + List> rules = [ + {'name': 'INSPECTION_CATEGORY', 'message': '请选择检查题目'}, + {'name': 'UNITS_ID', 'message': '请选择被检查单位'}, + {'name': 'PERSONNELMANAGEMENT_ID', 'message': '请选择被检查单位现场负责人'}, + {'name': 'INSPECTION_TYPE', 'message': '请选择检查类型不能为空'}, + {'name': 'INSPECTION_PLACE', 'message': '请输入检查场所'}, + {'name': 'INSPECTION_TIME_START', 'message': '请选择检查开始时间'}, + {'name': 'INSPECTION_TIME_END', 'message': '请选择作业结束时间'}, + {'name': 'INSPECTION_USERS', 'message': '请输入检查人员'}, + ]; + + Map form = { + 'INSPECTION_SOURCE': '5', // 检查来源(4-监管端 5-企业端) + 'hiddenList': [ + { + 'ISRELEVANT': '2', + 'SOURCE': '5', // 隐患来源 + 'RECTIFICATIONTYPE': '2', + }, + ], + 'INSPECTION_USER_SIGN_TIME': '', + 'INSPECTION_USER_OPINION': '', + }; + + @override + void initState() { + super.initState(); + form['INSPECTION_ID'] = widget.INSPECTION_ID; + form['hiddenList'] = []; + WidgetsBinding.instance.addPostFrameCallback((_) { + _getData(); + }); + } + + Future _getData() async { + try { + final result = await ApiService.getSafeCheckStartGoEdit( + widget.INSPECTION_ID, + ); + // 在 await 之后检查 mounted,避免页面已经被 pop 导致 setState 报错 + if (!mounted) return; + setState(() { + form = result['pd'] ?? {}; + inspectorList = + FormUtils.hasValue(form, 'inspectorList') + ? form['inspectorList'] + : []; + _syncMultiTextsFromForm(); + }); + } catch (e, st) { + print('加载单条数据失败: $e\n$st'); + if (mounted) { + ToastUtil.showNormal(context, '加载数据失败:$e'); + } + } + } + + Future _choosePerson() async { + final choice = await BottomPicker.show( + context, + items: personList.map((val) => val['NAME'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + // 用户点击确定并选择了 choice + setState(() { + form['INSPECTED_SITEUSER_INDEX'] = choice; + final data = FormUtils.findMapForKeyValue(personList, 'NAME', choice); + form['INSPECTED_SITEUSER_ID'] = data['USER_ID']; + form['INSPECTED_SITEUSER_NAME'] = data['NAME']; + + FocusHelper.clearFocus(context); + }); + } + } + + Future _chooseType() async { + final choice = await BottomPicker.show( + context, + items: typeList.map((val) => val['name'] as String).toList(), + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: 0, + ); + if (choice != null) { + // 用户点击确定并选择了 choice + setState(() { + form['INSPECTION_TYPE_NAME'] = choice; + final data = FormUtils.findMapForKeyValue(typeList, 'name', choice); + form['INSPECTION_TYPE'] = data['id']; + + FocusHelper.clearFocus(context); + }); + } + } + + Future _openDrawer(Map hiddenForm, int index) async { + try { + final result = await openCustomDrawer( + context, + SafeDrawerPage( + initialHidden: hiddenForm, + editType: + widget.isEdit + ? (index < 0 ? SafeEditType.add : SafeEditType.edit) + : SafeEditType.see, + toCheckUnitList: toCheckUnitList, + ), + ); + + if (result != null && mounted) { + setState(() { + if (index < 0) { + // 新增 + form['hiddenList'].add(result); + } else { + // 修改 + form['hiddenList'][index] = result; + } + }); + } + } catch (e) { + print("打开抽屉失败: $e"); + ToastUtil.showNormal(context, "打开抽屉失败"); + } + } + + Future openCustomDrawer(BuildContext context, Widget child) { + return Navigator.of(context).push( + PageRouteBuilder( + opaque: false, + barrierDismissible: true, + barrierColor: Colors.black54, + pageBuilder: (_, __, ___) { + return Align( + alignment: Alignment.centerRight, + child: FractionallySizedBox( + widthFactor: 4 / 5, + child: Material(color: Colors.white, child: child), + ), + ); + }, + transitionsBuilder: (_, anim, __, child) { + return SlideTransition( + position: Tween( + begin: const Offset(1, 0), + end: Offset.zero, + ).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut)), + child: child, + ); + }, + ), + ); + } + + /// 将 form['situationList'](若存在)转换为 List + List _situationListToStrings() { + final List cur = List.from(form['situationList'] ?? []); + if (cur.isEmpty) return ['']; // 保持至少一行,和 uni-app 行为一致 + return cur.map((e) { + if (e is Map && e['SITUATION'] != null) return e['SITUATION'].toString(); + return ''; + }).toList(); + } + + /// 将 List 转换成 situationList(保留已存在的 INSPECTION_SITUATION_ID) + List> _stringsToSituationList(List texts) { + final List existing = List.from( + form['situationList'] ?? [], + ); + final List> out = []; + + for (int i = 0; i < texts.length; i++) { + final s = texts[i] ?? ''; + if (i < existing.length && existing[i] is Map) { + // 保留已有项的其他字段(如 INSPECTION_SITUATION_ID),只覆盖 SITUATION + final Map copy = Map.from( + existing[i], + ); + copy['SITUATION'] = s; + // 若没有 INSPECTION_SITUATION_ID 字段,确保它存在(保持原 uni-app 结构) + copy['INSPECTION_SITUATION_ID'] = copy['INSPECTION_SITUATION_ID'] ?? ''; + out.add(copy); + } else { + // 新增项 + out.add({'INSPECTION_SITUATION_ID': '', 'SITUATION': s}); + } + } + + // 如果用户删除了行,要把超出的 existing id 记录到 delSituations(可选) + // 下面为示例:把被删除的旧 ID 收集到 delSituations,便于编辑时提交删除列表 + if (existing.isNotEmpty && existing.length > texts.length) { + for (int j = texts.length; j < existing.length; j++) { + final ex = existing[j]; + if (ex is Map) { + final id = (ex['INSPECTION_SITUATION_ID'] ?? '').toString(); + if (id.isNotEmpty) { + delSituations.add(id); + } + } + } + } + + return out; + } + + /// 在加载数据之后调用:把已存在的 form['situationList'] 同步到 multiTexts(用于 MultiTextFieldWithTitle) + void _syncMultiTextsFromForm() { + final List arr = _situationListToStrings(); + setState(() { + multiTexts = arr; + }); + } + + // ------------ 提交入口 ------------ + Future _submit() async { + if (!widget.isEdit) { + Navigator.of(context).pop(); + + return; + } + bool required = true; + // 基于 rules 验证 + for (final r in rules) { + final name = r['name'] ?? ''; + final message = r['message'] ?? '请完善表单'; + final v = form[name]; + if (v == null || v.toString().isEmpty || v.toString() == '请选择') { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, message); + required = false; + break; + } + } + if (!required) return; + + // situationList 每项 SITUATION 非空 + final situations = (form['situationList'] as List?) ?? []; + for (var i = 0; i < situations.length; i++) { + final s = Map.from(situations[i]); + if ((s['SITUATION'] ?? '').toString().trim().isEmpty) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '请填写第${i + 1}项检查情况'); + return; + } + } + + // 检查 inspectorList 中是否有重复 INSPECTION_USER_ID + final List> inspectors = form['inspectorList'] ?? []; + final seenIds = {}; + for (final it in inspectors) { + final id = (it as Map)['INSPECTION_USER_ID']?.toString() ?? ''; + if (id.isNotEmpty) { + if (seenIds.contains(id)) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '检查人重复!请检查数据'); + return; + } + seenIds.add(id); + } + } + + //根据 hiddenList 构建需要上传的文件数组 + final origHiddenList = (form['hiddenList'] as List?) ?? []; + final List>> hiddenFilesPerHidden = []; + + for (var i = 0; i < origHiddenList.length; i++) { + final hidden = Map.from(origHiddenList[i] as Map); + final List> fileList = []; + + // hiddenImgs (多张) + final hiddenImgs = (hidden['hiddenImgs'] as List?) ?? []; + for (var j = 0; j < hiddenImgs.length; j++) { + final img = hiddenImgs[j]; + // 如果是字符串路径 + if (img is String) { + fileList.add({'type': 3, 'FILEPATH': img}); + } else if (img is Map) { + final hasId = (img['IMGFILES_ID'] ?? '').toString().isNotEmpty; + if (!hasId) { + fileList.add({ + 'type': 3, + 'FILEPATH': img['FILEPATH'] ?? img['path'] ?? '', + }); + } + } + } + + // zgImgs (整改图) + final zgImgs = (hidden['zgImgs'] as List?) ?? []; + for (var j = 0; j < zgImgs.length; j++) { + final img = zgImgs[j]; + if (img is String) { + fileList.add({'type': 4, 'FILEPATH': img}); + } else if (img is Map) { + final hasId = (img['IMGFILES_ID'] ?? '').toString().isNotEmpty; + if (!hasId) { + fileList.add({ + 'type': 4, + 'FILEPATH': img['FILEPATH'] ?? img['path'] ?? '', + }); + } + } + } + + // hiddenVideos (只取第一个) + final hiddenVideos = (hidden['hiddenVideos'] as List?) ?? []; + if (hiddenVideos.isNotEmpty) { + final v = hiddenVideos[0]; + if (v is String) { + fileList.add({'type': 102, 'FILEPATH': v}); + } else if (v is Map) { + final hasId = (v['IMGFILES_ID'] ?? '').toString().isNotEmpty; + if (!hasId) { + fileList.add({ + 'type': 102, + 'FILEPATH': v['FILEPATH'] ?? v['path'] ?? '', + }); + } + } + } + + hiddenFilesPerHidden.add(fileList); + } + + // 确保当前登录用户在 inspectorList 中(依据 SessionService) + final loginUser = SessionService.instance.loginUser ?? {}; + final loginUserId = SessionService.instance.loginUserId ?? ''; + final idx = inspectors.indexWhere((item) { + final m = Map.from(item as Map); + return (m['INSPECTION_USER_ID'] ?? '') == + (loginUser['USER_ID'] ?? loginUserId); + }); + if (idx < 0) { + inspectors.add({ + 'INSPECTION_INSPECTOR_ID': '', + 'INSPECTION_DEPARTMENT_ID': loginUser['DEPARTMENT_ID'] ?? '', + 'INSPECTION_DEPARTMENT_NAME': loginUser['DEPARTMENT_NAME'] ?? '', + 'INSPECTION_USER_ID': loginUser['USER_ID'] ?? loginUserId, + 'INSPECTION_USER_INDEX': '', + 'INSPECTION_USER_NAME': loginUser['NAME'] ?? '', + }); + } + + // 准备 form 字段(JSON 字符串等) + form['INSPECTORJSON'] = jsonEncode(inspectors); + form['SITUATIONJSON'] = jsonEncode(situations); + form['HIDDENJSON'] = jsonEncode(origHiddenList); + form['delInspectors'] = delInspectors.join(','); + form['delSituations'] = delSituations.join(','); + form['delHiddens'] = delHiddens.join(','); + form['delHiddenFiles'] = delHiddenFiles.join(','); + form['CREATOR'] = loginUser['USER_ID'] ?? loginUserId; + form['CORPINFO_ID'] = SessionService.instance.corpinfoId ?? ''; + form['ACTION_USER'] = loginUser['NAME'] ?? ''; + LoadingDialogHelper.show(); // 显示 loading + + // 提交主表 + try { + final requestData = { + 'CORPINFO_ID': form['CORPINFO_ID'], + ...form, + }; + final res = await ApiService.safeKeyprojectCheckSubmit(requestData); + // 如果你的 ApiService 返回结构不同,请按实际调整判断 + if (res != null && res['result'] == 'success') { + final pd = res['pd'] ?? {}; + final List returnedHiddenList = pd['hiddenList'] ?? []; + + // 如果没有附件需要上传,直接完成 + final hasFiles = hiddenFilesPerHidden.any((lst) => lst.isNotEmpty); + if (!hasFiles) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '提交成功'); + Navigator.of(context).pop(); + return; + } + + // 若每个 hidden 有 punishForm,需要先提交罚单 + for (var i = 0; i < returnedHiddenList.length; i++) { + if (i < (form['hiddenList'] as List).length) { + final hidden = Map.from( + (form['hiddenList'] as List)[i], + ); + final punishForm = hidden['punishForm']; + if (punishForm != null) { + final hid = (returnedHiddenList[i]['HIDDEN_ID'] ?? '').toString(); + punishForm['HIDDEN_ID'] = hid; + // await 调用罚单提交(在下面实现) + await fnSubmit(Map.from(punishForm)); + } + } + } + + // 上传所有附件(按隐患对应的 hiddenId) + // 把返回的 hiddenList 转为 Map 列表,确保索引一致 + final returnedHiddenMapList = + returnedHiddenList + .map((e) => Map.from(e)) + .toList(); + await uploadHiddenFiles(hiddenFilesPerHidden, returnedHiddenMapList); + + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '提交成功'); + Navigator.of(context).pop(); + } else { + LoadingDialogHelper.hide(); + final msg = + res != null + ? (res['msg'] ?? res['msaesge'] ?? res['message'] ?? '提交失败') + : '提交失败'; + ToastUtil.showNormal(context, msg); + } + } catch (e) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '提交异常:$e'); + } + } + + // ========== 上传附件的方法 ========== + Future uploadHiddenFiles( + List>> hiddenFilesPerHidden, + List> returnedHiddenList, + ) async { + for (var i = 0; i < hiddenFilesPerHidden.length; i++) { + final filesForHidden = hiddenFilesPerHidden[i]; + if (filesForHidden.isEmpty) continue; + final hiddenId = + i < returnedHiddenList.length + ? (returnedHiddenList[i]['HIDDEN_ID']?.toString() ?? '') + : ''; + if (hiddenId.isEmpty) continue; + + for (final f in filesForHidden) { + final filePath = f['FILEPATH']?.toString() ?? ''; + final type = f['type']?.toString() ?? ''; + if (filePath.isEmpty) continue; + try { + await ApiService.addImgFiles(filePath, type, hiddenId); + } catch (e) { + // 你可以记录失败项或重试,这里先忽略单文件错误 + print('上传文件失败: $e (path=$filePath)'); + } + } + } + } + + // ========== 罚单提交方法(对应原 fnSubmit) ========== + Future fnSubmit(Map? ordForm) async { + if (ordForm == null) return false; + + final Map punishRules = { + 'REASON': '请填写处罚原因', + 'AMOUT': '请填写处罚金额', + 'DATE': '请选择下发处罚时间', + }; + // 校验 + for (final entry in punishRules.entries) { + final key = entry.key; + final msg = entry.value; + final val = ordForm[key]; + if (val == null || val.toString().trim().isEmpty) { + ToastUtil.showNormal(context, msg); + return false; + } + } + + final requestData = Map.from(ordForm); + requestData['CORPINFO_ID'] = SessionService.instance.corpinfoId ?? ''; + requestData['CREATOR'] = SessionService.instance.loginUserId ?? ''; + requestData['OPERATOR'] = SessionService.instance.loginUserId ?? ''; + + try { + LoadingDialogHelper.show(); + final res = await ApiService.safeCheckPunishSubmit(requestData); + LoadingDialogHelper.hide(); + if (FormUtils.hasValue(res, 'result') && res['result'] == 'success') { + return true; + } else { + final msg = + res != null + ? (res['msg'] ?? res['msaesge'] ?? res['message'] ?? '提交失败') + : '提交失败'; + ToastUtil.showNormal(context, msg); + return false; + } + } catch (e) { + LoadingDialogHelper.hide(); + ToastUtil.showNormal(context, '罚单提交异常:$e'); + return false; + } + } + + Widget _personUnitItem(Map item, int index) { + return DottedBorderBox( + child: Column( + children: [ + ItemListWidget.selectableLineTitleTextRightButton( + isRequired: widget.isEdit, + label: '${index + 1}.检查人员单位:', + isEditable: widget.isEdit, + onTapClean: () { + setState(() {}); + }, + text: item['INSPECTION_DEPARTMENT_NAME'] ?? '请选择', + onTap: () {}, + ), + Divider(), + ItemListWidget.selectableLineTitleTextRightButton( + isRequired: widget.isEdit, + label: 'type.personName', + isEditable: widget.isEdit, + text: item['INSPECTION_USER_NAME'] ?? '请选择', + onTap: () {}, + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: MyAppbar(title: "安全检查发起", actions: []), + + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12), + child: + form.isNotEmpty + ? ListView( + children: [ + Column( + children: [ + ItemListWidget.itemContainer( + horizontal: 0, + Column( + children: [ + ListItemFactory.createYesNoSection( + title: '检查题目:', + groupValue: chooseTitleType, + yesLabel: '安全', + noLabel: '综合', + isEdit: widget.isEdit, + isRequired: true, + horizontalPadding: 5, + verticalPadding: 0, + text: + '${form['INSPECTION_SUBJECT'] ?? ''}现场检查记录', + onChanged: (val) { + setState(() { + chooseTitleType = val; + form['INSPECTION_SUBJECT'] = + val == true ? '安全' : '综合'; + }); + }, + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '被检查单位:', + isEditable: false, + text: form['INSPECTED_DEPARTMENT_NAMES'], + onChanged: (val) { + setState(() { + form['INSPECTED_DEPARTMENT_NAME'] = val; + }); + }, + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '被检查单位现场负责人:', + isEditable: widget.isEdit, + onTap: () { + _choosePerson(); + }, + text: form['INSPECTED_SITEUSER_INDEX'] ?? '', + ), + const Divider(), + + ItemListWidget.singleLineTitleText( + label: '检查场所:', + isEditable: widget.isEdit, + text: form['INSPECTION_PLACE'], + hintText: '请输入检查场所', + onChanged: (val) { + setState(() { + form['INSPECTION_PLACE'] = val; + }); + }, + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '检查类型:', + onTap: () { + _chooseType(); + }, + isEditable: widget.isEdit, + text: form['INSPECTION_TYPE_NAME'] ?? '', + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '检查开始时间:', + isEditable: widget.isEdit, + text: form['INSPECTION_TIME_START'] ?? '', + onTap: () async { + DateTime? picked = + await BottomDateTimePicker.showDate( + context, + ); + if (picked != null) { + setState(() { + form['INSPECTION_TIME_START'] = + DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + FocusHelper.clearFocus(context); + } + }, + ), + const Divider(), + + ItemListWidget.selectableLineTitleTextRightButton( + label: '检查结束时间:', + isEditable: widget.isEdit, + text: form['INSPECTION_TIME_END'] ?? '', + onTap: () async { + DateTime? picked = + await BottomDateTimePicker.showDate( + context, + ); + if (picked != null) { + setState(() { + form['INSPECTION_TIME_END'] = + DateFormat( + 'yyyy-MM-dd HH:mm', + ).format(picked); + }); + FocusHelper.clearFocus(context); + } + }, + ), + + const Divider(), + MultiTextFieldWithTitle( + label: "检查情况", + // 更合适的标题 + isEditable: widget.isEdit, + // 使用父组件的编辑状态 + hintText: "请输入检查情况...", + texts: multiTexts, + onTextsChanged: (texts) { + setState(() { + multiTexts = texts; // 保存到状态变量 + form['situationList'] = + _stringsToSituationList(texts); + }); + }, + ), + + const Divider(), + ItemListWidget.itemContainer( + Column( + children: [ + ListItemFactory.headerTitle('检查人员'), + Expanded(child: ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: inspectorList.length, + itemBuilder: (context, index) { + return _personUnitItem(inspectorList[index], index); + }, + ),) + ], + ), + ), + + const Divider(), + ItemListWidget.itemContainer( + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.headerTitle('发现问题'), + if (widget.isEdit) + CustomButton( + text: " 添加 ", + height: 30, + padding: const EdgeInsets.symmetric( + vertical: 2, + horizontal: 5, + ), + backgroundColor: Colors.blue, + onPressed: () { + _openDrawer( + form, + -1, + ); // 添加括号和 await + FocusHelper.clearFocus(context); + }, + ), + ], + ), + ), + HiddenListTable( + hiddenList: form['hiddenList'] ?? [], + forbidEdit: widget.isEdit, + baseImgPath: ApiService.baseImgPath, + personSignImg: form['PERSON_SIGN_IMG'] ?? '', + personSignTime: + form['PERSON_SIGN_TIME'] ?? '', + showHidden: (item, idx) { + _openDrawer(item, idx); + }, + removeHidden: (item, idx) { + /* 删除逻辑 */ + }, + context: context, + ), + + if (!widget.isEdit) + Column( + children: [ + const Divider(), + ItemListWidget.twoRowTitleAndImages( + title: '签字', + onTapCallBack: (p) { + presentOpaque( + SingleImageViewer(imageUrl: p), + context, + ); + }, + imageUrls: [ + '${form['PERSON_SIGN_IMG'] ?? ''}', + ], + ), + const Divider(), + ItemListWidget.singleLineTitleText( + label: '签字时间', + isEditable: false, + text: form['PERSON_SIGN_TIME'] ?? '', + ), + ], + ), + ], + ), + ), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (widget.isEdit) + SizedBox( + width: 150, + child: CustomButton( + text: '返回', + textStyle: TextStyle( + color: Colors.white, + fontSize: 17, + ), + backgroundColor: Colors.black38, + onPressed: () => Navigator.pop(context), + ), + ), + SizedBox( + width: 150, + child: CustomButton( + text: widget.isEdit ? '提交' : '返回', + textStyle: TextStyle( + color: Colors.white, + fontSize: 17, + ), + backgroundColor: Colors.blue, + onPressed: _submit, + ), + ), + ], + ), + ], + ), + ], + ) + : SizedBox(), + ), + ), + ); + } +} diff --git a/lib/pages/home/SafeCheck/Start/safeCheck_start_list_page.dart b/lib/pages/home/SafeCheck/Start/safeCheck_start_list_page.dart new file mode 100644 index 0000000..b989ef0 --- /dev/null +++ b/lib/pages/home/SafeCheck/Start/safeCheck_start_list_page.dart @@ -0,0 +1,481 @@ +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/toast_util.dart'; +import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_detail.dart'; +import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/dh_work_detai/hotwork_apply_detail.dart'; +import 'package:qhd_prevention/pages/my_appbar.dart'; +import 'package:qhd_prevention/tools/tools.dart'; +import 'package:qhd_prevention/customWidget/bottom_picker.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; +import 'package:qhd_prevention/customWidget/search_bar_widget.dart'; +import 'package:qhd_prevention/http/ApiService.dart'; + +class SafecheckStartListPage extends StatefulWidget { + final String flow; + + const SafecheckStartListPage({Key? key, required this.flow}) + : super(key: key); + + @override + _SafecheckStartListPageState createState() => _SafecheckStartListPageState(); +} + +class _SafecheckStartListPageState extends State { + // Data and state variables + List list = []; + int currentPage = 1; + int rows = 10; + int totalPage = 1; + bool isLoading = false; + List stepList = [ + {'id': '', 'name': '请选择'}, + {'id': '0', 'name': '待检查人核实'}, + {'id': '1', 'name': '检查人核实中'}, + {'id': '2', 'name': '待被检查人确认'}, + {'id': '3', 'name': '待指派'}, + {'id': '4', 'name': '指派中'}, + {'id': '5', 'name': '指派完成'}, + {'id': '6', 'name': '检查待验收'}, + {'id': '7', 'name': '检查已验收'}, + {'id': '8', 'name': '已归档'}, + {'id': '-1', 'name': '检查人核实打回'}, + {'id': '-2', 'name': '被检查人申辩'}, + ]; + final TextEditingController _searchController = TextEditingController(); + + int sindex = 0; + String searchKeywords = ''; + + List> flowList = []; + final GlobalKey _scaffoldKey = GlobalKey(); + + final ScrollController _scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _fetchData(); + _scrollController.addListener(_onScroll); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + void _onScroll() { + if (_scrollController.position.pixels >= + _scrollController.position.maxScrollExtent && + !isLoading) { + if (currentPage < totalPage) { + currentPage++; + _fetchData(); + } + } + } + + Future _fetchData() async { + if (isLoading) return; + setState(() => isLoading = true); + + try { + final data = { + 'INSPECTION_STATUS': sindex > 0 ? stepList[sindex]['id'] : '', + 'KEYWORDS': searchKeywords, + }; + final url = + '/app/safetyenvironmental/list?showCount=-1¤tPage=$currentPage'; + final response = await ApiService.getSafeCheckSearchList(data, url); + + setState(() { + if (currentPage == 1) { + list = response['varList']; + } else { + list.addAll(response['varList']); + } + Map page = response['page']; + totalPage = page['totalPage'] ?? 1; + isLoading = false; + }); + } catch (e) { + print('Error fetching data: $e'); + setState(() => isLoading = false); + } + } + + void _search() { + searchKeywords = _searchController.text.trim(); + currentPage = 1; + list.clear(); + _fetchData(); + } + + /// 申请 + void _handleApply() { + // 处理申请按钮点击逻辑 + pushPage(SafecheckStartDetail(INSPECTION_ID: '', isEdit: true), context); + } + + /// 打开流程图 + Future _openFlowDrawer(String ID) async { + try { + final response = await ApiService.safeCheckFlowList(ID); + final List? newFlow = response['varList']; + if (newFlow == null || newFlow.isEmpty) { + ToastUtil.showNormal(context, '暂无流程图数据'); + return; + } + + setState(() { + flowList = List>.from(newFlow); + }); + Future.microtask(() { + _scaffoldKey.currentState?.openEndDrawer(); + }); + } catch (e) { + print('Error fetching flow data: $e'); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('获取流程图失败: $e'))); + } + } + + void _goToDetail(Map item) async { + + pushPage(SafecheckStartDetail(INSPECTION_ID: item['INSPECTION_ID'] ?? '', isEdit: false), context); + + setState(() { + _fetchData(); + }); + + } + + Widget _buildFlowStepItem({ + required Map item, + required bool isFirst, + required bool isLast, + required int status, // 1 = 完成, 0 = 进行中, -1 = 未到达 + }) { + // 依据状态设色 + final Color dotColor = + status == 1 ? Colors.blue : (status == 0 ? Colors.blue : Colors.grey); + final Color textColor = + status == 1 + ? Colors.blue + : (status == 0 ? Colors.blue : Colors.black54); + + return ListTile( + visualDensity: VisualDensity(vertical: -4), + + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + leading: Container( + width: 24, + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + // 上方线段或占位 + isFirst + ? SizedBox(height: 6 + 5) + : Expanded(child: Container(width: 1, color: Colors.grey[300])), + // 圆点 + CircleAvatar(radius: 6, backgroundColor: dotColor), + // 下方线段或占位 + isLast + ? SizedBox(height: 6 + 5) + : Expanded(child: Container(width: 1, color: Colors.grey[300])), + ], + ), + ), + title: Text( + item['title'] ?? '', + style: TextStyle(color: textColor, fontSize: 15), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (item['desc'] != null) + Text( + item['desc'], + style: TextStyle(color: textColor, fontSize: 13), + ), + ], + ), + ); + } + + String _checkStatusWithId(String id) { + for (Map item in stepList) { + if (item['id'] == id) { + return item['name']; + } + } + return ''; + } + + Widget _buildListItem(Map item) { + return Card( + color: Colors.white, + margin: const EdgeInsets.all(8.0), + child: InkWell( + onTap: () => _goToDetail(item), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "安全检查记录", + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "检查状态: ${_checkStatusWithId(item['INSPECTION_STATUS'].toString())}", + ), + Text("检查类型: ${item['INSPECTION_TYPE_NAME'] ?? ''}"), + ], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("检查人: ${item['INSPECTION_USER_NAME'] ?? ''}"), + ], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "检查发起人: ${item['INSPECTION_ORIGINATOR_NAME'] ?? ''}", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("被检查人: ${item['INSPECTED_SITEUSER_NAME'] ?? ''}"), + ], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "检查时间: ${item['INSPECTION_TIME_START'] ?? ''}至${item['INSPECTION_TIME_END'] ?? ''}", + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + CustomButton( + text: '流程图', + height: 35, + padding: EdgeInsets.symmetric(horizontal: 12), + margin: EdgeInsets.only(left: 0), + backgroundColor: Colors.blue, + onPressed: () => _openFlowDrawer(item['INSPECTION_ID']), + ), + SizedBox(width: 1), + ], + ), + ], + ), + ), + ), + ); + } + + // 显示底部选择器 + Future _showStepPicker() async { + if (stepList.isEmpty) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('正在加载步骤数据,请稍后...'))); + if (stepList.isEmpty) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('无法加载步骤数据'))); + return; + } + } + + // 创建选项列表 + final options = stepList.map((e) => e['name'] as String).toList(); + + // 显示底部选择器 + final choice = await BottomPicker.show( + context, + items: options, + itemBuilder: (item) => Text(item, textAlign: TextAlign.center), + initialIndex: sindex, + ); + + if (choice != null) { + // 找到选择的索引 + final newIndex = options.indexOf(choice); + if (newIndex != -1) { + setState(() { + sindex = newIndex; + }); + _search(); + } + } + } + + Widget _buildListContent() { + if (isLoading && list.isEmpty) { + // 初始加载时显示居中的加载指示器 + return Center(child: CircularProgressIndicator()); + } else if (list.isEmpty) { + // 没有数据 + return NoDataWidget.show(); + } else { + // 有数据或加载更多 + return ListView.builder( + padding: EdgeInsets.zero, + + controller: _scrollController, + itemCount: list.length + (isLoading ? 1 : 0), + itemBuilder: (context, index) { + if (index >= list.length) { + // 加载更多时在列表底部显示加载指示器 + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Center(child: CircularProgressIndicator()), + ); + } + return _buildListItem(list[index]); + }, + ); + } + } + + @override + Widget build(BuildContext context) { + final int lastDoneIndex = flowList.lastIndexWhere((e) => e['STATUS'] == 1); + + return Scaffold( + key: _scaffoldKey, + appBar: MyAppbar( + title: '${widget.flow}', + actions: [ + TextButton( + onPressed: _handleApply, + child: const Text( + '发起', + style: TextStyle(color: Colors.white, fontSize: 17), + ), + ), + + ], + ), + endDrawer: Drawer( + child: SafeArea( + child: + flowList.isEmpty + ? Center(child: Text('暂无流程图数据')) + : ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 16), + itemCount: flowList.length + 1, // +1 用来放标题 + itemBuilder: (context, i) { + if (i == 0) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + child: Text( + '查看流程图', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ); + } + final idx = i - 1; + final item = flowList[idx]; + final bool isFirst = idx == 0; + final bool isLast = idx == flowList.length - 1; + + // 根据 lastDoneIndex 自动计算“进行中” + final int status = item['active'] == '1' ? 1 : -1; + + return _buildFlowStepItem( + item: item, + isFirst: isFirst, + isLast: isLast, + status: status, + ); + }, + ), + ), + ), + + body: Column( + children: [ + // Filter bar + Container( + color: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8), + child: Row( + children: [ + // 底部弹窗选择器 + SizedBox( + width: 65, + child: TextButton( + onPressed: _showStepPicker, + style: TextButton.styleFrom( + padding: EdgeInsets.symmetric( + vertical: 12, + horizontal: 5, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '筛选', + style: TextStyle(color: Colors.black87, fontSize: 16), + ), + Icon(Icons.arrow_drop_down, color: Colors.grey), + ], + ), + ), + ), + Expanded( + flex: 2, + child: SearchBarWidget( + showResetButton: false, + hintText: "请输入关键字", + // isClickableOnly: true, + onSearch: (text) { + _search(); + }, + controller: _searchController, + ), + ), + ], + ), + ), + const Divider(height: 1), + // List + Expanded(child: _buildListContent()), + ], + ), + ); + } +} diff --git a/lib/pages/home/SafeCheck/safeCheck_tab_list.dart b/lib/pages/home/SafeCheck/safeCheck_tab_list.dart index 8927e2b..b6eb385 100644 --- a/lib/pages/home/SafeCheck/safeCheck_tab_list.dart +++ b/lib/pages/home/SafeCheck/safeCheck_tab_list.dart @@ -4,6 +4,7 @@ import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/pages/KeyProjects/Danger/danger_list_page.dart'; import 'package:qhd_prevention/pages/KeyProjects/KeyProject/keyProject_list_page.dart'; import 'package:qhd_prevention/pages/KeyProjects/SafeCheck/safeCheck_list_page.dart'; +import 'package:qhd_prevention/pages/home/SafeCheck/Start/safeCheck_start_list_page.dart'; import 'package:qhd_prevention/pages/home/tap/tabList/work_tab_icon_grid.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/tools/tools.dart'; @@ -52,31 +53,38 @@ class _SafecheckTabListState extends State { _getData(); } Future _getData() async { - LoadingDialogHelper.show(); - final data = await ApiService.getKeyProjectCount(); - LoadingDialogHelper.hide(); + final data = await ApiService.getSafeCheckCount(); setState(() { - final eight_work_count = data['pd'] ?? {}; + final checkedCount = data['checkedCount']['checkedCount'] ?? ''; + final repulseAndCheckCount = data['repulseAndCheckCount']['repulseAndCheckCount'] ?? ''; + final confirmCount = data['confirmCount']['confirmCount'] ?? ''; + final repulseCount = data['repulseCount']['repulseCount'] ?? ''; + buttonInfos = [ { "icon": "assets/icon-apps/icon-yxkj-1.png", - "title": "重点工程管理", - "unreadCount": eight_work_count['GC_COUNT'] ?? '0', + "title": "安全检查\n发起", + "unreadCount": repulseCount, }, { - "icon": "assets/icon-apps/icon-yxkj-1.png", - "title": "安全检查管理", - "unreadCount": '0', + "icon": "assets/icon-apps/icon-yxkj-4.png", + "title": "检查人\n确认", + "unreadCount": confirmCount, }, { - "icon": "assets/icon-apps/icon-yxkj-1.png", - "title": "隐患管理", - "unreadCount": eight_work_count['CF_COUNT'] ?? '0', + "icon": "assets/icon-apps/icon-yxkj-2.png", + "title": "被检查人\n签字/申辩", + "unreadCount":checkedCount, }, { - "icon": "assets/icon-apps/icon-yxkj-1.png", - "title": "处罚管理", - "unreadCount": eight_work_count['HIDDEN_COUNT'] ?? '0', + "icon": "assets/icon-apps/icon-yxkj-2.png", + "title": "隐患指派\n及验收", + "unreadCount":repulseAndCheckCount, + }, + { + "icon": "assets/icon-apps/icon-yxkj-2.png", + "title": "申辩记录", + "unreadCount":'0', }, ]; @@ -88,7 +96,7 @@ class _SafecheckTabListState extends State { switch (index) { case 0: { title = '安全检查发起'; - // await pushPage(KeyprojectListPage(flow: title), context); + await pushPage(SafecheckStartListPage(flow: title), context); } break; case 1: { title = '安全检查核实'; diff --git a/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart b/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart index d695d50..a7100c6 100644 --- a/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/home_gas_test_page.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:dotted_line/dotted_line.dart'; + import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart'; diff --git a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/qtfx_work_detail/electricity_gas_test_page.dart b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/qtfx_work_detail/electricity_gas_test_page.dart index 42c7ce6..2cb1a32 100644 --- a/lib/pages/home/tap/tabList/special_wrok/lsyd_work/qtfx_work_detail/electricity_gas_test_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/lsyd_work/qtfx_work_detail/electricity_gas_test_page.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:dotted_line/dotted_line.dart'; + import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; diff --git a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_test_page.dart b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_test_page.dart index 2804f4b..60b80ae 100644 --- a/lib/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_test_page.dart +++ b/lib/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_test_page.dart @@ -1,6 +1,6 @@ import 'dart:io'; import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_list.dart'; -import 'package:dotted_line/dotted_line.dart'; + import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; diff --git a/pubspec.lock b/pubspec.lock index c0a4f7a..1b0f070 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -177,14 +177,14 @@ packages: url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.1" - dotted_line: + dotted_border: dependency: "direct main" description: - name: dotted_line - sha256: "41e3d655939559815daa1370fc1e07673a205fa628cf40ce3af45d90029a77b6" + name: dotted_border + sha256: "99b091ec6891ba0c5331fdc2b502993c7c108f898995739a73c6845d71dad70c" url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.2.3" + version: "3.1.0" encrypt: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index edb6b31..e24e667 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -73,8 +73,8 @@ dependencies: geolocator: ^10.0.0 #打开外部预览app url_launcher: ^6.0.9 - #虚线 - dotted_line: ^3.2.3 + #虚线框 + dotted_border: ^3.1.0 #未读角标 flutter_new_badger: ^1.1.1 #loading