diff --git a/lib/customWidget/single_image_viewer.dart b/lib/customWidget/single_image_viewer.dart index 649840c..66a1e91 100644 --- a/lib/customWidget/single_image_viewer.dart +++ b/lib/customWidget/single_image_viewer.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:photo_view/photo_view.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; @@ -8,6 +10,12 @@ class SingleImageViewer extends StatelessWidget { @override Widget build(BuildContext context) { + ImageProvider provider; + if (imageUrl.toLowerCase().startsWith('http')) { + provider = NetworkImage(imageUrl); + } else { + provider = FileImage(File(imageUrl)); + } return Scaffold( backgroundColor: Colors.black.withValues(alpha: 0.5), appBar: MyAppbar( @@ -21,7 +29,7 @@ class SingleImageViewer extends StatelessWidget { ), body: Center( child: PhotoView( - imageProvider: NetworkImage(imageUrl), + imageProvider: provider, backgroundDecoration: BoxDecoration(color: Colors.black.withValues(alpha:0.5)), minScale: PhotoViewComputedScale.contained, maxScale: PhotoViewComputedScale.covered * 2, diff --git a/lib/http/ApiService.dart b/lib/http/ApiService.dart index 6e4930a..b9496e6 100644 --- a/lib/http/ApiService.dart +++ b/lib/http/ApiService.dart @@ -692,16 +692,15 @@ U6Hzm1ninpWeE+awIDAQAB ); } /// 所有安全防护措施 - static Future> listSignFinishAllMeasures(String hotworkId) { + static Future> listSignFinishAllMeasures() { final String tm = DateTime.now().millisecondsSinceEpoch.toString(); return HttpManager().request( basePath, - '/app/hotwork/listAllMeasuresForSign?tm=$tm', + '/app/hotwork/listAllMeasures?tm=$tm', method: Method.post, data: { "CORPINFO_ID":SessionService.instance.corpinfoId, - "CONFIRM_ID":SessionService.instance.loginUserId, - "HOTWORK_ID":hotworkId, + "USER_ID":SessionService.instance.loginUserId, }, ); } diff --git a/lib/pages/home/tap/item_list_widget.dart b/lib/pages/home/tap/item_list_widget.dart index 01c9fb9..079e26b 100644 --- a/lib/pages/home/tap/item_list_widget.dart +++ b/lib/pages/home/tap/item_list_widget.dart @@ -319,7 +319,6 @@ class ItemListWidget { } /// 单行布局: /// 标题 + 文字 + 按钮 - static Widget OneRowButtonTitleText({ required String label, // 标题 required String text, // 显示内容或提示 @@ -359,4 +358,35 @@ class ItemListWidget { ) ); } + /// 单行布局: + /// 标题 + 按钮 + static Widget OneRowButtonTitle({ + required String label, // 标题 + required String buttonText, // 按钮文字 + required VoidCallback? onTap, // 第一行点击回调 + double fontSize = 15, // 字体大小 + Color btnColor = Colors.blue, + + }) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold), + ), + CustomButton( + text: buttonText, + height: 30, + padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5), + backgroundColor: btnColor, + onPressed: onTap, + ), + ], + ) + ); + } + } diff --git a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart index 84cfd17..a9b26bb 100644 --- a/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart +++ b/lib/pages/home/tap/tabList/special_wrok/dh_work_detai/MeasuresListWidget.dart @@ -601,6 +601,7 @@ class _SelectionPopupState extends State { @override Widget build(BuildContext context) { return Dialog( + backgroundColor: Colors.white, insetPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 40), child: SizedBox( width: double.infinity, @@ -616,6 +617,7 @@ class _SelectionPopupState extends State { // 作业类型下拉 Expanded( child: DropdownButton( + dropdownColor: Colors.white, style: TextStyle(), isExpanded: true, value: selectedWorkName, @@ -643,7 +645,7 @@ class _SelectionPopupState extends State { children: [ Text(selectedDate == null ? '选择作业申请时间' - : selectedDate!.toString().split(' ')[0]), + : selectedDate!.toString().split(' ')[0],style: TextStyle(color: Colors.blue),), SizedBox(width: 5,), Icon(Icons.arrow_drop_down, color: Colors.grey, size: 20,), ], )), @@ -667,6 +669,7 @@ class _SelectionPopupState extends State { : item['NAME'] as String? ?? ''; final checked = value.contains(key); return CheckboxListTile( + activeColor: Colors.blue, title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/pages/home/tap/tabList/special_wrok/qtfx_work_detail/gasWork_apply_detail.dart b/lib/pages/home/tap/tabList/special_wrok/qtfx_work_detail/gasWork_apply_detail.dart deleted file mode 100644 index 9cc313b..0000000 --- a/lib/pages/home/tap/tabList/special_wrok/qtfx_work_detail/gasWork_apply_detail.dart +++ /dev/null @@ -1,744 +0,0 @@ -import 'package:flutter/material.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 '../../../../../../http/ApiService.dart'; -import '../../../../../my_appbar.dart'; -import '../../special_Wrok/dh_work_detai/MeasuresListWidget.dart'; -import '../../special_Wrok/qtfx_work_detail/hotwork_gas_list.dart'; - -enum EditUserType { - ANALYZE('分析单位', '分析单位负责人'), - GUARDIAN('监护人单位', '监护人'), - CONFESS('安全交底人单位', '安全交底人'), - ACCEPT_CONFESS('接受交底人单位', '接受交底人'), - CONFIRM('作业负责人单位', '作业负责人'), - LEADER('所在单位', '所在单位负责人'), - AUDIT('安全管理部门', '安全管理部门负责人'), - APPROVE('动火审批单位', '动火审批负责人'), - MONITOR('动火前在岗部门', '动火前在岗班长'), - WORK_START('作业开始负责人单位', '作业开始负责人'), - WORK_END('作业结束负责人单位', '作业结束负责人'), - ACCEPT('验收部门', '验收部门负责人'); - - /// 对应的单位显示名 - final String displayName; - final String personName; - - const EditUserType(this.displayName, this.personName); -} - -class GasWorkApplyDetail extends StatefulWidget { - const GasWorkApplyDetail({ - super.key, - required this.HOTWORK_ID, - required this.flow, - }); - - final String HOTWORK_ID; - final String flow; - - @override - State createState() => _GasWorkApplyDetailState(); -} - -class _GasWorkApplyDetailState extends State { - late bool isEditable = false; - final levelList = ["特级", "一级", "二级"]; - - /// 编辑还是新增 - late String msg = 'add'; - - /// 详情 - late Map pd = {}; - late Map signs = {}; - late List> measuresList = []; - - final TextEditingController _contentController = TextEditingController(); - final TextEditingController _locationController = TextEditingController(); - final TextEditingController _methodController = TextEditingController(); - final TextEditingController _hotworkPersonController = - TextEditingController(); - final TextEditingController _relatedController = TextEditingController(); - final TextEditingController _riskController = TextEditingController(); - - /// 动火人及证书编号 - late List workUserList = []; - - // 存储各单位的人员列表 - final Map>> _personCache = {}; - - @override - void initState() { - super.initState(); - if (widget.HOTWORK_ID.length > 0) { - msg = 'edit'; - _getData(); - _getHotWorkNameList(); - } else { - isEditable = true; - pd['APPLY_DEPARTMENT_ID'] = SessionService.instance.deptId; - pd['APPLY_DEPARTMENT_NAME'] = - SessionService.instance.loginUser!['DEPARTMENT_NAME'] ?? ''; - pd['APPLY_USER_ID'] = SessionService.instance.loginUserId; - pd['APPLY_USER_NAME'] = SessionService.instance.username; - } - _contentController.addListener(() { - setState(() { - pd['WORK_CONTENT'] = _contentController.text.trim(); - }); - }); - _locationController.addListener(() { - pd['WORK_PLACE'] = _locationController.text.trim(); - }); - _methodController.addListener(() { - pd['WORK_FUNCTION'] = _methodController.text.trim(); - }); - _hotworkPersonController.addListener(() { - pd['WORK_USER'] = _hotworkPersonController.text.trim(); - }); - _relatedController.addListener(() { - pd['SPECIAL_WORK'] = _relatedController.text.trim(); - }); - _riskController.addListener(() { - pd['RISK_IDENTIFICATION'] = _riskController.text.trim(); - }); - } - - void set_pd_DEPARTMENT_ID(EditUserType type, String id) { - pd['${type.name}_DEPARTMENT_ID'] = id; - } - - void set_pd_DEPARTMENT_NAME(EditUserType type, String name) { - pd['${type.name}_DEPARTMENT_NAME'] = name; - } - - void set_pd_USER_ID(EditUserType type, String id) { - pd['${type.name}_USER_ID'] = id; - } - - void set_pd_USER_Name(EditUserType type, String name) { - pd['${type.name}_USER_NAME'] = name; - } - - String get_pd_DEPARTMENT_ID(EditUserType type) { - return pd['${type.name}_DEPARTMENT_ID'] ?? ''; - } - - String get_pd_DEPARTMENT_NAME(EditUserType type) { - return pd['${type.name}_DEPARTMENT_NAME'] ?? ''; - } - - String get_pd_USER_ID(EditUserType type) { - return pd['${type.name}_USER_ID'] ?? ''; - } - - String get_pd_USER_Name(EditUserType type) { - return pd['${type.name}_USER_NAME'] ?? ''; - } - - Future _chooseLevel() async { - final choice = await BottomPicker.show( - context, - items: levelList, - itemBuilder: (item) => Text(item, textAlign: TextAlign.center), - initialIndex: 0, - ); - if (choice != null) { - // 用户点击确定并选择了 choice - setState(() { - pd['WORK_LEVEL'] = choice; - }); - } - } - - Future _chooseHorkUser() async{ - // - final choice = await BottomPicker.show( - context, - items: workUserList.map((item) => item['NAME'] as String).toList(), - itemBuilder: (item) => Text(item, textAlign: TextAlign.center), - initialIndex: 0, - ); - if (choice != null) { - pd['WORK_USER'] = choice; - _hotworkPersonController.text = choice; - Map result = workUserList.firstWhere( - (item) => item['NAME'] == choice, - orElse: () => {}, // 避免找不到时报错 - ); - if (FormUtils.hasValue(result, 'USER_ID')) { - pd['WORK_USER_ID'] = result['USER_ID']; - } - } - } - - Widget _defaultDetail() { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ItemListWidget.singleLineTitleText( - label: '申请单位:', - isEditable: false, - text: pd['APPLY_DEPARTMENT_NAME'] ?? '', - ), - Divider(), - ItemListWidget.singleLineTitleText( - label: '申请人:', - isEditable: false, - text: pd['APPLY_USER_NAME'] ?? '', - ), - if (FormUtils.hasValue(pd, 'CHECK_NO')) - Column( - children: [ - Divider(), - ItemListWidget.singleLineTitleText( - label: '编号:', - isEditable: false, - text: pd['CHECK_NO'] ?? '', - ), - ], - ), - - Divider(), - ItemListWidget.multiLineTitleTextField( - label: '作业内容:', - isEditable: isEditable, - controller: _contentController, - text: pd['WORK_CONTENT'] ?? '', - ), - Divider(), - ItemListWidget.singleLineTitleText( - label: '动火地点及动火部位:', - isEditable: isEditable, - controller: _locationController, - text: pd['WORK_PLACE'] ?? '', - ), - Divider(), - ItemListWidget.selectableLineTitleTextField( - label: '动火作业级别', - isEditable: isEditable, - onTap: () { - _chooseLevel(); - }, - text: pd['WORK_LEVEL'] ?? '', - ), - Divider(), - ItemListWidget.singleLineTitleText( - label: '动火方式:', - isEditable: isEditable, - controller: _methodController, - text: pd['WORK_FUNCTION'] ?? '', - ), - if (pd['WORK_START_DATE'] != null && - pd['WORK_START_DATE'].toString().isNotEmpty) - Column( - children: [ - Divider(), - ItemListWidget.singleLineTitleText( - label: '动火作业\n实施时间:', - isEditable: isEditable, - controller: _methodController, - text: - pd['WORK_START_DATE'] ?? - '' + - '至' + - (pd['WORK_END_DATE'] - ? pd['WORK_END_DATE'] ?? '' - : '--') ?? - '', - ), - ], - ), - Divider(), - ItemListWidget.twoRowSelectableTitleText( - label: '动火人及证书编号:', - isEditable: isEditable, - onTap: () { - _chooseHorkUser(); - }, - controller: _hotworkPersonController, - text: pd['WORK_USER'] ?? '', - ), - Divider(), - ItemListWidget.twoRowButtonTitleText( - label: '关联的其他特殊作业及安全作业票编号', - isEditable: isEditable, - onTap: () { - showDialog( - context: context, - builder: (_) => SelectionPopup( - type: 'assignments', - initialValue: pd['SPECIAL_WORK'] ?? '', - onConfirm: (val) { - // val 为逗号分隔的选中值 - setState(() { - pd['SPECIAL_WORK'] = val; - _relatedController.text = val; - }); - }, - ), - ); - // identification - }, - hintText: '请输入关联的其他特殊作业及安全作业票编号', - controller: _relatedController, - text: pd['SPECIAL_WORK'] ?? '', - ), - Divider(), - ItemListWidget.twoRowButtonTitleText( - label: '风险辨识结果', - isEditable: isEditable, - onTap: () { - - showDialog( - context: context, - builder: (_) => SelectionPopup( - type: 'identification', - initialValue: pd['RISK_IDENTIFICATION'] ?? '', - onConfirm: (val) { - // val 为逗号分隔的选中值 - setState(() { - pd['RISK_IDENTIFICATION'] = val; - _riskController.text = val; - }); - }, - ), - ); - }, - hintText: '请输入风险辨识结果', - controller: _riskController, - text: pd['RISK_IDENTIFICATION'] ?? '', - ), - if (FormUtils.hasValue(pd, 'ANALYZE_TIME')) - Column( - children: [ - Divider(), - ItemListWidget.OneRowButtonTitleText( - label: '分析人', - text: pd['ANALYZE_USER_NAME'] ?? '', - onTap: () { - pushPage( - HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID), - context, - ); - }, - ), - ], - ), - ], - ); - } - - Widget _card(Widget child) { - return Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(5), - ), - child: child, - ); - } - - Widget _chooseItem(EditUserType type) { - return Column( - children: [ - ItemListWidget.selectableLineTitleTextField( - label: type.displayName, - isEditable: isEditable, - text: pd['${type.name}_DEPARTMENT_NAME'] ?? '请选择', - onTap: () => chooseUnitHandle(type), - ), - Divider(), - ItemListWidget.selectableLineTitleTextField( - label: type.personName, - isEditable: isEditable, - text: pd['${type.name}_USER_NAME'] ?? '请选择', - onTap: () => choosePersonHandle(type), - ), - ], - ); - } - - /// 弹出单位选择 - void chooseUnitHandle(EditUserType type) { - showModalBottomSheet( - context: context, - isScrollControlled: true, - barrierColor: Colors.black54, - backgroundColor: Colors.transparent, - builder: - (_) => DepartmentPicker( - onSelected: (id, name) async { - setState(() { - set_pd_DEPARTMENT_ID(type, id); - set_pd_DEPARTMENT_NAME(type, name); - set_pd_USER_ID(type, ''); - set_pd_USER_Name(type, ''); - }); - // 拉取该单位的人员列表并缓存 - final result = await ApiService.getListTreePersonList(id); - _personCache[type] = List>.from( - result['userList'] as List, - ); - }, - ), - ); - } - - /// 弹出人员选择,需先选择单位 - void choosePersonHandle(EditUserType type) { - final unitId = get_pd_DEPARTMENT_ID(type); - final personList = _personCache[type] ?? []; - if (unitId == null || personList.isEmpty) { - final unitName = type.displayName; - ToastUtil.showNormal(context, '请先选择$unitName'); - return; - } - DepartmentPersonPicker.show( - context, - personsData: personList, - onSelected: (userId, name) { - setState(() { - set_pd_USER_ID(type, userId); - set_pd_USER_Name(type, name); - }); - }, - ); - } - - /// 提交 1 提交 0暂存 - Future _submit(String status) async { - // 通用文本字段校验规则 - final textRules = >[ - {'value': _contentController.text.trim(), 'message': '请填写作业内容'}, - {'value': _locationController.text.trim(), 'message': '请填写动火地点及部位'}, - {'value': _methodController.text.trim(), 'message': '请填写动火方式'}, - {'value': _hotworkPersonController.text.trim(), 'message': '请填写动火人及证书编号'}, - { - 'value': _relatedController.text.trim(), - 'message': '请输入关联的其他特殊作业及安全作业票编号', - }, - {'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'}, - ]; - final level = pd['WORK_LEVEL'] as String? ?? ''; - - /// 各项负责人校验 - final unitRules = [ - EditUserType.ANALYZE, - EditUserType.GUARDIAN, - EditUserType.CONFESS, - EditUserType.ACCEPT_CONFESS, - EditUserType.CONFIRM, - EditUserType.LEADER, - EditUserType.AUDIT, - EditUserType.APPROVE, - EditUserType.MONITOR, - EditUserType.WORK_START, - EditUserType.WORK_END, - EditUserType.ACCEPT, - ]; - if (status == '1') { - // 文本校验 - for (var rule in textRules) { - if ((rule['value'] as String).isEmpty) { - ToastUtil.showNormal(context, rule['message']); - return; - } - } - // 级别校验 - if (level.length == 0) { - ToastUtil.showNormal(context, '请选择动火级别'); - return; - } - - for (var type in unitRules) { - if (get_pd_DEPARTMENT_ID(type).length == 0) { - ToastUtil.showNormal(context, '请选择${type.displayName}'); - return; - } - if (get_pd_USER_ID(type).length == 0) { - ToastUtil.showNormal(context, '请选择${type.displayName}负责人'); - return; - } - } - } - // LoadingDialogHelper.show(context); - - String TASK_ID = '0'; - if (level == '特级') { - TASK_ID = '1'; - } else if (level == '一级') { - TASK_ID = '2'; - } else if (level == '二级') { - TASK_ID = '3'; - } - - // 提交参数 - Map payload = {}; - if (msg == 'add') { - payload = { - 'HOTWORK_ID': widget.HOTWORK_ID, - 'CREATOR': SessionService.instance.loginUserId, - 'OPERATOR': SessionService.instance.loginUserId, - 'ACTION_USER': SessionService.instance.username, - 'APPLY_STATUS': status, - 'CORPINFO_ID': SessionService.instance.corpinfoId, - 'USER_ID': SessionService.instance.loginUserId, - 'STEP_ID': status, - 'WORK_LEVEL': TASK_ID, - }; - } - pd.forEach((key, value) { - payload[key] = value; - }); - LoadingDialogHelper.show(context); - - try { - String url = "/app/hotwork/" + msg; - final result = await ApiService.submitHotwork(url, payload); - if (result['result'] == 'success') { - ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存'); - } - } catch (e) { - ToastUtil.showNormal(context, '操作失败:$e'); - } - LoadingDialogHelper.hide(context); - } - 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); - setState(() { - pd = data['pd']; - if (pd['STEP_ID'] == 0) { - isEditable = true; - } else { - _getSigns(pd['HOTWORK_ID'] ?? ''); - _getMeasures(pd['HOTWORK_ID'] ?? ''); - } - // 给所有输入框赋值 - _contentController.text = pd['WORK_CONTENT'] ?? ''; - _locationController.text = pd['WORK_PLACE'] ?? ''; - _methodController.text = pd['WORK_FUNCTION'] ?? ''; - _hotworkPersonController.text = pd['WORK_USER'] ?? ''; - _relatedController.text = pd['SPECIAL_WORK'] ?? ''; - _riskController.text = pd['RISK_IDENTIFICATION'] ?? ''; - - }); - // final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); - // setState(() { - // pd = data['pd']; - // }); - // LoadingDialogHelper.hide(context); - } - - Future _getSigns(String homework_id) async { - final data = await ApiService.listSignFinished( - homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, - ); - setState(() { - signs = data['signs'] ?? {}; - }); - } - - 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'] ?? >[], - ); - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: MyAppbar(title: '动火安全作业申请'), - body: SafeArea( - child: SingleChildScrollView( - padding: EdgeInsets.all(12), - child: Column( - children: [ - _card(_defaultDetail()), - if (isEditable) - Column( - children: [ - SizedBox(height: 15), - _card(_chooseItem(EditUserType.ANALYZE)), - SizedBox(height: 15), - _card(_chooseItem(EditUserType.GUARDIAN)), - SizedBox(height: 15), - _card(_chooseItem(EditUserType.CONFESS)), - SizedBox(height: 15), - _card(_chooseItem(EditUserType.ACCEPT_CONFESS)), - SizedBox(height: 15), - _card(_chooseItem(EditUserType.CONFIRM)), - SizedBox(height: 15), - _card(_chooseItem(EditUserType.LEADER)), - SizedBox(height: 15), - _card(_chooseItem(EditUserType.AUDIT)), - SizedBox(height: 15), - _card(_chooseItem(EditUserType.APPROVE)), - SizedBox(height: 15), - _card(_chooseItem(EditUserType.MONITOR)), - SizedBox(height: 15), - _card( - Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - _chooseItem(EditUserType.WORK_START), - Divider(), - Row( - children: [ - SizedBox(width: 12), - Text( - '友情提示:负责填写作业实际开始时间', - style: TextStyle(color: Colors.red), - ), - ], - ), - SizedBox(height: 5), - ], - ), - ), - SizedBox(height: 15), - _card( - Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - _chooseItem(EditUserType.WORK_END), - Divider(), - Row( - children: [ - SizedBox(width: 12), - Text( - '友情提示:负责填写作业实际结束时间', - style: TextStyle(color: Colors.red), - ), - ], - ), - SizedBox(height: 5), - ], - ), - ), - SizedBox(height: 15), - _card(_chooseItem(EditUserType.ACCEPT)), - SizedBox(height: 15), - ], - ), - if (measuresList.length > 0) - Column( - children: [ - SizedBox(height: 20), - ListItemFactory.createBuildSimpleSection('安全防护措施'), - Container( - color: Colors.white, - child: MeasuresListWidget( - measuresList: - measuresList, // List> - baseImgPath: ApiService.baseImgPath, - ), - ), - ], - ), - 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, - ), - ), - ], - ), - SignaturesListWidget( - signs: signs, - pd: pd, - baseImgPath: ApiService.baseImgPath, - ), - isEditable - ? Row( - spacing: 10, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: CustomButton( - height: 45, - textStyle: TextStyle( - fontSize: 16, - color: Colors.white, - ), - text: '提交', - backgroundColor: Colors.blue, - onPressed: () { - _submit('1'); - }, - ), - ), - - Expanded( - child: CustomButton( - textStyle: TextStyle( - fontSize: 16, - color: Colors.white, - ), - text: '暂存', - backgroundColor: Colors.green, - onPressed: () { - _submit('0'); - }, - ), - ), - ], - ) - : Column( - children: [ - SizedBox(height: 20), - Row( - children: [ - SizedBox(width: 50), - Expanded( - child: CustomButton( - height: 45, - textStyle: TextStyle( - fontSize: 16, - color: Colors.white, - ), - text: '返回', - backgroundColor: Colors.green, - onPressed: () { - Navigator.pop(context); - }, - ), - ), - SizedBox(width: 50), - ], - ), - ], - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/pages/home/tap/tabList/special_wrok/szaq_work_detail/SafeFunctionDialog.dart b/lib/pages/home/tap/tabList/special_wrok/szaq_work_detail/SafeFunctionDialog.dart new file mode 100644 index 0000000..ea5bbd0 --- /dev/null +++ b/lib/pages/home/tap/tabList/special_wrok/szaq_work_detail/SafeFunctionDialog.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; +import 'package:qhd_prevention/customWidget/custom_button.dart'; + +/// 安全措施弹窗 +class SafeFunctionDialog extends StatefulWidget { + final List> data; + + /// 点击确认回调,返回选中的措施列表 + final void Function(List selectedMeasures) onConfirm; + + /// 构造函数 + const SafeFunctionDialog({ + Key? key, + required this.data, + required this.onConfirm, + }) : super(key: key); + + @override + State createState() => _SafeFunctionDialogState(); +} + +class _SafeFunctionDialogState extends State { + /// 存放所有可选项的文字 + late final List _allMeasures; + + /// 存放当前被选中的文字 + final List _selectedMeasures = []; + + @override + void initState() { + super.initState(); + _allMeasures = + widget.data + .map((e) => e['PROTECTIVE_MEASURES'] as String? ?? '') + .toList(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + insetPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 24), + content: SizedBox( + width: MediaQuery.of(context).size.width - 24, + height: + MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top - + MediaQuery.of(context).padding.bottom, + child: Column( + children: [ + Expanded( + child: ListView.builder( + itemCount: _allMeasures.length, + itemBuilder: (ctx, index) { + final measure = _allMeasures[index]; + final checked = _selectedMeasures.contains(measure); + return CheckboxListTile( + // 将复选框移到左侧 + controlAffinity: ListTileControlAffinity.leading, + // 调整左右间隙 + contentPadding: const EdgeInsets.symmetric(horizontal: 0), + activeColor: Colors.blue, + title: Text(measure, style: const TextStyle(fontSize: 14)), + value: checked, + onChanged: (value) { + setState(() { + if (value == true) { + _selectedMeasures.add(measure); + } else { + _selectedMeasures.remove(measure); + } + }); + }, + ); + }, + ), + ), + const SizedBox(height: 16), + Row( + children: [ + Expanded( + child: CustomButton( + text: '确定', + height: 40, + backgroundColor: Colors.blue, + onPressed: () { + widget.onConfirm(_selectedMeasures); + Navigator.of(context).pop(); + }, + ), + ), + const SizedBox(width: 12), + Expanded( + child: CustomButton( + text: '关闭', + height: 40, + backgroundColor: Colors.grey.shade300, + textStyle: TextStyle(color: Colors.grey.shade600), + onPressed: () => Navigator.of(context).pop(), + ), + ), + ], + ), + ], + ), + ), + ); + } +} + +Future showSafeFunctionDialog( + BuildContext context, + List> data, + void Function(List) onConfirm, +) { + return showDialog( + context: context, + barrierDismissible: false, + builder: (_) => SafeFunctionDialog(data: data, onConfirm: onConfirm), + ); +} 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 1d47d13..7e0d9e8 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 @@ -1,7 +1,9 @@ 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'; @@ -10,31 +12,14 @@ 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/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 'SafeFunctionDialog.dart'; -enum EditUserType { - ANALYZE('分析单位', '分析单位负责人'), - GUARDIAN('监护人单位', '监护人'), - CONFESS('安全交底人单位', '安全交底人'), - ACCEPT_CONFESS('接受交底人单位', '接受交底人'), - CONFIRM('作业负责人单位', '作业负责人'), - LEADER('所在单位', '所在单位负责人'), - AUDIT('安全管理部门', '安全管理部门负责人'), - APPROVE('动火审批单位', '动火审批负责人'), - MONITOR('动火前在岗部门', '动火前在岗班长'), - WORK_START('作业开始负责人单位', '作业开始负责人'), - WORK_END('作业结束负责人单位', '作业结束负责人'), - ACCEPT('验收部门', '验收部门负责人'); - - /// 对应的单位显示名 - final String displayName; - final String personName; - - const EditUserType(this.displayName, this.personName); -} class HotworkSetSafeDetail extends StatefulWidget { const HotworkSetSafeDetail({ @@ -61,120 +46,29 @@ class _HotworkSetSafeDetailState extends State { late Map pd = {}; late List> measuresList = []; - final TextEditingController _contentController = TextEditingController(); - final TextEditingController _locationController = TextEditingController(); - final TextEditingController _methodController = TextEditingController(); - final TextEditingController _hotworkPersonController = - TextEditingController(); - final TextEditingController _relatedController = TextEditingController(); - final TextEditingController _riskController = TextEditingController(); /// 动火人及证书编号 late List workUserList = []; + /// 安全防护措施列表 - late List measuresListCopy = []; - - // 存储各单位的人员列表 - final Map>> _personCache = {}; - + late List measuresListCopy = []; + List imagePaths = []; + List signTimes = []; // 签字时间列表 @override void initState() { super.initState(); _getData(); _getHotWorkNameList(); addMeasuresListCopy(); - _contentController.addListener(() { - setState(() { - pd['WORK_CONTENT'] = _contentController.text.trim(); - }); - }); - _locationController.addListener(() { - pd['WORK_PLACE'] = _locationController.text.trim(); - }); - _methodController.addListener(() { - pd['WORK_FUNCTION'] = _methodController.text.trim(); - }); - _hotworkPersonController.addListener(() { - pd['WORK_USER'] = _hotworkPersonController.text.trim(); - }); - _relatedController.addListener(() { - pd['SPECIAL_WORK'] = _relatedController.text.trim(); - }); - _riskController.addListener(() { - pd['RISK_IDENTIFICATION'] = _riskController.text.trim(); - }); + } - void set_pd_DEPARTMENT_ID(EditUserType type, String id) { - pd['${type.name}_DEPARTMENT_ID'] = id; + String measuresListToJson() { + final List> jsonList = + measuresListCopy.map((item) => item.toJson()).toList(); + return jsonEncode(jsonList); } - void set_pd_DEPARTMENT_NAME(EditUserType type, String name) { - pd['${type.name}_DEPARTMENT_NAME'] = name; - } - - void set_pd_USER_ID(EditUserType type, String id) { - pd['${type.name}_USER_ID'] = id; - } - - void set_pd_USER_Name(EditUserType type, String name) { - pd['${type.name}_USER_NAME'] = name; - } - - String get_pd_DEPARTMENT_ID(EditUserType type) { - return pd['${type.name}_DEPARTMENT_ID'] ?? ''; - } - - String get_pd_DEPARTMENT_NAME(EditUserType type) { - return pd['${type.name}_DEPARTMENT_NAME'] ?? ''; - } - - String get_pd_USER_ID(EditUserType type) { - return pd['${type.name}_USER_ID'] ?? ''; - } - - String get_pd_USER_Name(EditUserType type) { - return pd['${type.name}_USER_NAME'] ?? ''; - } - - Future _chooseLevel() async { - final choice = await BottomPicker.show( - context, - items: levelList, - itemBuilder: (item) => Text(item, textAlign: TextAlign.center), - initialIndex: 0, - ); - if (choice != null) { - // 用户点击确定并选择了 choice - setState(() { - pd['WORK_LEVEL'] = choice; - FocusHelper.clearFocus(context); - }); - } - } - - Future _chooseHorkUser() async { - final choice = await BottomPicker.show( - context, - items: workUserList.map((item) => item['NAME'] as String).toList(), - itemBuilder: (item) => Text(item, textAlign: TextAlign.center), - initialIndex: 0, - ); - if (choice != null) { - setState(() { - pd['WORK_USER'] = choice; - _hotworkPersonController.text = choice; - Map result = workUserList.firstWhere( - (item) => item['NAME'] == choice, - orElse: () => {}, // 避免找不到时报错 - ); - if (FormUtils.hasValue(result, 'USER_ID')) { - pd['WORK_USER_ID'] = result['USER_ID']; - } - FocusHelper.clearFocus(context); - }); - } - } Widget _defaultDetail() { return Column( @@ -207,30 +101,24 @@ class _HotworkSetSafeDetailState extends State { ItemListWidget.multiLineTitleTextField( label: '作业内容:', isEditable: isEditable, - controller: _contentController, text: pd['WORK_CONTENT'] ?? '', ), Divider(), ItemListWidget.singleLineTitleText( label: '动火地点及动火部位:', isEditable: isEditable, - controller: _locationController, text: pd['WORK_PLACE'] ?? '', ), Divider(), ItemListWidget.selectableLineTitleTextField( label: '动火作业级别', isEditable: isEditable, - onTap: () { - _chooseLevel(); - }, text: pd['WORK_LEVEL'] ?? '', ), Divider(), ItemListWidget.singleLineTitleText( label: '动火方式:', isEditable: isEditable, - controller: _methodController, text: pd['WORK_FUNCTION'] ?? '', ), if (pd['WORK_START_DATE'] != null && @@ -241,7 +129,6 @@ class _HotworkSetSafeDetailState extends State { ItemListWidget.singleLineTitleText( label: '动火作业\n实施时间:', isEditable: isEditable, - controller: _methodController, text: pd['WORK_START_DATE'] ?? '' + @@ -257,65 +144,22 @@ class _HotworkSetSafeDetailState extends State { ItemListWidget.twoRowSelectableTitleText( label: '动火人及证书编号:', isEditable: isEditable, - onTap: () { - _chooseHorkUser(); - }, - controller: _hotworkPersonController, text: pd['WORK_USER'] ?? '', ), Divider(), ItemListWidget.twoRowButtonTitleText( label: '关联的其他特殊作业及安全作业票编号', isEditable: isEditable, - onTap: () { - showDialog( - context: context, - builder: - (_) => SelectionPopup( - type: 'assignments', - initialValue: pd['SPECIAL_WORK'] ?? '', - onConfirm: (val) { - // val 为逗号分隔的选中值 - setState(() { - pd['SPECIAL_WORK'] = val; - _relatedController.text = val; - }); - }, - ), - ).then((_) { - FocusHelper.clearFocus(context); - }); - // identification - }, + onTap: ()=>{}, hintText: '请输入关联的其他特殊作业及安全作业票编号', - controller: _relatedController, text: pd['SPECIAL_WORK'] ?? '', ), Divider(), ItemListWidget.twoRowButtonTitleText( label: '风险辨识结果', isEditable: isEditable, - onTap: () { - showDialog( - context: context, - builder: - (_) => SelectionPopup( - type: 'identification', - initialValue: pd['RISK_IDENTIFICATION'] ?? '', - onConfirm: (val) { - // val 为逗号分隔的选中值 - setState(() { - pd['RISK_IDENTIFICATION'] = val; - _riskController.text = val; - }); - }, - ), - ).then((_) { - FocusHelper.clearFocus(context); - }); - }, + onTap: () {}, hintText: '请输入风险辨识结果', - controller: _riskController, text: pd['RISK_IDENTIFICATION'] ?? '', ), if (FormUtils.hasValue(pd, 'ANALYZE_TIME')) @@ -348,29 +192,41 @@ class _HotworkSetSafeDetailState extends State { ); } - Widget _chooseItem(EditUserType type) { + Widget _chooseItem(MeasureItem item) { return Column( children: [ ItemListWidget.selectableLineTitleTextField( - label: type.displayName, - isEditable: isEditable, - text: pd['${type.name}_DEPARTMENT_NAME'] ?? '请选择', - onTap: () => chooseUnitHandle(type), + label: "确认单位", + isEditable: true, + text: item.DEPARTMENT_NAME ?? '请选择', + onTap: () => chooseUnitHandle(item), ), - Divider(), + Divider(height: 8, color: Colors.grey.shade200), ItemListWidget.selectableLineTitleTextField( - label: type.personName, - isEditable: isEditable, - text: pd['${type.name}_USER_NAME'] ?? '请选择', - onTap: () => choosePersonHandle(type), + label: '确认人', + isEditable: true, + text: item.USER_NAME ?? '请选择', + onTap: () => choosePersonHandle(item), + ), + Divider(height: 8, color: Colors.grey.shade200), + // 安全措施 + ItemListWidget.OneRowButtonTitle( + label: '安全措施:', + buttonText: '选择安全措施', + onTap: () { + showSafeFunctionDialog(context, measuresList, (selected) { + // 在这里处理用户的选择结果 + debugPrint('用户选择了:' + json.encode(selected)); + }); + }, ), ], ); } /// 弹出单位选择 - void chooseUnitHandle(EditUserType type) { - FocusHelper.clearFocus(context); + void chooseUnitHandle(MeasureItem item) { + showModalBottomSheet( context: context, isScrollControlled: true, @@ -380,50 +236,48 @@ class _HotworkSetSafeDetailState extends State { (_) => DepartmentPicker( onSelected: (id, name) async { setState(() { - set_pd_DEPARTMENT_ID(type, id); - set_pd_DEPARTMENT_NAME(type, name); - set_pd_USER_ID(type, ''); - set_pd_USER_Name(type, ''); + item.DEPARTMENT_ID = id; + item.DEPARTMENT_NAME = name; }); - _getPersonListForUnitId(id, type); + _getPersonListForUnitId(item); }, ), ).then((_) { - FocusHelper.clearFocus(context); + }); } - Future _getPersonListForUnitId(String id, EditUserType type) async { + Future _getPersonListForUnitId(MeasureItem item) async { // 拉取该单位的人员列表并缓存 - final result = await ApiService.getListTreePersonList(id); + final result = await ApiService.getListTreePersonList(item.DEPARTMENT_ID); setState(() { - _personCache[type] = List>.from( - result['userList'] as List, + + item.userList = List>.from( + result['userList'] ?? >[], ); }); } /// 弹出人员选择,需先选择单位 - void choosePersonHandle(EditUserType type) async { - FocusHelper.clearFocus(context); + void choosePersonHandle(MeasureItem item) async { - String unitId = get_pd_DEPARTMENT_ID(type); - final personList = _personCache[type] ?? []; + String unitId = item.DEPARTMENT_ID; + final personList = item.userList; if (!unitId.isNotEmpty) { - final unitName = type.displayName; + final unitName = item.DEPARTMENT_NAME; ToastUtil.showNormal(context, '请先选择$unitName'); return; } if (personList.isEmpty) { // 一般这种情况是因为重新编辑没有缓存对应部门的负责人,所以先拉取一下接口 - await _getPersonListForUnitId(unitId, type); - final list = _personCache[type] ?? []; + await _getPersonListForUnitId(item); + final list = item.userList; if (list.isEmpty) { // 如果还是没数据,说明该部门没有可选的人 ToastUtil.showNormal(context, '暂无数据,请选择其他单位'); } else { - choosePersonHandle(type); + choosePersonHandle(item); } return; } @@ -432,83 +286,108 @@ class _HotworkSetSafeDetailState extends State { personsData: personList, onSelected: (userId, name) { setState(() { - set_pd_USER_ID(type, userId); - set_pd_USER_Name(type, name); + item.USER_NAME = name; + item.USER_ID = userId; + print(json.encode(measuresListCopy)); }); }, ).then((_) { - FocusHelper.clearFocus(context); + }); } + /// 签字 + 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: Image.file( + File(path), + width: 200, + height: 150, + fit: BoxFit.cover, + ), + 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 提交 0暂存 Future _submit(String status) async { // 通用文本字段校验规则 - final textRules = >[ - {'value': _contentController.text.trim(), 'message': '请填写作业内容'}, - {'value': _locationController.text.trim(), 'message': '请填写动火地点及部位'}, - {'value': _methodController.text.trim(), 'message': '请填写动火方式'}, - {'value': _hotworkPersonController.text.trim(), 'message': '请填写动火人及证书编号'}, - { - 'value': _relatedController.text.trim(), - 'message': '请输入关联的其他特殊作业及安全作业票编号', - }, - {'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'}, - ]; - final level = pd['WORK_LEVEL'] ?? ''; - print('---level-$level'); - /// 各项负责人校验 - final unitRules = [ - EditUserType.ANALYZE, - EditUserType.GUARDIAN, - EditUserType.CONFESS, - EditUserType.ACCEPT_CONFESS, - EditUserType.CONFIRM, - EditUserType.LEADER, - EditUserType.AUDIT, - EditUserType.APPROVE, - EditUserType.MONITOR, - EditUserType.WORK_START, - EditUserType.WORK_END, - EditUserType.ACCEPT, - ]; if (status == '1') { - // 文本校验 - for (var rule in textRules) { - if ((rule['value'] as String).isEmpty) { - ToastUtil.showNormal(context, rule['message']); - return; - } - } - // 级别校验 - if (level.length == 0) { - ToastUtil.showNormal(context, '请选择动火级别'); - return; - } - - for (var type in unitRules) { - if (get_pd_DEPARTMENT_ID(type).length == 0) { - ToastUtil.showNormal(context, '请选择${type.displayName}'); - return; - } - if (get_pd_USER_ID(type).length == 0) { - ToastUtil.showNormal(context, '请选择${type.displayName}负责人'); - return; - } - } + // // 文本校验 + // for (var rule in textRules) { + // if ((rule['value'] as String).isEmpty) { + // ToastUtil.showNormal(context, rule['message']); + // return; + // } + // } + // // 级别校验 + // if (level.length == 0) { + // ToastUtil.showNormal(context, '请选择动火级别'); + // return; + // } + // + // for (MeasureItem item in measuresListCopy) { + // if (item.USER_ID.isNotEmpty) { + // ToastUtil.showNormal(context, '请选择确认人'); + // return; + // } + // } } // LoadingDialogHelper.show(context); - String taskId = '0'; - if (level == '特级') { - taskId = '1'; - } else if (level == '一级') { - taskId = '2'; - } else if (level == '二级') { - taskId = '3'; - } - // 提交参数 if (msg == 'add') { pd['CORPINFO_ID'] = SessionService.instance.corpinfoId; @@ -517,7 +396,6 @@ class _HotworkSetSafeDetailState extends State { pd['ACTION_USER'] = SessionService.instance.username; pd['APPLY_STATUS'] = status; pd['STEP_ID'] = status; - pd['TASK_ID'] = taskId; pd['HOTWORK_ID'] = widget.HOTWORK_ID; pd['APPLY_DEPARTMENT_ID'] = SessionService.instance.deptId; pd['APPLY_DEPARTMENT_NAME'] = @@ -565,18 +443,8 @@ class _HotworkSetSafeDetailState extends State { final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); setState(() { pd = data['pd']; - if (pd['STEP_ID'] == 0) { - isEditable = true; - } else { - _getMeasures(pd['HOTWORK_ID'] ?? ''); - } - // 给所有输入框赋值 - _contentController.text = pd['WORK_CONTENT'] ?? ''; - _locationController.text = pd['WORK_PLACE'] ?? ''; - _methodController.text = pd['WORK_FUNCTION'] ?? ''; - _hotworkPersonController.text = pd['WORK_USER'] ?? ''; - _relatedController.text = pd['SPECIAL_WORK'] ?? ''; - _riskController.text = pd['RISK_IDENTIFICATION'] ?? ''; + _getMeasures(); + }); // final data = await ApiService.getHomeworkFindById(widget.HOTWORK_ID); // setState(() { @@ -585,21 +453,21 @@ class _HotworkSetSafeDetailState extends State { // LoadingDialogHelper.hide(context); } - Future _getMeasures(String homework_id) async { - final data = await ApiService.listSignFinishAllMeasures( - homework_id.length > 0 ? homework_id : widget.HOTWORK_ID, - ); + Future _getMeasures() async { + final data = await ApiService.listSignFinishAllMeasures(); setState(() { measuresList = List>.from( - data['finishMeasuresList'] ?? >[], + data['measuresList'] ?? >[], ); }); } + void removeMeasuresListCopy(int index) { setState(() { measuresListCopy.removeAt(index); }); } + void addMeasuresListCopy() { setState(() { measuresListCopy.add( @@ -616,6 +484,148 @@ class _HotworkSetSafeDetailState extends State { ); }); } + + /// 安全防护措施 + Widget _setSafeDetailWidget() { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + padding: EdgeInsets.symmetric(horizontal: 5), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ListItemFactory.createBuildSimpleSection('安全防护措施'), + CustomButton( + text: '添加', + padding: EdgeInsets.symmetric(horizontal: 15), + backgroundColor: Colors.blue, + height: 32, + onPressed: () => addMeasuresListCopy(), + ), + ], + ), + ...measuresListCopy.asMap().entries.map((entry) { + int index = entry.key; + MeasureItem item = entry.value; + return Padding( + padding: EdgeInsets.all(0), + child: Stack( + children: [ + Container( + color: Colors.white, + + child: Padding( + padding: EdgeInsets.all(12), + child: Container( + padding: EdgeInsets.all(2), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + border: Border.all(color: Colors.grey.shade300), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 部门 picker + _chooseItem(item), + + ...item.selectMeasures.asMap().entries.map((e) { + int idx = e.key; + String txt = e.value; + return Padding( + padding: EdgeInsets.only(top: 4), + child: Text('${idx + 1}. $txt'), + ); + }), + ], + ), + ), + ), + ), + if (index != 0) + Positioned( + top: 0, + right: 0, + child: GestureDetector( + onTap: () => removeMeasuresListCopy(index), + child: Stack( + alignment: AlignmentDirectional.center, + children: [ + Container( + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(10), + ), + width: 20, + height: 20, + ), + Icon(Icons.close, color: Colors.white, size: 15), + ], + ), + ), + ), + ], + ), + ); + }), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(), + 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('0'); + }, + ), + ), + ], + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -628,156 +638,9 @@ class _HotworkSetSafeDetailState extends State { _card(_defaultDetail()), SizedBox(height: 20), - Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - ), - padding: EdgeInsets.symmetric(horizontal: 5), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ListItemFactory.createBuildSimpleSection('安全防护措施'), - CustomButton( - text: '添加', - padding: EdgeInsets.symmetric(horizontal: 15), - backgroundColor: Colors.blue, - height: 36, - onPressed: () {}, - ), - ], - ), - ), - ...measuresListCopy.asMap().entries.map((entry) { - int index = entry.key; - MeasureItem item = entry.value; - return Padding( - padding: EdgeInsets.all(12), - child: Stack( - children: [ - Card( - child: Padding( - padding: EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 部门 picker - ItemListWidget.selectableLineTitleTextField( - label: '确认单位:', - isEditable: isEditable, - onTap: () { - - }, - text:item.DEPARTMENT_NAME ?? '请选择', - ), - SizedBox(height: 8), - // 用户 picker - ItemListWidget.selectableLineTitleTextField( - label: '确认人:', - isEditable: isEditable, - onTap: () { - - }, - text:item.USER_NAME ?? '请选择', - ), - SizedBox(height: 8), - // 安全措施 - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('安全措施:'), - ElevatedButton( - onPressed: () {}, - style: ElevatedButton.styleFrom(shape: StadiumBorder()), - child: Text('选择安全措施'), - ), - ], - ), - ...item.selectMeasures.asMap().entries.map((e) { - int idx = e.key; - String txt = e.value; - return Padding( - padding: EdgeInsets.only(top: 4), - child: Text('${idx + 1}. $txt'), - ); - }), - ], - ), - ), - ), - if (index != 0) - Positioned( - top: 0, - right: 0, - child: IconButton( - icon: Icon(Icons.close, color: Colors.red), - onPressed: () => removeMeasuresListCopy(index), - ), - ), - ], - ), - ); - }), - isEditable - ? Row( - spacing: 10, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: CustomButton( - height: 45, - textStyle: TextStyle( - fontSize: 16, - color: Colors.white, - ), - text: '提交', - backgroundColor: Colors.blue, - onPressed: () { - _submit('1'); - }, - ), - ), - - Expanded( - child: CustomButton( - textStyle: TextStyle( - fontSize: 16, - color: Colors.white, - ), - text: '暂存', - backgroundColor: Colors.green, - onPressed: () { - _submit('0'); - }, - ), - ), - ], - ) - : Column( - children: [ - SizedBox(height: 20), - Row( - children: [ - SizedBox(width: 50), - Expanded( - child: CustomButton( - height: 45, - textStyle: TextStyle( - fontSize: 16, - color: Colors.white, - ), - text: '返回', - backgroundColor: Colors.green, - onPressed: () { - Navigator.pop(context); - }, - ), - ), - SizedBox(width: 50), - ], - ), - ], - ), + _setSafeDetailWidget(), + SizedBox(height: 20), + _bottomButtons(), ], ), ), @@ -785,13 +648,14 @@ class _HotworkSetSafeDetailState extends State { ); } } + class MeasureItem { final double id; String DEPARTMENT_ID; String DEPARTMENT_NAME; String USER_ID; String USER_NAME; - List userList; + List> userList; int userIndex; List selectMeasures; @@ -801,9 +665,21 @@ class MeasureItem { this.DEPARTMENT_NAME = '', this.USER_ID = '', this.USER_NAME = '', - List? userList, + List>? userList, this.userIndex = -1, List? selectMeasures, - }) : userList = userList ?? [], - selectMeasures = selectMeasures ?? []; -} \ No newline at end of file + }) : userList = userList ?? [], + selectMeasures = selectMeasures ?? []; + Map toJson() { + return { + 'id': id, + 'DEPARTMENT_ID': DEPARTMENT_ID, + 'DEPARTMENT_NAME': DEPARTMENT_NAME, + 'USER_ID': USER_ID, + 'USER_NAME': USER_NAME, + 'userList': userList, + 'userIndex': userIndex, + 'selectMeasures': selectMeasures, + }; + } +} diff --git a/lib/pages/mine/mine_sign_page.dart b/lib/pages/mine/mine_sign_page.dart index e034ed2..8a7f50f 100644 --- a/lib/pages/mine/mine_sign_page.dart +++ b/lib/pages/mine/mine_sign_page.dart @@ -31,7 +31,7 @@ class _SignatureConfirmPageState extends State { bool _hasSignature = false; File? fileN; Uint8List? _postBytes; - late String imagepath=""; + late String imagepath = ""; void _clearSignature() { setState(() { @@ -48,25 +48,22 @@ class _SignatureConfirmPageState extends State { return; } - _saveSignPic(); - - // // 保存签名逻辑 // ScaffoldMessenger.of(context).showSnackBar( // const SnackBar(content: Text('签名已确认')), // ); //模拟保存后返回 - - } // 保存签名 void _saveSignPic() async { - RenderRepaintBoundary boundary = _signatureKey.currentContext!.findRenderObject() as RenderRepaintBoundary; + RenderRepaintBoundary boundary = _signatureKey.currentContext! + .findRenderObject() as RenderRepaintBoundary; var image = await boundary.toImage(pixelRatio: 1); - ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png); + ByteData? byteData = + await image.toByteData(format: ui.ImageByteFormat.png); int timestamp = DateTime.now().millisecondsSinceEpoch; @@ -81,7 +78,7 @@ class _SignatureConfirmPageState extends State { } var file = await File(path).create(recursive: true); - if(byteData != null) { + if (byteData != null) { file.writeAsBytesSync(byteData.buffer.asInt8List(), flush: true); setState(() { @@ -95,13 +92,6 @@ class _SignatureConfirmPageState extends State { } } - - - - - - - @override void initState() { super.initState(); @@ -129,7 +119,6 @@ class _SignatureConfirmPageState extends State { body: SafeArea( child: Column( children: [ - // 标题区域 _buildTitleBar(), // MyAppbar(title: "签字"), @@ -202,36 +191,39 @@ class _SignatureConfirmPageState extends State { }, child: RepaintBoundary( key: _signatureKey, - child: Stack( - children: [ - // if (imagepath.length > 0) - // Image.file( - // File(imagepath), // 显示选择的图片文件 - // fit: BoxFit.contain, // 设置图片填充方式为完整显示,保持宽高比例 - // ), + child: Container( + // 给画布添加白色背景,不透明导出 + color: Colors.white, + child: Stack( + children: [ + // if (imagepath.length > 0) + // Image.file( + // File(imagepath), // 显示选择的图片文件 + // fit: BoxFit.contain, // 设置图片填充方式为完整显示,保持宽高比例 + // ), + // 背景横线 + _buildBackgroundLines(), - // 背景横线 - _buildBackgroundLines(), + // 签名画布 + CustomPaint( + painter: SignaturePainter(points: _points), + size: Size.infinite, + ), - // 签名画布 - CustomPaint( - painter: SignaturePainter(points: _points), - size: Size.infinite, - ), - - // 提示文字 - if (!_hasSignature) - const Center( - child: Text( - '请在此处签名', - style: TextStyle( - fontSize: 16, - color: Colors.grey, + // 提示文字 + if (!_hasSignature) + const Center( + child: Text( + '请在此处签名', + style: TextStyle( + fontSize: 16, + color: Colors.grey, + ), ), ), - ), - ], + ], + ), ), ), ), @@ -259,53 +251,51 @@ class _SignatureConfirmPageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - // 重签按钮 - Expanded( - child: OutlinedButton( - onPressed: _clearSignature, - style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 8), - side: const BorderSide(color: Colors.blue), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: const Text( - '重签', - style: TextStyle( - fontSize: 16, - color: Colors.blue, - fontWeight: FontWeight.w500, - ), - ), - - ), - ), - - const SizedBox(width: 20), - - // 确定按钮 - Expanded( - child: ElevatedButton( - onPressed: _confirmSignature, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - padding: const EdgeInsets.symmetric(vertical: 8), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: const Text( - '确定', - style: TextStyle( - fontSize: 16, - color: Colors.white, - fontWeight: FontWeight.w500, + // 重签按钮 + Expanded( + child: OutlinedButton( + onPressed: _clearSignature, + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 8), + side: const BorderSide(color: Colors.blue), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + '重签', + style: TextStyle( + fontSize: 16, + color: Colors.blue, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + + const SizedBox(width: 20), + + // 确定按钮 + Expanded( + child: ElevatedButton( + onPressed: _confirmSignature, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: const EdgeInsets.symmetric(vertical: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + '确定', + style: TextStyle( + fontSize: 16, + color: Colors.white, + fontWeight: FontWeight.w500, + ), ), ), ), - ), - ], ), ); @@ -333,7 +323,8 @@ class SignaturePainter extends CustomPainter { } @override - bool shouldRepaint(SignaturePainter oldDelegate) => oldDelegate.points != points; + bool shouldRepaint(SignaturePainter oldDelegate) => + oldDelegate.points != points; } // 背景横线绘制器 @@ -341,7 +332,7 @@ class BackgroundLinesPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { Paint paint = Paint() - // ..color = Colors.grey[200]! + // ..color = Colors.grey[200]! ..color = Color.from(alpha: 0, red: 0, green: 0, blue: 0)! ..strokeWidth = 0; @@ -357,9 +348,4 @@ class BackgroundLinesPainter extends CustomPainter { @override bool shouldRepaint(CustomPainter oldDelegate) => false; - - - - - } \ No newline at end of file diff --git a/lib/tools/tools.dart b/lib/tools/tools.dart index 9484bde..17092a2 100644 --- a/lib/tools/tools.dart +++ b/lib/tools/tools.dart @@ -284,20 +284,46 @@ void presentPage(BuildContext context, Widget page) { } class LoadingDialogHelper { - static void show(BuildContext context) { + /// 显示 loading,对话框中可选文字 + static void show(BuildContext context, {String? message}) { showDialog( context: context, barrierDismissible: false, - builder: (_) => const Center(child: CircularProgressIndicator()), + builder: (_) => Center( + child: Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.black87, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + if (message != null) ...[ + const SizedBox(height: 12), + Text( + message, + style: TextStyle(color: Colors.white), + ), + ], + ], + ), + ), + ), ); } + /// 隐藏 loading static void hide(BuildContext context) { if (Navigator.canPop(context)) { Navigator.of(context).pop(); } } } + /// 将秒数转换为 “HH:MM:SS” 格式 String secondsCount(dynamic seconds) { // 先尝试解析出一个 double 值